├── BspZipGUI ├── icon.ico ├── App.config ├── Properties │ ├── Settings.settings │ ├── Settings.Designer.cs │ ├── AssemblyInfo.cs │ ├── Resources.Designer.cs │ └── Resources.resx ├── App.xaml.cs ├── Models │ ├── LogText.cs │ └── Abstract │ │ ├── StringWrapper.cs │ │ └── StringBuilderWrapper.cs ├── Tool │ ├── Execute │ │ ├── Repack.cs │ │ ├── Extract.cs │ │ ├── Cubemaps.cs │ │ ├── Bspzip.cs │ │ ├── Pack.cs │ │ └── FilePack.cs │ ├── Utils │ │ ├── Constants.cs │ │ ├── XmlUtils.cs │ │ ├── FunctionalException.cs │ │ └── FileUtils.cs │ └── Xml │ │ └── ToolSettings.cs ├── settings.xml ├── settings_backup.txt ├── BspZipGUI.csproj ├── App.xaml └── MainWindow.xaml ├── Images ├── LogsEnd.jpg ├── LogsMenu.jpg ├── MainMenu.jpg ├── ExtractMenu.jpg ├── MainMenuNew.jpg ├── RepackLogs.jpg ├── RepackMenu.jpg ├── CubemapsMenu.jpg ├── MainMenuNew2.jpg ├── MainMenuNew3.jpg ├── MultiPackLogs.jpg ├── MultiPackMenu.jpg ├── SettingsMenu.jpg ├── MAX_PATH_Warning.jpg ├── ExampleCustomFolder.jpg ├── SettingsWhitelist.jpg ├── SettingsCustomFolder.jpg └── SettingsMultiCustomFolder.jpg ├── .editorconfig ├── BspZipGUI.sln ├── README.md └── .gitignore /BspZipGUI/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Moltard/BspZipGUI/HEAD/BspZipGUI/icon.ico -------------------------------------------------------------------------------- /Images/LogsEnd.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Moltard/BspZipGUI/HEAD/Images/LogsEnd.jpg -------------------------------------------------------------------------------- /Images/LogsMenu.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Moltard/BspZipGUI/HEAD/Images/LogsMenu.jpg -------------------------------------------------------------------------------- /Images/MainMenu.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Moltard/BspZipGUI/HEAD/Images/MainMenu.jpg -------------------------------------------------------------------------------- /Images/ExtractMenu.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Moltard/BspZipGUI/HEAD/Images/ExtractMenu.jpg -------------------------------------------------------------------------------- /Images/MainMenuNew.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Moltard/BspZipGUI/HEAD/Images/MainMenuNew.jpg -------------------------------------------------------------------------------- /Images/RepackLogs.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Moltard/BspZipGUI/HEAD/Images/RepackLogs.jpg -------------------------------------------------------------------------------- /Images/RepackMenu.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Moltard/BspZipGUI/HEAD/Images/RepackMenu.jpg -------------------------------------------------------------------------------- /Images/CubemapsMenu.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Moltard/BspZipGUI/HEAD/Images/CubemapsMenu.jpg -------------------------------------------------------------------------------- /Images/MainMenuNew2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Moltard/BspZipGUI/HEAD/Images/MainMenuNew2.jpg -------------------------------------------------------------------------------- /Images/MainMenuNew3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Moltard/BspZipGUI/HEAD/Images/MainMenuNew3.jpg -------------------------------------------------------------------------------- /Images/MultiPackLogs.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Moltard/BspZipGUI/HEAD/Images/MultiPackLogs.jpg -------------------------------------------------------------------------------- /Images/MultiPackMenu.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Moltard/BspZipGUI/HEAD/Images/MultiPackMenu.jpg -------------------------------------------------------------------------------- /Images/SettingsMenu.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Moltard/BspZipGUI/HEAD/Images/SettingsMenu.jpg -------------------------------------------------------------------------------- /Images/MAX_PATH_Warning.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Moltard/BspZipGUI/HEAD/Images/MAX_PATH_Warning.jpg -------------------------------------------------------------------------------- /Images/ExampleCustomFolder.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Moltard/BspZipGUI/HEAD/Images/ExampleCustomFolder.jpg -------------------------------------------------------------------------------- /Images/SettingsWhitelist.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Moltard/BspZipGUI/HEAD/Images/SettingsWhitelist.jpg -------------------------------------------------------------------------------- /Images/SettingsCustomFolder.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Moltard/BspZipGUI/HEAD/Images/SettingsCustomFolder.jpg -------------------------------------------------------------------------------- /Images/SettingsMultiCustomFolder.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Moltard/BspZipGUI/HEAD/Images/SettingsMultiCustomFolder.jpg -------------------------------------------------------------------------------- /BspZipGUI/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /BspZipGUI/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /BspZipGUI/App.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Configuration; 4 | using System.Data; 5 | using System.Linq; 6 | using System.Threading.Tasks; 7 | using System.Windows; 8 | 9 | namespace BspZipGUI 10 | { 11 | /// 12 | /// Logique d'interaction pour App.xaml 13 | /// 14 | public partial class App : Application 15 | { 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | [*.cs] 2 | 3 | # IDE0058: La valeur d'expression n'est jamais utilisée 4 | dotnet_diagnostic.IDE0058.severity = none 5 | 6 | # IDE0046: Convertir en expression conditionnelle 7 | dotnet_diagnostic.IDE0046.severity = none 8 | 9 | # IDE0045: Convertir en expression conditionnelle 10 | dotnet_style_prefer_conditional_expression_over_assignment = false 11 | 12 | # IDE0010: Ajouter les instructions case manquantes 13 | dotnet_diagnostic.IDE0010.severity = none 14 | -------------------------------------------------------------------------------- /BspZipGUI/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace BspZipGUI.Properties 12 | { 13 | 14 | 15 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 16 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] 17 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase 18 | { 19 | 20 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 21 | 22 | public static Settings Default 23 | { 24 | get 25 | { 26 | return defaultInstance; 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /BspZipGUI/Models/LogText.cs: -------------------------------------------------------------------------------- 1 | using BspZipGUI.Models.Abstract; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.ComponentModel; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace BspZipGUI.Models 10 | { 11 | 12 | /// 13 | /// Store the logs and update the GUI automatically 14 | /// 15 | public class LogText : StringWrapper 16 | { 17 | 18 | #region Attributes 19 | 20 | public string Logs 21 | { 22 | get => text; 23 | set 24 | { 25 | if (text != value) 26 | { 27 | text = value; 28 | NotifyPropertyChanged(); 29 | } 30 | } 31 | } 32 | 33 | #endregion 34 | 35 | #region Constructor 36 | 37 | public LogText() : base() { } 38 | 39 | #endregion 40 | 41 | #region INotifyPropertyChanged 42 | 43 | public override event PropertyChangedEventHandler PropertyChanged; 44 | 45 | public override void NotifyPropertyChanged() 46 | { 47 | PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Logs")); 48 | } 49 | 50 | #endregion 51 | 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /BspZipGUI.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.31613.86 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BspZipGUI", "BspZipGUI\BspZipGUI.csproj", "{0C5AD6ED-D712-4784-A6F0-259E7EAF38A3}" 7 | EndProject 8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{5B835E84-E09F-4DEA-A8BB-38F4CE6B0F1C}" 9 | ProjectSection(SolutionItems) = preProject 10 | .editorconfig = .editorconfig 11 | EndProjectSection 12 | EndProject 13 | Global 14 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 15 | Debug|Any CPU = Debug|Any CPU 16 | Release|Any CPU = Release|Any CPU 17 | EndGlobalSection 18 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 19 | {0C5AD6ED-D712-4784-A6F0-259E7EAF38A3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 20 | {0C5AD6ED-D712-4784-A6F0-259E7EAF38A3}.Debug|Any CPU.Build.0 = Debug|Any CPU 21 | {0C5AD6ED-D712-4784-A6F0-259E7EAF38A3}.Release|Any CPU.ActiveCfg = Release|Any CPU 22 | {0C5AD6ED-D712-4784-A6F0-259E7EAF38A3}.Release|Any CPU.Build.0 = Release|Any CPU 23 | EndGlobalSection 24 | GlobalSection(SolutionProperties) = preSolution 25 | HideSolutionNode = FALSE 26 | EndGlobalSection 27 | GlobalSection(ExtensibilityGlobals) = postSolution 28 | SolutionGuid = {55D6D993-9364-40B3-AD50-ACACE78FE95C} 29 | EndGlobalSection 30 | EndGlobal 31 | -------------------------------------------------------------------------------- /BspZipGUI/Models/Abstract/StringWrapper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace BspZipGUI.Models.Abstract 9 | { 10 | 11 | /// 12 | /// Wrapper for String that update the GUI 13 | /// 14 | public abstract class StringWrapper : INotifyPropertyChanged 15 | { 16 | #region Attributes 17 | 18 | protected string text; 19 | 20 | #endregion 21 | 22 | #region Constructor 23 | 24 | public StringWrapper() 25 | { 26 | Clear(); 27 | } 28 | 29 | #endregion 30 | 31 | #region Methods 32 | 33 | public void Append(string str) 34 | { 35 | text += str; 36 | NotifyPropertyChanged(); 37 | } 38 | public void AppendLine() 39 | { 40 | text += "\n"; 41 | NotifyPropertyChanged(); 42 | } 43 | public void AppendLine(string str) 44 | { 45 | text += str + "\n"; 46 | NotifyPropertyChanged(); 47 | } 48 | public void Clear() 49 | { 50 | text = string.Empty; 51 | NotifyPropertyChanged(); 52 | } 53 | 54 | #endregion 55 | 56 | #region INotifyPropertyChanged 57 | 58 | public abstract event PropertyChangedEventHandler PropertyChanged; 59 | 60 | public abstract void NotifyPropertyChanged(); 61 | 62 | #endregion 63 | 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /BspZipGUI/Models/Abstract/StringBuilderWrapper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace BspZipGUI.Models.Abstract 9 | { 10 | /// 11 | /// Wrapper for StringBuilder that update the GUI 12 | /// 13 | public abstract class StringBuilderWrapper : INotifyPropertyChanged 14 | { 15 | #region Attributes 16 | 17 | private readonly StringBuilder _sb; 18 | 19 | /* public string Text 20 | { 21 | get => _sb.ToString(); 22 | set => AppendLine(value); 23 | }*/ 24 | 25 | #endregion 26 | 27 | #region Constructor 28 | 29 | public StringBuilderWrapper() 30 | { 31 | _sb = new StringBuilder(); 32 | } 33 | 34 | public StringBuilderWrapper(StringBuilder sb) 35 | { 36 | _sb = sb; 37 | } 38 | 39 | #endregion 40 | 41 | #region Methods 42 | 43 | public void Append(string str) 44 | { 45 | _sb.Append(str); 46 | NotifyPropertyChanged(); 47 | } 48 | public void AppendLine(string str) 49 | { 50 | _sb.AppendLine(str); 51 | NotifyPropertyChanged(); 52 | } 53 | public void Clear() 54 | { 55 | _sb.Clear(); 56 | NotifyPropertyChanged(); 57 | } 58 | 59 | #endregion 60 | 61 | #region INotifyPropertyChanged 62 | 63 | public abstract event PropertyChangedEventHandler PropertyChanged; 64 | 65 | public abstract void NotifyPropertyChanged(); 66 | 67 | #endregion INotifyPropertyChanged 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /BspZipGUI/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Resources; 3 | using System.Runtime.CompilerServices; 4 | using System.Runtime.InteropServices; 5 | using System.Windows; 6 | 7 | // Les informations générales relatives à un assembly dépendent de 8 | // l'ensemble d'attributs suivant. Changez les valeurs de ces attributs pour modifier les informations 9 | // associées à un assembly. 10 | [assembly: AssemblyTitle("BspZipGUI")] 11 | [assembly: AssemblyDescription("")] 12 | [assembly: AssemblyConfiguration("")] 13 | [assembly: AssemblyCompany("")] 14 | [assembly: AssemblyProduct("BspZipGUI")] 15 | [assembly: AssemblyCopyright("Copyright © 2020")] 16 | [assembly: AssemblyTrademark("")] 17 | [assembly: AssemblyCulture("")] 18 | 19 | // L'affectation de la valeur false à ComVisible rend les types invisibles dans cet assembly 20 | // aux composants COM. Si vous devez accéder à un type dans cet assembly à partir de 21 | // COM, affectez la valeur true à l'attribut ComVisible sur ce type. 22 | [assembly: ComVisible(false)] 23 | 24 | //Pour commencer à générer des applications localisables, définissez 25 | //CultureUtiliséePourCoder dans votre fichier .csproj 26 | //dans . Par exemple, si vous utilisez le français 27 | //dans vos fichiers sources, définissez à fr-FR. Puis, supprimez les marques de commentaire de 28 | //l'attribut NeutralResourceLanguage ci-dessous. Mettez à jour "fr-FR" dans 29 | //la ligne ci-après pour qu'elle corresponde au paramètre UICulture du fichier projet. 30 | 31 | //[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)] 32 | 33 | 34 | [assembly: ThemeInfo( 35 | ResourceDictionaryLocation.None, //où se trouvent les dictionnaires de ressources spécifiques à un thème 36 | //(utilisé si une ressource est introuvable dans la page, 37 | // ou dictionnaires de ressources de l'application) 38 | ResourceDictionaryLocation.SourceAssembly //où se trouve le dictionnaire de ressources générique 39 | //(utilisé si une ressource est introuvable dans la page, 40 | // dans l'application ou dans l'un des dictionnaires de ressources spécifiques à un thème) 41 | )] 42 | 43 | 44 | // Les informations de version pour un assembly se composent des quatre valeurs suivantes : 45 | // 46 | // Version principale 47 | // Version secondaire 48 | // Numéro de build 49 | // Révision 50 | // 51 | // Vous pouvez spécifier toutes les valeurs ou indiquer les numéros de build et de révision par défaut 52 | // en utilisant '*', comme indiqué ci-dessous : 53 | // [assembly: AssemblyVersion("1.0.*")] 54 | [assembly: AssemblyVersion("1.0.0.0")] 55 | [assembly: AssemblyFileVersion("1.0.0.0")] 56 | -------------------------------------------------------------------------------- /BspZipGUI/Tool/Execute/Repack.cs: -------------------------------------------------------------------------------- 1 | using BspZipGUI.Models; 2 | using BspZipGUI.Tool.Utils; 3 | using BspZipGUI.Tool.Xml; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Diagnostics; 7 | using System.Linq; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | 11 | namespace BspZipGUI.Tool.Execute 12 | { 13 | internal class Repack : Bspzip 14 | { 15 | 16 | #region Attributes 17 | 18 | /// 19 | /// If the goal is to Compress or Uncompress the BSP 20 | /// 21 | private readonly bool isCompress; 22 | 23 | #endregion 24 | 25 | #region Constructor 26 | 27 | public Repack(ToolSettings toolSettings, GameConfig game, string bspPath, LogText logsOutput, bool isCompress) : 28 | base(toolSettings, game, bspPath, logsOutput) 29 | { 30 | this.isCompress = isCompress; 31 | } 32 | 33 | #endregion 34 | 35 | #region Methods 36 | 37 | /// 38 | /// Start the repacking for the current options 39 | /// 40 | /// Error when creating the bsp backup 41 | /// Error during bspzip execution 42 | public override void Start() 43 | { 44 | UpdateSettings(); 45 | logsOutput.AppendLine(); 46 | try 47 | { 48 | CreateBackupBsp(); 49 | } 50 | catch (Exception ex) 51 | { 52 | throw new BspBackupCreationException(MessageConstants.MessageCopyBspFail, ex); 53 | } 54 | try 55 | { 56 | StartProcess(); 57 | } 58 | catch (Exception ex) 59 | { 60 | throw new BspZipExecutionException(MessageConstants.MessageBspzipFail, ex); 61 | } 62 | } 63 | 64 | /// 65 | /// Return the arguments to launch bspzip.exe, to repack or un-repack a BSP 66 | /// 67 | protected override string GetProcessArguments() 68 | { 69 | // bspzip -repack [ -compress ] "" 70 | StringBuilder sb = new StringBuilder("-repack "); 71 | if (isCompress) 72 | { 73 | sb.Append("-compress "); 74 | } 75 | sb.Append($"\"{bspPath}\""); 76 | return sb.ToString(); 77 | } 78 | 79 | /// 80 | /// 81 | /// 82 | protected override void UpdateSettings() 83 | { 84 | toolSettings.LastBsp = bspPath; 85 | toolSettings.LastGame = game.Name; 86 | SaveSettings(); 87 | } 88 | 89 | #endregion 90 | 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /BspZipGUI/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // Ce code a été généré par un outil. 4 | // Version du runtime :4.0.30319.42000 5 | // 6 | // Les modifications apportées à ce fichier peuvent provoquer un comportement incorrect et seront perdues si 7 | // le code est régénéré. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace BspZipGUI.Properties 12 | { 13 | 14 | 15 | /// 16 | /// Une classe de ressource fortement typée destinée, entre autres, à la consultation des chaînes localisées. 17 | /// 18 | // Cette classe a été générée automatiquement par la classe StronglyTypedResourceBuilder 19 | // à l'aide d'un outil, tel que ResGen ou Visual Studio. 20 | // Pour ajouter ou supprimer un membre, modifiez votre fichier .ResX, puis réexécutez ResGen 21 | // avec l'option /str ou régénérez votre projet VS. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources 26 | { 27 | 28 | private static global::System.Resources.ResourceManager resourceMan; 29 | 30 | private static global::System.Globalization.CultureInfo resourceCulture; 31 | 32 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 33 | internal Resources() 34 | { 35 | } 36 | 37 | /// 38 | /// Retourne l'instance ResourceManager mise en cache utilisée par cette classe. 39 | /// 40 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 41 | internal static global::System.Resources.ResourceManager ResourceManager 42 | { 43 | get 44 | { 45 | if ((resourceMan == null)) 46 | { 47 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("BspZipGUI.Properties.Resources", typeof(Resources).Assembly); 48 | resourceMan = temp; 49 | } 50 | return resourceMan; 51 | } 52 | } 53 | 54 | /// 55 | /// Remplace la propriété CurrentUICulture du thread actuel pour toutes 56 | /// les recherches de ressources à l'aide de cette classe de ressource fortement typée. 57 | /// 58 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 59 | internal static global::System.Globalization.CultureInfo Culture 60 | { 61 | get 62 | { 63 | return resourceCulture; 64 | } 65 | set 66 | { 67 | resourceCulture = value; 68 | } 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /BspZipGUI/Tool/Execute/Extract.cs: -------------------------------------------------------------------------------- 1 | using BspZipGUI.Models; 2 | using BspZipGUI.Tool.Utils; 3 | using BspZipGUI.Tool.Xml; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Diagnostics; 7 | using System.Linq; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | 11 | namespace BspZipGUI.Tool.Execute 12 | { 13 | internal class Extract : Bspzip 14 | { 15 | 16 | #region Attributes 17 | 18 | /// 19 | /// True if extract to a zip file, false if extract to a folder 20 | /// 21 | private readonly bool isExtractToZip; 22 | 23 | /// 24 | /// Name of the zip file or folder path 25 | /// 26 | private readonly string extractPath; 27 | 28 | #endregion 29 | 30 | #region Constructor 31 | 32 | public Extract(ToolSettings toolSettings, GameConfig game, string bspPath, LogText logsOutput, bool isExtractToZip, string extractPath) : 33 | base(toolSettings, game, bspPath, logsOutput) 34 | { 35 | this.isExtractToZip = isExtractToZip; 36 | this.extractPath = extractPath; 37 | } 38 | 39 | #endregion 40 | 41 | #region Methods 42 | 43 | /// 44 | /// Extract the map content into a zip file or folder 45 | /// 46 | /// Error during bspzip execution 47 | public override void Start() 48 | { 49 | UpdateSettings(); 50 | try 51 | { 52 | StartProcess(); 53 | } 54 | catch (Exception ex) 55 | { 56 | throw new BspZipExecutionException(MessageConstants.MessageBspzipFail, ex); 57 | } 58 | } 59 | 60 | /// 61 | /// Return the arguments to launch bspzip.exe, to extract or delete cubemaps from a BSP 62 | /// 63 | /// 64 | protected override string GetProcessArguments() 65 | { 66 | // bspzip -extract "" "" 67 | // bspzip -extractfiles "" "" 68 | StringBuilder sb; 69 | if (isExtractToZip) 70 | { 71 | sb = new StringBuilder("-extract ") 72 | .Append($"\"{bspPath}\" ") 73 | .Append($"\"{extractPath}\""); 74 | } 75 | else 76 | { 77 | sb = new StringBuilder("-extractfiles ") 78 | .Append($"\"{bspPath}\" ") 79 | .Append($"\"{extractPath}\""); 80 | } 81 | return sb.ToString(); 82 | } 83 | 84 | /// 85 | /// 86 | /// 87 | protected override void UpdateSettings() 88 | { 89 | toolSettings.LastBsp = bspPath; 90 | toolSettings.LastGame = game.Name; 91 | if (!isExtractToZip) 92 | { 93 | toolSettings.LastExtractDirectory = extractPath; 94 | } 95 | SaveSettings(); 96 | } 97 | 98 | 99 | #endregion 100 | 101 | } 102 | 103 | 104 | 105 | 106 | } 107 | -------------------------------------------------------------------------------- /BspZipGUI/Tool/Execute/Cubemaps.cs: -------------------------------------------------------------------------------- 1 | using BspZipGUI.Models; 2 | using BspZipGUI.Tool.Utils; 3 | using BspZipGUI.Tool.Xml; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Diagnostics; 7 | using System.Linq; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | 11 | namespace BspZipGUI.Tool.Execute 12 | { 13 | internal class Cubemaps : Bspzip 14 | { 15 | 16 | #region Attributes 17 | 18 | /// 19 | /// True if extract cubemaps, false if delete cubemaps 20 | /// 21 | private readonly bool isExtractCubemap; 22 | 23 | /// 24 | /// Path for the cubemap extraction 25 | /// 26 | private readonly string extractPath; 27 | 28 | #endregion 29 | 30 | #region Constructor 31 | 32 | public Cubemaps(ToolSettings toolSettings, GameConfig game, string bspPath, LogText logsOutput, bool isExtractCubemap, string extractPath) : 33 | base(toolSettings, game, bspPath, logsOutput) 34 | { 35 | this.isExtractCubemap = isExtractCubemap; 36 | this.extractPath = extractPath; 37 | } 38 | 39 | #endregion 40 | 41 | #region Methods 42 | 43 | /// 44 | /// Extract or Delete cubemaps 45 | /// 46 | /// Error during bspzip execution 47 | public override void Start() 48 | { 49 | UpdateSettings(); 50 | if (!isExtractCubemap) 51 | { 52 | // If delete cubemaps, we make a backup of the bsp 53 | try 54 | { 55 | CreateBackupBsp(); 56 | } 57 | catch (Exception ex) 58 | { 59 | logsOutput.AppendLine(); 60 | throw new BspBackupCreationException(MessageConstants.MessageCopyBspFail, ex); 61 | } 62 | } 63 | try 64 | { 65 | StartProcess(); 66 | } 67 | catch (Exception ex) 68 | { 69 | logsOutput.AppendLine(); 70 | throw new BspZipExecutionException(MessageConstants.MessageBspzipFail, ex); 71 | } 72 | } 73 | 74 | /// 75 | /// Return the arguments to launch bspzip.exe, to extract or delete cubemaps from a BSP 76 | /// 77 | /// 78 | protected override string GetProcessArguments() 79 | { 80 | // bspzip -extractcubemaps "" "" 81 | // bspzip -deletecubemaps "" 82 | StringBuilder sb; 83 | if (isExtractCubemap) 84 | { 85 | sb = new StringBuilder("-extractcubemaps ") 86 | .Append($"\"{bspPath}\" ") 87 | .Append($"\"{extractPath}\""); 88 | } 89 | else 90 | { 91 | sb = new StringBuilder("-deletecubemaps ") 92 | .Append($"\"{bspPath}\""); 93 | } 94 | return sb.ToString(); 95 | } 96 | 97 | /// 98 | /// 99 | /// 100 | protected override void UpdateSettings() 101 | { 102 | toolSettings.LastBsp = bspPath; 103 | toolSettings.LastGame = game.Name; 104 | if (isExtractCubemap) 105 | { 106 | toolSettings.LastExtractDirectory = extractPath; 107 | } 108 | SaveSettings(); 109 | } 110 | 111 | #endregion 112 | 113 | } 114 | 115 | 116 | 117 | 118 | } 119 | -------------------------------------------------------------------------------- /BspZipGUI/settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Counter-Strike: Global Offensive 5 | My Current Project 6 | 7 | False 8 | 9 | 10 | Counter-Strike: Source 11 | C:\Program Files (x86)\Steam\steamapps\common\Counter-Strike Source\bin\bspzip.exe 12 | C:\Program Files (x86)\Steam\steamapps\common\Counter-Strike Source\cstrike\gameinfo.txt 13 | 14 | 15 | Counter-Strike: Global Offensive 16 | C:\Program Files (x86)\Steam\steamapps\common\Counter-Strike Global Offensive\bin\bspzip.exe 17 | C:\Program Files (x86)\Steam\steamapps\common\Counter-Strike Global Offensive\csgo\gameinfo.txt 18 | 19 | 20 | Half-Life 2 21 | C:\Program Files (x86)\Steam\steamapps\common\Half-Life 2\bin\bspzip.exe 22 | C:\Program Files (x86)\Steam\steamapps\common\Half-Life 2\hl2\gameinfo.txt 23 | 24 | 25 | 26 | 27 | My Current Project 28 | C:\MyMappingProject\CurrentProject 29 | 30 | 31 | 32 | 33 | cfg 34 | 35 | .cfg 36 | .txt 37 | 38 | 39 | 40 | maps 41 | 42 | .txt 43 | .kv 44 | 45 | 46 | 47 | materials 48 | 49 | .vmt 50 | .vtf 51 | 52 | 53 | 54 | models 55 | 56 | .mdl 57 | .phy 58 | .vtx 59 | .vvd 60 | .ani 61 | 62 | 63 | 64 | particles 65 | 66 | .pcf 67 | .txt 68 | 69 | 70 | 71 | resource 72 | 73 | .dds 74 | .txt 75 | 76 | 77 | 78 | scripts 79 | 80 | .lua 81 | .nut 82 | .txt 83 | 84 | 85 | 86 | sound 87 | 88 | .mp3 89 | .txt 90 | .wav 91 | 92 | 93 | 94 | materials/correction 95 | 96 | .raw 97 | 98 | 99 | 100 | materials/panorama/images/map_icons/screenshots 101 | 102 | .png 103 | 104 | 105 | 106 | 107 | 108 | My Current Projects 109 | 110 | C:\MyMappingProject\CurrentProject 111 | 112 | 113 | 114 | 115 | -------------------------------------------------------------------------------- /BspZipGUI/settings_backup.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Counter-Strike: Global Offensive 5 | My Current Project 6 | 7 | False 8 | 9 | 10 | Counter-Strike: Source 11 | C:\Program Files (x86)\Steam\steamapps\common\Counter-Strike Source\bin\bspzip.exe 12 | C:\Program Files (x86)\Steam\steamapps\common\Counter-Strike Source\cstrike\gameinfo.txt 13 | 14 | 15 | Counter-Strike: Global Offensive 16 | C:\Program Files (x86)\Steam\steamapps\common\Counter-Strike Global Offensive\bin\bspzip.exe 17 | C:\Program Files (x86)\Steam\steamapps\common\Counter-Strike Global Offensive\csgo\gameinfo.txt 18 | 19 | 20 | Half-Life 2 21 | C:\Program Files (x86)\Steam\steamapps\common\Half-Life 2\bin\bspzip.exe 22 | C:\Program Files (x86)\Steam\steamapps\common\Half-Life 2\hl2\gameinfo.txt 23 | 24 | 25 | 26 | 27 | My Current Project 28 | C:\MyMappingProject\CurrentProject 29 | 30 | 31 | 32 | 33 | cfg 34 | 35 | .cfg 36 | .txt 37 | 38 | 39 | 40 | maps 41 | 42 | .txt 43 | .kv 44 | 45 | 46 | 47 | materials 48 | 49 | .vmt 50 | .vtf 51 | 52 | 53 | 54 | models 55 | 56 | .mdl 57 | .phy 58 | .vtx 59 | .vvd 60 | .ani 61 | 62 | 63 | 64 | particles 65 | 66 | .pcf 67 | .txt 68 | 69 | 70 | 71 | resource 72 | 73 | .dds 74 | .txt 75 | 76 | 77 | 78 | scripts 79 | 80 | .lua 81 | .nut 82 | .txt 83 | 84 | 85 | 86 | sound 87 | 88 | .mp3 89 | .txt 90 | .wav 91 | 92 | 93 | 94 | materials/correction 95 | 96 | .raw 97 | 98 | 99 | 100 | materials/panorama/images/map_icons/screenshots 101 | 102 | .png 103 | 104 | 105 | 106 | 107 | 108 | My Current Projects 109 | 110 | C:\MyMappingProject\CurrentProject 111 | 112 | 113 | 114 | 115 | -------------------------------------------------------------------------------- /BspZipGUI/Tool/Utils/Constants.cs: -------------------------------------------------------------------------------- 1 | namespace BspZipGUI.Tool.Utils 2 | { 3 | /// 4 | /// Class containing constants used in the app 5 | /// 6 | internal static class Constants 7 | { 8 | 9 | #region Constants 10 | 11 | /// 12 | /// The max path length correctly supported by Windows.
13 | /// Trying to pack a file that use a path longer than this will break the bspzip.exe process itself.
14 | /// It won't pack any file after encountering the limit.
15 | /// This will only be used to show a warning to the user after the execution. 16 | ///
17 | public const int MAX_PATH = 260; 18 | 19 | public const string ExtensionBsp = ".bsp"; 20 | public const string ExtensionExe = ".exe"; 21 | public const string ExtensionTxt = ".txt"; 22 | public const string ExtensionZip = ".zip"; 23 | 24 | public const string BspZipFile = "bspzip.exe"; 25 | public const string GameinfoFile = "gameinfo.txt"; 26 | 27 | /// 28 | /// The name of the text file listing all files to pack 29 | /// 30 | public const string FilesListText = "filesList.txt"; 31 | 32 | public const char Slash = '/'; 33 | public const char Backslash = '\\'; 34 | 35 | #endregion 36 | 37 | } 38 | 39 | /// 40 | /// Class containing constants message used in the GUI 41 | /// 42 | internal static class MessageConstants 43 | { 44 | 45 | #region Constants 46 | 47 | //public const string MessageToolNotWork = "The tool will not work correctly"; 48 | 49 | public const string MessageSettingsSaveSuccess = "Saved settings to settings.xml"; 50 | public const string MessageSettingsSaveError = "Error trying to save settings"; 51 | public const string MessageSettingsCreateError = "Error creating settings.xml"; 52 | public const string MessageSettingsReadError = "Error while reading the settings"; 53 | public const string MessageSettingsUseDefault = "Trying to use the default settings instead"; 54 | public const string MessageSettingsDefaultReadError = "Error while reading the default settings"; 55 | public const string MessageSettingsUseEmpty = "Generating empty settings"; 56 | 57 | public const string MessageFileNotBsp = "The file is not .bsp"; 58 | public const string MessageBspFileNotFound = "The .bsp file doesn't exist"; 59 | public const string MessageDirectoryNotFound = "The directory doesn't exist"; 60 | public const string MessageCustomFolderNotFound = "The Custom Folder doesn't exist"; 61 | public const string MessageMultiCustomFolderEmpty = "The list of Custom Folders is empty"; 62 | public const string MessageMultiCustomFolderNotFound = "One or multiple Custom Folder(s) don't exist"; 63 | public const string MessageSimpleMultiCustomFolderNotSelected = "Invalid state: No Custom Folder(s) were selected"; 64 | public const string MessageMultiCustomFolderSettingsNotSelected = "No Custom Folders selected"; 65 | public const string MessageCustomFolderInvalid = "Invalid Custom Folder selected"; 66 | public const string MessageGameNotFound = "Can't find the specified bspzip.exe and/or gameinfo.txt"; 67 | public const string MessageGameInvalid = "Invalid Game selected"; 68 | 69 | public const string MessageBspPacking = "Bsp packing in progress..."; 70 | public const string MessageBspRepackCompress = "Bsp compressing in progress..."; 71 | public const string MessageBspRepackDecompress = "Bsp decompressing in progress..."; 72 | public const string MessageBspExtractFile = "Bsp extraction in progress..."; 73 | public const string MessageBspExtractCubemaps = "Cubemaps extraction in progress..."; 74 | public const string MessageBspDeleteCubemaps = "Cubemaps deletion in progress..."; 75 | 76 | public const string MessageSeeLogs = "\nSee logs for more details"; 77 | 78 | public const string MessageBspzipSuccess = "Success"; 79 | public const string MessageBspzipFail = "Error: bspzip.exe process ended unexpectedly"; 80 | public const string MessageCopyBspFail = "Error: Couldn't make a copy of the BSP"; 81 | public const string MessageListFilesNotFound = "Error: Couldn't find the list of files to pack"; 82 | public const string MessageListFilesFail = "Error: Couldn't create the list of files to pack"; 83 | public const string MessageMaxPathSizeWarning = "One or more file(s) path(s) are longer than 260 characters (MAX_PATH)"; 84 | public const string MessageMaxPathSizeSuggestion = "Suggestion: Move your custom directory to a shorter path"; 85 | public const string MessageBspzipPackingWarning = "Warning: bspzip.exe may not have packed all the files correctly"; 86 | 87 | public const string MessageWhitelistWarning = "You unchecked \"Use Directory Whitelist\", it will pack every single files from the directory " + 88 | "and its subdirectories.\nAre you really sure ?\n(Be careful not to use a path like C:\\)"; 89 | 90 | #endregion 91 | 92 | } 93 | 94 | } 95 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BspZipGUI 2 | A clean GUI tool that use Valve's BSPZIP command line utility. It is used to embed a folder with custom files (materials, models, sound,..) into a BSP file. 3 | 4 | 5 | 6 | You can also repack a map to compress (or decompress it). 7 | 8 | It can be used for any Source Engine game that provide a bspzip.exe. 9 | 10 | This tool idea is based on [Geotavros's BspZipGui](https://github.com/geotavros/BspZipGui) . 11 | 12 | ## Download 13 | 14 | [BspZipGUI v3.0](https://github.com/Moltard/BspZipGUI/releases/latest) 15 | 16 | ## How to use - Bsp Packer 17 | 18 | Select a game you defined (3 are setup per default), a custom folder you defined and load a BSP. 19 | 20 | Check 'Use Directory Whitelist' (recommended) to only pack files from whitelisted directories (see Settings). 21 | 22 | 23 | 24 | Press Pack Bsp. 25 | 26 | 27 | 28 | 29 | ## How to use - Bsp Multi Packer 30 | 31 | Select a game you defined (3 are setup per default), a 'multi custom' folder you defined and load a BSP. 32 | 33 | **A multi custom folder contains one or more custom folder. The content of all those folders will be packed into the map** 34 | 35 | Check 'Use Directory Whitelist' (recommended) to only pack files from whitelisted directories (see Settings). 36 | 37 | 38 | 39 | Press Pack Bsp. 40 | 41 | 42 | 43 | 44 | ## How to use - Bsp Repack 45 | 46 | Select a game you defined (3 are setup per default) and load a BSP. 47 | 48 | 49 | 50 | Press Compress Bsp or Decompress Bsp. 51 | 52 | 53 | 54 | ## How to use - Bsp Extract 55 | 56 | Select a game you defined (3 are setup per default) and load a BSP. 57 | 58 | 59 | 60 | Either select a directory (drag drop possible) and click Extract to Directory, or click Extract to Zip. 61 | 62 | All packed files will be extracted to the directory/zip file. 63 | 64 | 65 | ## How to use - Bsp Cubemaps 66 | 67 | Select a game you defined (3 are setup per default) and load a BSP. 68 | 69 | 70 | 71 | Either select a directory (drag drop possible) and click Extract cubemaps, or click Delete cubemaps. 72 | 73 | /!\\ Deleting cubemaps actually delete every VTF files packed. That is how bspzip work. 74 | 75 | 76 | 77 | ## How to setup 78 | 79 | ### Games 80 | 81 | Add new 'games' configs with the 'Add...' button. Delete them with the 'Delete' button. 82 | 83 | Setup your games by loading the gameinfo.txt and bspzip.exe. 84 | 85 | 86 | 87 | --- 88 | 89 | ### Custom Folders 90 | 91 | Add new 'custom folders' configs with the 'Add...' button. Delete them with the 'Delete' button. 92 | 93 | Load the folder with the custom files that you want to embed in your map. 94 | 95 | 96 | 97 | --- 98 | 99 | ### Multi Custom Folders 100 | 101 | Add new 'multi custom folders' configs with the 'Add...' button. Delete them with the 'Delete' button. 102 | 103 | Add the folders with the custom files that you want to embed in your map, with the "Add Directory" button. 104 | 105 | Remove a folder from the list with Remove Selected. 106 | 107 | Drag and dropping is supported. 108 | 109 | 110 | 111 | --- 112 | 113 | ### Directories Whitelist 114 | 115 | Define a whitelist of subfolders and type of files (through their extensions) that can be packed. 116 | 117 | 118 | 119 | (All the subfolders you would need are already setup, but you can edit to your needs) 120 | 121 | ### Extra setting 122 | 123 | To not bloat the UI, one setting can only be changed by modifying **settings.xml**, which is the Asynchronous / Synchronous log output. When bspzip.exe is getting executed, you can either get the output of the process as it is getting executed (Async) or get all of it at once after it has finished (Sync). 124 | 125 | It doesn't really affect the execution, but you can change it if you want by editing the following line: 126 | 127 | \False\ 128 | 129 | - **False** means that logs are Asynchronously displayed, which is the default behavior 130 | - **True** means that logs are Synchronously displayed 131 | - Not having the line (if you had old settings), will use Asynchronous mode and next time settings are saved, the value will be at **False**. 132 | 133 | 134 | ### MAX_PATH size limit 135 | 136 | Windows has a hardcoded limitation for file paths, which affect bspzip.exe. 137 | 138 | - [Microsoft Doc](https://learn.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation) 139 | 140 | If a file path is 260 or more characters, and is packed by bspzip.exe, it will cause issues with packing the rest of the files. 141 | Because it's not something fixable with this software, I instead added a warning if such case happens. 142 | 143 | 144 | 145 | The solution is simple and just requires you to have your custom folder in a shorter path. 146 | 147 | 148 | ### Developers 149 | 150 | - [Moltard](https://github.com/Moltard) 151 | 152 | 153 | 154 | -------------------------------------------------------------------------------- /BspZipGUI/BspZipGUI.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {0C5AD6ED-D712-4784-A6F0-259E7EAF38A3} 8 | WinExe 9 | BspZipGUI 10 | BspZipGUI 11 | v4.6.1 12 | 512 13 | {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 14 | 4 15 | true 16 | true 17 | 18 | 19 | AnyCPU 20 | true 21 | full 22 | false 23 | bin\Debug\ 24 | DEBUG;TRACE 25 | prompt 26 | 4 27 | 28 | 29 | AnyCPU 30 | pdbonly 31 | true 32 | bin\Release\ 33 | TRACE 34 | prompt 35 | 4 36 | 37 | 38 | icon.ico 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 4.0 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | MSBuild:Compile 59 | Designer 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | MSBuild:Compile 77 | Designer 78 | 79 | 80 | App.xaml 81 | Code 82 | 83 | 84 | MainWindow.xaml 85 | Code 86 | 87 | 88 | 89 | 90 | Code 91 | 92 | 93 | True 94 | True 95 | Resources.resx 96 | 97 | 98 | True 99 | Settings.settings 100 | True 101 | 102 | 103 | ResXFileCodeGenerator 104 | Resources.Designer.cs 105 | 106 | 107 | .editorconfig 108 | 109 | 110 | SettingsSingleFileGenerator 111 | Settings.Designer.cs 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | Designer 120 | PreserveNewest 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | -------------------------------------------------------------------------------- /BspZipGUI/Properties/Resources.resx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | text/microsoft-resx 107 | 108 | 109 | 2.0 110 | 111 | 112 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 113 | 114 | 115 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | -------------------------------------------------------------------------------- /BspZipGUI/Tool/Utils/XmlUtils.cs: -------------------------------------------------------------------------------- 1 | using BspZipGUI.Tool.Xml; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using System.Xml.Serialization; 8 | 9 | namespace BspZipGUI.Tool.Utils 10 | { 11 | internal static class XmlUtils 12 | { 13 | #region Constants 14 | 15 | /// 16 | /// Name of the Settings file 17 | /// 18 | private const string xmlSettings = "settings.xml"; 19 | 20 | /// 21 | /// Path to the default settings, stored in the App 22 | /// 23 | private const string xmlSettingsBackup = "BspZipGUI.settings_backup.txt"; 24 | 25 | #endregion 26 | 27 | #region Methods 28 | 29 | /// 30 | /// Get the settings of the application by reading settings.xml 31 | /// 32 | /// If an error happens during the parsingof the file 33 | /// The initialized Settings or null if the file doesn't exist 34 | public static ToolSettings GetSettingsFromFile() 35 | { 36 | if (System.IO.File.Exists(xmlSettings)) 37 | { 38 | // If settings.xml exist, we load it 39 | return DeserializeSettingsFromFile(xmlSettings); 40 | } 41 | return null; 42 | } 43 | 44 | 45 | /// 46 | /// Read and parse the given settings file 47 | /// 48 | /// Name of the file to parse 49 | /// If an error happens during the parsing 50 | /// Parsed settings 51 | private static ToolSettings DeserializeSettingsFromFile(string filename) 52 | { 53 | try 54 | { 55 | using (System.IO.StreamReader reader = new System.IO.StreamReader(filename)) 56 | { 57 | XmlSerializer serializer = new XmlSerializer(typeof(ToolSettings)); 58 | return (ToolSettings)serializer.Deserialize(reader); 59 | } 60 | } 61 | catch (Exception ex) 62 | { 63 | throw new SettingsSerializationException(MessageConstants.MessageSettingsReadError, ex); 64 | } 65 | } 66 | 67 | /// 68 | /// Get the default settings stored in the .exe 69 | /// 70 | /// If an error happens when reading and parsing the default settings 71 | /// Parsed settings 72 | public static ToolSettings GetSettingsFromResource() 73 | { 74 | // If it doesn't exist, we load it from the embedded ressource and recreate it 75 | string xmlText; 76 | try 77 | { 78 | xmlText = FileUtils.ReadResourceFile(xmlSettingsBackup); 79 | } 80 | catch (Exception ex) 81 | { 82 | throw new SettingsSerializationException(MessageConstants.MessageSettingsDefaultReadError, ex); 83 | } 84 | return DeserializeSettingsFromText(xmlText); 85 | } 86 | 87 | 88 | /// 89 | /// Read and parse the given string into Settings 90 | /// 91 | /// the text to parse 92 | /// If an error happens during the parsing 93 | /// Parsed settings 94 | private static ToolSettings DeserializeSettingsFromText(string xmlText) 95 | { 96 | try 97 | { 98 | using (System.IO.StringReader reader = new System.IO.StringReader(xmlText)) 99 | { 100 | XmlSerializer serializer = new XmlSerializer(typeof(ToolSettings)); 101 | return (ToolSettings)serializer.Deserialize(reader); 102 | } 103 | } 104 | catch (Exception ex) 105 | { 106 | throw new SettingsSerializationException(MessageConstants.MessageSettingsDefaultReadError, ex); 107 | } 108 | } 109 | 110 | /// 111 | /// Save the given settings in settings.xml 112 | /// 113 | /// The settings to save 114 | /// If an error happens during the serialization of the file 115 | /// true if successful, false if the Settings are null 116 | public static bool SerializeSettings(ToolSettings settings) 117 | { 118 | if (settings != null) 119 | { 120 | System.Xml.XmlWriterSettings xmlWritterSettings = 121 | new System.Xml.XmlWriterSettings() { Indent = true }; 122 | try 123 | { 124 | using (System.IO.StreamWriter writer = new System.IO.StreamWriter(xmlSettings)) 125 | using (System.Xml.XmlWriter xmlWriter = System.Xml.XmlWriter.Create(writer, xmlWritterSettings)) 126 | { 127 | XmlSerializer serializer = new XmlSerializer(typeof(ToolSettings)); 128 | serializer.Serialize(xmlWriter, settings); 129 | } 130 | return true; 131 | } 132 | catch (Exception ex) 133 | { 134 | throw new SettingsSerializationException(MessageConstants.MessageSettingsSaveError, ex); 135 | } 136 | } 137 | return false; 138 | } 139 | 140 | #endregion 141 | 142 | } 143 | 144 | 145 | 146 | } 147 | -------------------------------------------------------------------------------- /BspZipGUI/Tool/Execute/Bspzip.cs: -------------------------------------------------------------------------------- 1 | using BspZipGUI.Models; 2 | using BspZipGUI.Tool.Utils; 3 | using BspZipGUI.Tool.Xml; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Diagnostics; 7 | using System.Linq; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | 11 | namespace BspZipGUI.Tool.Execute 12 | { 13 | internal abstract class Bspzip 14 | { 15 | 16 | #region Attributes 17 | 18 | /// 19 | /// The current settings of the tool 20 | /// 21 | protected readonly ToolSettings toolSettings; 22 | 23 | /// 24 | /// The game used for packing (bspzip.exe) 25 | /// 26 | protected readonly GameConfig game; 27 | 28 | /// 29 | /// The path to the BSP file 30 | /// 31 | protected readonly string bspPath; 32 | 33 | /// 34 | /// The output logs from the bspzip process 35 | /// 36 | protected readonly LogText logsOutput; 37 | 38 | /// 39 | /// Should the logs be written asynchronously or synchronously 40 | /// 41 | protected readonly bool isSyncLogsOutput; 42 | 43 | #endregion 44 | 45 | #region Constructor 46 | 47 | protected Bspzip(ToolSettings toolSettings, GameConfig game, string bspPath, LogText logsOutput) 48 | { 49 | this.toolSettings = toolSettings; 50 | this.game = game; 51 | this.bspPath = bspPath; 52 | this.logsOutput = logsOutput; 53 | isSyncLogsOutput = toolSettings.IsSyncLogs; 54 | } 55 | 56 | #endregion 57 | 58 | #region Methods 59 | 60 | /// 61 | /// Function to start bspzip.exe
62 | /// Implemented by the classes that inherit from 63 | ///
64 | public abstract void Start(); 65 | 66 | /// 67 | /// Launch a process of bspzip.exe with the given arguments 68 | /// 69 | protected void StartProcess() 70 | { 71 | ProcessStartInfo startInfo = new ProcessStartInfo(game.BspZip, GetProcessArguments()) 72 | { 73 | UseShellExecute = false, 74 | RedirectStandardOutput = true, 75 | RedirectStandardError = true, 76 | CreateNoWindow = true, 77 | 78 | }; 79 | startInfo.EnvironmentVariables["VPROJECT"] = game.GameInfoFolder; 80 | 81 | Process p = new Process { StartInfo = startInfo }; 82 | 83 | if (isSyncLogsOutput) 84 | { 85 | // Sync output 86 | p.Start(); 87 | string output = p.StandardOutput.ReadToEnd(); 88 | logsOutput.AppendLine(output); 89 | p.WaitForExit(); 90 | } 91 | else 92 | { 93 | // Async output 94 | p.OutputDataReceived += new DataReceivedEventHandler(ProcessOutputHandler); 95 | p.ErrorDataReceived += new DataReceivedEventHandler(ProcessErrorHandler); 96 | p.Start(); 97 | p.BeginOutputReadLine(); 98 | p.WaitForExit(); 99 | p.Close(); 100 | } 101 | 102 | // To stop the output reading 103 | // p.CancelOutputRead(); 104 | // p.Close() 105 | 106 | 107 | } 108 | 109 | /// 110 | /// Create a backup of the BSP 111 | /// 112 | /// Exception of any type thrown if an error happened during the file deletion/creation 113 | protected void CreateBackupBsp() 114 | { 115 | string backupBsp = bspPath + "_old"; 116 | if (System.IO.File.Exists(backupBsp)) 117 | { 118 | System.IO.File.Delete(backupBsp); 119 | } 120 | System.IO.File.Copy(bspPath, backupBsp); 121 | logsOutput.AppendLine($"Created a copy of \"{bspPath}\" \n=> \"{backupBsp}\"\n"); 122 | } 123 | 124 | /// 125 | /// Get the arguments used to launch bspzip 126 | /// 127 | /// The arguments to use for the bspzip process 128 | protected abstract string GetProcessArguments(); 129 | 130 | /// 131 | /// Save the current settings in settings.xml
132 | /// Log any error that may happen while saving 133 | ///
134 | protected void SaveSettings() 135 | { 136 | try 137 | { 138 | XmlUtils.SerializeSettings(toolSettings); 139 | } 140 | catch (SettingsSerializationException ex) 141 | { 142 | logsOutput.AppendLine(ex.GetMessageAndInner()); 143 | logsOutput.AppendLine(); 144 | } 145 | } 146 | 147 | /// 148 | /// Update the values of based on last parameters used
149 | /// And save them in settings.xml 150 | ///
151 | protected abstract void UpdateSettings(); 152 | 153 | /// 154 | /// Delegate function used to log the process outputs 155 | /// 156 | /// 157 | /// 158 | private void ProcessOutputHandler(object sender, DataReceivedEventArgs e) 159 | { 160 | logsOutput.AppendLine(e.Data); 161 | } 162 | 163 | /// 164 | /// Delegate function used to log the process errors 165 | /// 166 | /// 167 | /// 168 | private void ProcessErrorHandler(object sender, DataReceivedEventArgs e) 169 | { 170 | logsOutput.AppendLine(e.Data); 171 | } 172 | 173 | #endregion 174 | 175 | } 176 | 177 | } 178 | -------------------------------------------------------------------------------- /BspZipGUI/Tool/Utils/FunctionalException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace BspZipGUI.Tool.Utils 8 | { 9 | 10 | /// 11 | /// Abstract Exception that other functional Exception will inherit 12 | /// 13 | internal abstract class FunctionalException : Exception 14 | { 15 | /// 16 | /// 17 | /// 18 | public FunctionalException() { } 19 | 20 | /// 21 | /// 22 | /// 23 | /// 24 | public FunctionalException(string message) : base(message) { } 25 | 26 | /// 27 | /// 28 | /// 29 | /// 30 | /// 31 | public FunctionalException(string message, Exception inner) : base(message, inner) { } 32 | 33 | /// 34 | /// Return the Message of Exception and the message of InnerException if available 35 | /// 36 | /// Exception message and the InnerException message if available 37 | public string GetMessageAndInner() 38 | { 39 | string errorMessage = Message; 40 | if (InnerException != null) 41 | { 42 | errorMessage += "\n" + InnerException.Message; 43 | } 44 | return errorMessage; 45 | } 46 | } 47 | 48 | /// 49 | /// Exception used when an unexpected error happen during the serialization / deserialization of the settings 50 | /// 51 | [Serializable] 52 | internal class SettingsSerializationException : FunctionalException 53 | { 54 | /// 55 | /// 56 | /// 57 | public SettingsSerializationException() { } 58 | 59 | /// 60 | /// 61 | /// 62 | /// 63 | public SettingsSerializationException(string message) : base(message) { } 64 | 65 | /// 66 | /// 67 | /// 68 | /// 69 | /// 70 | public SettingsSerializationException(string message, Exception inner) : base(message, inner) { } 71 | 72 | } 73 | 74 | 75 | /// 76 | /// Exception used when an unexpected error happen during the creation of the list of files to pack 77 | /// 78 | [Serializable] 79 | internal class FilePackCreationException : FunctionalException 80 | { 81 | /// 82 | /// 83 | /// 84 | public FilePackCreationException() { } 85 | 86 | /// 87 | /// 88 | /// 89 | /// 90 | public FilePackCreationException(string message) : base(message) { } 91 | 92 | /// 93 | /// 94 | /// 95 | /// 96 | /// 97 | public FilePackCreationException(string message, Exception inner) : base(message, inner) { } 98 | 99 | } 100 | 101 | 102 | /// 103 | /// Exception used when an unexpected error happen during the creation of the bsp backup 104 | /// 105 | [Serializable] 106 | internal class BspBackupCreationException : FunctionalException 107 | { 108 | /// 109 | /// 110 | /// 111 | public BspBackupCreationException() { } 112 | 113 | /// 114 | /// 115 | /// 116 | /// 117 | public BspBackupCreationException(string message) : base(message) { } 118 | 119 | /// 120 | /// 121 | /// 122 | /// 123 | /// 124 | public BspBackupCreationException(string message, Exception inner) : base(message, inner) { } 125 | 126 | } 127 | 128 | /// 129 | /// Exception used when an unexpected error happen during the bspzip execution 130 | /// 131 | [Serializable] 132 | internal class BspZipExecutionException : FunctionalException 133 | { 134 | /// 135 | /// 136 | /// 137 | public BspZipExecutionException() { } 138 | 139 | /// 140 | /// 141 | /// 142 | /// 143 | public BspZipExecutionException(string message) : base(message) { } 144 | 145 | /// 146 | /// 147 | /// 148 | /// 149 | /// 150 | public BspZipExecutionException(string message, Exception inner) : base(message, inner) { } 151 | 152 | } 153 | 154 | /// 155 | /// Exception used when the process encounters one or multiple paths that are longer than 156 | /// 157 | [Serializable] 158 | internal class MaxPathSizeLimitException : FunctionalException 159 | { 160 | /// 161 | /// 162 | /// 163 | public MaxPathSizeLimitException() { } 164 | 165 | /// 166 | /// 167 | /// 168 | /// 169 | public MaxPathSizeLimitException(string message) : base(message) { } 170 | 171 | /// 172 | /// 173 | /// 174 | /// 175 | /// 176 | public MaxPathSizeLimitException(string message, Exception inner) : base(message, inner) { } 177 | 178 | } 179 | 180 | } 181 | -------------------------------------------------------------------------------- /BspZipGUI/App.xaml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 30 15 | 2 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 41 | 42 | 45 | 46 | 49 | 50 | 53 | 54 | 55 | 56 | 57 | 61 | 62 | 66 | 67 | 72 | 73 | 79 | 80 | 81 | 88 | 89 | 92 | 93 | 98 | 103 | 104 | 109 | 110 | 114 | 115 | 121 | 122 | 125 | 126 | 129 | 130 | 131 | 132 | 136 | 137 | 138 | 139 | -------------------------------------------------------------------------------- /BspZipGUI/Tool/Execute/Pack.cs: -------------------------------------------------------------------------------- 1 | using BspZipGUI.Models; 2 | using BspZipGUI.Tool.Utils; 3 | using BspZipGUI.Tool.Xml; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Diagnostics; 7 | using System.Linq; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | 11 | namespace BspZipGUI.Tool.Execute 12 | { 13 | internal class Pack : Bspzip 14 | { 15 | 16 | #region Attributes 17 | 18 | /// 19 | /// The custom directory with the files to pack 20 | /// 21 | private readonly MapConfig mapContent; 22 | 23 | /// 24 | /// The multiple custom directory with the files to pack 25 | /// 26 | private readonly MultiMapConfig multiMapContent; 27 | 28 | /// 29 | /// Only pack specific files types in each specific subfolders 30 | /// 31 | private readonly bool useWhitelist; 32 | 33 | /// 34 | /// Path of the BSP to create 35 | /// 36 | private readonly string outputBspPath; 37 | 38 | #endregion 39 | 40 | #region Constructor 41 | 42 | public Pack(ToolSettings toolSettings, GameConfig game, string bspPath, LogText logsOutput, MapConfig mapContent, string outputBspPath, bool useWhitelist) : 43 | base(toolSettings, game, bspPath, logsOutput) 44 | { 45 | this.mapContent = mapContent; 46 | this.outputBspPath = outputBspPath; 47 | this.useWhitelist = useWhitelist; 48 | } 49 | 50 | public Pack(ToolSettings toolSettings, GameConfig game, string bspPath, LogText logsOutput, MultiMapConfig multiMapContent, string outputBspPath, bool useWhitelist) : 51 | base(toolSettings, game, bspPath, logsOutput) 52 | { 53 | this.multiMapContent = multiMapContent; 54 | this.outputBspPath = outputBspPath; 55 | this.useWhitelist = useWhitelist; 56 | } 57 | 58 | #endregion 59 | 60 | #region Methods 61 | 62 | /// 63 | /// Start the packing for the current options 64 | /// 65 | /// Error when creating the list of file to pack 66 | /// Error when creating the bsp backup 67 | /// Error during bspzip execution 68 | public override void Start() 69 | { 70 | UpdateSettings(); 71 | logsOutput.AppendLine(); 72 | bool hasMaxPathSize; 73 | FilePack filePack = new FilePack(GetCustomDirectories(), useWhitelist, toolSettings.DirectoriesRestrictions); 74 | try 75 | { 76 | hasMaxPathSize = filePack.FindAllFilesToPack(); 77 | filePack.OutputToFile(); 78 | } 79 | catch (Exception ex) 80 | { 81 | throw new FilePackCreationException(MessageConstants.MessageListFilesFail, ex); 82 | } 83 | if (!System.IO.File.Exists(Constants.FilesListText)) 84 | { 85 | // Just for safety, but the file is supposed to exist 86 | throw new FilePackCreationException(MessageConstants.MessageListFilesNotFound); 87 | } 88 | logsOutput.AppendLine("Created " + Constants.FilesListText); 89 | if (bspPath.Equals(outputBspPath)) 90 | { 91 | // If we override the original BSP, we make a backup 92 | try 93 | { 94 | CreateBackupBsp(); 95 | } 96 | catch (Exception ex) 97 | { 98 | throw new BspBackupCreationException(MessageConstants.MessageCopyBspFail, ex); 99 | } 100 | } 101 | try 102 | { 103 | StartProcess(); 104 | } 105 | catch (Exception ex) 106 | { 107 | throw new BspZipExecutionException(MessageConstants.MessageBspzipFail, ex); 108 | } 109 | if (hasMaxPathSize) 110 | { 111 | // One or multiple path longer than MAX_PATH were encountered 112 | // bspzip.exe likely didn't pack correctly the files 113 | // We add the list to the logs and throw an exception 114 | logsOutput.AppendLine($"/!\\ {MessageConstants.MessageMaxPathSizeWarning} :"); 115 | foreach (string path in filePack.MaxPathSizeList) 116 | { 117 | logsOutput.AppendLine($"- {path.Length} : \"{path}\""); 118 | } 119 | throw new MaxPathSizeLimitException(MessageConstants.MessageMaxPathSizeSuggestion); 120 | } 121 | } 122 | 123 | /// 124 | /// Return the arguments to launch bspzip.exe, to pack the files in a BSP 125 | /// 126 | /// 127 | protected override string GetProcessArguments() 128 | { 129 | // bspzip -addlist "" "" "" 130 | StringBuilder sb = new StringBuilder("-addlist ") 131 | .Append($"\"{bspPath}\" ") 132 | .Append($"\"{Constants.FilesListText}\" ") 133 | .Append($"\"{outputBspPath}\""); 134 | return sb.ToString(); 135 | } 136 | 137 | /// 138 | /// 139 | /// 140 | protected override void UpdateSettings() 141 | { 142 | toolSettings.LastBsp = bspPath; 143 | toolSettings.LastGame = game.Name; 144 | if (mapContent != null) 145 | toolSettings.LastCustomDirectory = mapContent.Name; 146 | if (multiMapContent != null) 147 | toolSettings.LastMultiCustomDirectory = multiMapContent.Name; 148 | SaveSettings(); 149 | } 150 | 151 | /// 152 | /// Get the list of custom directories to use for packing 153 | /// 154 | /// A list of directories paths cleaned from any extra character 155 | private ICollection GetCustomDirectories() 156 | { 157 | // Clean the directories path first 158 | CleanCustomDirectoriesPath(); 159 | if (mapContent != null) 160 | return new List { mapContent.CleanedPath }; 161 | if (multiMapContent != null) 162 | return multiMapContent.HashSetCleanedPath; 163 | return new List(); 164 | } 165 | 166 | /// 167 | /// Remove any extra / from the custom directories 168 | /// 169 | private void CleanCustomDirectoriesPath() 170 | { 171 | if (mapContent != null) 172 | mapContent.CleanPath(); 173 | if (multiMapContent != null) 174 | multiMapContent.CleanPaths(); 175 | } 176 | 177 | #endregion 178 | 179 | } 180 | 181 | } 182 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Aa][Rr][Mm]/ 27 | [Aa][Rr][Mm]64/ 28 | bld/ 29 | [Bb]in/ 30 | [Oo]bj/ 31 | [Ll]og/ 32 | 33 | # Visual Studio 2015/2017 cache/options directory 34 | .vs/ 35 | # Uncomment if you have tasks that create the project's static files in wwwroot 36 | #wwwroot/ 37 | 38 | # Visual Studio 2017 auto generated files 39 | Generated\ Files/ 40 | 41 | # MSTest test Results 42 | [Tt]est[Rr]esult*/ 43 | [Bb]uild[Ll]og.* 44 | 45 | # NUnit 46 | *.VisualState.xml 47 | TestResult.xml 48 | nunit-*.xml 49 | 50 | # Build Results of an ATL Project 51 | [Dd]ebugPS/ 52 | [Rr]eleasePS/ 53 | dlldata.c 54 | 55 | # Benchmark Results 56 | BenchmarkDotNet.Artifacts/ 57 | 58 | # .NET Core 59 | project.lock.json 60 | project.fragment.lock.json 61 | artifacts/ 62 | 63 | # StyleCop 64 | StyleCopReport.xml 65 | 66 | # Files built by Visual Studio 67 | *_i.c 68 | *_p.c 69 | *_h.h 70 | *.ilk 71 | *.meta 72 | *.obj 73 | *.iobj 74 | *.pch 75 | *.pdb 76 | *.ipdb 77 | *.pgc 78 | *.pgd 79 | *.rsp 80 | *.sbr 81 | *.tlb 82 | *.tli 83 | *.tlh 84 | *.tmp 85 | *.tmp_proj 86 | *_wpftmp.csproj 87 | *.log 88 | *.vspscc 89 | *.vssscc 90 | .builds 91 | *.pidb 92 | *.svclog 93 | *.scc 94 | 95 | # Chutzpah Test files 96 | _Chutzpah* 97 | 98 | # Visual C++ cache files 99 | ipch/ 100 | *.aps 101 | *.ncb 102 | *.opendb 103 | *.opensdf 104 | *.sdf 105 | *.cachefile 106 | *.VC.db 107 | *.VC.VC.opendb 108 | 109 | # Visual Studio profiler 110 | *.psess 111 | *.vsp 112 | *.vspx 113 | *.sap 114 | 115 | # Visual Studio Trace Files 116 | *.e2e 117 | 118 | # TFS 2012 Local Workspace 119 | $tf/ 120 | 121 | # Guidance Automation Toolkit 122 | *.gpState 123 | 124 | # ReSharper is a .NET coding add-in 125 | _ReSharper*/ 126 | *.[Rr]e[Ss]harper 127 | *.DotSettings.user 128 | 129 | # JustCode is a .NET coding add-in 130 | .JustCode 131 | 132 | # TeamCity is a build add-in 133 | _TeamCity* 134 | 135 | # DotCover is a Code Coverage Tool 136 | *.dotCover 137 | 138 | # AxoCover is a Code Coverage Tool 139 | .axoCover/* 140 | !.axoCover/settings.json 141 | 142 | # Visual Studio code coverage results 143 | *.coverage 144 | *.coveragexml 145 | 146 | # NCrunch 147 | _NCrunch_* 148 | .*crunch*.local.xml 149 | nCrunchTemp_* 150 | 151 | # MightyMoose 152 | *.mm.* 153 | AutoTest.Net/ 154 | 155 | # Web workbench (sass) 156 | .sass-cache/ 157 | 158 | # Installshield output folder 159 | [Ee]xpress/ 160 | 161 | # DocProject is a documentation generator add-in 162 | DocProject/buildhelp/ 163 | DocProject/Help/*.HxT 164 | DocProject/Help/*.HxC 165 | DocProject/Help/*.hhc 166 | DocProject/Help/*.hhk 167 | DocProject/Help/*.hhp 168 | DocProject/Help/Html2 169 | DocProject/Help/html 170 | 171 | # Click-Once directory 172 | publish/ 173 | 174 | # Publish Web Output 175 | *.[Pp]ublish.xml 176 | *.azurePubxml 177 | # Note: Comment the next line if you want to checkin your web deploy settings, 178 | # but database connection strings (with potential passwords) will be unencrypted 179 | *.pubxml 180 | *.publishproj 181 | 182 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 183 | # checkin your Azure Web App publish settings, but sensitive information contained 184 | # in these scripts will be unencrypted 185 | PublishScripts/ 186 | 187 | # NuGet Packages 188 | *.nupkg 189 | # NuGet Symbol Packages 190 | *.snupkg 191 | # The packages folder can be ignored because of Package Restore 192 | **/[Pp]ackages/* 193 | # except build/, which is used as an MSBuild target. 194 | !**/[Pp]ackages/build/ 195 | # Uncomment if necessary however generally it will be regenerated when needed 196 | #!**/[Pp]ackages/repositories.config 197 | # NuGet v3's project.json files produces more ignorable files 198 | *.nuget.props 199 | *.nuget.targets 200 | 201 | # Microsoft Azure Build Output 202 | csx/ 203 | *.build.csdef 204 | 205 | # Microsoft Azure Emulator 206 | ecf/ 207 | rcf/ 208 | 209 | # Windows Store app package directories and files 210 | AppPackages/ 211 | BundleArtifacts/ 212 | Package.StoreAssociation.xml 213 | _pkginfo.txt 214 | *.appx 215 | *.appxbundle 216 | *.appxupload 217 | 218 | # Visual Studio cache files 219 | # files ending in .cache can be ignored 220 | *.[Cc]ache 221 | # but keep track of directories ending in .cache 222 | !?*.[Cc]ache/ 223 | 224 | # Others 225 | ClientBin/ 226 | ~$* 227 | *~ 228 | *.dbmdl 229 | *.dbproj.schemaview 230 | *.jfm 231 | *.pfx 232 | *.publishsettings 233 | orleans.codegen.cs 234 | 235 | # Including strong name files can present a security risk 236 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 237 | #*.snk 238 | 239 | # Since there are multiple workflows, uncomment next line to ignore bower_components 240 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 241 | #bower_components/ 242 | 243 | # RIA/Silverlight projects 244 | Generated_Code/ 245 | 246 | # Backup & report files from converting an old project file 247 | # to a newer Visual Studio version. Backup files are not needed, 248 | # because we have git ;-) 249 | _UpgradeReport_Files/ 250 | Backup*/ 251 | UpgradeLog*.XML 252 | UpgradeLog*.htm 253 | ServiceFabricBackup/ 254 | *.rptproj.bak 255 | 256 | # SQL Server files 257 | *.mdf 258 | *.ldf 259 | *.ndf 260 | 261 | # Business Intelligence projects 262 | *.rdl.data 263 | *.bim.layout 264 | *.bim_*.settings 265 | *.rptproj.rsuser 266 | *- [Bb]ackup.rdl 267 | *- [Bb]ackup ([0-9]).rdl 268 | *- [Bb]ackup ([0-9][0-9]).rdl 269 | 270 | # Microsoft Fakes 271 | FakesAssemblies/ 272 | 273 | # GhostDoc plugin setting file 274 | *.GhostDoc.xml 275 | 276 | # Node.js Tools for Visual Studio 277 | .ntvs_analysis.dat 278 | node_modules/ 279 | 280 | # Visual Studio 6 build log 281 | *.plg 282 | 283 | # Visual Studio 6 workspace options file 284 | *.opt 285 | 286 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 287 | *.vbw 288 | 289 | # Visual Studio LightSwitch build output 290 | **/*.HTMLClient/GeneratedArtifacts 291 | **/*.DesktopClient/GeneratedArtifacts 292 | **/*.DesktopClient/ModelManifest.xml 293 | **/*.Server/GeneratedArtifacts 294 | **/*.Server/ModelManifest.xml 295 | _Pvt_Extensions 296 | 297 | # Paket dependency manager 298 | .paket/paket.exe 299 | paket-files/ 300 | 301 | # FAKE - F# Make 302 | .fake/ 303 | 304 | # CodeRush personal settings 305 | .cr/personal 306 | 307 | # Python Tools for Visual Studio (PTVS) 308 | __pycache__/ 309 | *.pyc 310 | 311 | # Cake - Uncomment if you are using it 312 | # tools/** 313 | # !tools/packages.config 314 | 315 | # Tabs Studio 316 | *.tss 317 | 318 | # Telerik's JustMock configuration file 319 | *.jmconfig 320 | 321 | # BizTalk build output 322 | *.btp.cs 323 | *.btm.cs 324 | *.odx.cs 325 | *.xsd.cs 326 | 327 | # OpenCover UI analysis results 328 | OpenCover/ 329 | 330 | # Azure Stream Analytics local run output 331 | ASALocalRun/ 332 | 333 | # MSBuild Binary and Structured Log 334 | *.binlog 335 | 336 | # NVidia Nsight GPU debugger configuration file 337 | *.nvuser 338 | 339 | # MFractors (Xamarin productivity tool) working folder 340 | .mfractor/ 341 | 342 | # Local History for Visual Studio 343 | .localhistory/ 344 | 345 | # BeatPulse healthcheck temp database 346 | healthchecksdb 347 | 348 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 349 | MigrationBackup/ 350 | -------------------------------------------------------------------------------- /BspZipGUI/Tool/Execute/FilePack.cs: -------------------------------------------------------------------------------- 1 | using BspZipGUI.Tool.Utils; 2 | using BspZipGUI.Tool.Xml; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace BspZipGUI.Tool.Execute 10 | { 11 | internal class FilePack 12 | { 13 | 14 | #region Attributes 15 | 16 | /// 17 | /// The list of files to pack: <internalPath, externalPath> 18 | /// 19 | private readonly IDictionary filesPathsList; 20 | 21 | /// 22 | /// List of pairs of <customDirectoryPath, length> 23 | /// 24 | private readonly ICollection> customDirectoriesPairs; 25 | 26 | /// 27 | /// The list of base directory and extension allowed to be packed 28 | /// 29 | private readonly ICollection directoriesRestrictions; 30 | 31 | /// 32 | /// Use the list of directory whitelist when packing 33 | /// 34 | private readonly bool useWhitelist; 35 | 36 | /// 37 | /// Store the list of paths that are longer than 38 | /// 39 | public readonly HashSet MaxPathSizeList; 40 | 41 | #endregion 42 | 43 | #region Constructor 44 | 45 | public FilePack(ICollection customDirectories, bool useWhitelist, ICollection directoriesRestrictions) 46 | { 47 | this.customDirectoriesPairs = new List>(); 48 | foreach (string customDirectory in customDirectories) 49 | { 50 | // Each directory path will be cleaned from any ending '/', so + 1 for the length 51 | this.customDirectoriesPairs.Add(new KeyValuePair(customDirectory, customDirectory.Length + 1)); 52 | } 53 | this.filesPathsList = new Dictionary(); 54 | this.directoriesRestrictions = directoriesRestrictions; 55 | this.useWhitelist = useWhitelist; 56 | this.MaxPathSizeList = new HashSet(); 57 | } 58 | 59 | #endregion 60 | 61 | #region Methods - Find Files 62 | 63 | /// 64 | /// Find all the files to pack and store them in the list 65 | /// 66 | /// true if one or multiple paths are longer than , false otherwise 67 | public bool FindAllFilesToPack() 68 | { 69 | if (useWhitelist) 70 | { 71 | // Add only the files matching specific extensions (defined in the Settings) 72 | return AddAllFilesFromWhitelistDirectories(); 73 | } 74 | else 75 | { 76 | // Add every single file from the directories and subdirectories, regardless of extension 77 | return AddAllFilesFromAnyDirectories(); 78 | } 79 | } 80 | 81 | /// 82 | /// Add every single files from the directories, to , regardless of their extensions 83 | /// 84 | /// true if one or multiple paths are longer than , false otherwise 85 | private bool AddAllFilesFromAnyDirectories() 86 | { 87 | bool hasMaxPathSize = false; 88 | foreach (KeyValuePair customDirectoryPair in customDirectoriesPairs) 89 | { 90 | List filesList = FileUtils.GetFilesListRecursive(customDirectoryPair.Key); 91 | if (filesList != null) 92 | { 93 | foreach (string path in filesList) 94 | { 95 | AppendPath(path, customDirectoryPair.Value); 96 | // bspzip.exe will not behave correctly if a very long path is in the file 97 | if (path.Length >= Constants.MAX_PATH) 98 | { 99 | MaxPathSizeList.Add(path); 100 | hasMaxPathSize = true; 101 | } 102 | } 103 | } 104 | } 105 | return hasMaxPathSize; 106 | } 107 | 108 | /// 109 | /// Add files matching specific extensions, located in specific subdirectories, to 110 | /// 111 | /// true if one or multiple paths are longer than , false otherwise 112 | private bool AddAllFilesFromWhitelistDirectories() 113 | { 114 | bool hasMaxPathSize = false; 115 | if (directoriesRestrictions != null) 116 | { 117 | // Go through each whitelisted subdirectory (materials, models,...) 118 | foreach (DirectoryRestrictions directoryRestrictions in directoriesRestrictions) 119 | { 120 | HashSet allowedExtensions = new HashSet(directoryRestrictions.AllowedExtension); 121 | 122 | // Go through each custom directory 123 | foreach (KeyValuePair customDirectoryPair in customDirectoriesPairs) 124 | { 125 | // Combine the custom directory path + the whitelisted subdirectory 126 | string subDirectory = System.IO.Path.Combine(customDirectoryPair.Key, directoryRestrictions.DirectoryName); 127 | if (System.IO.Directory.Exists(subDirectory)) 128 | { 129 | List filesList = FileUtils.GetFilesListRecursive(subDirectory); 130 | if (filesList != null) 131 | { 132 | foreach (string path in filesList) 133 | { 134 | // Verify the file extension 135 | if (FileUtils.IsExtension(path, allowedExtensions)) 136 | { 137 | AppendPath(path, customDirectoryPair.Value); 138 | // bspzip.exe will not behave correctly if a very long path is in the file 139 | if (path.Length >= Constants.MAX_PATH) 140 | { 141 | MaxPathSizeList.Add(path); 142 | hasMaxPathSize = true; 143 | } 144 | } 145 | } 146 | } 147 | } 148 | } 149 | } 150 | } 151 | return hasMaxPathSize; 152 | } 153 | 154 | /// 155 | /// Append a given file path to 156 | /// 157 | /// Absolute path of a file 158 | private void AppendPath(string externalPath, int customDirectoryLength) 159 | { 160 | string internalPath = GetInternalPath(externalPath, customDirectoryLength); 161 | if (!filesPathsList.ContainsKey(internalPath)) 162 | filesPathsList.Add(new KeyValuePair(internalPath, externalPath)); 163 | } 164 | 165 | /// 166 | /// Get the local path of a given file (based on the custom directory) 167 | /// 168 | /// Absolute path of a file 169 | /// The relative path to save in the txt file used by bspzip.exe 170 | private string GetInternalPath(string externalPath, int customDirectoryLength) 171 | { 172 | // e.g. c:\programfiles\....\materials\myFolder\texture.vtf 173 | // => materials/myFolder/texture.vtf 174 | return externalPath.Substring(customDirectoryLength). 175 | Replace(Constants.Backslash, Constants.Slash); 176 | } 177 | 178 | #endregion 179 | 180 | #region Methods - Write List 181 | 182 | /// 183 | /// Create filesList.txt with the paths of each files, located in 184 | /// 185 | /// In case there is an error during the file creation 186 | public void OutputToFile() 187 | { 188 | List outputLines = new List(); 189 | foreach (KeyValuePair entry in filesPathsList) 190 | { 191 | outputLines.Add(entry.Key); 192 | outputLines.Add(entry.Value); 193 | } 194 | 195 | if (System.IO.File.Exists(Constants.FilesListText)) 196 | { 197 | System.IO.File.Delete(Constants.FilesListText); 198 | } 199 | System.IO.File.WriteAllLines(Constants.FilesListText, outputLines); 200 | } 201 | 202 | #endregion 203 | 204 | } 205 | 206 | 207 | } 208 | -------------------------------------------------------------------------------- /BspZipGUI/Tool/Utils/FileUtils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace BspZipGUI.Tool.Utils 8 | { 9 | /// 10 | /// Enum to handle File Browser Dialog and Save Browser Dialog 11 | /// 12 | internal enum FileFilters 13 | { 14 | None, 15 | Bsp, 16 | Zip, 17 | BspZipExe, 18 | GameinfoTxt 19 | } 20 | 21 | /// 22 | /// Class containing useful file related functions 23 | /// 24 | internal static class FileUtils 25 | { 26 | #region Constants 27 | 28 | private const string filterGameinfoTxt = "Gameinfo (gameinfo.txt)|gameinfo.txt|Text files (*.txt)|*.txt|All files (*.*)|*.*"; 29 | private const string filterBsp = "BSP Files (*.bsp)| *.bsp"; 30 | private const string filterZip = "ZIP Files (*.zip)| *.zip"; 31 | private const string filterBspZipExe = "BspZip (bspzip.exe)| bspzip.exe|Executable files (*.exe)|*.exe"; 32 | 33 | private const string titleBspInput = "Select a .bsp"; 34 | private const string titleBspZipExe = "Select bspzip.exe file"; 35 | private const string titleGameinfoTxt = "Select gameinfo.txt file"; 36 | private const string titleBspOutput = "Save .bsp as"; 37 | private const string titleZipOutput = "Save .zip as"; 38 | 39 | private const string titleDirectory = "Select a directory"; 40 | private const string directorySelection = "[Folder Selection]"; 41 | private const string searchPatternAny = "*.*"; 42 | 43 | #endregion 44 | 45 | #region Methods - Files / Folders 46 | 47 | /// 48 | /// Read contents of an embedded resource file 49 | /// 50 | /// An exception of any type, if there is an error when reading the stream 51 | public static string ReadResourceFile(string filename) 52 | { 53 | System.Reflection.Assembly thisAssembly = System.Reflection.Assembly.GetExecutingAssembly(); 54 | using (System.IO.Stream stream = thisAssembly.GetManifestResourceStream(filename)) 55 | { 56 | using (System.IO.StreamReader reader = new System.IO.StreamReader(stream)) 57 | { 58 | return reader.ReadToEnd(); 59 | } 60 | } 61 | } 62 | 63 | /// 64 | /// Tries to get the directory name of a given path
65 | /// Remove any extra '/' in the path and '/' becomes '\' 66 | ///
67 | /// The path of a file or directory 68 | /// The directory path if successful, null otherwise 69 | public static string TryGetDirectoryName(string path) 70 | { 71 | try 72 | { 73 | return System.IO.Path.GetDirectoryName(path); 74 | } 75 | catch 76 | { 77 | return null; 78 | } 79 | } 80 | 81 | /// 82 | /// Get the list of all files in a given directory and its subdirectories 83 | /// 84 | /// The path of the directory 85 | /// An exception of any type, if there is an error getting the list of files 86 | /// The path of all files in the directories, null if the directory doesn't exist 87 | public static List GetFilesListRecursive(string directory) 88 | { 89 | if (System.IO.Directory.Exists(directory)) 90 | { 91 | string[] files = System.IO.Directory.GetFiles(directory, searchPatternAny, System.IO.SearchOption.AllDirectories); 92 | return new List(files); 93 | } 94 | return null; 95 | } 96 | 97 | /// 98 | /// Check if the name of a file is the same as an expected one 99 | /// 100 | /// The path of the file 101 | /// Expected file name 102 | /// true if it's the same file name, false otherwise 103 | public static bool IsFileName(string path, string expectedFile) 104 | { 105 | string file = System.IO.Path.GetFileName(path).ToLower(); 106 | return file.Equals(expectedFile.ToLower()); 107 | } 108 | 109 | /// 110 | /// Check if the extension of a given file is matching a specific extension 111 | /// 112 | /// Path of the file 113 | /// An extension 114 | /// True if the file has the good extension, false otherwise 115 | public static bool IsExtension(string fileName, string allowedExtension) 116 | { 117 | return allowedExtension.Equals(System.IO.Path.GetExtension(fileName)); 118 | } 119 | 120 | /// 121 | /// Check if the extension of a given file is matching a list of extensions 122 | /// 123 | /// Path of the file 124 | /// List of extensions 125 | /// True if the file has the good extension, false otherwise 126 | public static bool IsExtension(string fileName, HashSet allowedExtensions) 127 | { 128 | return allowedExtensions.Contains(System.IO.Path.GetExtension(fileName)); 129 | } 130 | 131 | /// 132 | /// Clean the path of a directory by removing extra '/' within the path 133 | /// 134 | /// Path of a directory 135 | /// The cleaned directory path 136 | public static string CleanDirectoryPath(string path) 137 | { 138 | return TryGetDirectoryName(path + Constants.Slash); 139 | } 140 | 141 | #endregion 142 | 143 | #region Methods - Dialog File / Folder 144 | 145 | /// 146 | /// Open the File Browser Dialog with the given preset () 147 | /// 148 | /// Filter for the preset parameters 149 | /// The path of the selected file, null if none 150 | public static string OpenFileDialog(FileFilters filter) 151 | { 152 | switch (filter) 153 | { 154 | case FileFilters.Bsp: 155 | return OpenFileDialog(filterBsp, titleBspInput); 156 | case FileFilters.BspZipExe: 157 | return OpenFileDialog(filterBspZipExe, titleBspZipExe); 158 | case FileFilters.GameinfoTxt: 159 | return OpenFileDialog(filterGameinfoTxt, titleGameinfoTxt); 160 | case FileFilters.None: 161 | default: 162 | break; 163 | } 164 | return OpenFileDialog(string.Empty, string.Empty); 165 | } 166 | 167 | /// 168 | /// Open the File Browser Dialog with given parameters 169 | /// 170 | /// Type of files to filter in the File Dialog 171 | /// Title of the File Dialog 172 | /// 173 | private static string OpenFileDialog(string filter, string title) 174 | { 175 | Microsoft.Win32.FileDialog fileDialog = new Microsoft.Win32.OpenFileDialog 176 | { 177 | Filter = filter, 178 | Title = title 179 | }; 180 | bool? result = fileDialog.ShowDialog(); 181 | if (result == true) 182 | { 183 | return fileDialog.FileName; 184 | } 185 | return null; 186 | } 187 | 188 | /// 189 | /// Open the File Browser Dialog as a Folder Browser Dialog
190 | /// Way better than the default "System.Windows.Forms.FolderBrowserDialog" 191 | ///
192 | /// Returns the selected folder, null if none 193 | public static string OpenFolderDialog() 194 | { 195 | Microsoft.Win32.FileDialog folderDialog = new Microsoft.Win32.OpenFileDialog 196 | { 197 | CheckFileExists = false, // Allow for the selection of a directory 198 | Title = titleDirectory, 199 | FileName = directorySelection // Default name 200 | }; 201 | bool? result = folderDialog.ShowDialog(); 202 | if (result == true) 203 | { 204 | return System.IO.Path.GetDirectoryName(folderDialog.FileName); 205 | } 206 | return null; 207 | } 208 | 209 | /// 210 | /// Open the Save File Browser Dialog with the given preset () and parameters 211 | /// 212 | /// Filter for the preset parameters 213 | /// Default filename 214 | /// Default extension 215 | /// Intitial directory to open the File Dialog in 216 | /// The path of the file to save, null if none 217 | public static string SaveFileDialog(FileFilters filter, string defaultFileName, string defaultExt, string initialDirectory = "") 218 | { 219 | switch (filter) 220 | { 221 | case FileFilters.Bsp: 222 | return SaveFileDialog(filterBsp, titleBspOutput, defaultFileName, defaultExt, initialDirectory); 223 | case FileFilters.Zip: 224 | return SaveFileDialog(filterZip, titleZipOutput, defaultFileName, defaultExt, initialDirectory); 225 | case FileFilters.None: 226 | default: 227 | break; 228 | } 229 | return SaveFileDialog(string.Empty, string.Empty, defaultFileName, defaultExt, initialDirectory); 230 | } 231 | 232 | /// 233 | /// Open the Save File Browser Dialog with the given parameters 234 | /// 235 | /// Type of files to filter in the File Dialog 236 | /// Title of the File Dialog 237 | /// Default filename 238 | /// Default extension 239 | /// Intitial directory to open the File Dialog in 240 | /// The path of the file to save, null if none 241 | private static string SaveFileDialog(string filter, string title, string defaultFileName, string defaultExt, string initialDirectory) 242 | { 243 | Microsoft.Win32.FileDialog saveDialog = new Microsoft.Win32.SaveFileDialog 244 | { 245 | InitialDirectory = initialDirectory, 246 | Filter = filter, 247 | FileName = defaultFileName, 248 | DefaultExt = defaultExt, 249 | Title = title, 250 | }; 251 | 252 | bool? result = saveDialog.ShowDialog(); 253 | if (result == true) 254 | { 255 | return saveDialog.FileName; 256 | } 257 | return null; 258 | } 259 | 260 | #endregion 261 | 262 | } 263 | } 264 | -------------------------------------------------------------------------------- /BspZipGUI/Tool/Xml/ToolSettings.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Collections.ObjectModel; 4 | using System.ComponentModel; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using System.Xml.Serialization; 9 | 10 | namespace BspZipGUI.Tool.Xml 11 | { 12 | 13 | #region Class - Settings 14 | 15 | /// 16 | /// Store all settings of the user 17 | /// 18 | [Serializable] 19 | [XmlRoot("AppSettings")] 20 | public class ToolSettings 21 | { 22 | 23 | #region Attributes 24 | 25 | /// 26 | /// Last BSP loaded in the tool 27 | /// 28 | [XmlElement(ElementName = "LastBsp", Order = 1)] 29 | public string LastBsp { get; set; } 30 | 31 | /// 32 | /// Last game name loaded by the tool 33 | /// 34 | [XmlElement(ElementName = "LastGame", Order = 2)] 35 | public string LastGame { get; set; } 36 | 37 | /// 38 | /// Last custom files directory name loaded by the tool 39 | /// 40 | [XmlElement(ElementName = "LastCustomDirectory", Order = 3)] 41 | public string LastCustomDirectory { get; set; } 42 | 43 | /// 44 | /// Last directory path loaded by the tool for extractions 45 | /// 46 | [XmlElement(ElementName = "LastExtractDirectory", Order = 4)] 47 | public string LastExtractDirectory { get; set; } 48 | 49 | /// 50 | /// Is the bspzip output written synchronously (1) or asynchronously (0) 51 | /// 52 | [XmlIgnore] 53 | public bool IsSyncLogs { get; private set; } 54 | 55 | /// 56 | /// Serialized value to represent 57 | /// 58 | [XmlElement(ElementName = "IsSyncLogs", Order = 5)] 59 | public string IsAsyncLogsSerialize 60 | { 61 | // This getter is automatically called when the xml file is serialized 62 | get { return IsSyncLogs ? "True" : "False"; } 63 | set 64 | { 65 | // This setter is automatically called when the xml file is deserialized 66 | if ("True".Equals(value)) 67 | IsSyncLogs = true; 68 | else if ("False".Equals(value)) 69 | IsSyncLogs = false; 70 | else 71 | IsSyncLogs = false; // Force Async if not defined in the config 72 | } 73 | } 74 | 75 | /// 76 | /// List of games configs (bspzip.exe directory) 77 | /// 78 | [XmlArray(ElementName = "BspZipDirectories", Order = 6)] 79 | [XmlArrayItem(ElementName = "Game")] 80 | //public List GamesConfigs { get; set; } 81 | public ObservableCollection GamesConfigs { get; set; } 82 | 83 | /// 84 | /// List of maps configs (custom file directory) 85 | /// 86 | [XmlArray(ElementName = "CustomFilesDirectories", Order = 7)] 87 | [XmlArrayItem(ElementName = "Map")] 88 | public ObservableCollection MapsConfigs { get; set; } 89 | 90 | /// 91 | /// List of base directories the tool can browse and the file extensions allowed 92 | /// 93 | [XmlArray(ElementName = "WhiteListDirectories", Order = 8)] 94 | [XmlArrayItem(ElementName = "Directory")] 95 | public ObservableCollection DirectoriesRestrictions { get; set; } 96 | 97 | /// 98 | /// List of multi maps configs (multiple custom file directories) 99 | /// 100 | [XmlArray(ElementName = "MultiCustomFilesDirectories", Order = 9)] 101 | [XmlArrayItem(ElementName = "Map")] 102 | public ObservableCollection MultiMapsConfigs { get; set; } 103 | 104 | /// 105 | /// Last multi custom files directory name loaded by the tool 106 | /// 107 | [XmlElement(ElementName = "LastMultiCustomDirectory", Order = 10)] 108 | public string LastMultiCustomDirectory { get; set; } 109 | 110 | #endregion 111 | 112 | #region Constructor 113 | 114 | public ToolSettings() 115 | { 116 | } 117 | 118 | #endregion 119 | 120 | #region Methods - Init Attributes 121 | 122 | /// 123 | /// Initialize the attributes that werent automatically initialized wtih the xml file (cause of missing parameters).
124 | /// And delete any invalid attribute 125 | ///
126 | public void InitAllAttributes() 127 | { 128 | if (GamesConfigs == null) 129 | { 130 | InitGamesConfigs(); 131 | } 132 | else 133 | { 134 | // Delete any invalid game config (missing data that was removed from the xml) 135 | for (int i = GamesConfigs.Count - 1; i >= 0; i--) 136 | { 137 | if (!GamesConfigs[i].IsValid()) 138 | { 139 | GamesConfigs.RemoveAt(i); 140 | } 141 | } 142 | } 143 | if (MapsConfigs == null) 144 | { 145 | InitMapsConfigs(); 146 | } 147 | else 148 | { 149 | // Delete any invalid map config (missing data that was removed from the xml) 150 | for (int i = MapsConfigs.Count - 1; i >= 0; i--) 151 | { 152 | if (!MapsConfigs[i].IsValid()) 153 | { 154 | MapsConfigs.RemoveAt(i); 155 | } 156 | } 157 | } 158 | if (MultiMapsConfigs == null) 159 | { 160 | InitMultiMapsConfigs(); 161 | } 162 | else 163 | { 164 | // Delete any invalid map config (missing data that was removed from the xml) 165 | for (int i = MultiMapsConfigs.Count - 1; i >= 0; i--) 166 | { 167 | if (!MultiMapsConfigs[i].IsValid()) 168 | { 169 | MultiMapsConfigs.RemoveAt(i); 170 | } 171 | else 172 | { 173 | // Delete any invalid directory in the list 174 | for (int j = MultiMapsConfigs[i].ListPath.Count - 1; j >= 0; j--) 175 | { 176 | if (MultiMapsConfigs[i].ListPath[j] == null) 177 | { 178 | MultiMapsConfigs[i].ListPath.RemoveAt(j); 179 | } 180 | } 181 | } 182 | } 183 | } 184 | 185 | if (DirectoriesRestrictions == null) 186 | { 187 | InitDirectoriesRestrictions(); 188 | } 189 | else 190 | { 191 | // Delete any invalid directory restriction (missing data that was removed from the xml) 192 | for (int i = DirectoriesRestrictions.Count - 1; i >= 0; i--) 193 | { 194 | if (!DirectoriesRestrictions[i].IsValid()) 195 | { 196 | DirectoriesRestrictions.RemoveAt(i); 197 | } 198 | } 199 | } 200 | 201 | if (LastBsp == null) 202 | { 203 | LastBsp = string.Empty; 204 | } 205 | if (LastCustomDirectory == null) 206 | { 207 | LastCustomDirectory = string.Empty; 208 | } 209 | if (LastMultiCustomDirectory == null) 210 | { 211 | LastMultiCustomDirectory = string.Empty; 212 | } 213 | if (LastGame == null) 214 | { 215 | LastGame = string.Empty; 216 | } 217 | if (LastExtractDirectory == null) 218 | { 219 | LastExtractDirectory = string.Empty; 220 | } 221 | } 222 | 223 | private void InitGamesConfigs() 224 | { 225 | GamesConfigs = new ObservableCollection(); 226 | } 227 | private void InitMapsConfigs() 228 | { 229 | MapsConfigs = new ObservableCollection(); 230 | } 231 | private void InitMultiMapsConfigs() 232 | { 233 | MultiMapsConfigs = new ObservableCollection(); 234 | } 235 | private void InitDirectoriesRestrictions() 236 | { 237 | DirectoriesRestrictions = new ObservableCollection(); 238 | } 239 | 240 | #endregion 241 | 242 | #region Methods - Find Configs 243 | 244 | /// 245 | /// Find the first MapConfig (custom files location) corresponding to the given name 246 | /// 247 | /// name to search 248 | /// Return the corresponding MapConfig. null if none found 249 | public MapConfig FindMapConfig(string name) 250 | { 251 | foreach (MapConfig mapConfig in MapsConfigs) 252 | { 253 | if (name.Equals(mapConfig.Name)) 254 | { 255 | return mapConfig; 256 | } 257 | } 258 | return null; 259 | } 260 | 261 | /// 262 | /// Find the first MultiMapConfig (custom files location) corresponding to the given name 263 | /// 264 | /// name to search 265 | /// Return the corresponding MultiMapConfig. null if none found 266 | public MultiMapConfig FindMultiMapConfig(string name) 267 | { 268 | foreach (MultiMapConfig multiMapConfig in MultiMapsConfigs) 269 | { 270 | if (name.Equals(multiMapConfig.Name)) 271 | { 272 | return multiMapConfig; 273 | } 274 | } 275 | return null; 276 | } 277 | 278 | /// 279 | /// Find the first GameConfig (bspzip.exe location) corresponding to the given name 280 | /// 281 | /// name to search 282 | /// Return the corresponding GameConfig. null if none found 283 | public GameConfig FindGameConfig(string name) 284 | { 285 | foreach (GameConfig gameConfig in GamesConfigs) 286 | { 287 | if (name.Equals(gameConfig.Name)) 288 | { 289 | return gameConfig; 290 | } 291 | } 292 | return null; 293 | } 294 | 295 | #endregion 296 | 297 | } 298 | 299 | #endregion 300 | 301 | #region Class - GameConfig 302 | 303 | /// 304 | /// Store a game name and the path to its bspzip.exe 305 | /// 306 | [Serializable] 307 | public class GameConfig : INotifyPropertyChanged 308 | { 309 | 310 | #region Attributes 311 | 312 | private string name; 313 | private string bspZip; 314 | private string gameInfo; 315 | 316 | /// 317 | /// Name of the game 318 | /// 319 | public string Name 320 | { 321 | get => name; 322 | set 323 | { 324 | if (name != value) 325 | { 326 | name = value; 327 | NotifyPropertyChanged("Name"); 328 | } 329 | } 330 | } 331 | 332 | /// 333 | /// Path to bspzip.exe 334 | /// 335 | public string BspZip 336 | { 337 | get => bspZip; 338 | set 339 | { 340 | if (bspZip != value) 341 | { 342 | bspZip = value; 343 | NotifyPropertyChanged("BspZip"); 344 | } 345 | } 346 | } 347 | 348 | /// 349 | /// Path to the gameinfo.txt 350 | /// 351 | public string GameInfo 352 | { 353 | get => gameInfo; 354 | set 355 | { 356 | if (gameInfo != value) 357 | { 358 | gameInfo = value; 359 | NotifyPropertyChanged("GameInfo"); 360 | } 361 | } 362 | } 363 | 364 | #endregion 365 | 366 | #region Constructor 367 | 368 | public GameConfig() 369 | { 370 | } 371 | 372 | /// 373 | /// Create a default GameConfig 374 | /// 375 | /// A new instance of 376 | public static GameConfig GetDefaultGameConfig() 377 | { 378 | return new GameConfig 379 | { 380 | Name = "New Game", 381 | BspZip = @"C:\Program Files (x86)\Steam\steamapps\common\Counter-Strike Global Offensive\bin\bspzip.exe", 382 | GameInfo = @"C:\Program Files (x86)\Steam\steamapps\common\Counter-Strike Global Offensive\csgo\gameinfo.txt" 383 | }; 384 | } 385 | 386 | #endregion 387 | 388 | #region Methods 389 | 390 | /// 391 | /// Path to the folder of gameinfo.txt 392 | /// 393 | public string GameInfoFolder => System.IO.Path.GetDirectoryName(GameInfo); 394 | 395 | /// 396 | /// Verify if all values of the GameConfig are not null 397 | /// 398 | /// true if all value are not null, false otherwise 399 | public bool IsValid() 400 | { 401 | if (Name != null && BspZip != null && GameInfo != null) 402 | { 403 | return true; 404 | } 405 | return false; 406 | } 407 | 408 | /// 409 | /// Verify if the bspzip.exe and gameinfo.txt files exist 410 | /// 411 | /// true if the files exist, false otherwise 412 | public bool FilesExist() 413 | { 414 | if (System.IO.File.Exists(BspZip) && System.IO.File.Exists(GameInfo)) 415 | { 416 | return true; 417 | } 418 | return false; 419 | } 420 | 421 | #endregion 422 | 423 | #region INotifyPropertyChanged 424 | 425 | public event PropertyChangedEventHandler PropertyChanged; 426 | 427 | public void NotifyPropertyChanged(string propName) 428 | { 429 | PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName)); 430 | } 431 | 432 | #endregion 433 | 434 | } 435 | 436 | #endregion 437 | 438 | #region Class - MapConfig 439 | 440 | /// 441 | /// Store a map name and the path to its custom folder 442 | /// 443 | [Serializable] 444 | public class MapConfig : INotifyPropertyChanged 445 | { 446 | 447 | #region Attributes 448 | 449 | private string name; 450 | private string path; 451 | 452 | /// 453 | /// Name of the map 454 | /// 455 | public string Name 456 | { 457 | get => name; 458 | set 459 | { 460 | if (name != value) 461 | { 462 | name = value; 463 | NotifyPropertyChanged("Name"); 464 | } 465 | } 466 | } 467 | 468 | /// 469 | /// Path to the custom folder with files to pack 470 | /// 471 | public string Path 472 | { 473 | get => path; 474 | set 475 | { 476 | if (path != value) 477 | { 478 | path = value; 479 | NotifyPropertyChanged("Path"); 480 | } 481 | } 482 | } 483 | 484 | /// 485 | /// Extra string that will contains the directory path cleaned from extra character 486 | /// 487 | [XmlIgnore] 488 | public string CleanedPath { get; private set; } 489 | 490 | #endregion 491 | 492 | #region Constructor 493 | 494 | public MapConfig() 495 | { 496 | CleanedPath = null; 497 | } 498 | 499 | /// 500 | /// Create a default MapConfig 501 | /// 502 | /// A new instance of 503 | public static MapConfig GetDefaultMapConfig() 504 | { 505 | return new MapConfig 506 | { 507 | Name = "New Custom Folder", 508 | Path = @"C:\MyMappingProject\CurrentProject" 509 | }; 510 | } 511 | 512 | #endregion 513 | 514 | #region Methods 515 | 516 | /// 517 | /// Clean the path of the directory by removing extra '/' within the path 518 | /// and storing it in 519 | /// 520 | public void CleanPath() 521 | { 522 | CleanedPath = Utils.FileUtils.CleanDirectoryPath(Path); 523 | } 524 | 525 | /// 526 | /// Verify if all values of the MapConfig are not null 527 | /// 528 | /// true if all value are not null, false otherwise 529 | public bool IsValid() 530 | { 531 | if (Name != null && Path != null) 532 | { 533 | return true; 534 | } 535 | return false; 536 | } 537 | 538 | /// 539 | /// Return true if the custom directory exist 540 | /// 541 | /// 542 | public bool DirectoryExists() 543 | { 544 | if (System.IO.Directory.Exists(Path)) 545 | { 546 | return true; 547 | } 548 | return false; 549 | } 550 | 551 | #endregion 552 | 553 | #region INotifyPropertyChanged 554 | 555 | public event PropertyChangedEventHandler PropertyChanged; 556 | 557 | public void NotifyPropertyChanged(string propName) 558 | { 559 | PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName)); 560 | } 561 | 562 | #endregion 563 | 564 | } 565 | 566 | #endregion 567 | 568 | #region Class - MultiMapConfig 569 | 570 | /// 571 | /// Store a map name and the paths to its custom folders 572 | /// 573 | [Serializable] 574 | public class MultiMapConfig : INotifyPropertyChanged 575 | { 576 | 577 | #region Attributes 578 | 579 | private string name; 580 | 581 | /// 582 | /// Name of the map 583 | /// 584 | [XmlElement(Order = 1)] 585 | public string Name 586 | { 587 | get => name; 588 | set 589 | { 590 | if (name != value) 591 | { 592 | name = value; 593 | NotifyPropertyChanged("Name"); 594 | } 595 | } 596 | } 597 | 598 | /// 599 | /// List of paths to the custom folders with files to pack 600 | /// 601 | [XmlArray(ElementName = "Paths", Order = 2)] 602 | [XmlArrayItem(ElementName = "Path")] 603 | public ObservableCollection ListPath { get; set; } 604 | 605 | /// 606 | /// List of custom folders paths separated by a new line.
607 | /// Used by the Textbox 608 | ///
609 | [XmlIgnore] 610 | public string Path 611 | { 612 | get 613 | { 614 | const string separator = "\n"; 615 | return string.Join(separator, ListPath); 616 | } 617 | } 618 | 619 | /// 620 | /// List of custom folders paths as a dashed list.
621 | /// Used for logs 622 | ///
623 | [XmlIgnore] 624 | public string PathAsDashedList 625 | { 626 | get 627 | { 628 | // Non breaking space = \u00A0 629 | const string separator = "\n-\u00A0"; 630 | return (ListPath.Count > 0 ? "-\u00A0" : "") + string.Join(separator, ListPath); 631 | } 632 | } 633 | 634 | /// 635 | /// List that will contains all the directories paths cleaned from any extra character.
636 | /// Needed because modifying asynchronously a ObservableCollection is not allowed 637 | ///
638 | [XmlIgnore] 639 | public HashSet HashSetCleanedPath { get; private set; } 640 | 641 | #endregion 642 | 643 | #region Constructor 644 | 645 | public MultiMapConfig() 646 | { 647 | HashSetCleanedPath = new HashSet(); 648 | } 649 | 650 | /// 651 | /// Create a default MultiMapConfig 652 | /// 653 | /// A new instance of 654 | public static MultiMapConfig GetDefaultMultiMapConfig() 655 | { 656 | return new MultiMapConfig 657 | { 658 | Name = "New Custom Folders", 659 | ListPath = new ObservableCollection() 660 | }; 661 | } 662 | 663 | #endregion 664 | 665 | #region Methods 666 | 667 | /// 668 | /// Verify if all values of the MultiMapConfig are not null 669 | /// 670 | /// true if all value are not null, false otherwise 671 | public bool IsValid() 672 | { 673 | if (Name != null && ListPath != null) 674 | { 675 | return true; 676 | } 677 | return false; 678 | } 679 | 680 | /// 681 | /// Returns true if the list of path is not empty, false otherwise 682 | /// 683 | /// 684 | public bool IsNotEmpty() 685 | { 686 | return ListPath.Count > 0; 687 | } 688 | 689 | /// 690 | /// Verify if all custom directories exist 691 | /// 692 | /// True if all custom directories exist, False otherwise 693 | public bool DirectoriesExists() 694 | { 695 | foreach (string directory in ListPath) 696 | { 697 | if (!System.IO.Directory.Exists(directory)) 698 | { 699 | return false; 700 | } 701 | } 702 | return true; 703 | } 704 | 705 | /// 706 | /// Get a list of all the non existing directories, in the Multi Custom folder config 707 | /// 708 | /// List of directories 709 | public List GetNonExistingDirectories() 710 | { 711 | List listDirectories = new List(); 712 | foreach (string directory in ListPath) 713 | { 714 | if (!System.IO.Directory.Exists(directory)) 715 | { 716 | listDirectories.Add(directory); 717 | } 718 | } 719 | return listDirectories; 720 | } 721 | 722 | /// 723 | /// Create a HashSet with all unique directories paths from 724 | /// 725 | /// A HashSet of string 726 | public HashSet GetHashSetFromList() 727 | { 728 | return new HashSet(ListPath); 729 | } 730 | 731 | /// 732 | /// Clear and insert every element from the HashSet parameter 733 | /// 734 | /// List of unique directory path 735 | public void SetHashSetToList(HashSet directoriesHashSet) 736 | { 737 | ListPath.Clear(); 738 | foreach(string directory in directoriesHashSet) 739 | { 740 | ListPath.Add(directory); 741 | } 742 | } 743 | 744 | /// 745 | /// Clean the paths of the directories by removing extra '/' within the list of paths 746 | /// and storing them in 747 | /// 748 | public void CleanPaths() 749 | { 750 | HashSetCleanedPath.Clear(); 751 | foreach (string path in ListPath) 752 | { 753 | HashSetCleanedPath.Add(Utils.FileUtils.CleanDirectoryPath(path)); 754 | } 755 | } 756 | 757 | /// 758 | /// Force an GUI update on the list of directory, in the MainWindow 759 | /// 760 | public void NotifyDirectoryListUpdate() 761 | { 762 | // To update the ListBox 763 | NotifyPropertyChanged("ListPath"); 764 | // To update the TextBox 765 | NotifyPropertyChanged("Path"); 766 | } 767 | 768 | #endregion 769 | 770 | #region INotifyPropertyChanged 771 | 772 | public event PropertyChangedEventHandler PropertyChanged; 773 | 774 | public void NotifyPropertyChanged(string propName) 775 | { 776 | PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName)); 777 | } 778 | 779 | #endregion 780 | 781 | } 782 | 783 | #endregion 784 | 785 | #region Class - DirectoryRestrictions 786 | 787 | /// 788 | /// Store a directory name and the list of allowed files 789 | /// 790 | [Serializable] 791 | public class DirectoryRestrictions : INotifyPropertyChanged 792 | { 793 | 794 | #region Attributes 795 | 796 | private string directoryName; 797 | 798 | /// 799 | /// Name of the directory (materials, models, ...) 800 | /// 801 | [XmlElement(Order = 1)] 802 | public string DirectoryName 803 | { 804 | get => directoryName; 805 | set 806 | { 807 | if (directoryName != value) 808 | { 809 | directoryName = value; 810 | NotifyPropertyChanged("DirectoryName"); 811 | } 812 | } 813 | } 814 | 815 | /// 816 | /// Files Extensions allowed 817 | /// 818 | [XmlArray(ElementName = "Extensions", Order = 2)] 819 | [XmlArrayItem(ElementName = "Extension")] 820 | public List AllowedExtension { get; set; } 821 | 822 | /// 823 | /// String version of the extensions allowed separated by | 824 | /// 825 | [XmlIgnore] 826 | public string ExtensionStr 827 | { 828 | get 829 | { 830 | const string separator = "|"; 831 | return string.Join(separator, AllowedExtension); 832 | } 833 | set 834 | { 835 | const char separator = '|'; 836 | AllowedExtension = new List(value.Split(separator)); 837 | } 838 | } 839 | 840 | #endregion 841 | 842 | #region Constructor 843 | 844 | public DirectoryRestrictions() 845 | { 846 | } 847 | 848 | /// 849 | /// Create a default DirectoryRestrictions 850 | /// 851 | /// A new instance of 852 | public static DirectoryRestrictions GetDefaultDirectoryRestrictions() 853 | { 854 | return new DirectoryRestrictions 855 | { 856 | DirectoryName = "directory_name", 857 | AllowedExtension = new List() { ".txt", ".jpg" } 858 | }; 859 | } 860 | 861 | #endregion 862 | 863 | #region Methods 864 | 865 | /// 866 | /// Verify if all values of the DirectoryRestrictions are not null 867 | /// 868 | /// true if all value are not null, false otherwise 869 | public bool IsValid() 870 | { 871 | if (DirectoryName != null && AllowedExtension != null) 872 | { 873 | return true; 874 | } 875 | return false; 876 | } 877 | 878 | #endregion 879 | 880 | #region INotifyPropertyChanged 881 | 882 | public event PropertyChangedEventHandler PropertyChanged; 883 | 884 | public void NotifyPropertyChanged(string propName) 885 | { 886 | PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName)); 887 | } 888 | 889 | #endregion 890 | 891 | } 892 | 893 | #endregion 894 | 895 | } 896 | -------------------------------------------------------------------------------- /BspZipGUI/MainWindow.xaml: -------------------------------------------------------------------------------- 1 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | Bsp Packer 19 | Bsp Packer - Multiple 20 | Bsp Repack 21 | Bsp Extract 22 | Bsp Cubemaps 23 | Logs 24 | Settings 25 | 26 | 27 | 28 | 29 | 30 | Browse 31 | Game 32 | Custom Folder 33 | Custom Folders 34 | Bsp File 35 | 36 | 37 | 38 | 39 | 40 | Pack a custom folder into a BSP 41 | 42 | Pack Bsp 43 | 44 | 45 | Pack a custom folder into a BSP 46 | 47 | 48 | Use Directory Whitelist 49 | Only pack defined folders and files (Settings) 50 | 51 | Output to a new Bsp 52 | Pack the files into a new Bsp 53 | 54 | 55 | 56 | Pack multiple custom folders into a BSP 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | Repack the BSP to compress it (or decompress it) 67 | Compressing may take several seconds depending of the size of the BSP 68 | /!\ Warning: Not available for every game (e.g. CS:GO) 69 | 70 | 71 | Compress Bsp 72 | Decompress Bsp 73 | 74 | 75 | 76 | 77 | 78 | 79 | Extract the content of the BSP into a directory or into a .zip file 80 | /!\ Warning: Using it on a repacked BSP will not work 81 | 82 | 83 | Extract to Directory 84 | Extract to zip 85 | 86 | 87 | 88 | 89 | 90 | 91 | Extract cubemaps of the BSP into a directory or delete all cubemaps 92 | /!\ Warning: Deleting cubemaps will actually delete every VTF packed in the map 93 | 94 | 95 | Extract cubemaps 96 | Delete cubemaps 97 | 98 | 99 | 100 | 101 | 102 | 103 | Directories Whitelist 104 | Custom Folders 105 | Custom Folders - Multiple 106 | 107 | Games 108 | 109 | Name 110 | Add... 111 | Add Directory 112 | Remove Selected 113 | Delete 114 | Setup Games 115 | Setup Custom Folders 116 | Setup Multiple Custom Folders 117 | Save Settings 118 | 119 | Bspzip Path 120 | Gameinfo Path 121 | 122 | Directory Path 123 | Directories 124 | Directory Name 125 | Allowed Extensions 126 | Separate extensions by '|'. For example: .vmt|.vtf 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 184 | 185 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 201 | 203 | 204 | 205 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 219 | 220 | 223 | 224 | 225 | 226 | 228 | 229 | 230 | 231 | 233 | 234 | 235 | 236 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 267 | 268 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 284 | 286 | 287 | 288 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 302 | 303 | 306 | 307 | 308 | 309 | 311 | 312 | 313 | 314 | 316 | 317 | 318 | 319 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 347 | 348 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 362 | 363 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 378 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 406 | 407 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 421 | 422 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 436 | 437 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 453 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 481 | 482 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 496 | 497 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 511 | 512 | 515 | 516 | 517 | 518 | 519 | 520 | 521 | 522 | 523 | 524 | 525 | 528 | 531 | 532 | 533 | 534 | 535 | 536 | 537 | 538 | 539 | 540 | 541 | 542 | 543 | 544 | 545 | 546 | 547 | 548 | 549 | 552 | 553 | 554 | 555 | 556 | 557 | 558 | 559 | 560 | 561 | 562 | 563 | 564 | 565 | 566 | 567 | 568 | 569 | 570 | 571 | 572 | 573 | 574 | 575 | 576 | 577 | 578 | 579 | 580 | 581 | 582 | 584 | 585 | 588 | 589 | 590 | 591 | 592 | 593 | 594 | 595 | 596 | 597 | 598 | 599 | 600 | 601 | 602 | 603 | 604 | 605 | 606 | 607 | 608 | 610 | 613 | 614 | 615 | 616 | 617 | 618 | 619 | 620 | 621 | 623 | 626 | 627 | 628 | 629 | 630 | 631 | 632 | 633 | 636 | 637 | 638 | 639 | 640 | 641 | 642 | 643 | 644 | 645 | 646 | 647 | 648 | 649 | 650 | 651 | 652 | 654 | 655 | 658 | 659 | 660 | 661 | 662 | 663 | 664 | 665 | 666 | 667 | 668 | 669 | 670 | 671 | 672 | 673 | 674 | 675 | 676 | 677 | 678 | 680 | 683 | 684 | 685 | 686 | 687 | 688 | 689 | 690 | 691 | 694 | 695 | 696 | 697 | 698 | 699 | 700 | 701 | 702 | 703 | 704 | 705 | 706 | 707 | 708 | 709 | 711 | 712 | 715 | 716 | 717 | 718 | 719 | 720 | 721 | 722 | 723 | 724 | 725 | 726 | 727 | 728 | 729 | 730 | 731 | 732 | 733 | 734 | 735 | 736 | 737 | 740 | 741 | 742 | 743 | 744 | 747 | 748 | 751 | 752 | 753 | 754 | 755 | 756 | 757 | 758 | 759 | 760 | 761 | 762 | 765 | 766 | 767 | 768 | 769 | 770 | 771 | 772 | 773 | 774 | 775 | 776 | 777 | 778 | 779 | 780 | 783 | 784 | 787 | 788 | 789 | 790 | 791 | 792 | 793 | 794 | 795 | 796 | 797 | 798 | 799 | 800 | 801 | 802 | 803 | 804 | 805 | 806 | 807 | 809 | 810 | 811 | 812 | 813 | 814 | 815 | 816 | 817 | 818 | 821 | 822 | 823 | 824 | 825 | 826 | 827 | 828 | 829 | 830 | 833 | 834 | 835 | 836 | 837 | 838 | 839 | 840 | 841 | 842 | 843 | --------------------------------------------------------------------------------