/// <summary> /// Attempt to extract a stream from an archive /// </summary> /// <param name="entryName">Name of the entry to be extracted</param> /// <param name="realEntry">Output representing the entry name that was found</param> /// <returns>MemoryStream representing the entry, null on error</returns> public override (MemoryStream, string) CopyToStream(string entryName) { MemoryStream ms = new MemoryStream(); string realEntry = null; try { TarArchive ta = TarArchive.Open(this.Filename, new ReaderOptions { LeaveStreamOpen = false, }); foreach (TarArchiveEntry entry in ta.Entries) { if (entry != null && !entry.IsDirectory && entry.Key.Contains(entryName)) { // Write the file out realEntry = entry.Key; entry.WriteTo(ms); } } ta.Dispose(); } catch (Exception ex) { logger.Error(ex); ms = null; realEntry = null; } return(ms, realEntry); }
static int Untar_Lua(IntPtr L) { var filename = Api.lua_tostring(L, 1); var path = Api.lua_tostring(L, 2); var unpackPath = Path.Combine(Application.persistentDataPath, path); if (!Directory.Exists(unpackPath)) { Directory.CreateDirectory(unpackPath); } TarArchive archive = null; try { var fs = File.Open(filename, FileMode.Open, FileAccess.Read); archive = TarArchive.CreateInputTarArchive(fs); List <string> names = new List <string>(); archive.ProgressMessageEvent += (ar, entry, msg) => { names.Add(entry.Name); }; archive.ExtractContents(unpackPath); archive.Dispose(); Api.lua_createtable(L, names.Count, 0); for (int i = 0; i < names.Count; ++i) { Api.lua_pushstring(L, names[i]); Api.lua_seti(L, -2, i); } return(1); } catch (Exception e) { if (archive != null) { archive.Dispose(); } Api.lua_pushnil(L); Api.lua_pushstring(L, e.Message); return(2); } }
/// <summary> /// Generate a list of DatItem objects from the header values in an archive /// </summary> /// <param name="omitFromScan">Hash representing the hashes that should be skipped</param> /// <param name="date">True if entry dates should be included, false otherwise (default)</param> /// <returns>List of DatItem objects representing the found data</returns> /// <remarks>TODO: All instances of Hash.DeepHashes should be made into 0x0 eventually</remarks> public override List <BaseFile> GetChildren(Hash omitFromScan = Hash.DeepHashes, bool date = false) { List <BaseFile> found = new List <BaseFile>(); string gamename = Path.GetFileNameWithoutExtension(_filename); try { TarArchive ta = TarArchive.Open(Utilities.TryOpenRead(_filename)); foreach (TarArchiveEntry entry in ta.Entries.Where(e => e != null && !e.IsDirectory)) { // If secure hashes are disabled, do a quickscan if (omitFromScan == Hash.SecureHashes) { found.Add(new BaseFile { Filename = entry.Key, Size = entry.Size, CRC = BitConverter.GetBytes(entry.Crc), Date = (date && entry.LastModifiedTime != null ? entry.LastModifiedTime?.ToString("yyyy/MM/dd hh:mm:ss") : null), Parent = gamename, }); } // Otherwise, use the stream directly else { Stream entryStream = entry.OpenEntryStream(); BaseFile tarEntryRom = Utilities.GetStreamInfo(entryStream, entry.Size, omitFromScan: omitFromScan); tarEntryRom.Filename = entry.Key; tarEntryRom.Parent = gamename; tarEntryRom.Date = entry.LastModifiedTime?.ToString("yyyy/MM/dd hh:mm:ss"); found.Add(tarEntryRom); entryStream.Dispose(); } } // Dispose of the archive ta.Dispose(); } catch (Exception ex) { Globals.Logger.Error(ex.ToString()); return(null); } return(found); }
/// <summary> /// Generate a list of DatItem objects from the header values in an archive /// </summary> /// <returns>List of DatItem objects representing the found data</returns> public override List <BaseFile> GetChildren() { List <BaseFile> found = new List <BaseFile>(); string gamename = Path.GetFileNameWithoutExtension(this.Filename); try { TarArchive ta = TarArchive.Open(FileExtensions.TryOpenRead(this.Filename)); foreach (TarArchiveEntry entry in ta.Entries.Where(e => e != null && !e.IsDirectory)) { // Create a blank item for the entry BaseFile tarEntryRom = new BaseFile(); // Perform a quickscan, if flagged to if (this.AvailableHashes == Hash.CRC) { tarEntryRom.Size = entry.Size; tarEntryRom.CRC = BitConverter.GetBytes(entry.Crc); } // Otherwise, use the stream directly else { using (Stream entryStream = entry.OpenEntryStream()) { tarEntryRom = entryStream.GetInfo(size: entry.Size, hashes: this.AvailableHashes); } } // Fill in comon details and add to the list tarEntryRom.Filename = entry.Key; tarEntryRom.Parent = gamename; tarEntryRom.Date = entry.LastModifiedTime?.ToString("yyyy/MM/dd hh:mm:ss"); found.Add(tarEntryRom); } // Dispose of the archive ta.Dispose(); } catch (Exception ex) { logger.Error(ex); return(null); } return(found); }
/// <summary> /// Attempt to extract a file as an archive /// </summary> /// <param name="outDir">Output directory for archive extraction</param> /// <returns>True if the extraction was a success, false otherwise</returns> public override bool CopyAll(string outDir) { bool encounteredErrors = true; try { // Create the temp directory Directory.CreateDirectory(outDir); // Extract all files to the temp directory TarArchive ta = TarArchive.Open(this.Filename); foreach (TarArchiveEntry entry in ta.Entries) { entry.WriteToDirectory(outDir, new ExtractionOptions { PreserveFileTime = true, ExtractFullPath = true, Overwrite = true }); } encounteredErrors = false; ta.Dispose(); } catch (EndOfStreamException ex) { // Catch this but don't count it as an error because SharpCompress is unsafe logger.Verbose(ex); } catch (InvalidOperationException ex) { logger.Warning(ex); encounteredErrors = true; } catch (Exception ex) { logger.Error(ex); encounteredErrors = true; } return(encounteredErrors); }
/// <summary> /// Write a set of input files to a tape archive (assuming the same output archive name) /// </summary> /// <param name="inputFiles">Input files to be moved</param> /// <param name="outDir">Output directory to build to</param> /// <param name="rom">DatItem representing the new information</param> /// <returns>True if the archive was written properly, false otherwise</returns> public override bool Write(List <string> inputFiles, string outDir, List <Rom> roms) { bool success = false; string tempFile = Path.Combine(outDir, $"tmp{Guid.NewGuid()}"); // 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, Sanitizer.RemovePathUnsafeCharacters(roms[0].Machine.Name) + (roms[0].Machine.Name.EndsWith(".tar") ? string.Empty : ".tar")); // Set internal variables TarArchive oldTarFile = TarArchive.Create(); TarArchive tarFile = TarArchive.Create(); 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)) { // Map all inputs to index Dictionary <string, int> inputIndexMap = new Dictionary <string, int>(); for (int i = 0; i < inputFiles.Count; i++) { inputIndexMap.Add(roms[i].Name.Replace('\\', '/'), i); } // Sort the keys in TZIP order List <string> keys = inputIndexMap.Keys.ToList(); keys.Sort(ZipFile.TrrntZipStringCompare); // Now add all of the files in order foreach (string key in keys) { // Get the index mapped to the key int index = inputIndexMap[key]; // Get temporary date-time if possible DateTime?usableDate = null; if (UseDates && !string.IsNullOrWhiteSpace(roms[index].Date) && DateTime.TryParse(roms[index].Date.Replace('\\', '/'), out DateTime dt)) { usableDate = dt; } // Copy the input stream to the output tarFile.AddEntry(roms[index].Name, FileExtensions.TryOpenRead(inputFiles[index]), size: roms[index].Size ?? 0, modified: usableDate); } } // Otherwise, sort the input files and write out in the correct order else { // Open the old archive for reading oldTarFile = TarArchive.Open(archiveFileName); // Get a list of all current entries List <string> entries = oldTarFile.Entries.Select(i => i.Key).ToList(); // Map all inputs to index Dictionary <string, int> inputIndexMap = new Dictionary <string, int>(); for (int i = 0; i < inputFiles.Count; i++) { // If the old one contains the new file, then just skip out if (entries.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 < entries.Count; i++) { inputIndexMap.Add(entries[i], i); } // If the number of entries is the same as the old archive, skip out if (inputIndexMap.Keys.Count <= entries.Count) { success = true; return(success); } // Get the order for the entries with the new file List <string> keys = inputIndexMap.Keys.ToList(); keys.Sort(ZipFile.TrrntZipStringCompare); // 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) { // Get temporary date-time if possible DateTime?usableDate = null; if (UseDates && !string.IsNullOrWhiteSpace(roms[-index - 1].Date) && DateTime.TryParse(roms[-index - 1].Date.Replace('\\', '/'), out DateTime dt)) { usableDate = dt; } // Copy the input file to the output tarFile.AddEntry(roms[-index - 1].Name, FileExtensions.TryOpenRead(inputFiles[-index - 1]), size: roms[-index - 1].Size ?? 0, modified: usableDate); } // Otherwise, copy the file from the old archive else { // Get the stream from the original archive TarArchiveEntry tae = oldTarFile.Entries.ElementAt(index); MemoryStream entry = new MemoryStream(); tae.OpenEntryStream().CopyTo(entry); // Copy the input stream to the output tarFile.AddEntry(key, entry, size: tae.Size, modified: tae.LastModifiedTime); } } } // Close the output tar file tarFile.SaveTo(tempFile, new WriterOptions(CompressionType.None)); success = true; } catch (Exception ex) { logger.Error(ex); success = false; } finally { tarFile.Dispose(); oldTarFile.Dispose(); } // If the old file exists, delete it and replace if (File.Exists(archiveFileName)) { FileExtensions.TryDelete(archiveFileName); } File.Move(tempFile, archiveFileName); return(true); }
/// <summary> /// Write an input stream to a tape archive /// </summary> /// <param name="inputStream">Input stream to be moved</param> /// <param name="outDir">Output directory to build to</param> /// <param name="rom">DatItem representing the new information</param> /// <param name="date">True if the date from the DAT should be used if available, false otherwise (default)</param> /// <param name="romba">True if files should be output in Romba depot folders, false otherwise</param> /// <returns>True if the archive was written properly, false otherwise</returns> public override bool Write(Stream inputStream, string outDir, Rom rom, bool date = false, bool romba = false) { bool success = false; string tempFile = Path.Combine(outDir, "tmp" + Guid.NewGuid().ToString()); // If either input is null or empty, return if (inputStream == null || rom == null || rom.Name == null) { return(success); } // If the stream is not readable, return if (!inputStream.CanRead) { return(success); } // Get the output archive name from the first rebuild rom string archiveFileName = Path.Combine(outDir, Utilities.RemovePathUnsafeCharacters(rom.MachineName) + (rom.MachineName.EndsWith(".tar") ? "" : ".tar")); // Set internal variables TarArchive oldTarFile = TarArchive.Create(); TarArchive tarFile = TarArchive.Create(); 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)) { // Get temporary date-time if possible DateTime?usableDate = null; if (date && !String.IsNullOrWhiteSpace(rom.Date) && DateTime.TryParse(rom.Date.Replace('\\', '/'), out DateTime dt)) { usableDate = dt; } // Copy the input stream to the output inputStream.Seek(0, SeekOrigin.Begin); tarFile.AddEntry(rom.Name, inputStream, size: rom.Size, modified: usableDate); } // Otherwise, sort the input files and write out in the correct order else { // Open the old archive for reading oldTarFile = TarArchive.Open(archiveFileName); // Get a list of all current entries List <string> entries = oldTarFile.Entries.Select(i => i.Key).ToList(); // Map all inputs to index Dictionary <string, int> inputIndexMap = new Dictionary <string, int>(); // If the old one doesn't contain the new file, then add it if (!entries.Contains(rom.Name.Replace('\\', '/'))) { inputIndexMap.Add(rom.Name.Replace('\\', '/'), -1); } // Then add all of the old entries to it too for (int i = 0; i < entries.Count; i++) { inputIndexMap.Add(entries[i], i); } // If the number of entries is the same as the old archive, skip out if (inputIndexMap.Keys.Count <= entries.Count) { success = true; return(success); } // Get the order for the entries with the new file List <string> 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]; // Get temporary date-time if possible DateTime?usableDate = null; if (date && !String.IsNullOrWhiteSpace(rom.Date) && DateTime.TryParse(rom.Date.Replace('\\', '/'), out DateTime dt)) { usableDate = dt; } // If we have the input file, add it now if (index < 0) { // Copy the input file to the output inputStream.Seek(0, SeekOrigin.Begin); tarFile.AddEntry(rom.Name, inputStream, size: rom.Size, modified: usableDate); } // Otherwise, copy the file from the old archive else { // Get the stream from the original archive TarArchiveEntry tae = oldTarFile.Entries.ElementAt(index); MemoryStream entry = new MemoryStream(); tae.OpenEntryStream().CopyTo(entry); // Copy the input stream to the output tarFile.AddEntry(key, entry, size: tae.Size, modified: tae.LastModifiedTime); } } } // Close the output tar file tarFile.SaveTo(tempFile, new WriterOptions(CompressionType.None)); success = true; } catch (Exception ex) { Console.WriteLine(ex); success = false; } finally { inputStream.Dispose(); tarFile.Dispose(); oldTarFile.Dispose(); } // If the old file exists, delete it and replace if (File.Exists(archiveFileName)) { Utilities.TryDeleteFile(archiveFileName); } File.Move(tempFile, archiveFileName); return(success); }
static int UntarFromResources_Lua(IntPtr L) { var uri = Api.lua_tostring(L, 1); var path = Api.lua_tostring(L, 2); var unpackPath = Path.Combine(Application.persistentDataPath, path); TarArchive archive = null; try { if (!Directory.Exists(unpackPath)) { Directory.CreateDirectory(unpackPath); } var forceUnpack = false; if (Api.lua_gettop(L) == 3) { forceUnpack = Api.lua_toboolean(L, 3); } var bytes = ResMgr.LoadBytes(uri); var ms = new MemoryStream(bytes); archive = TarArchive.CreateInputTarArchive(ms); List <string> names = new List <string>(); archive.ProgressMessageEvent += (ar, entry, msg) => { names.Add(entry.Name); }; bool shouldUnpack = false; if (forceUnpack) { shouldUnpack = true; } else { archive.ListContents(); for (int i = 0; i < names.Count; ++i) { var p = Path.Combine(unpackPath, names[i]); if (!File.Exists(p)) { shouldUnpack = true; break; } } if (shouldUnpack) { archive.Dispose(); archive = TarArchive.CreateInputTarArchive(new MemoryStream(bytes)); } } if (shouldUnpack) { archive.ExtractContents(unpackPath); } archive.Dispose(); Api.lua_createtable(L, names.Count, 0); for (int i = 0; i < names.Count; ++i) { Api.lua_pushstring(L, names[i]); Api.lua_seti(L, -2, i); } return(1); } catch (Exception e) { if (archive != null) { archive.Dispose(); } Api.lua_pushnil(L); Api.lua_pushstring(L, e.Message); return(2); } }
public void Dispose() { archive.Dispose(); }