diff --git a/SabreTools.Helper/Dats/Partials/DatFile.Rebuild.cs b/SabreTools.Helper/Dats/Partials/DatFile.Rebuild.cs index 8c8a27a2..7cf7a4ec 100644 --- a/SabreTools.Helper/Dats/Partials/DatFile.Rebuild.cs +++ b/SabreTools.Helper/Dats/Partials/DatFile.Rebuild.cs @@ -561,6 +561,7 @@ namespace SabreTools.Helper.Dats case OutputFormat.TorrentRar: break; case OutputFormat.TorrentXZ: + rebuilt &= ArchiveTools.WriteTorrentXZ(file, outDir, item, date: date); break; case OutputFormat.TorrentZip: rebuilt &= ArchiveTools.WriteTorrentZip(file, outDir, item, date: date); @@ -645,6 +646,7 @@ namespace SabreTools.Helper.Dats case OutputFormat.TorrentRar: break; case OutputFormat.TorrentXZ: + rebuilt &= ArchiveTools.WriteTorrentXZ(file, outDir, item, date: date); break; case OutputFormat.TorrentZip: rebuilt &= ArchiveTools.WriteTorrentZip(file, outDir, item, date: date); @@ -749,6 +751,8 @@ namespace SabreTools.Helper.Dats case OutputFormat.TorrentRar: break; case OutputFormat.TorrentXZ: + rebuilt &= ArchiveTools.WriteTorrentXZ(file + ".new", outDir, item, date: date); + rebuilt &= ArchiveTools.WriteTorrentXZ(file, outDir, rom, date: date); break; case OutputFormat.TorrentZip: rebuilt &= ArchiveTools.WriteTorrentZip(file + ".new", outDir, item, date: date); diff --git a/SabreTools.Helper/README.1ST b/SabreTools.Helper/README.1ST index a0b3e1f0..66338fb0 100644 --- a/SabreTools.Helper/README.1ST +++ b/SabreTools.Helper/README.1ST @@ -435,10 +435,11 @@ Options: invalidate the output files as proper TorrentZip files because the date will not match the standard. - -t7z Enable Torrent 7zip output [NOT IMPLEMENTED] + -t7z Enable Torrent 7zip output Instead of ouputting the files to folder, files will be rebuilt to Torrent7Zip (T7Z) files. This format is based on the LZMA container format 7zip, but with custom header - information. This is currently unused by any major application. + information. This is currently unused by any major application. Currently does not + produce proper Torrent-compatible outputs. -tar Enable Tape ARchive output Instead of outputting the fiels to folder, files will be rebuilt to Tape ARchive (TAR) @@ -471,10 +472,11 @@ Options: files. This format is based on the RAR propietary format but with custom header information. This is currently unused by any major application; - -txz Enable Torrent XZ output [NOT IMPLEMENTED] + -txz Enable Torrent XZ output Instead of outputting files to folder, files will be rebuilt to Torrent XZ (TXZ) files. This format is based on the LZMA container format XZ, but with custom header - information. This is currently unused by any major application; + information. This is currently unused by any major application. Currently does not + produce proper Torrent-compatible outputs. -tzip Enable Torrent Zip output Instead of ouputting files to folder, files will be rebuilt to TorrentZip (TZ) files. @@ -561,10 +563,11 @@ Options: invalidate the output files as proper TorrentZip files because the date will not match the standard. - -t7z Enable Torrent 7zip output [NOT IMPLEMENTED] + -t7z Enable Torrent 7zip output Instead of ouputting the files to folder, files will be rebuilt to Torrent7Zip (T7Z) files. This format is based on the LZMA container format 7zip, but with custom header - information. This is currently unused by any major application. + information. This is currently unused by any major application. Currently does not + produce proper Torrent-compatible outputs. -tar Enable Tape ARchive output Instead of outputting the fiels to folder, files will be rebuilt to Tape ARchive (TAR) @@ -595,12 +598,13 @@ Options: -trar Enable Torrent RAR output [NOT IMPLEMENTED] Instead of outputting files to folder, files will be rebuilt to Torrent RAR (TRAR) files. This format is based on the RAR propietary format but with custom header - information. This is currently unused by any major application; + information. This is currently unused by any major application. - -txz Enable Torrent XZ output [NOT IMPLEMENTED] + -txz Enable Torrent XZ output Instead of outputting files to folder, files will be rebuilt to Torrent XZ (TXZ) files. This format is based on the LZMA container format XZ, but with custom header - information. This is currently unused by any major application; + information. This is currently unused by any major application. Currently does not + produce proper Torrent-compatible outputs. -tzip Enable Torrent Zip output Instead of ouputting files to folder, files will be rebuilt to TorrentZip (TZ) files. diff --git a/SabreTools.Helper/Tools/ArchiveTools.cs b/SabreTools.Helper/Tools/ArchiveTools.cs index 2cf02e9e..1bcd3b03 100644 --- a/SabreTools.Helper/Tools/ArchiveTools.cs +++ b/SabreTools.Helper/Tools/ArchiveTools.cs @@ -1310,7 +1310,7 @@ namespace SabreTools.Helper.Tools } /// - /// (UNIMPLEMENTED) Write a set of input files to a torrent7z archive (assuming the same output archive name) + /// Write a set of input files to a torrent7z archive (assuming the same output archive name) /// /// Input filenames to be moved /// Output directory to build to @@ -1693,7 +1693,7 @@ namespace SabreTools.Helper.Tools } /// - /// (UNIMPLEMENTED) Write a set of input files to a torrentxz archive (assuming the same output archive name) + /// Write a set of input files to a torrentxz archive (assuming the same output archive name) /// /// Input filenames to be moved /// Output directory to build to @@ -1702,7 +1702,208 @@ namespace SabreTools.Helper.Tools /// True if the archive was written properly, false otherwise public static bool WriteTorrentXZ(List inputFiles, string outDir, List roms, bool date = false) { - return false; + bool success = false; + string tempFile = Path.Combine(outDir, "tmp" + Guid.NewGuid().ToString()); + + // If either list of roms is null or empty, return + if (inputFiles == null || roms == null || inputFiles.Count == 0 || roms.Count == 0) + { + return success; + } + + // If the number of inputs is less than the number of available roms, return + if (inputFiles.Count < roms.Count) + { + return success; + } + + // If one of the files doesn't exist, return + foreach (string file in inputFiles) + { + if (!File.Exists(file)) + { + return success; + } + } + + // Get the output archive name from the first rebuild rom + string archiveFileName = Path.Combine(outDir, Style.RemovePathUnsafeCharacters(roms[0].Machine.Name) + (roms[0].Machine.Name.EndsWith(".7z") ? "" : ".7z")); + + // Set internal variables + SevenZipBase.SetLibraryPath("7za.dll"); + SevenZipExtractor oldZipFile; + SevenZipCompressor zipFile; + + try + { + // If the full output path doesn't exist, create it + if (!Directory.Exists(Path.GetDirectoryName(archiveFileName))) + { + Directory.CreateDirectory(Path.GetDirectoryName(archiveFileName)); + } + + // If the archive doesn't exist, create it and put the single file + if (!File.Exists(archiveFileName)) + { + zipFile = new SevenZipCompressor() + { + ArchiveFormat = OutArchiveFormat.XZ, + CompressionLevel = SevenZip.CompressionLevel.Normal, + }; + + // Map all inputs to index + Dictionary inputIndexMap = new Dictionary(); + for (int i = 0; i < inputFiles.Count; i++) + { + inputIndexMap.Add(roms[i].Name.Replace('\\', '/'), i); + } + + // Sort the keys in TZIP order + List keys = inputIndexMap.Keys.ToList(); + keys.Sort(ZipFile.TorrentZipStringCompare); + + // Create the temp directory + string tempPath = Path.Combine(Path.GetTempPath(), new Guid().ToString()); + if (!Directory.Exists(tempPath)) + { + Directory.CreateDirectory(tempPath); + } + + // Now add all of the files in order + foreach (string key in keys) + { + string newkey = Path.Combine(tempPath, key); + + File.Move(inputFiles[inputIndexMap[key]], newkey); + zipFile.CompressFiles(tempFile, newkey); + File.Move(newkey, inputFiles[inputIndexMap[key]]); + } + + FileTools.CleanDirectory(tempPath); + try + { + Directory.Delete(tempPath); + } + catch { } + } + + // Otherwise, sort the input files and write out in the correct order + else + { + // Open the old archive for reading + Stream oldZipFileStream = File.OpenRead(archiveFileName); + oldZipFile = new SevenZipExtractor(oldZipFileStream); + + // Map all inputs to index + Dictionary inputIndexMap = new Dictionary(); + for (int i = 0; i < inputFiles.Count; i++) + { + // If the old one contains the new file, then just skip out + if (oldZipFile.ArchiveFileNames.Contains(roms[i].Name.Replace('\\', '/'))) + { + continue; + } + + inputIndexMap.Add(roms[i].Name.Replace('\\', '/'), -(i + 1)); + } + + // Then add all of the old entries to it too + for (int i = 0; i < oldZipFile.FilesCount; i++) + { + inputIndexMap.Add(oldZipFile.ArchiveFileNames[i], i); + } + + // If the number of entries is the same as the old archive, skip out + if (inputIndexMap.Keys.Count <= oldZipFile.FilesCount) + { + success = true; + return success; + } + + // Otherwise, process the old zipfile + zipFile = new SevenZipCompressor() + { + ArchiveFormat = OutArchiveFormat.XZ, + CompressionLevel = SevenZip.CompressionLevel.Normal, + }; + Stream zipFileStream = File.OpenWrite(tempFile); + + // Get the order for the entries with the new file + List keys = inputIndexMap.Keys.ToList(); + keys.Sort(ZipFile.TorrentZipStringCompare); + + // Copy over all files to the new archive + foreach (string key in keys) + { + // Get the index mapped to the key + int index = inputIndexMap[key]; + + // If we have the input file, add it now + if (index < 0) + { + zipFile.CompressFiles(zipFileStream, inputFiles[-index - 1]); + } + + // Otherwise, copy the file from the old archive + else + { + Stream oldZipFileEntryStream = File.Open(inputFiles[index], FileMode.Create, FileAccess.ReadWrite, FileShare.None); + oldZipFile.ExtractFile(index, oldZipFileEntryStream); + zipFile.CompressFiles(zipFileStream, inputFiles[index]); + + oldZipFileEntryStream.Dispose(); + try + { + File.Delete(inputFiles[index]); + } + catch { } + } + } + + zipFileStream.Dispose(); + oldZipFile.Dispose(); + } + + success = true; + } + catch (Exception ex) + { + Console.WriteLine(ex); + success = false; + } + + // If the old file exists, delete it and replace + if (File.Exists(archiveFileName)) + { + File.Delete(archiveFileName); + } + File.Move(tempFile, archiveFileName); + + // Now make the file TXZ + // TODO: Add ACTUAL TXZ compatible code (based on T7z) + + BinaryWriter bw = new BinaryWriter(File.Open(archiveFileName, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite)); + bw.Seek(0, SeekOrigin.Begin); + bw.Write(Constants.Torrent7ZipHeader); + bw.Seek(0, SeekOrigin.End); + + oldZipFile = new SevenZipExtractor(File.Open(archiveFileName, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite)); + + // Get the correct signature to use (Default 0, Unicode 1, SingleFile 2, StripFileNames 4) + byte[] tempsig = Constants.Torrent7ZipSignature; + if (oldZipFile.FilesCount > 1) + { + tempsig[16] = 0x2; + } + else + { + tempsig[16] = 0; + } + + bw.Write(tempsig); + bw.Dispose(); + + return true; } /// diff --git a/SabreTools.Helper/Tools/FileTools.cs b/SabreTools.Helper/Tools/FileTools.cs index 624419ff..fb71767c 100644 --- a/SabreTools.Helper/Tools/FileTools.cs +++ b/SabreTools.Helper/Tools/FileTools.cs @@ -611,7 +611,7 @@ namespace SabreTools.Helper.Tools } if ((omitFromScan & Hash.xxHash) == 0) { - rom.SHA512 = xxHash.Digest().ToString("X").ToLowerInvariant(); + rom.SHA512 = xxHash.Digest().ToString("X").ToLowerInvariant(); // TODO: Not sure how long xxHash is supposed to be. Add the length here and in constants later } // Dispose of the hashers diff --git a/SabreTools/Partials/SabreTools.Help.cs b/SabreTools/Partials/SabreTools.Help.cs index b3258f57..8372dad8 100644 --- a/SabreTools/Partials/SabreTools.Help.cs +++ b/SabreTools/Partials/SabreTools.Help.cs @@ -418,13 +418,11 @@ namespace SabreTools FeatureType.Flag, null)); */ - /* sort.AddFeature("txz", new Feature( new List() { "-txz", "--txz" }, "Enable TorrentXZ output", FeatureType.Flag, null)); - */ sort.AddFeature("tzip", new Feature( new List() { "-tzip", "--tzip" }, "Enable TorrentZip output", @@ -556,13 +554,11 @@ namespace SabreTools FeatureType.Flag, null)); */ - /* sortDepot.AddFeature("txz", new Feature( new List() { "-txz", "--txz" }, "Enable TorrentXZ output", FeatureType.Flag, null)); - */ sortDepot.AddFeature("tzip", new Feature( new List() { "-tzip", "--tzip" }, "Enable TorrentZip output",