private void btnExtract_Click(object sender, EventArgs e) { List<string> extractFailures = new List<string>(); // // If an out path hasn't been provided, make our own // if (txtFolder.Text == String.Empty) { txtFolder.Text = Path.GetFileNameWithoutExtension(txtFile.Text); } // // Remove slash, add slash (enforces a slash) // txtFolder.Text = txtFolder.Text.TrimEnd('/'); txtFolder.Text = txtFolder.Text + '/'; Addon addon; try { FileStream fs = new FileStream(txtFile.Text, FileMode.Open, FileAccess.ReadWrite); addon = new Addon(new Reader(fs)); } catch (Exception ex) { MessageBox.Show("Can't open the selected file.\nError happened: " + ex.Message, "Failed to extract addon", MessageBoxButtons.OK, MessageBoxIcon.Stop); return; } foreach (ContentFile entry in addon.Files) { // Make sure folder exists try { Directory.CreateDirectory(txtFolder.Text + Path.GetDirectoryName(entry.Path)); } catch (Exception) { // Noop } // Write the file to the disk try { using (FileStream file = new FileStream(txtFolder.Text + entry.Path, FileMode.Create, FileAccess.Write)) { file.Write(entry.Content, 0, (int)entry.Size); } } catch (Exception) { extractFailures.Add(entry.Path); } } if (extractFailures.Count == 0) MessageBox.Show("Successfully extracted the addon.", "Extract addon", MessageBoxButtons.OK, MessageBoxIcon.Information); else if (extractFailures.Count == 1) MessageBox.Show("Failed to extract " + extractFailures[0], "Extract addon", MessageBoxButtons.OK, MessageBoxIcon.Warning); else if (extractFailures.Count > 1) { DialogResult showFailedFiles = MessageBox.Show(extractFailures.Count + " files failed to extract." + "\n\nShow a list of failures?", "Extract addon", MessageBoxButtons.YesNo, MessageBoxIcon.Warning); if (showFailedFiles == DialogResult.Yes) { string temppath = ContentFile.GenerateExternalPath( new Random().Next() + "_failedExtracts") + ".txt"; try { File.WriteAllText(temppath, "These files failed to extract:\r\n\r\n" + String.Join("\r\n", extractFailures.ToArray())); } catch (Exception) { MessageBox.Show("Can't show the list, an error happened generating it.", "Extract addon", MessageBoxButtons.OK, MessageBoxIcon.Stop); return; } // The file will be opened by the user's default text file handler (Notepad?) System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo(temppath) { UseShellExecute = true, }); } } btnAbort_Click(sender, e); // Close the form return; }
/// <summary> /// Sets the tags of an addon. /// </summary> /// <param name="addon">The addon to modify.</param> /// <param name="tagsInput">Optional. The new tags the addon should have.</param> private static void SetTags(Addon addon, string[] tagsInput = null) { List <string> tags = new List <string>(2); if (tagsInput == null || tagsInput.Length == 0 || tagsInput[0] == String.Empty) { bool allTagsValid = false; while (!allTagsValid) { tags.Clear(); Console.ForegroundColor = ConsoleColor.Yellow; Console.WriteLine("Tags? "); Console.ResetColor(); Console.Write("Please choose ZERO, ONE or TWO from the following: "); Console.WriteLine(String.Join(" ", Tags.Misc)); tagsInput = Console.ReadLine().Split(' '); allTagsValid = true; if (tagsInput[0] != String.Empty) { // More than zero (one or two) elements: add the first one. if (tagsInput.Length > 0) { if (!Tags.TagExists(tagsInput[0])) { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("The specified tag \"" + tagsInput[0] + "\" is not valid."); Console.ResetColor(); allTagsValid = false; continue; } else { tags.Add(tagsInput[0]); } } // More than one (two) elements: add the second one too. if (tagsInput.Length > 1) { if (!Tags.TagExists(tagsInput[1])) { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("The specified tag \"" + tagsInput[1] + "\" is not valid."); Console.ResetColor(); allTagsValid = false; continue; } else { tags.Add(tagsInput[1]); } } if (tagsInput.Length > 2) { Console.ForegroundColor = ConsoleColor.DarkYellow; Console.WriteLine("More than two tags specified. Only the first two is saved."); Console.ResetColor(); } } } } else { if (tagsInput[0] != String.Empty) { // More than zero (one or two) elements: add the first one. if (tagsInput.Length > 0) { if (!Tags.TagExists(tagsInput[0])) { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("The specified tag \"" + tagsInput[0] + "\" is not valid."); Console.ResetColor(); return; } else { tags.Add(tagsInput[0]); } } // More than one (two) elements: add the second one too. if (tagsInput.Length > 1) { if (!Tags.TagExists(tagsInput[1])) { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("The specified tag \"" + tagsInput[1] + "\" is not valid."); Console.ResetColor(); return; } else { tags.Add(tagsInput[1]); } } if (tagsInput.Length > 2) { Console.ForegroundColor = ConsoleColor.DarkYellow; Console.WriteLine("More than two tags specified. Only the first two is saved."); Console.ResetColor(); } } } addon.Tags = tags; }
/// <summary> /// Creates a JSON string using the properties of the provided Addon. /// </summary> /// <param name="addon">The addon which metadata is to be used.</param> /// <returns>The compiled JSON string.</returns> /// <exception cref="AddonJSONException">Errors regarding creating the JSON.</exception> public static string BuildDescription(Addon addon) { DescriptionJSON tree = new DescriptionJSON(); tree.Description = addon.Description; // Load the addon type if (addon.Type.ToLowerInvariant() == String.Empty || addon.Type.ToLowerInvariant() == null) throw new AddonJSONException("type is empty!"); else { if (!SharpGMad.Tags.TypeExists(addon.Type.ToLowerInvariant())) throw new AddonJSONException("type isn't a supported type!"); else tree.Type = addon.Type.ToLowerInvariant(); } // Parse the tags tree.Tags = new List<string>(); if (addon.Tags.Count > 2) throw new AddonJSONException("too many tags - specify 2 only!"); else { foreach (string tag in addon.Tags) { if (tag == String.Empty || tag == null) continue; if (!SharpGMad.Tags.TagExists(tag.ToLowerInvariant())) throw new AddonJSONException("tag isn't a supported word!"); else tree.Tags.Add(tag.ToLowerInvariant()); } } string strOutput; using (MemoryStream stream = new MemoryStream()) { DataContractJsonSerializer jsonFormatter = new DataContractJsonSerializer(typeof(DescriptionJSON)); try { jsonFormatter.WriteObject(stream, tree); } catch (SerializationException ex) { throw new AddonJSONException("Couldn't create json", ex); } stream.Seek(0, SeekOrigin.Begin); byte[] bytes = new byte[stream.Length]; stream.Read(bytes, 0, (int)stream.Length); strOutput = Encoding.ASCII.GetString(bytes); } return strOutput; }
/// <summary> /// Legacy GMAD operation to create an addon file using contents of a specified folder. /// </summary> /// <param name="strFolder">The folder containing the raw content.</param> /// <param name="strOutfile">The path of the addon file to write.</param> /// <param name="warnInvalid">Whether there should be a warning for files failing to validate /// instead of a full exception halt.</param> /// <returns>Integer error code: 0 if success, 1 if error.</returns> static int CreateAddonFile(string strFolder, string strOutfile, bool warnInvalid) { // // Make sure there's a slash on the end // strFolder = strFolder.TrimEnd('/'); strFolder = strFolder + "/"; // // Make sure OutFile ends in .gma // strOutfile = Path.GetFileNameWithoutExtension(strOutfile); strOutfile += ".gma"; Console.WriteLine("Looking in folder \"" + strFolder + "\""); Addon addon = null; if (File.Exists(strFolder + Path.DirectorySeparatorChar + "addon.json")) { // Use addon.json for metadata if it exists if (File.Exists(strFolder + Path.DirectorySeparatorChar + "info.txt") || File.Exists(strFolder + Path.DirectorySeparatorChar + "addon.txt")) { Console.ForegroundColor = ConsoleColor.Yellow; Console.WriteLine("Both addon.json and legacy info.txt/addon.txt found in source folder."); Console.WriteLine("addon.json takes priority"); Console.ResetColor(); } // // Load the Addon Info file // Json addonInfo; try { addonInfo = new Json(strFolder + "addon.json"); } catch (AddonJSONException ex) { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine(strFolder + "addon.json error: " + ex.Message); Console.ResetColor(); return(1); } addon = new Addon(addonInfo); } else if (File.Exists(strFolder + Path.DirectorySeparatorChar + "info.txt") || File.Exists(strFolder + Path.DirectorySeparatorChar + "addon.txt")) { // Load the addon metadata from the old file structure: info.txt/addon.txt string legacyInfo = String.Empty; try { if (File.Exists(strFolder + Path.DirectorySeparatorChar + "info.txt")) { legacyInfo = File.ReadAllText(strFolder + Path.DirectorySeparatorChar + "info.txt"); } else if (File.Exists(strFolder + Path.DirectorySeparatorChar + "addon.txt")) { legacyInfo = File.ReadAllText(strFolder + Path.DirectorySeparatorChar + "addon.txt"); } } catch (Exception ex) { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("Failed to read metadata."); Console.ResetColor(); Console.WriteLine(ex.Message); return(1); } addon = new Addon(); // Parse the read data Regex regex = new System.Text.RegularExpressions.Regex("\"([A-Za-z_\r\n]*)\"[\\s]*\"([\\s\\S]*?)\"", RegexOptions.IgnoreCase | RegexOptions.Multiline); MatchCollection matches = regex.Matches(legacyInfo); // info.txt/addon.txt files usually have these values not directly mapped into GMAs as well. string AuthorName = String.Empty; string AuthorEmail = String.Empty; string AuthorURL = String.Empty; string Version = String.Empty; string Date = String.Empty; foreach (Match keyMatch in matches) { if (keyMatch.Groups.Count == 3) { // All match should have 2 groups matched (the 0th group is the whole match.) switch (keyMatch.Groups[1].Value.ToLowerInvariant()) { case "name": addon.Title = keyMatch.Groups[2].Value; break; case "version": Version = keyMatch.Groups[2].Value; break; case "up_date": Date = keyMatch.Groups[2].Value; break; case "author_name": //addon.Author = keyMatch.Groups[2].Value; // GMAD writer only writes "Author Name" right now... AuthorName = keyMatch.Groups[2].Value; break; case "author_email": AuthorEmail = keyMatch.Groups[2].Value; break; case "author_url": AuthorURL = keyMatch.Groups[2].Value; break; case "info": addon.Description = keyMatch.Groups[2].Value; break; } } } // Prettify the loaded Description. string newDescription = String.Empty; bool hasNewDescription = (!String.IsNullOrWhiteSpace(AuthorName) || !String.IsNullOrWhiteSpace(AuthorEmail) || !String.IsNullOrWhiteSpace(AuthorURL) || !String.IsNullOrWhiteSpace(Version) || !String.IsNullOrWhiteSpace(Date)); if (hasNewDescription) { newDescription = "## Converted by SharpGMad " + Program.PrettyVersion + " at " + DateTime.Now.ToString("yyyy. MM. dd. hh:mm:ss") + " (+" + TimeZoneInfo.Local.BaseUtcOffset.ToString("hhmm") + ")"; } if (!String.IsNullOrWhiteSpace(AuthorName)) { newDescription += "\n# AuthorName: " + AuthorName; } if (!String.IsNullOrWhiteSpace(AuthorEmail)) { newDescription += "\n# AuthorEmail: " + AuthorEmail; } if (!String.IsNullOrWhiteSpace(AuthorURL)) { newDescription += "\n# AuthorURL: " + AuthorURL; } if (!String.IsNullOrWhiteSpace(Version)) { newDescription += "\n# Version: " + Version; } if (!String.IsNullOrWhiteSpace(Date)) { newDescription += "\n# Date: " + Date; } if (hasNewDescription) { // If anything was added to the prettifiction newDescription += "\n## End conversion info"; addon.Description = newDescription + (!String.IsNullOrWhiteSpace(addon.Description) ? Environment.NewLine + addon.Description : null); } Console.WriteLine("Addon: " + addon.Title); if (hasNewDescription) { Console.WriteLine(newDescription); } Console.ForegroundColor = ConsoleColor.Magenta; Console.WriteLine("You need to set the title, and optionally, the tags for this addon!"); Console.ResetColor(); SetType(addon); SetTags(addon); } else { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("Failed to find addon metadata file \"addon.json\", \"info.txt\" or \"addon.txt\""); Console.ResetColor(); return(1); } // // Get a list of files in the specified folder // foreach (string f in Directory.GetFiles(strFolder, "*", SearchOption.AllDirectories)) { string file = f; file = file.Replace(strFolder, String.Empty); file = file.Replace('\\', '/'); if (file == "addon.json" || file == "info.txt") { continue; // Don't read the metadata file } Console.WriteLine("\t" + file); try { addon.CheckRestrictions(file); addon.AddFile(file, File.ReadAllBytes(f)); } catch (IOException) { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("Unable to read file " + file); Console.ResetColor(); continue; } catch (IgnoredException) { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("\t\t[Ignored]"); Console.ResetColor(); continue; } catch (WhitelistException) { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("\t\t[Not allowed by whitelist]"); Console.ResetColor(); if (!warnInvalid) { return(1); } } } // // Sort the list into alphabetical order, no real reason - we're just ODC // addon.Sort(); // // Create an addon file in a buffer // // // Save the buffer to the provided name // FileStream fs; try { fs = new FileStream(strOutfile, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None); } catch (Exception) { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("Couldn't save to file \"" + strOutfile + "\""); Console.ResetColor(); return(1); } fs.SetLength(0); try { Writer.Create(addon, fs); } catch (Exception) { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("Failed to create the addon."); Console.ResetColor(); return(1); } fs.Flush(); // // Success! // Console.WriteLine("Successfully saved to \"" + strOutfile + "\" [" + ((int)fs.Length).HumanReadableSize() + "]"); fs.Dispose(); return(0); }
/// <summary> /// Legacy GMAD operation to extract an addon file to a specified folder. /// </summary> /// <param name="strFile">The file path of the GMA to extract.</param> /// <param name="strOutPath">The folder where the addon is to be extracted to.</param> /// <param name="gmod12">True if the extract should also create a legacy info.txt file.</param> /// <returns>Integer error code: 0 if success, 1 if error.</returns> static int ExtractAddonFile(string strFile, string strOutPath = "", bool gmod12 = false) { Console.WriteLine("Opening " + strFile); // // If an out path hasn't been provided, make our own // if (strOutPath == String.Empty) { strOutPath = Path.GetFileNameWithoutExtension(strFile); } // // Remove slash, add slash (enforces a slash) // strOutPath = strOutPath.TrimEnd('/'); strOutPath = strOutPath + '/'; Addon addon; try { FileStream fs = new FileStream(strFile, FileMode.Open, FileAccess.Read, FileShare.None); addon = new Addon(new Reader(fs)); } catch (Exception ex) { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("There was a problem opening or parsing the file"); Console.ResetColor(); Console.WriteLine(ex.Message); return(1); } Console.WriteLine("Extracting Files:"); foreach (ContentFile entry in addon.Files) { Console.WriteLine("\t" + entry.Path + " [" + ((int)entry.Size).HumanReadableSize() + "]"); // Make sure folder exists try { Directory.CreateDirectory(strOutPath + Path.GetDirectoryName(entry.Path)); } catch (Exception) { // Noop } // Write the file to the disk try { using (FileStream file = new FileStream(strOutPath + entry.Path, FileMode.Create, FileAccess.Write)) { file.Write(entry.Content, 0, (int)entry.Size); } } catch (Exception) { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("\t\tCouldn't extract!"); Console.ResetColor(); } } if (gmod12) // Write a legacy info.txt schema { // The description has paramteres if the addon was created by a conversion. // Extract them out. Regex regex = new System.Text.RegularExpressions.Regex("^# ([\\s\\S]*?): ([\\s\\S]*?)$", RegexOptions.IgnoreCase | RegexOptions.Multiline); MatchCollection matches = regex.Matches(addon.Description); // info.txt/addon.txt files usually have these values not directly mapped into GMAs as well. string AuthorName = String.Empty; string AuthorEmail = String.Empty; string AuthorURL = String.Empty; string Version = String.Empty; string Date = String.Empty; foreach (Match keyMatch in matches) { if (keyMatch.Groups.Count == 3) { // All match should have 2 groups matched (the 0th group is the whole match.) switch (keyMatch.Groups[1].Value.ToLowerInvariant()) { case "version": Version = keyMatch.Groups[2].Value.TrimEnd('\n', '\r', '\t'); break; case "date": Date = keyMatch.Groups[2].Value.TrimEnd('\n', '\r', '\t'); break; case "authorname": AuthorName = keyMatch.Groups[2].Value.TrimEnd('\n', '\r', '\t'); break; case "authoremail": AuthorEmail = keyMatch.Groups[2].Value.TrimEnd('\n', '\r', '\t'); break; case "authorurl": AuthorURL = keyMatch.Groups[2].Value.TrimEnd('\n', '\r', '\t'); break; } } } string endConversionInfo = "## End conversion info"; string description = addon.Description; if (addon.Description.IndexOf(endConversionInfo) > 0) { description = addon.Description.Substring(addon.Description.IndexOf(endConversionInfo) + endConversionInfo.Length); description = description.TrimStart('\r', '\n'); } File.WriteAllText(strOutPath + "info.txt", "\"AddonInfo\"\n" + "{\n" + "\t" + "\"name\"" + "\t" + "\"" + addon.Title + "\"\n" + "\t" + "\"version\"" + "\t" + "\"" + Version + "\"\n" + "\t" + "\"up_date\"" + "\t" + "\"" + (String.IsNullOrWhiteSpace(Date) ? addon.Timestamp.ToString("ddd MM dd hh:mm:ss yyyy", System.Globalization.CultureInfo.InvariantCulture) : DateTime.Now.ToString("ddd MM dd hh:mm:ss yyyy", System.Globalization.CultureInfo.InvariantCulture) + " (+" + TimeZoneInfo.Local.BaseUtcOffset.ToString("hhmm") + ")") + "\"\n" + "\t" + "\"author_name\"" + "\t" + "\"" + AuthorName + "\"\n" + // addon.Author would be nice "\t" + "\"author_email\"" + "\t" + "\"" + AuthorEmail + "\"\n" + "\t" + "\"author_url\"" + "\t" + "\"" + AuthorURL + "\"\n" + "\t" + "\"info\"" + "\t" + "\"" + description + "\"\n" + "\t" + "\"override\"" + "\t" + "\"1\"\n" + "}"); } Console.WriteLine("Done!"); return(0); }
/// <summary> /// Creates a new, empty addon and encapsulates it within a realtime instance. /// </summary> /// <param name="filename">The path of the addon file to create.</param> /// <returns>A RealtimeAddon instance.</returns> /// <exception cref="UnauthorizedAccessException">The specified file already exists on the local filesystem.</exception> /// <exception cref="IOException">There was an error creating a specified file.</exception> public static RealtimeAddon New(string filename) { if (File.Exists(filename)) { throw new UnauthorizedAccessException("The file already exists."); } if (Path.GetExtension(filename) != "gma") { filename = Path.GetFileNameWithoutExtension(filename); filename += ".gma"; } FileStream fs; try { fs = new FileStream(filename, FileMode.Create, FileAccess.ReadWrite, FileShare.None); } catch (IOException) { throw; } Addon addon = new Addon(); RealtimeAddon realtime = new RealtimeAddon(addon, fs); return realtime; }
/// <summary> /// Creates a JSON string using the properties of the provided Addon. /// </summary> /// <param name="addon">The addon which metadata is to be used.</param> /// <returns>The compiled JSON string.</returns> /// <exception cref="AddonJSONException">Errors regarding creating the JSON.</exception> public static string BuildDescription(Addon addon) { DescriptionJSON tree = new DescriptionJSON(); tree.Description = addon.Description; // Load the addon type if (addon.Type.ToLowerInvariant() == String.Empty || addon.Type.ToLowerInvariant() == null) { throw new AddonJSONException("type is empty!"); } else { if (!SharpGMad.Tags.TypeExists(addon.Type.ToLowerInvariant())) { throw new AddonJSONException("type isn't a supported type!"); } else { tree.Type = addon.Type.ToLowerInvariant(); } } // Parse the tags tree.Tags = new List <string>(); if (addon.Tags.Count > 2) { throw new AddonJSONException("too many tags - specify 2 only!"); } else { foreach (string tag in addon.Tags) { if (tag == String.Empty || tag == null) { continue; } if (!SharpGMad.Tags.TagExists(tag.ToLowerInvariant())) { throw new AddonJSONException("tag isn't a supported word!"); } else { tree.Tags.Add(tag.ToLowerInvariant()); } } } string strOutput; using (MemoryStream stream = new MemoryStream()) { DataContractJsonSerializer jsonFormatter = new DataContractJsonSerializer(typeof(DescriptionJSON)); try { jsonFormatter.WriteObject(stream, tree); } catch (SerializationException ex) { throw new AddonJSONException("Couldn't create json", ex); } stream.Seek(0, SeekOrigin.Begin); byte[] bytes = new byte[stream.Length]; stream.Read(bytes, 0, (int)stream.Length); strOutput = Encoding.ASCII.GetString(bytes); strOutput = strOutput.Replace("\\u000d", "").Replace("\\u0009", "\\t").Replace("\\u000a", "\\n"); } return(strOutput); }
private void btnExtract_Click(object sender, EventArgs e) { List <string> extractFailures = new List <string>(); // // If an out path hasn't been provided, make our own // if (txtFolder.Text == String.Empty) { txtFolder.Text = Path.GetFileNameWithoutExtension(txtFile.Text); } // // Remove slash, add slash (enforces a slash) // txtFolder.Text = txtFolder.Text.TrimEnd('/'); txtFolder.Text = txtFolder.Text + '/'; Addon addon; try { FileStream fs = new FileStream(txtFile.Text, FileMode.Open, FileAccess.ReadWrite); addon = new Addon(new Reader(fs)); } catch (Exception ex) { MessageBox.Show("Can't open the selected file.\nError happened: " + ex.Message, "Failed to extract addon", MessageBoxButtons.OK, MessageBoxIcon.Stop); return; } foreach (ContentFile entry in addon.Files) { // Make sure folder exists try { Directory.CreateDirectory(txtFolder.Text + Path.GetDirectoryName(entry.Path)); } catch (Exception) { // Noop } // Write the file to the disk try { using (FileStream file = new FileStream(txtFolder.Text + entry.Path, FileMode.Create, FileAccess.Write)) { file.Write(entry.Content, 0, (int)entry.Size); } } catch (Exception) { extractFailures.Add(entry.Path); } } if (chkWriteLegacy.Checked) // Write a legacy info.txt schema { // The description has paramteres if the addon was created by a conversion. // Extract them out. Regex regex = new System.Text.RegularExpressions.Regex("^# ([\\s\\S]*?): ([\\s\\S]*?)$", RegexOptions.IgnoreCase | RegexOptions.Multiline); MatchCollection matches = regex.Matches(addon.Description); // info.txt/addon.txt files usually have these values not directly mapped into GMAs as well. string AuthorName = String.Empty; string AuthorEmail = String.Empty; string AuthorURL = String.Empty; string Version = String.Empty; string Date = String.Empty; foreach (Match keyMatch in matches) { if (keyMatch.Groups.Count == 3) { // All match should have 2 groups matched (the 0th group is the whole match.) switch (keyMatch.Groups[1].Value.ToLowerInvariant()) { case "version": Version = keyMatch.Groups[2].Value.TrimEnd('\n', '\r', '\t'); break; case "date": Date = keyMatch.Groups[2].Value.TrimEnd('\n', '\r', '\t'); break; case "authorname": AuthorName = keyMatch.Groups[2].Value.TrimEnd('\n', '\r', '\t'); break; case "authoremail": AuthorEmail = keyMatch.Groups[2].Value.TrimEnd('\n', '\r', '\t'); break; case "authorurl": AuthorURL = keyMatch.Groups[2].Value.TrimEnd('\n', '\r', '\t'); break; } } } string endConversionInfo = "## End conversion info"; string description = addon.Description; if (addon.Description.IndexOf(endConversionInfo) > 0) { description = addon.Description.Substring(addon.Description.IndexOf(endConversionInfo) + endConversionInfo.Length); description = description.TrimStart('\r', '\n'); } File.WriteAllText(txtFolder.Text + Path.DirectorySeparatorChar + "info.txt", "\"AddonInfo\"\n" + "{\n" + "\t" + "\"name\"" + "\t" + "\"" + addon.Title + "\"\n" + "\t" + "\"version\"" + "\t" + "\"" + Version + "\"\n" + "\t" + "\"up_date\"" + "\t" + "\"" + (String.IsNullOrWhiteSpace(Date) ? addon.Timestamp.ToString("ddd MM dd hh:mm:ss yyyy", System.Globalization.CultureInfo.InvariantCulture) : DateTime.Now.ToString("ddd MM dd hh:mm:ss yyyy", System.Globalization.CultureInfo.InvariantCulture) + " (+" + TimeZoneInfo.Local.BaseUtcOffset.ToString("hhmm") + ")") + "\"\n" + "\t" + "\"author_name\"" + "\t" + "\"" + AuthorName + "\"\n" + // addon.Author would be nice "\t" + "\"author_email\"" + "\t" + "\"" + AuthorEmail + "\"\n" + "\t" + "\"author_url\"" + "\t" + "\"" + AuthorURL + "\"\n" + "\t" + "\"info\"" + "\t" + "\"" + description + "\"\n" + "\t" + "\"override\"" + "\t" + "\"1\"\n" + "}"); } if (extractFailures.Count == 0) { MessageBox.Show("Successfully extracted the addon.", "Extract addon", MessageBoxButtons.OK, MessageBoxIcon.Information); } else if (extractFailures.Count == 1) { MessageBox.Show("Failed to extract " + extractFailures[0], "Extract addon", MessageBoxButtons.OK, MessageBoxIcon.Warning); } else if (extractFailures.Count > 1) { DialogResult showFailedFiles = MessageBox.Show(extractFailures.Count + " files failed to extract." + "\n\nShow a list of failures?", "Extract addon", MessageBoxButtons.YesNo, MessageBoxIcon.Warning); if (showFailedFiles == DialogResult.Yes) { string temppath = ContentFile.GenerateExternalPath( new Random().Next() + "_failedExtracts") + ".txt"; try { File.WriteAllText(temppath, "These files failed to extract:\r\n\r\n" + String.Join("\r\n", extractFailures.ToArray())); } catch (Exception) { MessageBox.Show("Can't show the list, an error happened generating it.", "Extract addon", MessageBoxButtons.OK, MessageBoxIcon.Stop); return; } // The file will be opened by the user's default text file handler (Notepad?) System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo(temppath) { UseShellExecute = true, }); } } btnAbort_Click(sender, e); // Close the form return; }
/// <summary> /// Loads the specified addon from the local filesystem and encapsulates it within a realtime instance. /// </summary> /// <param name="filename">The path to the file on the local filesystem.</param> /// <param name="readOnly">True if the file is to be opened read-only, false otherwise</param> /// <returns>A RealtimeAddon instance.</returns> /// <exception cref="FileNotFoundException">Happens if the specified file does not exist.</exception> /// <exception cref="IOException">Thrown if there is a problem opening the specified file.</exception> /// <exception cref="ReaderException">Thrown if the addon reader and parser encounters an error.</exception> /// <exception cref="ArgumentException">Happens if a file with the same path is already added.</exception> /// <exception cref="WhitelistException">There is a file prohibited from storing by the global whitelist.</exception> /// <exception cref="IgnoredException">There is a file prohibited from storing by the addon's ignore list.</exception> public static RealtimeAddon Load(string filename, bool readOnly = false) { if (!File.Exists(filename)) { throw new FileNotFoundException("The specified file " + filename + " does not exist."); } FileStream fs = null; try { if (!readOnly) { fs = new FileStream(filename, FileMode.Open, FileAccess.ReadWrite, FileShare.None); } else { fs = new FileStream(filename, FileMode.Open, FileAccess.Read); } } catch (IOException) { if (fs != null) { fs.Dispose(); } throw; } Reader r; try { r = new Reader(fs); } catch (IOException) { fs.Dispose(); throw; } catch (ReaderException) { fs.Dispose(); throw; } Addon addon; try { addon = new Addon(r); } catch (ArgumentException) { fs.Dispose(); throw; } catch (WhitelistException) { fs.Dispose(); throw; } catch (IgnoredException) { fs.Dispose(); throw; } RealtimeAddon realtime = new RealtimeAddon(addon, fs); realtime.AddonReader = r; return(realtime); }
/// <summary> /// Sets the tags of an addon. /// </summary> /// <param name="addon">The addon to modify.</param> /// <param name="tagsInput">Optional. The new tags the addon should have.</param> private static void SetTags(Addon addon, string[] tagsInput = null) { List<string> tags = new List<string>(2); if (tagsInput == null || tagsInput.Length == 0 || tagsInput[0] == String.Empty) { bool allTagsValid = false; while (!allTagsValid) { tags.Clear(); Console.ForegroundColor = ConsoleColor.Yellow; Console.WriteLine("Tags? "); Console.ResetColor(); Console.Write("Please choose ZERO, ONE or TWO from the following: "); Console.WriteLine(String.Join(" ", Tags.Misc)); tagsInput = Console.ReadLine().Split(' '); allTagsValid = true; if (tagsInput[0] != String.Empty) { // More than zero (one or two) elements: add the first one. if (tagsInput.Length > 0) { if (!Tags.TagExists(tagsInput[0])) { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("The specified tag \"" + tagsInput[0] + "\" is not valid."); Console.ResetColor(); allTagsValid = false; continue; } else tags.Add(tagsInput[0]); } // More than one (two) elements: add the second one too. if (tagsInput.Length > 1) { if (!Tags.TagExists(tagsInput[1])) { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("The specified tag \"" + tagsInput[1] + "\" is not valid."); Console.ResetColor(); allTagsValid = false; continue; } else tags.Add(tagsInput[1]); } if (tagsInput.Length > 2) { Console.ForegroundColor = ConsoleColor.DarkYellow; Console.WriteLine("More than two tags specified. Only the first two is saved."); Console.ResetColor(); } } } } else { if (tagsInput[0] != String.Empty) { // More than zero (one or two) elements: add the first one. if (tagsInput.Length > 0) { if (!Tags.TagExists(tagsInput[0])) { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("The specified tag \"" + tagsInput[0] + "\" is not valid."); Console.ResetColor(); return; } else tags.Add(tagsInput[0]); } // More than one (two) elements: add the second one too. if (tagsInput.Length > 1) { if (!Tags.TagExists(tagsInput[1])) { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("The specified tag \"" + tagsInput[1] + "\" is not valid."); Console.ResetColor(); return; } else tags.Add(tagsInput[1]); } if (tagsInput.Length > 2) { Console.ForegroundColor = ConsoleColor.DarkYellow; Console.WriteLine("More than two tags specified. Only the first two is saved."); Console.ResetColor(); } } } addon.Tags = tags; }
/// <summary> /// Sets the type of an addon. /// </summary> /// <param name="addon">The addon to modify.</param> /// <param name="type">Optional. The new type the addon should have.</param> private static void SetType(Addon addon, string type = null) { if (type == String.Empty || type == null) { while (!Tags.TypeExists(type)) { Console.ForegroundColor = ConsoleColor.Yellow; Console.WriteLine("Type? "); Console.ResetColor(); Console.Write("Please choose ONE from the following: "); Console.WriteLine(String.Join(" ", Tags.Type)); type = Console.ReadLine(); if (!Tags.TypeExists(type)) { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("The specified type is not valid."); Console.ResetColor(); } } } else { if (!Tags.TypeExists(type)) { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("The specified type is not valid."); Console.ResetColor(); return; } } addon.Type = type; }
/// <summary> /// Legacy GMAD operation to extract an addon file to a specified folder. /// </summary> /// <param name="strFile">The file path of the GMA to extract.</param> /// <param name="strOutPath">The folder where the addon is to be extracted to.</param> /// <returns>Integer error code: 0 if success, 1 if error.</returns> static int ExtractAddonFile(string strFile, string strOutPath = "") { Console.WriteLine("Opening " + strFile); // // If an out path hasn't been provided, make our own // if (strOutPath == String.Empty) { strOutPath = Path.GetFileNameWithoutExtension(strFile); } // // Remove slash, add slash (enforces a slash) // strOutPath = strOutPath.TrimEnd('/'); strOutPath = strOutPath + '/'; Addon addon; try { FileStream fs = new FileStream(strFile, FileMode.Open, FileAccess.ReadWrite, FileShare.None); addon = new Addon(new Reader(fs)); } catch (Exception ex) { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("There was a problem opening or parsing the file"); Console.ResetColor(); Console.WriteLine(ex.Message); return 1; } Console.WriteLine("Extracting Files:"); foreach (ContentFile entry in addon.Files) { Console.WriteLine("\t" + entry.Path + " [" + ((int)entry.Size).HumanReadableSize() + "]"); // Make sure folder exists try { Directory.CreateDirectory(strOutPath + Path.GetDirectoryName(entry.Path)); } catch (Exception) { // Noop } // Write the file to the disk try { using (FileStream file = new FileStream(strOutPath + entry.Path, FileMode.Create, FileAccess.Write)) { file.Write(entry.Content, 0, (int)entry.Size); } } catch (Exception) { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("\t\tCouldn't extract!"); Console.ResetColor(); } } Console.WriteLine("Done!"); return 0; }
/// <summary> /// Legacy GMAD operation to create an addon file using contents of a specified folder. /// </summary> /// <param name="strFolder">The folder containing the raw content.</param> /// <param name="strOutfile">The path of the addon file to write.</param> /// <param name="warnInvalid">Whether there should be a warning for files failing to validate /// instead of a full exception halt.</param> /// <returns>Integer error code: 0 if success, 1 if error.</returns> static int CreateAddonFile(string strFolder, string strOutfile, bool warnInvalid, bool gmod12convert = false) { // // Make sure there's a slash on the end // strFolder = strFolder.TrimEnd('/'); strFolder = strFolder + "/"; // // Make sure OutFile ends in .gma // strOutfile = Path.GetFileNameWithoutExtension(strOutfile); strOutfile += ".gma"; Console.WriteLine("Looking in folder \"" + strFolder + "\""); Addon addon = null; if (!gmod12convert) { // // Load the Addon Info file // Json addonInfo; try { addonInfo = new Json(strFolder + "addon.json"); } catch (AddonJSONException ex) { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine(strFolder + "addon.json error: " + ex.Message); Console.ResetColor(); return 1; } addon = new Addon(addonInfo); } else if (gmod12convert) { // Load the addon metadata from the old file structure: info.txt or addon.txt. string legacyInfoFile; if (File.Exists(strFolder + "\\info.txt")) legacyInfoFile = "info.txt"; else if (File.Exists(strFolder + "\\addon.txt")) legacyInfoFile = "addon.txt"; else { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("Failed to find legacy addon metadata file \"info.txt\" or \"addon.txt\""); Console.ResetColor(); return 1; } string legacyInfo; try { legacyInfo = File.ReadAllText(strFolder + Path.DirectorySeparatorChar + legacyInfoFile); } catch (Exception ex) { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("Failed to read metadata."); Console.ResetColor(); Console.WriteLine(ex.Message); return 1; } addon = new Addon(); // Parse the read data Regex regex = new System.Text.RegularExpressions.Regex("\"([A-Za-z_\r\n])*\"", RegexOptions.IgnoreCase); MatchCollection matches = regex.Matches(legacyInfo); foreach (Match keyMatch in matches) { if (keyMatch.Value.ToLowerInvariant() == "\"name\"") addon.Title = keyMatch.NextMatch().Value; else if (keyMatch.Value.ToLowerInvariant() == "\"info\"") addon.Description = keyMatch.NextMatch().Value; else if (keyMatch.Value.ToLowerInvariant() == "\"author_name\"") addon.Author = keyMatch.NextMatch().Value; // Current GMAD writer only writes "Author Name", not real value } Console.WriteLine(addon.Title + " by " + addon.Author); Console.WriteLine("You need to set the title, and optionally, the tags for this addon!"); SetType(addon); SetTags(addon); } // // Get a list of files in the specified folder // foreach (string f in Directory.GetFiles(strFolder, "*", SearchOption.AllDirectories)) { string file = f; file = file.Replace(strFolder, String.Empty); file = file.Replace('\\', '/'); Console.WriteLine("\t" + file); try { addon.CheckRestrictions(file); addon.AddFile(file, File.ReadAllBytes(f)); } catch (IOException) { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("Unable to read file " + file); Console.ResetColor(); continue; } catch (IgnoredException) { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("\t\t[Ignored]"); Console.ResetColor(); continue; } catch (WhitelistException) { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("\t\t[Not allowed by whitelist]"); Console.ResetColor(); if (!warnInvalid) return 1; } } // // Sort the list into alphabetical order, no real reason - we're just ODC // addon.Sort(); // // Create an addon file in a buffer // // // Save the buffer to the provided name // FileStream fs; try { fs = new FileStream(strOutfile, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None); } catch (Exception) { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("Couldn't save to file \"" + strOutfile + "\""); Console.ResetColor(); return 1; } fs.SetLength(0); try { Writer.Create(addon, fs); } catch (Exception) { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("Failed to create the addon."); Console.ResetColor(); return 1; } fs.Flush(); // // Success! // Console.WriteLine("Successfully saved to \"" + strOutfile + "\" [" + ((int)fs.Length).HumanReadableSize() + "]"); fs.Dispose(); return 0; }
/// <summary> /// Compiles the specified addon into the specified stream. /// </summary> /// <param name="addon">The addon to compile.</param> /// <param name="stream">The stream which the result should be written to.</param> /// <exception cref="IOExecption">Happens if there is a problem with the specified stream.</exception> public static void Create(Addon addon, Stream stream) { BinaryWriter writer = new BinaryWriter(stream); writer.BaseStream.Seek(0, SeekOrigin.Begin); writer.BaseStream.SetLength(0); // Header (5) writer.Write(Addon.Ident.ToCharArray()); // Ident (4) writer.Write((char)Addon.Version); // Version (1) // SteamID (8) [unused] writer.Write((ulong)0); // TimeStamp (8) writer.Write((ulong) (((TimeSpan)(DateTime.Now - new DateTime(1970, 1, 1, 0, 0, 0).ToLocalTime())).TotalSeconds)); // Required content (a list of strings) writer.Write((char)0); // signifies nothing // Addon Name (n) writer.WriteNullTerminatedString(addon.Title); // Addon Description (n) writer.WriteNullTerminatedString(addon.DescriptionJSON); // Addon Author (n) [unused] writer.WriteNullTerminatedString("Author Name"); // Addon version (4) [unused] writer.Write((int)1); // File list uint fileNum = 0; foreach (ContentFile f in addon.Files) { // Remove prefix / from filename string file = f.Path.TrimStart('/'); fileNum++; writer.Write(fileNum); // File number (4) writer.WriteNullTerminatedString(file.ToLowerInvariant()); // File name (all lower case!) (n) writer.Write(f.Size); // File size (8) unsigned long writer.Write(f.CRC); // File CRC (4) long long } writer.Flush(); // Zero to signify the end of files fileNum = 0; writer.Write(fileNum); // The files foreach (ContentFile f in addon.Files) { writer.Write(f.Content); writer.Flush(); } // CRC what we've written (to verify that the download isn't s*****d) (4) writer.Seek(0, SeekOrigin.Begin); byte[] buffer_whole = new byte[writer.BaseStream.Length]; writer.BaseStream.Read(buffer_whole, 0, (int)writer.BaseStream.Length); ulong addonCRC = System.Cryptography.CRC32.ComputeChecksum(buffer_whole); writer.Write(addonCRC); writer.Flush(); writer.BaseStream.Seek(0, SeekOrigin.Begin); }
/// <summary> /// Creates the RealtimeAddon instance with the specified Addon to encapsulate and the FileStream pointing to the /// local filesystem. This method cannot be called externally. /// </summary> /// <param name="addon">The addon to encapsulate.</param> /// <param name="stream">The FileStream pointing to the GMA file on the local filesystem.</param> protected RealtimeAddon(Addon addon, FileStream stream) : this() { OpenAddon = addon; AddonStream = stream; }
/// <summary> /// Loads the specified addon from the local filesystem and encapsulates it within a realtime instance. /// </summary> /// <param name="filename">The path to the file on the local filesystem.</param> /// <returns>A RealtimeAddon instance.</returns> /// <exception cref="FileNotFoundException">Happens if the specified file does not exist.</exception> /// <exception cref="IOException">Thrown if there is a problem opening the specified file.</exception> /// <exception cref="ReaderException">Thrown if the addon reader and parser encounters an error.</exception> /// <exception cref="ArgumentException">Happens if a file with the same path is already added.</exception> /// <exception cref="WhitelistException">There is a file prohibited from storing by the global whitelist.</exception> /// <exception cref="IgnoredException">There is a file prohibited from storing by the addon's ignore list.</exception> public static RealtimeAddon Load(string filename) { if (!File.Exists(filename)) { throw new FileNotFoundException("The specified file " + filename + " does not exist."); } FileStream fs; try { fs = new FileStream(filename, FileMode.Open, FileAccess.ReadWrite, FileShare.None); } catch (IOException) { throw; } Reader r; try { r = new Reader(fs); } catch (IOException) { throw; } catch (ReaderException) { throw; } Addon addon; try { addon = new Addon(r); } catch (ArgumentException) { throw; } catch (WhitelistException) { throw; } catch (IgnoredException) { throw; } RealtimeAddon realtime = new RealtimeAddon(addon, fs); realtime.AddonReader = r; return realtime; }
private void btnCreate_Click(object sender, EventArgs e) { if (cmbTag1.SelectedItem == cmbTag2.SelectedItem && !(cmbTag1.SelectedItem.ToString() == "(empty)" && cmbTag2.SelectedItem.ToString() == "(empty)")) { MessageBox.Show("You selected the same tag twice!", "Update metadata", MessageBoxButtons.OK, MessageBoxIcon.Asterisk); return; } List <CreateError> errors = new List <CreateError>(); // // Make sure there's a slash on the end // txtFolder.Text = txtFolder.Text.TrimEnd('/'); txtFolder.Text = txtFolder.Text + "/"; // // Make sure OutFile ends in .gma // txtFile.Text = Path.GetFileNameWithoutExtension(txtFile.Text); txtFile.Text += ".gma"; Addon addon = null; if (gboConvertMetadata.Visible == false) { // // Load the Addon Info file // Json addonInfo; try { addonInfo = new Json(txtFolder.Text + "addon.json"); } catch (AddonJSONException ex) { MessageBox.Show(ex.Message, "addon.json parse error", MessageBoxButtons.OK, MessageBoxIcon.Stop); return; } addon = new Addon(addonInfo); } else if (gboConvertMetadata.Visible == true) { // Load the addon metadata from the old file structure: info.txt/addon.txt if (!File.Exists(txtFolder.Text + Path.DirectorySeparatorChar + "info.txt") && !File.Exists(txtFolder.Text + Path.DirectorySeparatorChar + "addon.txt")) { MessageBox.Show("A legacy metadata file \"info.txt\" or \"addon.txt\" could not be found!", "Failed to create the addon", MessageBoxButtons.OK, MessageBoxIcon.Stop); return; } string legacyInfo = String.Empty;; try { if (File.Exists(txtFolder.Text + Path.DirectorySeparatorChar + "info.txt")) { legacyInfo = File.ReadAllText(txtFolder.Text + Path.DirectorySeparatorChar + "info.txt"); } else if (File.Exists(txtFolder.Text + Path.DirectorySeparatorChar + "addon.txt")) { legacyInfo = File.ReadAllText(txtFolder.Text + Path.DirectorySeparatorChar + "addon.txt"); } } catch (Exception ex) { MessageBox.Show("There was an error reading the metadata.\n\n" + ex.Message, "Failed to create the addon", MessageBoxButtons.OK, MessageBoxIcon.Stop); return; } addon = new Addon(); // Parse the read data Regex regex = new System.Text.RegularExpressions.Regex("\"([A-Za-z_\r\n]*)\"[\\s]*\"([\\s\\S]*?)\"", RegexOptions.IgnoreCase | RegexOptions.Multiline); MatchCollection matches = regex.Matches(legacyInfo); // info.txt/addon.txt files usually have these values not directly mapped into GMAs as well. string AuthorName = String.Empty; string AuthorEmail = String.Empty; string AuthorURL = String.Empty; string Version = String.Empty; string Date = String.Empty; foreach (Match keyMatch in matches) { if (keyMatch.Groups.Count == 3) { // All match should have 2 groups matched (the 0th group is the whole match.) switch (keyMatch.Groups[1].Value.ToLowerInvariant()) { case "name": addon.Title = keyMatch.Groups[2].Value; break; case "version": Version = keyMatch.Groups[2].Value; break; case "up_date": Date = keyMatch.Groups[2].Value; break; case "author_name": //addon.Author = keyMatch.Groups[2].Value; // GMAD writer only writes "Author Name" right now... AuthorName = keyMatch.Groups[2].Value; break; case "author_email": AuthorEmail = keyMatch.Groups[2].Value; break; case "author_url": AuthorURL = keyMatch.Groups[2].Value; break; case "info": addon.Description = keyMatch.Groups[2].Value; break; } } } // Prettify the loaded Description. string newDescription = String.Empty; bool hasNewDescription = (!String.IsNullOrWhiteSpace(AuthorName) || !String.IsNullOrWhiteSpace(AuthorEmail) || !String.IsNullOrWhiteSpace(AuthorURL) || !String.IsNullOrWhiteSpace(Version) || !String.IsNullOrWhiteSpace(Date)); if (hasNewDescription) { newDescription = "## Converted by SharpGMad " + Program.PrettyVersion + " at " + DateTime.Now.ToString("ddd MM dd hh:mm:ss yyyy", System.Globalization.CultureInfo.InvariantCulture) + " (+" + TimeZoneInfo.Local.BaseUtcOffset.ToString("hhmm") + ")"; } if (!String.IsNullOrWhiteSpace(AuthorName)) { newDescription += "\n# AuthorName: " + AuthorName; } if (!String.IsNullOrWhiteSpace(AuthorEmail)) { newDescription += "\n# AuthorEmail: " + AuthorEmail; } if (!String.IsNullOrWhiteSpace(AuthorURL)) { newDescription += "\n# AuthorURL: " + AuthorURL; } if (!String.IsNullOrWhiteSpace(Version)) { newDescription += "\n# Version: " + Version; } if (!String.IsNullOrWhiteSpace(Date)) { newDescription += "\n# Date: " + Date; } if (hasNewDescription) { // If anything was added to the prettifiction newDescription += "\n## End conversion info"; addon.Description = newDescription + (!String.IsNullOrWhiteSpace(addon.Description) ? Environment.NewLine + addon.Description : null); } if (cmbType.SelectedItem != null && Tags.TypeExists(cmbType.SelectedItem.ToString())) { addon.Type = cmbType.SelectedItem.ToString(); } else { // This should not happen in normal operation // nontheless we check against it MessageBox.Show("The selected type is invalid!", "Update metadata", MessageBoxButtons.OK, MessageBoxIcon.Asterisk); return; } if (((cmbTag1.SelectedItem.ToString() != "(empty)") && !Tags.TagExists(cmbTag1.SelectedItem.ToString())) || ((cmbTag2.SelectedItem.ToString() != "(empty)") && !Tags.TagExists(cmbTag2.SelectedItem.ToString()))) { // This should not happen in normal operation // nontheless we check against it MessageBox.Show("The selected tags are invalid!", "Update metadata", MessageBoxButtons.OK, MessageBoxIcon.Asterisk); return; } addon.Tags = new List <string>(2); if (cmbTag1.SelectedItem.ToString() != "(empty)") { addon.Tags.Add(cmbTag1.SelectedItem.ToString()); } if (cmbTag2.SelectedItem.ToString() != "(empty)") { addon.Tags.Add(cmbTag2.SelectedItem.ToString()); } } // // Get a list of files in the specified folder // foreach (string f in Directory.GetFiles(txtFolder.Text, "*", SearchOption.AllDirectories)) { string file = f; file = file.Replace(txtFolder.Text, String.Empty); file = file.Replace('\\', '/'); if (file == "addon.json" || file == "info.txt") { continue; // Don't read the metadata file } try { addon.CheckRestrictions(file); addon.AddFile(file, File.ReadAllBytes(f)); } catch (IOException) { errors.Add(new CreateError() { Path = file, Type = CreateErrorType.FileRead }); continue; } catch (IgnoredException) { errors.Add(new CreateError() { Path = file, Type = CreateErrorType.Ignored }); continue; } catch (WhitelistException) { errors.Add(new CreateError() { Path = file, Type = CreateErrorType.WhitelistFailure }); if (!chkWarnInvalid.Checked) { MessageBox.Show("The following file is not allowed by the whitelist:\n" + file, "Failed to create the addon", MessageBoxButtons.OK, MessageBoxIcon.Stop); return; } } } // // Sort the list into alphabetical order, no real reason - we're just ODC // addon.Sort(); // // Create an addon file in a buffer // // // Save the buffer to the provided name // FileStream gmaFS; try { gmaFS = new FileStream(txtFile.Text, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None); gmaFS.SetLength(0); // Truncate the file Writer.Create(addon, gmaFS); } catch (Exception be) { MessageBox.Show("An exception happened while compiling the addon.\n\n" + be.Message, "Failed to create the addon", MessageBoxButtons.OK, MessageBoxIcon.Stop); return; } gmaFS.Flush(); // // Success! // if (errors.Count == 0) { MessageBox.Show("Successfully created the addon.", "Create addon", MessageBoxButtons.OK, MessageBoxIcon.Information); } else if (errors.Count == 1) { MessageBox.Show("Successfully created the addon.\nThe file " + errors[0].Path + " was not added " + "because " + errors[0].Type, "Create addon", MessageBoxButtons.OK, MessageBoxIcon.Warning); } else if (errors.Count > 1) { DialogResult showFailedFiles = MessageBox.Show(errors.Count + " files failed to add." + "\n\nShow a list of failures?", "Create addon", MessageBoxButtons.YesNo, MessageBoxIcon.Warning); if (showFailedFiles == DialogResult.Yes) { string temppath = ContentFile.GenerateExternalPath( new Random().Next() + "_failedCreations") + ".txt"; string msgboxMessage = String.Empty; foreach (CreateError err in errors) { msgboxMessage += err.Path + ", "; switch (err.Type) { case CreateErrorType.FileRead: msgboxMessage += "failed to read the file"; break; case CreateErrorType.Ignored: msgboxMessage += "the file is ignored by the addon's configuration"; break; case CreateErrorType.WhitelistFailure: msgboxMessage += "the file is not allowed by the global whitelist"; break; } msgboxMessage += "\r\n"; } msgboxMessage = msgboxMessage.TrimEnd('\r', '\n'); try { File.WriteAllText(temppath, "These files failed to add:\r\n\r\n" + msgboxMessage); } catch (Exception) { MessageBox.Show("Can't show the list, an error happened generating it.", "Extract addon", MessageBoxButtons.OK, MessageBoxIcon.Stop); return; } // The file will be opened by the user's default text file handler (Notepad?) System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo(temppath) { UseShellExecute = true, }); } } gmaFS.Dispose(); btnAbort_Click(sender, e); // Close the form return; }
private void btnCreate_Click(object sender, EventArgs e) { if (cmbTag1.SelectedItem == cmbTag2.SelectedItem && !(cmbTag1.SelectedItem.ToString() == "(empty)" && cmbTag2.SelectedItem.ToString() == "(empty)")) { MessageBox.Show("You selected the same tag twice!", "Update metadata", MessageBoxButtons.OK, MessageBoxIcon.Asterisk); return; } List<CreateError> errors = new List<CreateError>(); // // Make sure there's a slash on the end // txtFolder.Text = txtFolder.Text.TrimEnd('/'); txtFolder.Text = txtFolder.Text + "/"; // // Make sure OutFile ends in .gma // txtFile.Text = Path.GetFileNameWithoutExtension(txtFile.Text); txtFile.Text += ".gma"; Addon addon = null; if (chkConvertNeeded.Checked == false) { // // Load the Addon Info file // Json addonInfo; try { addonInfo = new Json(txtFolder.Text + "addon.json"); } catch (AddonJSONException ex) { MessageBox.Show(ex.Message, "addon.json parse error", MessageBoxButtons.OK, MessageBoxIcon.Stop); return; } addon = new Addon(addonInfo); } else if (chkConvertNeeded.Checked == true) { // Load the addon metadata from the old file structure: info.txt or addon.txt. string legacyInfoFile; if (File.Exists(txtFolder.Text + "\\info.txt")) legacyInfoFile = "info.txt"; else if (File.Exists(txtFolder.Text + "\\addon.txt")) legacyInfoFile = "addon.txt"; else { MessageBox.Show("A legacy metadata file \"info.txt\" or \"addon.txt\" could not be found!", "Failed to create the addon", MessageBoxButtons.OK, MessageBoxIcon.Stop); return; } string legacyInfo; try { legacyInfo = File.ReadAllText(txtFolder.Text + Path.DirectorySeparatorChar + legacyInfoFile); } catch (Exception ex) { MessageBox.Show("There was an error reading the metadata.\n\n" + ex.Message, "Failed to create the addon", MessageBoxButtons.OK, MessageBoxIcon.Stop); return; } addon = new Addon(); // Parse the read data Regex regex = new System.Text.RegularExpressions.Regex("\"([A-Za-z_\r\n])*\"", RegexOptions.IgnoreCase); MatchCollection matches = regex.Matches(legacyInfo); foreach (Match keyMatch in matches) { if (keyMatch.Value.ToLowerInvariant() == "\"name\"") addon.Title = keyMatch.NextMatch().Value; else if (keyMatch.Value.ToLowerInvariant() == "\"info\"") addon.Description = keyMatch.NextMatch().Value; else if (keyMatch.Value.ToLowerInvariant() == "\"author_name\"") addon.Author = keyMatch.NextMatch().Value; // Current GMAD writer only writes "Author Name", not real value } if (cmbType.SelectedItem != null && Tags.TypeExists(cmbType.SelectedItem.ToString())) addon.Type = cmbType.SelectedItem.ToString(); else { // This should not happen in normal operation // nontheless we check against it MessageBox.Show("The selected type is invalid!", "Update metadata", MessageBoxButtons.OK, MessageBoxIcon.Asterisk); return; } if (((cmbTag1.SelectedItem.ToString() != "(empty)") && !Tags.TagExists(cmbTag1.SelectedItem.ToString())) || ((cmbTag2.SelectedItem.ToString() != "(empty)") && !Tags.TagExists(cmbTag2.SelectedItem.ToString()))) { // This should not happen in normal operation // nontheless we check against it MessageBox.Show("The selected tags are invalid!", "Update metadata", MessageBoxButtons.OK, MessageBoxIcon.Asterisk); return; } addon.Tags = new List<string>(2); if (cmbTag1.SelectedItem.ToString() != "(empty)") addon.Tags.Add(cmbTag1.SelectedItem.ToString()); if (cmbTag2.SelectedItem.ToString() != "(empty)") addon.Tags.Add(cmbTag2.SelectedItem.ToString()); } // // Get a list of files in the specified folder // foreach (string f in Directory.GetFiles(txtFolder.Text, "*", SearchOption.AllDirectories)) { string file = f; file = file.Replace(txtFolder.Text, String.Empty); file = file.Replace('\\', '/'); try { addon.CheckRestrictions(file); addon.AddFile(file, File.ReadAllBytes(f)); } catch (IOException) { errors.Add(new CreateError() { Path = file, Type = CreateErrorType.FileRead }); continue; } catch (IgnoredException) { errors.Add(new CreateError() { Path = file, Type = CreateErrorType.Ignored }); continue; } catch (WhitelistException) { errors.Add(new CreateError() { Path = file, Type = CreateErrorType.WhitelistFailure }); if (!chkWarnInvalid.Checked) { MessageBox.Show("The following file is not allowed by the whitelist:\n" + file, "Failed to create the addon", MessageBoxButtons.OK, MessageBoxIcon.Stop); return; } } } // // Sort the list into alphabetical order, no real reason - we're just ODC // addon.Sort(); // // Create an addon file in a buffer // // // Save the buffer to the provided name // FileStream gmaFS; try { gmaFS = new FileStream(txtFile.Text, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None); gmaFS.SetLength(0); // Truncate the file Writer.Create(addon, gmaFS); } catch (Exception be) { MessageBox.Show("An exception happened while compiling the addon.\n\n" + be.Message, "Failed to create the addon", MessageBoxButtons.OK, MessageBoxIcon.Stop); return; } gmaFS.Flush(); // // Success! // if (errors.Count == 0) MessageBox.Show("Successfully extracted the addon.", "Create addon", MessageBoxButtons.OK, MessageBoxIcon.Information); else if (errors.Count == 1) MessageBox.Show("Successfully created the addon.\nThe file " + errors[0] + " was not added.", "Create addon", MessageBoxButtons.OK, MessageBoxIcon.Warning); else if (errors.Count > 1) { DialogResult showFailedFiles = MessageBox.Show(errors.Count + " files failed to add." + "\n\nShow a list of failures?", "Create addon", MessageBoxButtons.YesNo, MessageBoxIcon.Warning); if (showFailedFiles == DialogResult.Yes) { string temppath = ContentFile.GenerateExternalPath( new Random().Next() + "_failedCreations") + ".txt"; string msgboxMessage = String.Empty; foreach (CreateError err in errors) { msgboxMessage += err.Path + ", "; switch (err.Type) { case CreateErrorType.FileRead: msgboxMessage += "failed to read the file"; break; case CreateErrorType.Ignored: msgboxMessage += "the file is ignored by the addon's configuration"; break; case CreateErrorType.WhitelistFailure: msgboxMessage += "the file is not allowed by the global whitelist"; break; } msgboxMessage += "\r\n"; } msgboxMessage = msgboxMessage.TrimEnd('\r', '\n'); try { File.WriteAllText(temppath, "These files failed to add:\r\n\r\n" + msgboxMessage); } catch (Exception) { MessageBox.Show("Can't show the list, an error happened generating it.", "Extract addon", MessageBoxButtons.OK, MessageBoxIcon.Stop); return; } // The file will be opened by the user's default text file handler (Notepad?) System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo(temppath) { UseShellExecute = true, }); } } gmaFS.Dispose(); btnAbort_Click(sender, e); // Close the form return; }