diff --git a/SrcMod/Shell/GlobalUsings.cs b/SrcMod/Shell/GlobalUsings.cs index 13ff962..1d90052 100644 --- a/SrcMod/Shell/GlobalUsings.cs +++ b/SrcMod/Shell/GlobalUsings.cs @@ -1,9 +1,13 @@ global using Nerd_STF.Mathematics; +global using SharpCompress.Archives.Rar; +global using SharpCompress.Archives.SevenZip; +global using SharpCompress.Readers; global using SrcMod.Shell; global using SrcMod.Shell.Modules.ObjectModels; global using System; global using System.Collections.Generic; global using System.Diagnostics; +global using System.Formats.Tar; global using System.IO; global using System.IO.Compression; global using System.Linq; diff --git a/SrcMod/Shell/Modules/CompressionModule.cs b/SrcMod/Shell/Modules/CompressionModule.cs index 35742a9..43d5804 100644 --- a/SrcMod/Shell/Modules/CompressionModule.cs +++ b/SrcMod/Shell/Modules/CompressionModule.cs @@ -1,14 +1,24 @@ namespace SrcMod.Shell.Modules; +// Some things that can be extracted can't be compressed by SharpCompress. +// In the future I might replace it with my own, but that'll take a *really* +// long time and I'm already planning to do that for the valve compression formats, +// so I'll seethe for now. [Module("compress")] public static class CompressionModule { - [Command("zip")] - public static void CompressZip(string source, string? destination = null, + [Command("gz")] + [Command("gzip")] + public static void CompressGZip(string source, string? destination = null, CompressionLevel level = CompressionLevel.Optimal) { - destination ??= Path.Combine(Path.GetDirectoryName(Path.GetFullPath(source))!, - $"{Path.GetFileNameWithoutExtension(source)}.zip"); + if (destination is null) + { + string full = Path.GetFullPath(source), + name = Path.GetFileName(full), + folder = Program.Shell!.WorkingDirectory; + destination ??= $"{folder}\\{name}.gz"; + } string absSource = Path.GetFullPath(source), localDest = Path.GetRelativePath(Program.Shell!.WorkingDirectory, destination); @@ -19,8 +29,109 @@ public static class CompressionModule string message = $"Compressing file at \"{source}\" into \"{localDest}\"..."; Write(message); - Stream writer = new FileStream(destination, FileMode.CreateNew); + FileStream writer = new(localDest, FileMode.CreateNew), + reader = new(absSource, FileMode.Open); + GZipStream gzip = new(writer, level); + + reader.CopyTo(gzip); + + gzip.Close(); + reader.Close(); + writer.Close(); + + Console.CursorLeft = 0; + Console.CursorTop -= (message.Length / Console.BufferWidth) + 1; + Write(new string(' ', message.Length), newLine: false); + } + else if (Directory.Exists(source)) throw new("The GZip format can only compress 1 file."); + else throw new("No file located at \"source\""); + } + + [Command("tar")] + [Command("tarball")] + public static void CompressTar(string source, string? destination = null) + { + if (destination is null) + { + string full = Path.GetFullPath(source), + name = Path.GetFileNameWithoutExtension(full), + folder = Program.Shell!.WorkingDirectory; + destination ??= $"{folder}\\{name}.tar"; + } + + string absSource = Path.GetFullPath(source), + localDest = Path.GetRelativePath(Program.Shell!.WorkingDirectory, destination); + + if (File.Exists(source)) throw new("The Tar format cannot compress a single file."); + else if (Directory.Exists(source)) + { + if (File.Exists(destination)) throw new($"File already exists at \"{localDest}\""); + + Write($"Compressing folder at \"{source}\" into \"{localDest}\"..."); + + FileStream writer = new(destination, FileMode.CreateNew); + TarFile.CreateFromDirectory(absSource, writer, false); + + writer.Close(); + + Console.CursorLeft = 0; + Write(new string(' ', Console.BufferWidth), newLine: false); + Console.SetCursorPosition(0, Console.CursorTop - 1); + Write(new string(' ', Console.BufferWidth), newLine: false); + } + else throw new("No file or directory located at \"source\""); + } + + // Rar can't be compressed. + + [Command("targz")] + [Command("tar.gz")] + [Command("tar-gz")] + public static void CompressTarGzip(string source, string? destination = null, + CompressionLevel level = CompressionLevel.Optimal) + { + if (destination is null) + { + string full = Path.GetFullPath(source), + name = Path.GetFileNameWithoutExtension(full), + folder = Program.Shell!.WorkingDirectory; + destination ??= $"{folder}\\{name}.tar.gz"; + } + + string firstDest = Path.GetFileNameWithoutExtension(destination); + + CompressTar(source, firstDest); + CompressGZip(firstDest, destination, level); + File.Delete(firstDest); + + Console.CursorLeft = 0; + Console.CursorTop--; + } + + [Command("zip")] + public static void CompressZip(string source, string? destination = null, + CompressionLevel level = CompressionLevel.Optimal, string comment = "") + { + if (destination is null) + { + string full = Path.GetFullPath(source), + name = Path.GetFileNameWithoutExtension(full), + folder = Program.Shell!.WorkingDirectory; + destination ??= $"{folder}\\{name}.zip"; + } + + string absSource = Path.GetFullPath(source), + localDest = Path.GetRelativePath(Program.Shell!.WorkingDirectory, destination); + + if (File.Exists(source)) + { + if (File.Exists(destination)) throw new($"File already exists at \"{localDest}\""); + string message = $"Compressing file at \"{source}\" into \"{localDest}\"..."; + Write(message); + + FileStream writer = new(destination, FileMode.CreateNew); ZipArchive archive = new(writer, ZipArchiveMode.Create); + if (!string.IsNullOrWhiteSpace(comment)) archive.Comment = comment; archive.CreateEntryFromFile(absSource, Path.GetFileName(absSource), level); @@ -37,18 +148,40 @@ public static class CompressionModule Write($"Compressing folder at \"{source}\" into \"{localDest}\"..."); - Stream writer = new FileStream(destination, FileMode.CreateNew); + FileStream writer = new(destination, FileMode.CreateNew); ZipArchive archive = new(writer, ZipArchiveMode.Create); + if (!string.IsNullOrWhiteSpace(comment)) archive.Comment = comment; List files = new(GetAllFiles(absSource)), relative = new(); - foreach (string f in files) relative.Add(Path.GetRelativePath(absSource, f)); + for (int i = 0; i < files.Count; i++) + { + string f = files[i]; + if (f.Trim().ToLower() == destination.Trim().ToLower()) + { + files.RemoveAt(i); + i--; + continue; + } + relative.Add(Path.GetRelativePath(absSource, f)); + } + + int failed = 0; LoadingBarStart(); for (int i = 0; i < files.Count; i++) { - archive.CreateEntryFromFile(files[i], relative[i], level); - LoadingBarSet((i + 1) / (float)files.Count, ConsoleColor.DarkGreen); + bool failedThisTime = false; + try + { + archive.CreateEntryFromFile(files[i], relative[i], level); + } + catch + { + failedThisTime = true; + failed++; + } + LoadingBarSet((i + 1) / (float)files.Count, failedThisTime ? ConsoleColor.Red : ConsoleColor.DarkGreen); ; Console.CursorLeft = 0; string message = $"{relative[i]}"; int remainder = Console.BufferWidth - message.Length; @@ -67,6 +200,13 @@ public static class CompressionModule Write(new string(' ', Console.BufferWidth), newLine: false); Console.SetCursorPosition(0, Console.CursorTop - 2); Write(new string(' ', Console.BufferWidth), newLine: false); + + if (failed > 0) + { + Console.CursorLeft = 0; + Write($"{failed} file{(failed == 1 ? " has" : "s have")} been ignored due to an error.", + ConsoleColor.DarkYellow); + } } else throw new("No file or directory located at \"source\""); @@ -91,4 +231,6 @@ public static class CompressionModule name = $"Compressed a file or folder into a zip archive located at \"{destination}\"" }); } + + // 7z can't be compressed. } diff --git a/SrcMod/Shell/Modules/ExtractionModule.cs b/SrcMod/Shell/Modules/ExtractionModule.cs new file mode 100644 index 0000000..75e0d6c --- /dev/null +++ b/SrcMod/Shell/Modules/ExtractionModule.cs @@ -0,0 +1,182 @@ +namespace SrcMod.Shell.Modules; + +[Module("extract")] +public static class ExtractionModule +{ + [Command("gz")] + [Command("gzip")] + public static void ExtractGZip(string source, string? destination = null) + { + if (!File.Exists(source)) throw new($"No file exists at \"{source}\"."); + + if (destination is null) + { + string full = Path.GetFullPath(source); + string folder = Program.Shell!.WorkingDirectory; + string name = Path.GetFileNameWithoutExtension(full); + + destination = $"{folder}\\{name}"; + } + + string absSource = Path.GetFullPath(source), + localDest = Path.GetRelativePath(Program.Shell!.WorkingDirectory, destination); + + if (File.Exists(destination)) throw new($"File already exists at \"{destination}\"."); + string message = $"Extracting file at \"{source}\" into \"{localDest}\"..."; + Write(message); + + FileStream writer = new(destination, FileMode.CreateNew), + reader = new(absSource, FileMode.Open); + GZipStream gzip = new(reader, CompressionMode.Decompress); + + gzip.CopyTo(writer); + + gzip.Close(); + reader.Close(); + writer.Close(); + + Console.CursorLeft = 0; + Console.CursorTop -= (message.Length / Console.BufferWidth) + 1; + Write(new string(' ', message.Length), newLine: false); + } + + [Command("rar")] + public static void ExtractRar(string source, string? destination = null) + { + if (!File.Exists(source)) throw new($"No file exists at \"{source}\"."); + + if (destination is null) + { + string full = Path.GetFullPath(source); + string folder = Program.Shell!.WorkingDirectory; + string name = Path.GetFileNameWithoutExtension(full); + + destination = $"{folder}\\{name}"; + } + + if (!Directory.Exists(destination)) Directory.CreateDirectory(destination); + + FileStream reader = new(source, FileMode.Open); + RarArchive rar = RarArchive.Open(reader); + + IReader data = rar.ExtractAllEntries(); + data.WriteAllToDirectory(destination, new() + { + ExtractFullPath = true, + Overwrite = true, + PreserveFileTime = true + }); + + rar.Dispose(); + reader.Dispose(); + } + + [Command("tar")] + [Command("tarball")] + public static void ExtractTar(string source, string? destination = null) + { + if (!File.Exists(source)) throw new($"No file exists at \"{source}\"."); + + if (destination is null) + { + string full = Path.GetFullPath(source); + string folder = Program.Shell!.WorkingDirectory; + string name = Path.GetFileNameWithoutExtension(full); + + destination = $"{folder}\\{name}"; + } + + if (!Directory.Exists(destination)) Directory.CreateDirectory(destination); + + FileStream reader = new(source, FileMode.Open); + TarFile.ExtractToDirectory(reader, Path.GetFileName(destination), true); + + reader.Dispose(); + } + + [Command("targz")] + [Command("tar.gz")] + [Command("tar-gz")] + public static void ExtractTarGz(string source, string? destination = null) + { + if (!File.Exists(source)) throw new($"No file exists at \"{source}\"."); + + if (destination is null) + { + string full = Path.GetFullPath(source); + string folder = Program.Shell!.WorkingDirectory; + string name = Path.GetFileNameWithoutExtension(Path.GetFileNameWithoutExtension(full)); + + destination = $"{folder}\\{name}"; + } + + string absSource = Path.GetFullPath(source), + temp = Path.Combine(Path.GetDirectoryName(absSource)!, Path.GetFileNameWithoutExtension(absSource)); + + ExtractGZip(source, temp); + ExtractTar(temp, destination); + + File.Delete(temp); + } + + [Command("zip")] + public static void ExtractZip(string source, string? destination = null) + { + if (!File.Exists(source)) throw new($"No file exists at \"{source}\"."); + + if (destination is null) + { + string full = Path.GetFullPath(source); + string folder = Program.Shell!.WorkingDirectory; + string name = Path.GetFileNameWithoutExtension(full); + + destination = $"{folder}\\{name}"; + } + + if (!Directory.Exists(destination)) Directory.CreateDirectory(destination); + + FileStream reader = new(source, FileMode.Open); + ZipArchive zip = new(reader, ZipArchiveMode.Read); + + if (!string.IsNullOrWhiteSpace(zip.Comment)) Write(zip.Comment); + + zip.ExtractToDirectory(destination, true); + + zip.Dispose(); + reader.Dispose(); + } + + [Command("7z")] + [Command("7zip")] + [Command("sevenzip")] + public static void Extract7Zip(string source, string? destination = null) + { + if (!File.Exists(source)) throw new($"No file exists at \"{source}\"."); + + if (destination is null) + { + string full = Path.GetFullPath(source); + string folder = Program.Shell!.WorkingDirectory; + string name = Path.GetFileNameWithoutExtension(full); + + destination = $"{folder}\\{name}"; + } + + if (!Directory.Exists(destination)) Directory.CreateDirectory(destination); + + FileStream reader = new(source, FileMode.Open); + SevenZipArchive zip = SevenZipArchive.Open(reader); + + IReader data = zip.ExtractAllEntries(); + data.WriteAllToDirectory(destination, new() + { + ExtractFullPath = true, + Overwrite = true, + PreserveAttributes = true, + PreserveFileTime = true + }); + + zip.Dispose(); + reader.Dispose(); + } +} diff --git a/SrcMod/Shell/Shell.cs b/SrcMod/Shell/Shell.cs index 0ea44b3..e62b406 100644 --- a/SrcMod/Shell/Shell.cs +++ b/SrcMod/Shell/Shell.cs @@ -4,7 +4,7 @@ public class Shell { public const string Author = "That_One_Nerd"; public const string Name = "SrcMod"; - public const string Version = "Alpha 0.2.2"; + public const string Version = "Alpha 0.3.0"; public readonly string? ShellDirectory; @@ -170,10 +170,13 @@ public class Shell if (command.NameId.Trim().ToLower() != commandName) continue; int start = module.NameIsPrefix ? 2 : 1; string[] args = parts.GetRange(start, parts.Count - start).ToArray(); - + +#if RELEASE try { +#endif command.Invoke(args); +#if RELEASE } catch (TargetInvocationException ex) { @@ -185,6 +188,7 @@ public class Shell Write($"[ERROR] {ex.Message}", ConsoleColor.Red); if (LoadingBarEnabled) LoadingBarEnd(); } +#endif return; } } diff --git a/SrcMod/Shell/Shell.csproj b/SrcMod/Shell/Shell.csproj index 511ed2f..d662f45 100644 --- a/SrcMod/Shell/Shell.csproj +++ b/SrcMod/Shell/Shell.csproj @@ -31,6 +31,7 @@ +