2016-10-24 12:58:57 -07:00
using System ;
2016-09-22 21:00:18 -07:00
using System.Collections.Generic ;
using System.Linq ;
using System.Text.RegularExpressions ;
2016-10-24 12:58:57 -07:00
using SabreTools.Helper.Data ;
using SabreTools.Helper.Dats ;
2016-10-28 21:49:29 -07:00
#if MONO
2016-10-27 11:35:17 -07:00
using System.IO ;
#else
2016-10-26 22:10:47 -07:00
using Alphaleonis.Win32.Filesystem ;
2016-10-24 12:58:57 -07:00
2016-10-26 22:10:47 -07:00
using BinaryReader = System . IO . BinaryReader ;
using BinaryWriter = System . IO . BinaryWriter ;
using EndOfStreamException = System . IO . EndOfStreamException ;
using FileStream = System . IO . FileStream ;
using SeekOrigin = System . IO . SeekOrigin ;
using Stream = System . IO . Stream ;
2016-10-30 21:15:33 -07:00
#endif
using Ionic.Zlib ;
using ROMVault2.SupportedFiles.Zip ;
2017-02-27 23:52:33 -08:00
using SevenZip ;
2016-10-30 21:15:33 -07:00
using SharpCompress.Archives ;
using SharpCompress.Archives.Rar ;
2017-03-15 00:14:56 -07:00
using SharpCompress.Archives.SevenZip ;
2016-10-30 21:15:33 -07:00
using SharpCompress.Archives.Tar ;
using SharpCompress.Common ;
using SharpCompress.Readers ;
using SharpCompress.Writers ;
2016-10-26 22:10:47 -07:00
2016-10-24 12:58:57 -07:00
namespace SabreTools.Helper.Tools
2016-09-22 21:00:18 -07:00
{
2017-02-27 23:00:57 -08:00
/ *
2017-03-05 21:47:09 -08:00
* TODO : Full archive support for : RAR , LRZip , ZPAQ ? , Zstd ? , LZ4 ?
2017-03-05 21:49:29 -08:00
* Torrent 7 - zip : https : //sourceforge.net/p/t7z/code/HEAD/tree/
2017-03-05 21:47:09 -08:00
* LRZIP : https : //github.com/ckolivas/lrzip
2017-03-14 15:18:07 -07:00
* ZPAQ : https : //github.com/zpaq/zpaq - In progress as external DLL
2017-03-05 21:47:09 -08:00
* Zstd : https : //github.com/skbkontur/ZstdNet
* LZ4 : https : //github.com/lz4/lz4
2017-02-27 23:00:57 -08:00
* /
2016-10-21 16:25:22 -07:00
public static class ArchiveTools
2016-09-22 21:00:18 -07:00
{
2016-10-07 12:16:33 -07:00
private const int _bufferSize = 4096 * 128 ;
2017-03-16 12:43:47 -07:00
#region Archive - to - Archive handling
/// <summary>
/// Transfer a single file from one archive to another
/// </summary>
/// <param name="inputArchive">Input archive name</param>
/// <param name="inputEntry">Input entry name</param>
/// <param name="outputDir">Output directory</param>
/// <param name="outputEntry">Output Rom information</param>
/// <param name="date">True if dates are preserved, false otherwise (default)</param>
/// <returns>True if the transfer was a success, false otherwise</returns>
public static bool Transfer ( string inputArchive , string inputEntry , string outputDir , Rom outputEntry , bool date = false )
{
// Verify inputs
// Create list versions
// return Transfer(multiple)
return false ;
}
/// <summary>
/// Transfer multiple files from one archive to another
/// </summary>
/// <param name="inputArchives">Input archive names</param>
/// <param name="inputEntries">Input entry names</param>
/// <param name="outputDir">Output directory</param>
/// <param name="outputEntries">Output Rom informations</param>
/// <param name="date">True if dates are preserved, false otherwise (default)</param>
/// <returns>True if the transfesr were a success, false otherwise</returns>
public static bool Transfer ( List < string > inputArchives , List < string > inputEntries , string outputDir , List < Rom > outputEntries , bool date = false )
{
// Verify inputs
// For each item, extract to stream, write to archive
return false ;
}
#endregion
2016-09-22 21:04:41 -07:00
#region Extraction
2016-09-22 21:00:18 -07:00
/// <summary>
/// Attempt to extract a file as an archive
/// </summary>
/// <param name="input">Name of the file to be extracted</param>
2016-10-31 14:26:23 -07:00
/// <param name="outDir">Output directory for archive extraction</param>
2016-10-05 17:23:44 -07:00
/// <param name="archiveScanLevel">ArchiveScanLevel representing the archive handling levels</param>
2016-09-22 21:00:18 -07:00
/// <returns>True if the extraction was a success, false otherwise</returns>
2017-03-01 21:26:27 -08:00
public static bool ExtractArchive ( string input , string outDir , ArchiveScanLevel archiveScanLevel )
2016-09-22 21:00:18 -07:00
{
bool encounteredErrors = true ;
// First get the archive type
2017-03-01 21:26:27 -08:00
ArchiveType ? at = GetCurrentArchiveType ( input ) ;
2016-09-22 21:00:18 -07:00
// If we got back null, then it's not an archive, so we we return
if ( at = = null )
{
return encounteredErrors ;
}
try
{
2016-10-28 16:39:28 -07:00
// 7-zip
2016-10-05 17:23:44 -07:00
if ( at = = ArchiveType . SevenZip & & ( archiveScanLevel & ArchiveScanLevel . SevenZipInternal ) ! = 0 )
2016-09-22 21:00:18 -07:00
{
2017-03-01 21:26:27 -08:00
Globals . Logger . Verbose ( "Found archive of type: " + at ) ;
2016-09-22 21:00:18 -07:00
// Create the temp directory
2016-10-31 14:26:23 -07:00
Directory . CreateDirectory ( outDir ) ;
2016-09-22 21:00:18 -07:00
// Extract all files to the temp directory
2017-03-15 20:07:28 -07:00
SevenZipArchive sza = SevenZipArchive . Open ( FileTools . TryOpenRead ( input ) ) ;
2017-03-15 00:14:56 -07:00
foreach ( SevenZipArchiveEntry entry in sza . Entries )
2016-09-22 21:00:18 -07:00
{
2017-03-15 00:14:56 -07:00
entry . WriteToDirectory ( outDir , new ExtractionOptions { PreserveFileTime = true , ExtractFullPath = true , Overwrite = true } ) ;
2017-03-14 13:56:27 -07:00
}
2017-03-15 00:14:56 -07:00
encounteredErrors = false ;
2016-09-22 21:00:18 -07:00
sza . Dispose ( ) ;
}
2016-10-28 16:39:28 -07:00
// GZip
2016-10-05 17:23:44 -07:00
else if ( at = = ArchiveType . GZip & & ( archiveScanLevel & ArchiveScanLevel . GZipInternal ) ! = 0 )
2016-09-22 21:00:18 -07:00
{
2017-03-01 21:26:27 -08:00
Globals . Logger . Verbose ( "Found archive of type: " + at ) ;
2016-09-22 21:00:18 -07:00
// Create the temp directory
2016-10-31 14:26:23 -07:00
Directory . CreateDirectory ( outDir ) ;
2016-09-22 21:00:18 -07:00
// Decompress the input stream
2017-03-16 00:08:39 -07:00
FileStream outstream = FileTools . TryCreate ( Path . Combine ( outDir , Path . GetFileNameWithoutExtension ( input ) ) ) ;
2017-03-15 20:07:28 -07:00
GZipStream gzstream = new GZipStream ( FileTools . TryOpenRead ( input ) , Ionic . Zlib . CompressionMode . Decompress ) ;
2016-09-22 21:00:18 -07:00
gzstream . CopyTo ( outstream ) ;
// Dispose of the streams
outstream . Dispose ( ) ;
gzstream . Dispose ( ) ;
encounteredErrors = false ;
}
2016-10-28 16:39:28 -07:00
// RAR
else if ( at = = ArchiveType . Rar & & ( archiveScanLevel & ArchiveScanLevel . RarInternal ) ! = 0 )
{
2017-03-01 21:26:27 -08:00
Globals . Logger . Verbose ( "Found archive of type: " + at ) ;
2016-10-28 16:39:28 -07:00
// Create the temp directory
2016-10-31 14:26:23 -07:00
Directory . CreateDirectory ( outDir ) ;
2016-10-28 16:39:28 -07:00
// Extract all files to the temp directory
RarArchive ra = RarArchive . Open ( input ) ;
foreach ( RarArchiveEntry entry in ra . Entries )
{
2016-10-31 14:26:23 -07:00
entry . WriteToDirectory ( outDir , new ExtractionOptions { PreserveFileTime = true , ExtractFullPath = true , Overwrite = true } ) ;
2016-10-28 16:39:28 -07:00
}
encounteredErrors = false ;
ra . Dispose ( ) ;
}
// TAR
else if ( at = = ArchiveType . Tar & & ( archiveScanLevel & ArchiveScanLevel . TarInternal ) ! = 0 )
{
2017-03-01 21:26:27 -08:00
Globals . Logger . Verbose ( "Found archive of type: " + at ) ;
2016-10-28 16:39:28 -07:00
// Create the temp directory
2016-10-31 14:26:23 -07:00
Directory . CreateDirectory ( outDir ) ;
2016-10-28 16:39:28 -07:00
// Extract all files to the temp directory
TarArchive ta = TarArchive . Open ( input ) ;
foreach ( TarArchiveEntry entry in ta . Entries )
{
2016-10-31 14:26:23 -07:00
entry . WriteToDirectory ( outDir , new ExtractionOptions { PreserveFileTime = true , ExtractFullPath = true , Overwrite = true } ) ;
2016-10-28 16:39:28 -07:00
}
encounteredErrors = false ;
ta . Dispose ( ) ;
}
// Zip
2016-10-08 23:28:09 -07:00
else if ( at = = ArchiveType . Zip & & ( archiveScanLevel & ArchiveScanLevel . ZipInternal ) ! = 0 )
{
2017-03-01 21:26:27 -08:00
Globals . Logger . Verbose ( "Found archive of type: " + at ) ;
2016-10-08 23:28:09 -07:00
// Create the temp directory
2016-10-31 14:26:23 -07:00
Directory . CreateDirectory ( outDir ) ;
2016-10-08 23:28:09 -07:00
// Extract all files to the temp directory
ZipFile zf = new ZipFile ( ) ;
ZipReturn zr = zf . Open ( input , new FileInfo ( input ) . LastWriteTime . Ticks , true ) ;
if ( zr ! = ZipReturn . ZipGood )
{
throw new Exception ( ZipFile . ZipErrorMessageText ( zr ) ) ;
}
for ( int i = 0 ; i < zf . EntriesCount & & zr = = ZipReturn . ZipGood ; i + + )
{
2017-02-26 22:42:38 -08:00
// Open the read stream
2017-02-27 23:52:33 -08:00
zr = zf . OpenReadStream ( i , false , out Stream readStream , out ulong streamsize , out SabreTools . Helper . Data . CompressionMethod cm , out uint lastMod ) ;
2016-10-08 23:28:09 -07:00
2016-10-19 21:49:58 -07:00
// Create the rest of the path, if needed
if ( ! String . IsNullOrEmpty ( Path . GetDirectoryName ( zf . Entries [ i ] . FileName ) ) )
{
2016-10-31 14:26:23 -07:00
Directory . CreateDirectory ( Path . Combine ( outDir , Path . GetDirectoryName ( zf . Entries [ i ] . FileName ) ) ) ;
2016-10-19 21:49:58 -07:00
}
// If the entry ends with a directory separator, continue to the next item, if any
if ( zf . Entries [ i ] . FileName . EndsWith ( Path . DirectorySeparatorChar . ToString ( ) )
| | zf . Entries [ i ] . FileName . EndsWith ( Path . AltDirectorySeparatorChar . ToString ( ) )
| | zf . Entries [ i ] . FileName . EndsWith ( Path . PathSeparator . ToString ( ) ) )
{
continue ;
}
2017-03-16 00:08:39 -07:00
FileStream writeStream = FileTools . TryCreate ( Path . Combine ( outDir , zf . Entries [ i ] . FileName ) ) ;
2016-10-08 23:28:09 -07:00
byte [ ] ibuffer = new byte [ _bufferSize ] ;
int ilen ;
while ( ( ilen = readStream . Read ( ibuffer , 0 , _bufferSize ) ) > 0 )
{
writeStream . Write ( ibuffer , 0 , ilen ) ;
writeStream . Flush ( ) ;
}
zr = zf . CloseReadStream ( ) ;
writeStream . Dispose ( ) ;
}
2017-01-26 17:06:47 -08:00
zf . Close ( ) ;
2016-10-19 21:49:58 -07:00
encounteredErrors = false ;
2016-10-08 23:28:09 -07:00
}
2016-09-22 21:00:18 -07:00
}
catch ( EndOfStreamException )
{
// Catch this but don't count it as an error because SharpCompress is unsafe
}
catch ( InvalidOperationException )
{
encounteredErrors = true ;
}
catch ( Exception )
{
// Don't log file open errors
encounteredErrors = true ;
}
return encounteredErrors ;
}
/// <summary>
/// Attempt to extract a file from an archive
/// </summary>
/// <param name="input">Name of the archive to be extracted</param>
2016-09-29 13:01:06 -07:00
/// <param name="entryName">Name of the entry to be extracted</param>
2016-09-22 21:00:18 -07:00
/// <param name="tempDir">Temporary directory for archive extraction</param>
/// <returns>Name of the extracted file, null on error</returns>
2017-03-01 21:26:27 -08:00
public static string ExtractItem ( string input , string entryName , string tempDir )
2016-09-22 21:00:18 -07:00
{
2017-01-27 16:53:29 -08:00
string realEntry = "" ;
2016-09-29 13:01:06 -07:00
// Set the real entry name
2017-01-27 16:53:29 -08:00
realEntry = "" ;
2016-09-29 13:01:06 -07:00
2016-09-22 21:00:18 -07:00
// First get the archive type
2017-03-01 21:26:27 -08:00
ArchiveType ? at = GetCurrentArchiveType ( input ) ;
2016-09-22 21:00:18 -07:00
// If we got back null, then it's not an archive, so we we return
if ( at = = null )
{
2017-01-30 22:08:33 -08:00
return realEntry ;
2016-09-22 21:00:18 -07:00
}
try
{
2016-10-28 16:39:28 -07:00
switch ( at )
2016-10-08 23:28:09 -07:00
{
2016-10-28 16:39:28 -07:00
case ArchiveType . SevenZip :
2017-03-15 00:14:56 -07:00
SevenZipArchive sza = SevenZipArchive . Open ( input , new ReaderOptions { LeaveStreamOpen = false , } ) ;
foreach ( SevenZipArchiveEntry entry in sza . Entries )
2016-10-08 23:28:09 -07:00
{
2017-03-15 00:14:56 -07:00
if ( entry ! = null & & ! entry . IsDirectory & & entry . Key . Contains ( entryName ) )
2016-10-08 23:28:09 -07:00
{
2017-03-15 00:14:56 -07:00
realEntry = entry . Key ;
2017-01-30 22:08:33 -08:00
// Get the output path
realEntry = Path . Combine ( Path . GetFullPath ( tempDir ) , realEntry ) ;
if ( ! Directory . Exists ( Path . GetDirectoryName ( realEntry ) ) )
{
Directory . CreateDirectory ( Path . GetDirectoryName ( realEntry ) ) ;
}
// Write the file out
2017-03-15 00:14:56 -07:00
entry . WriteToFile ( realEntry ) ;
2016-10-28 16:39:28 -07:00
break ;
2016-10-08 23:28:09 -07:00
}
2016-10-28 16:39:28 -07:00
}
sza . Dispose ( ) ;
break ;
case ArchiveType . GZip :
// Decompress the input stream
realEntry = Path . GetFileNameWithoutExtension ( input ) ;
2017-03-15 20:07:28 -07:00
GZipStream gzstream = new GZipStream ( FileTools . TryOpenRead ( input ) , Ionic . Zlib . CompressionMode . Decompress ) ;
2016-10-28 16:39:28 -07:00
2017-01-30 22:08:33 -08:00
// Get the output path
realEntry = Path . Combine ( Path . GetFullPath ( tempDir ) , realEntry ) ;
if ( ! Directory . Exists ( Path . GetDirectoryName ( realEntry ) ) )
{
Directory . CreateDirectory ( Path . GetDirectoryName ( realEntry ) ) ;
}
// Write the file out
2017-03-15 20:07:28 -07:00
FileStream gzfileout = FileTools . TryCreate ( realEntry ) ;
2017-01-30 22:08:33 -08:00
byte [ ] gbuffer = new byte [ _bufferSize ] ;
int glen ;
while ( ( glen = gzstream . Read ( gbuffer , 0 , _bufferSize ) ) > 0 )
{
gzfileout . Write ( gbuffer , 0 , glen ) ;
gzfileout . Flush ( ) ;
}
// Dispose of the streams
2016-10-28 16:39:28 -07:00
gzstream . Dispose ( ) ;
2017-01-30 22:08:33 -08:00
gzfileout . Dispose ( ) ;
2016-10-28 16:39:28 -07:00
break ;
case ArchiveType . Rar :
RarArchive ra = RarArchive . Open ( input , new ReaderOptions { LeaveStreamOpen = false , } ) ;
foreach ( RarArchiveEntry entry in ra . Entries )
{
if ( entry ! = null & & ! entry . IsDirectory & & entry . Key . Contains ( entryName ) )
{
realEntry = entry . Key ;
2017-01-30 22:08:33 -08:00
// Get the output path
realEntry = Path . Combine ( Path . GetFullPath ( tempDir ) , realEntry ) ;
if ( ! Directory . Exists ( Path . GetDirectoryName ( realEntry ) ) )
{
Directory . CreateDirectory ( Path . GetDirectoryName ( realEntry ) ) ;
}
// Write the file out
entry . WriteToFile ( realEntry ) ;
2016-10-28 16:39:28 -07:00
break ;
}
}
ra . Dispose ( ) ;
break ;
2016-10-08 23:28:09 -07:00
2016-10-28 16:39:28 -07:00
case ArchiveType . Tar :
TarArchive ta = TarArchive . Open ( input , new ReaderOptions { LeaveStreamOpen = false , } ) ;
foreach ( TarArchiveEntry entry in ta . Entries )
{
if ( entry ! = null & & ! entry . IsDirectory & & entry . Key . Contains ( entryName ) )
{
realEntry = entry . Key ;
2017-01-30 22:08:33 -08:00
// Get the output path
realEntry = Path . Combine ( Path . GetFullPath ( tempDir ) , realEntry ) ;
if ( ! Directory . Exists ( Path . GetDirectoryName ( realEntry ) ) )
{
Directory . CreateDirectory ( Path . GetDirectoryName ( realEntry ) ) ;
}
// Write the file out
entry . WriteToFile ( realEntry ) ;
2016-10-28 16:39:28 -07:00
break ;
}
2016-10-08 23:28:09 -07:00
}
2016-10-28 16:39:28 -07:00
ta . Dispose ( ) ;
break ;
case ArchiveType . Zip :
ZipFile zf = new ZipFile ( ) ;
ZipReturn zr = zf . Open ( input , new FileInfo ( input ) . LastWriteTime . Ticks , true ) ;
if ( zr ! = ZipReturn . ZipGood )
2016-09-22 21:00:18 -07:00
{
2016-10-28 16:39:28 -07:00
throw new Exception ( ZipFile . ZipErrorMessageText ( zr ) ) ;
2016-09-22 21:00:18 -07:00
}
2016-10-28 16:39:28 -07:00
for ( int i = 0 ; i < zf . EntriesCount & & zr = = ZipReturn . ZipGood ; i + + )
{
if ( zf . Entries [ i ] . FileName . Contains ( entryName ) )
{
realEntry = zf . Entries [ i ] . FileName ;
2017-02-26 22:42:38 -08:00
// Open the read stream
2017-02-27 23:52:33 -08:00
zr = zf . OpenReadStream ( i , false , out Stream readStream , out ulong streamsize , out SabreTools . Helper . Data . CompressionMethod cm , out uint lastMod ) ;
2016-10-28 16:39:28 -07:00
2017-01-30 22:08:33 -08:00
// Get the output path
realEntry = Path . Combine ( Path . GetFullPath ( tempDir ) , realEntry ) ;
if ( ! Directory . Exists ( Path . GetDirectoryName ( realEntry ) ) )
{
Directory . CreateDirectory ( Path . GetDirectoryName ( realEntry ) ) ;
}
// Write the file out
2017-03-15 20:07:28 -07:00
FileStream zipfileout = FileTools . TryCreate ( realEntry ) ;
2017-01-30 22:08:33 -08:00
byte [ ] zbuffer = new byte [ _bufferSize ] ;
int zlen ;
while ( ( zlen = readStream . Read ( zbuffer , 0 , _bufferSize ) ) > 0 )
2016-10-28 16:39:28 -07:00
{
2017-01-30 22:08:33 -08:00
zipfileout . Write ( zbuffer , 0 , zlen ) ;
zipfileout . Flush ( ) ;
2016-10-28 16:39:28 -07:00
}
zr = zf . CloseReadStream ( ) ;
2017-01-30 22:08:33 -08:00
zipfileout . Dispose ( ) ;
2016-10-28 16:39:28 -07:00
}
}
2017-03-16 12:43:47 -07:00
zf . Dispose ( ) ;
2016-10-28 16:39:28 -07:00
break ;
2016-09-22 21:00:18 -07:00
}
}
catch ( Exception ex )
{
2017-03-01 21:26:27 -08:00
Globals . Logger . Error ( ex . ToString ( ) ) ;
2017-01-30 22:08:33 -08:00
realEntry = "" ;
2017-01-30 14:07:12 -08:00
}
2017-01-30 22:08:33 -08:00
return realEntry ;
2016-09-22 21:00:18 -07:00
}
#endregion
2016-09-22 21:04:41 -07:00
#region Information
2016-09-22 21:00:18 -07:00
/// <summary>
/// Generate a list of RomData objects from the header values in an archive
/// </summary>
/// <param name="input">Input file to get data from</param>
/// <returns>List of RomData objects representing the found data</returns>
2017-03-16 12:43:47 -07:00
/// TODO: Can this be merged with the extended one?
2017-03-01 21:26:27 -08:00
public static List < Rom > GetArchiveFileInfo ( string input )
2016-09-22 21:00:18 -07:00
{
List < Rom > roms = new List < Rom > ( ) ;
string gamename = Path . GetFileNameWithoutExtension ( input ) ;
2017-01-11 09:30:35 -08:00
// First, we check that there is anything being passed as the input
if ( String . IsNullOrEmpty ( input ) )
{
return roms ;
}
// Next, get the archive type
2017-03-01 21:26:27 -08:00
ArchiveType ? at = GetCurrentArchiveType ( input ) ;
2016-09-22 21:00:18 -07:00
// If we got back null, then it's not an archive, so we we return
if ( at = = null )
{
return roms ;
}
// If we got back GZip, try to get TGZ info first
else if ( at = = ArchiveType . GZip )
{
2017-03-01 21:26:27 -08:00
Rom possibleTgz = GetTorrentGZFileInfo ( input ) ;
2016-09-22 21:00:18 -07:00
// If it was, then add it to the outputs and continue
2017-01-11 09:31:27 -08:00
if ( possibleTgz ! = null & & possibleTgz . Name ! = null )
2016-09-22 21:00:18 -07:00
{
roms . Add ( possibleTgz ) ;
return roms ;
}
}
IReader reader = null ;
try
{
2017-03-01 21:26:27 -08:00
Globals . Logger . Verbose ( "Found archive of type: " + at ) ;
2016-09-22 21:00:18 -07:00
long size = 0 ;
2017-01-27 16:53:29 -08:00
string crc = "" ;
2016-09-22 21:00:18 -07:00
2016-10-28 17:53:27 -07:00
switch ( at )
2016-10-08 23:28:09 -07:00
{
2016-10-28 17:53:27 -07:00
case ArchiveType . SevenZip :
2017-03-15 00:14:56 -07:00
SevenZipArchive sza = SevenZipArchive . Open ( input , new ReaderOptions { LeaveStreamOpen = false } ) ;
foreach ( SevenZipArchiveEntry entry in sza . Entries )
2016-10-28 17:53:27 -07:00
{
if ( entry ! = null & & ! entry . IsDirectory )
{
2017-03-15 00:14:56 -07:00
Globals . Logger . Verbose ( "Entry found: '" + entry . Key + "': "
+ ( size = = 0 ? entry . Size : size ) + ", "
2017-01-27 16:53:29 -08:00
+ ( crc = = "" ? entry . Crc . ToString ( "X" ) . ToLowerInvariant ( ) : crc ) ) ;
2016-09-22 21:00:18 -07:00
2016-10-28 17:53:27 -07:00
roms . Add ( new Rom
{
Type = ItemType . Rom ,
2017-03-15 00:14:56 -07:00
Name = entry . Key ,
Size = ( size = = 0 ? entry . Size : size ) ,
2017-01-27 16:53:29 -08:00
CRC = ( crc = = "" ? entry . Crc . ToString ( "X" ) . ToLowerInvariant ( ) : crc ) ,
2016-10-28 17:53:27 -07:00
Machine = new Machine
{
Name = gamename ,
} ,
} ) ;
}
}
break ;
2016-10-08 23:28:09 -07:00
2016-10-28 17:53:27 -07:00
case ArchiveType . GZip : // Get the CRC and size from the file
2017-03-15 20:07:28 -07:00
BinaryReader br = new BinaryReader ( FileTools . TryOpenRead ( input ) ) ;
2016-10-28 17:53:27 -07:00
br . BaseStream . Seek ( - 8 , SeekOrigin . End ) ;
byte [ ] headercrc = br . ReadBytes ( 4 ) ;
crc = BitConverter . ToString ( headercrc . Reverse ( ) . ToArray ( ) ) . Replace ( "-" , string . Empty ) . ToLowerInvariant ( ) ;
byte [ ] headersize = br . ReadBytes ( 4 ) ;
size = BitConverter . ToInt32 ( headersize . Reverse ( ) . ToArray ( ) , 0 ) ;
br . Dispose ( ) ;
break ;
2016-10-08 23:28:09 -07:00
2016-10-28 17:53:27 -07:00
case ArchiveType . Rar :
RarArchive ra = RarArchive . Open ( input , new ReaderOptions { LeaveStreamOpen = false } ) ;
foreach ( RarArchiveEntry entry in ra . Entries )
2016-10-08 23:28:09 -07:00
{
2016-10-28 17:53:27 -07:00
if ( entry ! = null & & ! entry . IsDirectory )
2016-10-24 12:58:57 -07:00
{
2017-03-01 21:26:27 -08:00
Globals . Logger . Verbose ( "Entry found: '" + entry . Key + "': "
2016-10-28 17:53:27 -07:00
+ ( size = = 0 ? entry . Size : size ) + ", "
2017-01-27 16:53:29 -08:00
+ ( crc = = "" ? entry . Crc . ToString ( "X" ) . ToLowerInvariant ( ) : crc ) ) ;
2016-10-28 15:13:29 -07:00
2016-10-28 17:53:27 -07:00
roms . Add ( new Rom
{
Type = ItemType . Rom ,
Name = entry . Key ,
Size = ( size = = 0 ? entry . Size : size ) ,
2017-01-27 16:53:29 -08:00
CRC = ( crc = = "" ? entry . Crc . ToString ( "X" ) . ToLowerInvariant ( ) : crc ) ,
2016-10-28 17:53:27 -07:00
Machine = new Machine
{
Name = gamename ,
} ,
} ) ;
}
}
break ;
case ArchiveType . Tar :
TarArchive ta = TarArchive . Open ( input , new ReaderOptions { LeaveStreamOpen = false } ) ;
foreach ( TarArchiveEntry entry in ta . Entries )
{
if ( entry ! = null & & ! entry . IsDirectory )
2016-10-28 15:13:29 -07:00
{
2017-03-01 21:26:27 -08:00
Globals . Logger . Verbose ( "Entry found: '" + entry . Key + "': "
2016-10-28 17:53:27 -07:00
+ ( size = = 0 ? entry . Size : size ) + ", "
2017-01-27 16:53:29 -08:00
+ ( crc = = "" ? entry . Crc . ToString ( "X" ) . ToLowerInvariant ( ) : crc ) ) ;
2016-10-28 15:13:29 -07:00
2016-10-28 17:53:27 -07:00
roms . Add ( new Rom
2016-10-28 15:13:29 -07:00
{
2016-10-28 17:53:27 -07:00
Type = ItemType . Rom ,
Name = entry . Key ,
Size = ( size = = 0 ? entry . Size : size ) ,
2017-01-27 16:53:29 -08:00
CRC = ( crc = = "" ? entry . Crc . ToString ( "X" ) . ToLowerInvariant ( ) : crc ) ,
2016-10-28 17:53:27 -07:00
Machine = new Machine
{
Name = gamename ,
} ,
} ) ;
}
2016-10-28 15:13:29 -07:00
}
2016-10-28 17:53:27 -07:00
break ;
case ArchiveType . Zip :
ZipFile zf = new ZipFile ( ) ;
ZipReturn zr = zf . Open ( input , new FileInfo ( input ) . LastWriteTime . Ticks , true ) ;
if ( zr ! = ZipReturn . ZipGood )
2016-09-22 21:00:18 -07:00
{
2016-10-28 17:53:27 -07:00
throw new Exception ( ZipFile . ZipErrorMessageText ( zr ) ) ;
}
for ( int i = 0 ; i < zf . EntriesCount & & zr = = ZipReturn . ZipGood ; i + + )
{
string newname = zf . Entries [ i ] . FileName ;
long newsize = ( size = = 0 ? ( long ) zf . Entries [ i ] . UncompressedSize : size ) ;
string newcrc = BitConverter . ToString ( zf . Entries [ i ] . CRC . Reverse ( ) . ToArray ( ) , 0 , zf . Entries [ i ] . CRC . Length ) . Replace ( "-" , string . Empty ) . ToLowerInvariant ( ) ;
2017-03-01 21:26:27 -08:00
Globals . Logger . Verbose ( "Entry found: '" + newname + "': " + newsize + ", " + newcrc ) ;
2016-09-22 21:00:18 -07:00
roms . Add ( new Rom
{
Type = ItemType . Rom ,
2016-10-28 17:53:27 -07:00
Name = newname ,
Size = newsize ,
CRC = newcrc ,
2016-10-24 12:58:57 -07:00
Machine = new Machine
{
Name = gamename ,
} ,
2016-09-22 21:00:18 -07:00
} ) ;
}
2016-10-28 17:53:27 -07:00
break ;
2016-09-22 21:00:18 -07:00
}
}
catch ( Exception ex )
{
2017-03-01 21:26:27 -08:00
Globals . Logger . Error ( ex . ToString ( ) ) ;
2016-09-22 21:00:18 -07:00
}
finally
{
reader ? . Dispose ( ) ;
}
return roms ;
}
2017-03-16 13:19:16 -07:00
/// <summary>
/// Generate a list of empty folders in an archive
/// </summary>
/// <param name="input">Input file to get data from</param>
/// <returns>List of empty folders in the archive</returns>
public static List < string > GetEmptyFoldersInArchive ( string input )
{
List < string > empties = new List < string > ( ) ;
string gamename = Path . GetFileNameWithoutExtension ( input ) ;
// First, we check that there is anything being passed as the input
if ( String . IsNullOrEmpty ( input ) )
{
return empties ;
}
// Next, get the archive type
ArchiveType ? at = GetCurrentArchiveType ( input ) ;
// If we got back null, then it's not an archive, so we we return
if ( at = = null )
{
return empties ;
}
IReader reader = null ;
try
{
Globals . Logger . Verbose ( "Found archive of type: " + at ) ;
switch ( at )
{
case ArchiveType . SevenZip :
SevenZipArchive sza = SevenZipArchive . Open ( input , new ReaderOptions { LeaveStreamOpen = false } ) ;
2017-03-16 14:16:46 -07:00
List < SevenZipArchiveEntry > sevenZipEntries = sza . Entries . OrderBy ( e = > e . Key , new NaturalSort . NaturalReversedComparer ( ) ) . ToList ( ) ;
string lastSevenZipEntry = null ;
foreach ( SevenZipArchiveEntry entry in sevenZipEntries )
2017-03-16 13:19:16 -07:00
{
2017-03-16 14:16:46 -07:00
if ( entry ! = null )
2017-03-16 13:19:16 -07:00
{
2017-03-16 14:16:46 -07:00
// If the current is a superset of last, we skip it
if ( lastSevenZipEntry ! = null & & lastSevenZipEntry . StartsWith ( entry . Key ) )
{
// No-op
}
// If the entry is a directory, we add it
else if ( entry . IsDirectory )
{
empties . Add ( entry . Key ) ;
lastSevenZipEntry = entry . Key ;
}
2017-03-16 13:19:16 -07:00
}
}
break ;
case ArchiveType . GZip :
// GZip files don't contain directories
break ;
case ArchiveType . Rar :
RarArchive ra = RarArchive . Open ( input , new ReaderOptions { LeaveStreamOpen = false } ) ;
2017-03-16 14:16:46 -07:00
List < RarArchiveEntry > rarEntries = ra . Entries . OrderBy ( e = > e . Key , new NaturalSort . NaturalReversedComparer ( ) ) . ToList ( ) ;
string lastRarEntry = null ;
foreach ( RarArchiveEntry entry in rarEntries )
2017-03-16 13:19:16 -07:00
{
2017-03-16 14:16:46 -07:00
if ( entry ! = null )
2017-03-16 13:19:16 -07:00
{
2017-03-16 14:16:46 -07:00
// If the current is a superset of last, we skip it
if ( lastRarEntry ! = null & & lastRarEntry . StartsWith ( entry . Key ) )
{
// No-op
}
// If the entry is a directory, we add it
else if ( entry . IsDirectory )
{
empties . Add ( entry . Key ) ;
lastRarEntry = entry . Key ;
}
2017-03-16 13:19:16 -07:00
}
}
break ;
case ArchiveType . Tar :
TarArchive ta = TarArchive . Open ( input , new ReaderOptions { LeaveStreamOpen = false } ) ;
2017-03-16 14:16:46 -07:00
List < TarArchiveEntry > tarEntries = ta . Entries . OrderBy ( e = > e . Key , new NaturalSort . NaturalReversedComparer ( ) ) . ToList ( ) ;
string lastTarEntry = null ;
foreach ( TarArchiveEntry entry in tarEntries )
2017-03-16 13:19:16 -07:00
{
2017-03-16 14:16:46 -07:00
if ( entry ! = null )
2017-03-16 13:19:16 -07:00
{
2017-03-16 14:16:46 -07:00
// If the current is a superset of last, we skip it
if ( lastTarEntry ! = null & & lastTarEntry . StartsWith ( entry . Key ) )
{
// No-op
}
// If the entry is a directory, we add it
else if ( entry . IsDirectory )
{
empties . Add ( entry . Key ) ;
lastTarEntry = entry . Key ;
}
2017-03-16 13:19:16 -07:00
}
}
break ;
case ArchiveType . Zip :
SharpCompress . Archives . Zip . ZipArchive za = SharpCompress . Archives . Zip . ZipArchive . Open ( input , new ReaderOptions { LeaveStreamOpen = false } ) ;
2017-03-16 14:16:46 -07:00
List < SharpCompress . Archives . Zip . ZipArchiveEntry > zipEntries = za . Entries . OrderBy ( e = > e . Key , new NaturalSort . NaturalReversedComparer ( ) ) . ToList ( ) ;
string lastZipEntry = null ;
foreach ( SharpCompress . Archives . Zip . ZipArchiveEntry entry in zipEntries )
2017-03-16 13:19:16 -07:00
{
2017-03-16 14:16:46 -07:00
if ( entry ! = null )
2017-03-16 13:19:16 -07:00
{
2017-03-16 14:16:46 -07:00
// If the current is a superset of last, we skip it
if ( lastZipEntry ! = null & & lastZipEntry . StartsWith ( entry . Key ) )
{
// No-op
}
// If the entry is a directory, we add it
else
{
if ( entry . IsDirectory )
{
empties . Add ( entry . Key ) ;
}
lastZipEntry = entry . Key ;
}
2017-03-16 13:19:16 -07:00
}
}
break ;
}
}
catch ( Exception ex )
{
Globals . Logger . Error ( ex . ToString ( ) ) ;
}
finally
{
reader ? . Dispose ( ) ;
}
return empties ;
}
2017-03-16 01:02:55 -07:00
/// <summary>
/// Generate a list of RomData objects from the header values in an archive
/// </summary>
/// <param name="input">Input file to get data from</param>
/// <returns>List of RomData objects representing the found data</returns>
/// <remarks>TODO: All instances of Hash.DeepHashes should be made into 0x0 eventually</remarks>
public static List < Rom > GetExtendedArchiveFileInfo ( string input , Hash omitFromScan = Hash . DeepHashes )
{
List < Rom > found = new List < Rom > ( ) ;
2017-03-16 14:16:46 -07:00
string gamename = Path . GetFileNameWithoutExtension ( input ) ;
2017-03-16 01:02:55 -07:00
// First get the archive type
ArchiveType ? at = GetCurrentArchiveType ( input ) ;
// If we got back null, then it's not an archive, so we we return
if ( at = = null )
{
return null ;
}
try
{
Globals . Logger . Verbose ( "Found archive of type: " + at ) ;
switch ( at )
{
// 7-zip
case ArchiveType . SevenZip :
SevenZipArchive sza = SevenZipArchive . Open ( FileTools . TryOpenRead ( input ) ) ;
foreach ( SevenZipArchiveEntry entry in sza . Entries )
{
// Create and populate the entry stream
MemoryStream entryStream = new MemoryStream ( ) ;
entry . WriteTo ( entryStream ) ;
// Get and add the extended Rom information
Rom sevenZipEntryRom = FileTools . GetStreamInfo ( entryStream , entryStream . Length , omitFromScan : omitFromScan ) ;
sevenZipEntryRom . Name = entry . Key ;
sevenZipEntryRom . Machine = new Machine ( )
{
2017-03-16 14:16:46 -07:00
Name = gamename ,
2017-03-16 01:02:55 -07:00
} ;
sevenZipEntryRom . Date = entry . LastModifiedTime ? . ToString ( "yyyy/MM/dd hh:mm:ss" ) ;
found . Add ( sevenZipEntryRom ) ;
}
// Dispose of the archive
sza . Dispose ( ) ;
break ;
// GZip
case ArchiveType . GZip :
// Create and populate the entry stream
MemoryStream outstream = new MemoryStream ( ) ;
GZipStream gzstream = new GZipStream ( FileTools . TryOpenRead ( input ) , Ionic . Zlib . CompressionMode . Decompress ) ;
gzstream . CopyTo ( outstream ) ;
// Get and add the extended Rom information
Rom gzipEntryRom = FileTools . GetStreamInfo ( outstream , outstream . Length , omitFromScan : omitFromScan ) ;
gzipEntryRom . Name = gzstream . FileName ;
gzipEntryRom . Machine = new Machine ( )
{
2017-03-16 14:16:46 -07:00
Name = gamename ,
2017-03-16 01:02:55 -07:00
} ;
gzipEntryRom . Date = gzstream . LastModified ? . ToString ( "yyyy/MM/dd hh:mm:ss" ) ;
found . Add ( gzipEntryRom ) ;
// Dispose of the archive
gzstream . Dispose ( ) ;
break ;
// RAR
case ArchiveType . Rar :
RarArchive ra = RarArchive . Open ( input ) ;
foreach ( RarArchiveEntry entry in ra . Entries )
{
// Create and populate the entry stream
MemoryStream entryStream = new MemoryStream ( ) ;
entry . WriteTo ( entryStream ) ;
// Get and add the extended Rom information
Rom rarEntryRom = FileTools . GetStreamInfo ( entryStream , entryStream . Length , omitFromScan : omitFromScan ) ;
rarEntryRom . Name = entry . Key ;
rarEntryRom . Machine = new Machine ( )
{
2017-03-16 14:16:46 -07:00
Name = gamename ,
2017-03-16 01:02:55 -07:00
} ;
rarEntryRom . Date = entry . LastModifiedTime ? . ToString ( "yyyy/MM/dd hh:mm:ss" ) ;
found . Add ( rarEntryRom ) ;
}
// Dispose of the archive
ra . Dispose ( ) ;
break ;
// TAR
case ArchiveType . Tar :
// Extract all files to the temp directory
TarArchive ta = TarArchive . Open ( input ) ;
foreach ( TarArchiveEntry entry in ta . Entries )
{
// Create and populate the entry stream
MemoryStream entryStream = new MemoryStream ( ) ;
entry . WriteTo ( entryStream ) ;
// Get and add the extended Rom information
Rom tarEntryName = FileTools . GetStreamInfo ( entryStream , entryStream . Length , omitFromScan : omitFromScan ) ;
tarEntryName . Name = entry . Key ;
tarEntryName . Machine = new Machine ( )
{
2017-03-16 14:16:46 -07:00
Name = gamename ,
2017-03-16 01:02:55 -07:00
} ;
tarEntryName . Date = entry . LastModifiedTime ? . ToString ( "yyyy/MM/dd hh:mm:ss" ) ;
found . Add ( tarEntryName ) ;
}
// Dispose of the archive
ta . Dispose ( ) ;
break ;
// Zip
case ArchiveType . Zip :
ZipFile zf = new ZipFile ( ) ;
ZipReturn zr = zf . Open ( input , new FileInfo ( input ) . LastWriteTime . Ticks , true ) ;
if ( zr ! = ZipReturn . ZipGood )
{
throw new Exception ( ZipFile . ZipErrorMessageText ( zr ) ) ;
}
for ( int i = 0 ; i < zf . EntriesCount & & zr = = ZipReturn . ZipGood ; i + + )
{
// Open the read stream
zr = zf . OpenReadStream ( i , false , out Stream readStream , out ulong streamsize , out SabreTools . Helper . Data . CompressionMethod cm , out uint lastMod ) ;
// If the entry ends with a directory separator, continue to the next item, if any
if ( zf . Entries [ i ] . FileName . EndsWith ( Path . DirectorySeparatorChar . ToString ( ) )
| | zf . Entries [ i ] . FileName . EndsWith ( Path . AltDirectorySeparatorChar . ToString ( ) )
| | zf . Entries [ i ] . FileName . EndsWith ( Path . PathSeparator . ToString ( ) ) )
{
continue ;
}
MemoryStream entryStream = new MemoryStream ( ) ;
byte [ ] ibuffer = new byte [ _bufferSize ] ;
int ilen ;
while ( ( ilen = readStream . Read ( ibuffer , 0 , _bufferSize ) ) > 0 )
{
entryStream . Write ( ibuffer , 0 , ilen ) ;
entryStream . Flush ( ) ;
}
zr = zf . CloseReadStream ( ) ;
// Get and add the extended Rom information
Rom rarEntryRom = FileTools . GetStreamInfo ( entryStream , entryStream . Length , omitFromScan : omitFromScan ) ;
rarEntryRom . Name = zf . Entries [ i ] . FileName ;
rarEntryRom . Machine = new Machine ( )
{
2017-03-16 14:16:46 -07:00
Name = gamename ,
2017-03-16 01:02:55 -07:00
} ;
rarEntryRom . Date = zf . Entries [ i ] . LastMod . ToString ( "yyyy/MM/dd hh:mm:ss" ) ; // TODO: Fix this from ticks
found . Add ( rarEntryRom ) ;
}
// Dispose of the archive
zf . Close ( ) ;
break ;
}
}
catch ( Exception )
{
// Don't log file open errors
2017-03-16 12:43:47 -07:00
return null ;
2017-03-16 01:02:55 -07:00
}
return found ;
}
2016-09-22 21:00:18 -07:00
/// <summary>
/// Retrieve file information for a single torrent GZ file
/// </summary>
/// <param name="input">Filename to get information from</param>
/// <returns>Populated RomData object if success, empty one on error</returns>
2017-03-01 21:26:27 -08:00
public static Rom GetTorrentGZFileInfo ( string input )
2016-09-22 21:00:18 -07:00
{
2016-11-02 10:29:34 -07:00
// Check for the file existing first
if ( ! File . Exists ( input ) )
{
return null ;
}
2016-09-22 21:00:18 -07:00
string datum = Path . GetFileName ( input ) . ToLowerInvariant ( ) ;
long filesize = new FileInfo ( input ) . Length ;
2016-11-02 10:29:34 -07:00
// If we have the romba depot files, just skip them gracefully
if ( datum = = ".romba_size" | | datum = = ".romba_size.backup" )
{
2017-03-01 21:26:27 -08:00
Globals . Logger . Verbose ( "Romba depot file found, skipping: " + input ) ;
2016-11-02 10:29:34 -07:00
return null ;
}
2016-09-22 21:00:18 -07:00
// Check if the name is the right length
2017-02-27 22:41:45 -08:00
if ( ! Regex . IsMatch ( datum , @"^[0-9a-f]{" + Constants . SHA1Length + @"}\.gz" ) ) // TODO: When updating to SHA-256, this needs to update to Constants.SHA256Length
2016-09-22 21:00:18 -07:00
{
2017-03-01 21:26:27 -08:00
Globals . Logger . Warning ( "Non SHA-1 filename found, skipping: '" + Path . GetFullPath ( input ) + "'" ) ;
2016-10-19 10:08:39 -07:00
return null ;
2016-09-22 21:00:18 -07:00
}
// Check if the file is at least the minimum length
if ( filesize < 40 /* bytes */ )
{
2017-03-01 21:26:27 -08:00
Globals . Logger . Warning ( "Possibly corrupt file '" + Path . GetFullPath ( input ) + "' with size " + Style . GetBytesReadable ( filesize ) ) ;
2016-10-19 10:08:39 -07:00
return null ;
2016-09-22 21:00:18 -07:00
}
// Get the Romba-specific header data
byte [ ] header ; // Get preamble header for checking
byte [ ] headermd5 ; // MD5
byte [ ] headercrc ; // CRC
2016-10-19 10:08:39 -07:00
ulong headersz ; // Int64 size
2017-03-15 20:07:28 -07:00
BinaryReader br = new BinaryReader ( FileTools . TryOpenRead ( input ) ) ;
2016-09-22 21:00:18 -07:00
header = br . ReadBytes ( 12 ) ;
headermd5 = br . ReadBytes ( 16 ) ;
headercrc = br . ReadBytes ( 4 ) ;
2016-10-19 10:08:39 -07:00
headersz = br . ReadUInt64 ( ) ;
2016-09-22 21:00:18 -07:00
br . Dispose ( ) ;
// If the header is not correct, return a blank rom
bool correct = true ;
for ( int i = 0 ; i < header . Length ; i + + )
{
// This is a temp fix to ignore the modification time and OS until romba can be fixed
if ( i = = 4 | | i = = 5 | | i = = 6 | | i = = 7 | | i = = 9 )
{
continue ;
}
correct & = ( header [ i ] = = Constants . TorrentGZHeader [ i ] ) ;
}
if ( ! correct )
{
2016-10-19 10:08:39 -07:00
return null ;
2016-09-22 21:00:18 -07:00
}
// Now convert the data and get the right position
string gzmd5 = BitConverter . ToString ( headermd5 ) . Replace ( "-" , string . Empty ) ;
string gzcrc = BitConverter . ToString ( headercrc ) . Replace ( "-" , string . Empty ) ;
2016-10-19 10:08:39 -07:00
long extractedsize = ( long ) headersz ;
2016-09-22 21:00:18 -07:00
Rom rom = new Rom
{
Type = ItemType . Rom ,
Name = Path . GetFileNameWithoutExtension ( input ) . ToLowerInvariant ( ) ,
Size = extractedsize ,
CRC = gzcrc . ToLowerInvariant ( ) ,
MD5 = gzmd5 . ToLowerInvariant ( ) ,
2017-02-27 21:27:43 -08:00
SHA1 = Path . GetFileNameWithoutExtension ( input ) . ToLowerInvariant ( ) , // TODO: When updating to SHA-256, this needs to update to SHA256
2016-10-24 12:58:57 -07:00
Machine = new Machine
{
Name = Path . GetFileNameWithoutExtension ( input ) . ToLowerInvariant ( ) ,
} ,
2016-09-22 21:00:18 -07:00
} ;
return rom ;
}
/// <summary>
/// Returns the archive type of an input file
/// </summary>
/// <param name="input">Input file to check</param>
/// <returns>ArchiveType of inputted file (null on error)</returns>
2017-03-01 21:26:27 -08:00
public static ArchiveType ? GetCurrentArchiveType ( string input )
2016-09-22 21:00:18 -07:00
{
ArchiveType ? outtype = null ;
2016-10-28 15:13:29 -07:00
// If the file is null, then we have no archive type
if ( input = = null )
{
return outtype ;
}
2016-09-22 21:00:18 -07:00
// First line of defense is going to be the extension, for better or worse
string ext = Path . GetExtension ( input ) . ToLowerInvariant ( ) ;
if ( ext . StartsWith ( "." ) )
{
ext = ext . Substring ( 1 ) ;
}
if ( ext ! = "7z" & & ext ! = "gz" & & ext ! = "lzma" & & ext ! = "rar"
& & ext ! = "rev" & & ext ! = "r00" & & ext ! = "r01" & & ext ! = "tar"
& & ext ! = "tgz" & & ext ! = "tlz" & & ext ! = "zip" & & ext ! = "zipx" )
{
return outtype ;
}
// Read the first bytes of the file and get the magic number
try
{
byte [ ] magic = new byte [ 8 ] ;
2017-03-15 20:07:28 -07:00
BinaryReader br = new BinaryReader ( FileTools . TryOpenRead ( input ) ) ;
2016-09-22 21:00:18 -07:00
magic = br . ReadBytes ( 8 ) ;
br . Dispose ( ) ;
// Convert it to an uppercase string
string mstr = string . Empty ;
for ( int i = 0 ; i < magic . Length ; i + + )
{
mstr + = BitConverter . ToString ( new byte [ ] { magic [ i ] } ) ;
}
mstr = mstr . ToUpperInvariant ( ) ;
// Now try to match it to a known signature
if ( mstr . StartsWith ( Constants . SevenZipSig ) )
{
outtype = ArchiveType . SevenZip ;
}
else if ( mstr . StartsWith ( Constants . GzSig ) )
{
outtype = ArchiveType . GZip ;
}
else if ( mstr . StartsWith ( Constants . RarSig ) | | mstr . StartsWith ( Constants . RarFiveSig ) )
{
outtype = ArchiveType . Rar ;
}
else if ( mstr . StartsWith ( Constants . TarSig ) | | mstr . StartsWith ( Constants . TarZeroSig ) )
{
outtype = ArchiveType . Tar ;
}
else if ( mstr . StartsWith ( Constants . ZipSig ) | | mstr . StartsWith ( Constants . ZipSigEmpty ) | | mstr . StartsWith ( Constants . ZipSigSpanned ) )
{
outtype = ArchiveType . Zip ;
}
}
catch ( Exception )
{
// Don't log file open errors
}
return outtype ;
}
/// <summary>
/// Get if the current file should be scanned internally and externally
/// </summary>
/// <param name="input">Name of the input file to check</param>
2016-10-05 17:23:44 -07:00
/// <param name="archiveScanLevel">ArchiveScanLevel representing the archive handling levels</param>
2016-09-22 21:00:18 -07:00
/// <param name="shouldExternalProcess">Output parameter determining if file should be processed externally</param>
/// <param name="shouldInternalProcess">Output parameter determining if file should be processed internally</param>
2016-10-05 17:23:44 -07:00
public static void GetInternalExternalProcess ( string input , ArchiveScanLevel archiveScanLevel ,
2017-03-01 21:26:27 -08:00
out bool shouldExternalProcess , out bool shouldInternalProcess )
2016-09-22 21:00:18 -07:00
{
shouldExternalProcess = true ;
shouldInternalProcess = true ;
2017-03-01 21:26:27 -08:00
ArchiveType ? archiveType = GetCurrentArchiveType ( input ) ;
2016-09-22 21:00:18 -07:00
switch ( archiveType )
{
case null :
shouldExternalProcess = true ;
shouldInternalProcess = false ;
break ;
case ArchiveType . GZip :
2016-10-05 17:23:44 -07:00
shouldExternalProcess = ( ( archiveScanLevel & ArchiveScanLevel . GZipExternal ) ! = 0 ) ;
shouldInternalProcess = ( ( archiveScanLevel & ArchiveScanLevel . GZipInternal ) ! = 0 ) ;
2016-09-22 21:00:18 -07:00
break ;
case ArchiveType . Rar :
2016-10-05 17:23:44 -07:00
shouldExternalProcess = ( ( archiveScanLevel & ArchiveScanLevel . RarExternal ) ! = 0 ) ;
shouldInternalProcess = ( ( archiveScanLevel & ArchiveScanLevel . RarInternal ) ! = 0 ) ;
2016-09-22 21:00:18 -07:00
break ;
case ArchiveType . SevenZip :
2016-10-05 17:23:44 -07:00
shouldExternalProcess = ( ( archiveScanLevel & ArchiveScanLevel . SevenZipExternal ) ! = 0 ) ;
shouldInternalProcess = ( ( archiveScanLevel & ArchiveScanLevel . SevenZipInternal ) ! = 0 ) ;
2016-09-22 21:00:18 -07:00
break ;
case ArchiveType . Zip :
2016-10-05 17:23:44 -07:00
shouldExternalProcess = ( ( archiveScanLevel & ArchiveScanLevel . ZipExternal ) ! = 0 ) ;
shouldInternalProcess = ( ( archiveScanLevel & ArchiveScanLevel . ZipInternal ) ! = 0 ) ;
break ;
}
}
/// <summary>
/// Get the archive scan level based on the inputs
/// </summary>
/// <param name="sevenzip">User-defined scan level for 7z archives</param>
/// <param name="gzip">User-defined scan level for GZ archives</param>
/// <param name="rar">User-defined scan level for RAR archives</param>
/// <param name="zip">User-defined scan level for Zip archives</param>
/// <returns>ArchiveScanLevel representing the levels</returns>
public static ArchiveScanLevel GetArchiveScanLevelFromNumbers ( int sevenzip , int gzip , int rar , int zip )
{
ArchiveScanLevel archiveScanLevel = 0x0000 ;
// 7z
sevenzip = ( sevenzip < 0 | | sevenzip > 2 ? 0 : sevenzip ) ;
switch ( sevenzip )
{
case 0 :
archiveScanLevel | = ArchiveScanLevel . SevenZipBoth ;
break ;
case 1 :
archiveScanLevel | = ArchiveScanLevel . SevenZipInternal ;
break ;
case 2 :
archiveScanLevel | = ArchiveScanLevel . SevenZipExternal ;
break ;
}
// GZip
gzip = ( gzip < 0 | | gzip > 2 ? 0 : gzip ) ;
switch ( gzip )
{
case 0 :
archiveScanLevel | = ArchiveScanLevel . GZipBoth ;
break ;
case 1 :
archiveScanLevel | = ArchiveScanLevel . GZipInternal ;
break ;
case 2 :
archiveScanLevel | = ArchiveScanLevel . GZipExternal ;
break ;
}
// RAR
rar = ( rar < 0 | | rar > 2 ? 0 : rar ) ;
switch ( rar )
{
case 0 :
archiveScanLevel | = ArchiveScanLevel . RarBoth ;
break ;
case 1 :
archiveScanLevel | = ArchiveScanLevel . RarInternal ;
break ;
case 2 :
archiveScanLevel | = ArchiveScanLevel . RarExternal ;
break ;
}
// Zip
zip = ( zip < 0 | | zip > 2 ? 0 : zip ) ;
switch ( zip )
{
case 0 :
archiveScanLevel | = ArchiveScanLevel . ZipBoth ;
break ;
case 1 :
archiveScanLevel | = ArchiveScanLevel . ZipInternal ;
break ;
case 2 :
archiveScanLevel | = ArchiveScanLevel . ZipExternal ;
2016-09-22 21:00:18 -07:00
break ;
}
2016-10-05 17:23:44 -07:00
return archiveScanLevel ;
2016-09-22 21:00:18 -07:00
}
/// <summary>
2016-11-07 21:31:52 -08:00
/// (INCOMPLETE) Retrieve file information for a RAR file
2016-09-22 21:00:18 -07:00
/// </summary>
2016-11-07 21:31:52 -08:00
/// <param name="input">Filename to get information from</param>
2017-03-01 21:26:27 -08:00
public static void GetRarFileInfo ( string input )
2016-09-22 21:00:18 -07:00
{
2016-11-07 21:31:52 -08:00
if ( ! File . Exists ( input ) )
{
return ;
}
2017-03-15 20:07:28 -07:00
BinaryReader br = new BinaryReader ( FileTools . TryOpenRead ( input ) ) ;
2016-11-07 21:31:52 -08:00
// Check for the signature first (Skipping the SFX Module)
byte [ ] signature = br . ReadBytes ( 8 ) ;
int startpos = 0 ;
while ( startpos < Constants . MibiByte & & BitConverter . ToString ( signature , 0 , 7 ) ! = Constants . RarSig & & BitConverter . ToString ( signature ) ! = Constants . RarFiveSig )
{
startpos + + ;
br . BaseStream . Position = startpos ;
signature = br . ReadBytes ( 8 ) ;
}
if ( BitConverter . ToString ( signature , 0 , 7 ) ! = Constants . RarSig & & BitConverter . ToString ( signature ) ! = Constants . RarFiveSig )
{
return ;
}
CoreRarArchive cra = new CoreRarArchive ( ) ;
if ( startpos > 0 )
{
br . BaseStream . Position = 0 ;
cra . SFX = br . ReadBytes ( startpos ) ;
}
// Get all archive header information
cra . HeaderCRC32 = br . ReadUInt32 ( ) ;
cra . HeaderSize = br . ReadUInt32 ( ) ;
uint headerType = br . ReadUInt32 ( ) ;
// Special encryption information
bool hasEncryptionHeader = false ;
// If it's encrypted
if ( headerType = = ( uint ) RarHeaderType . ArchiveEncryption )
{
hasEncryptionHeader = true ;
cra . EncryptionHeaderCRC32 = cra . HeaderCRC32 ;
cra . EncryptionHeaderSize = cra . HeaderSize ;
cra . EncryptionHeaderFlags = ( RarHeaderFlags ) br . ReadUInt32 ( ) ;
cra . EncryptionVersion = br . ReadUInt32 ( ) ;
cra . EncryptionFlags = br . ReadUInt32 ( ) ;
cra . KDFCount = br . ReadByte ( ) ;
cra . Salt = br . ReadBytes ( 16 ) ;
cra . CheckValue = br . ReadBytes ( 12 ) ;
cra . HeaderCRC32 = br . ReadUInt32 ( ) ;
cra . HeaderSize = br . ReadUInt32 ( ) ;
headerType = br . ReadUInt32 ( ) ;
}
cra . HeaderFlags = ( RarHeaderFlags ) br . ReadUInt32 ( ) ;
if ( ( cra . HeaderFlags & RarHeaderFlags . ExtraAreaPresent ) ! = 0 )
{
cra . ExtraAreaSize = br . ReadUInt32 ( ) ;
}
cra . ArchiveFlags = ( RarArchiveFlags ) br . ReadUInt32 ( ) ;
if ( ( cra . ArchiveFlags & RarArchiveFlags . VolumeNumberField ) ! = 0 )
{
cra . VolumeNumber = br . ReadUInt32 ( ) ;
}
if ( ( ( cra . HeaderFlags & RarHeaderFlags . ExtraAreaPresent ) ! = 0 ) & & cra . ExtraAreaSize ! = 0 )
{
cra . ExtraArea = br . ReadBytes ( ( int ) cra . ExtraAreaSize ) ;
}
// Archive Comment Service Header
// Now for file headers
for ( ; ; )
{
CoreRarArchiveEntry crae = new CoreRarArchiveEntry ( ) ;
crae . HeaderCRC32 = br . ReadUInt32 ( ) ;
crae . HeaderSize = br . ReadUInt32 ( ) ;
crae . HeaderType = ( RarHeaderType ) br . ReadUInt32 ( ) ;
if ( crae . HeaderType = = RarHeaderType . EndOfArchive )
{
break ;
}
crae . HeaderFlags = ( RarHeaderFlags ) br . ReadUInt32 ( ) ;
if ( ( crae . HeaderFlags & RarHeaderFlags . ExtraAreaPresent ) ! = 0 )
{
crae . ExtraAreaSize = br . ReadUInt32 ( ) ;
}
if ( ( crae . HeaderFlags & RarHeaderFlags . DataAreaPresent ) ! = 0 )
{
crae . DataAreaSize = br . ReadUInt32 ( ) ;
}
crae . FileFlags = ( RarFileFlags ) br . ReadUInt32 ( ) ;
crae . UnpackedSize = br . ReadUInt32 ( ) ;
if ( ( crae . FileFlags & RarFileFlags . UnpackedSizeUnknown ) ! = 0 )
{
crae . UnpackedSize = 0 ;
}
crae . Attributes = br . ReadUInt32 ( ) ;
crae . mtime = br . ReadUInt32 ( ) ;
crae . DataCRC32 = br . ReadUInt32 ( ) ;
crae . CompressionInformation = br . ReadUInt32 ( ) ;
crae . HostOS = br . ReadUInt32 ( ) ;
crae . NameLength = br . ReadUInt32 ( ) ;
crae . Name = br . ReadBytes ( ( int ) crae . NameLength ) ;
if ( ( crae . HeaderFlags & RarHeaderFlags . ExtraAreaPresent ) ! = 0 )
{
uint extraSize = br . ReadUInt32 ( ) ;
switch ( br . ReadUInt32 ( ) ) // Extra Area Type
{
case 0x01 : // File encryption information
crae . EncryptionSize = extraSize ;
crae . EncryptionFlags = ( RarEncryptionFlags ) br . ReadUInt32 ( ) ;
crae . KDFCount = br . ReadByte ( ) ;
crae . Salt = br . ReadBytes ( 16 ) ;
crae . IV = br . ReadBytes ( 16 ) ;
crae . CheckValue = br . ReadBytes ( 12 ) ;
break ;
case 0x02 : // File data hash
crae . HashSize = extraSize ;
crae . HashType = br . ReadUInt32 ( ) ;
crae . HashData = br . ReadBytes ( 32 ) ;
break ;
case 0x03 : // High precision file time
crae . TimeSize = extraSize ;
crae . TimeFlags = ( RarTimeFlags ) br . ReadUInt32 ( ) ;
if ( ( crae . TimeFlags & RarTimeFlags . TimeInUnixFormat ) ! = 0 )
{
if ( ( crae . TimeFlags & RarTimeFlags . ModificationTimePresent ) ! = 0 )
{
crae . TimeMtime64 = br . ReadUInt64 ( ) ;
}
if ( ( crae . TimeFlags & RarTimeFlags . CreationTimePresent ) ! = 0 )
{
crae . TimeCtime64 = br . ReadUInt64 ( ) ;
}
if ( ( crae . TimeFlags & RarTimeFlags . LastAccessTimePresent ) ! = 0 )
{
crae . TimeLtime64 = br . ReadUInt64 ( ) ;
}
}
else
{
if ( ( crae . TimeFlags & RarTimeFlags . ModificationTimePresent ) ! = 0 )
{
crae . TimeMtime = br . ReadUInt32 ( ) ;
}
if ( ( crae . TimeFlags & RarTimeFlags . CreationTimePresent ) ! = 0 )
{
crae . TimeCtime = br . ReadUInt32 ( ) ;
}
if ( ( crae . TimeFlags & RarTimeFlags . LastAccessTimePresent ) ! = 0 )
{
crae . TimeLtime = br . ReadUInt32 ( ) ;
}
}
break ;
case 0x04 : // File version number
crae . VersionSize = extraSize ;
/* crae.VersionFlags = */ br . ReadUInt32 ( ) ;
crae . VersionNumber = br . ReadUInt32 ( ) ;
break ;
case 0x05 : // File system redirection
crae . RedirectionSize = extraSize ;
crae . RedirectionType = ( RarRedirectionType ) br . ReadUInt32 ( ) ;
crae . RedirectionFlags = br . ReadUInt32 ( ) ;
crae . RedirectionNameLength = br . ReadUInt32 ( ) ;
crae . RedirectionName = br . ReadBytes ( ( int ) crae . RedirectionNameLength ) ;
break ;
case 0x06 : // Unix owner and group information
crae . UnixOwnerSize = extraSize ;
crae . UnixOwnerFlags = ( RarUnixOwnerRecordFlags ) br . ReadUInt32 ( ) ;
if ( ( crae . UnixOwnerFlags & RarUnixOwnerRecordFlags . UserNameStringIsPresent ) ! = 0 )
{
crae . UnixOwnerUserNameLength = br . ReadUInt32 ( ) ;
crae . UnixOwnerUserName = br . ReadBytes ( ( int ) crae . UnixOwnerUserNameLength ) ;
}
if ( ( crae . UnixOwnerFlags & RarUnixOwnerRecordFlags . GroupNameStringIsPresent ) ! = 0 )
{
crae . UnixOwnerGroupNameLength = br . ReadUInt32 ( ) ;
crae . UnixOwnerGroupName = br . ReadBytes ( ( int ) crae . UnixOwnerGroupNameLength ) ;
}
if ( ( crae . UnixOwnerFlags & RarUnixOwnerRecordFlags . NumericUserIdIsPresent ) ! = 0 )
{
crae . UnixOwnerUserId = br . ReadUInt32 ( ) ;
}
if ( ( crae . UnixOwnerFlags & RarUnixOwnerRecordFlags . NumericGroupIdIsPresent ) ! = 0 )
{
crae . UnixOwnerGroupId = br . ReadUInt32 ( ) ;
}
break ;
case 0x07 : // Service header data array
break ;
}
}
if ( ( crae . HeaderFlags & RarHeaderFlags . DataAreaPresent ) ! = 0 )
{
crae . DataArea = br . ReadBytes ( ( int ) crae . DataAreaSize ) ;
}
}
2016-09-22 21:00:18 -07:00
}
2017-02-28 13:35:22 -08:00
/// <summary>
/// (INCOMPLETE) Get the T7Z status of the file
/// </summary>
/// <param name="filename">Name of the file to check</param>
/// <returns>0 if the file isn't 7z, 1 if the file is t7z, 2 if the file is 7z</returns>
2017-03-01 21:26:27 -08:00
public static int IsT7z ( string filename )
2017-02-28 13:35:22 -08:00
{
int ist7z = 0 ;
if ( File . Exists ( filename ) )
{
try
{
2017-03-15 20:07:28 -07:00
Stream fread = FileTools . TryOpenRead ( filename ) ;
2017-02-28 13:35:22 -08:00
uint ar , offs = 0 ;
fread . Seek ( 0 , SeekOrigin . Begin ) ;
byte [ ] buffer = new byte [ 128 ] ;
ar = ( uint ) fread . Read ( buffer , 0 , 4 + Constants . Torrent7ZipSignature . Length + 4 ) ;
if ( ar < ( 4 + Constants . Torrent7ZipSignature . Length + 4 ) )
{
if ( ar > = Constants . Torrent7ZipSignature . Length + 4 )
{
ar - = ( uint ) ( Constants . Torrent7ZipSignature . Length + 4 ) ;
}
if ( ar < = Constants . Torrent7ZipHeader . Length )
{
ar = ( uint ) Constants . Torrent7ZipHeader . Length ;
}
// memset(buffer+offs+ar,0,crcsz-ar)
}
fread . Dispose ( ) ;
}
catch
{
2017-03-01 21:26:27 -08:00
Globals . Logger . Warning ( "File '" + filename + "' could not be opened" ) ;
2017-02-28 13:35:22 -08:00
ist7z = 0 ;
}
}
return ist7z ;
}
2016-09-22 21:00:18 -07:00
#endregion
2016-09-22 21:04:41 -07:00
#region Writing
2016-09-22 21:00:18 -07:00
/// <summary>
2016-10-25 16:04:45 -07:00
/// Write an input file to a tape archive
2016-09-22 21:00:18 -07:00
/// </summary>
/// <param name="inputFile">Input filename to be moved</param>
/// <param name="outDir">Output directory to build to</param>
/// <param name="rom">RomData representing the new information</param>
2016-10-14 16:58:15 -07:00
/// <param name="date">True if the date from the DAT should be used if available, false otherwise (default)</param>
2016-09-22 21:00:18 -07:00
/// <returns>True if the archive was written properly, false otherwise</returns>
2017-03-01 21:26:27 -08:00
public static bool WriteTAR ( string inputFile , string outDir , Rom rom , bool date = false )
2016-09-22 21:00:18 -07:00
{
// Wrap the individual inputs into lists
2017-03-01 20:28:32 -08:00
List < string > inputFiles = new List < string > ( ) { inputFile } ;
List < Rom > roms = new List < Rom > ( ) { rom } ;
2016-09-22 21:00:18 -07:00
2017-03-01 21:26:27 -08:00
return WriteTAR ( inputFiles , outDir , roms , date : date ) ;
2016-09-22 21:00:18 -07:00
}
/// <summary>
2016-10-25 16:04:45 -07:00
/// Write a set of input files to a tape archive (assuming the same output archive name)
2016-09-22 21:00:18 -07:00
/// </summary>
/// <param name="inputFile">Input filenames to be moved</param>
/// <param name="outDir">Output directory to build to</param>
/// <param name="rom">List of Rom representing the new information</param>
2016-10-14 16:58:15 -07:00
/// <param name="date">True if the date from the DAT should be used if available, false otherwise (default)</param>
2016-09-22 21:00:18 -07:00
/// <returns>True if the archive was written properly, false otherwise</returns>
2017-03-01 21:26:27 -08:00
public static bool WriteTAR ( List < string > inputFiles , string outDir , List < Rom > roms , bool date = false )
2016-10-25 16:04:45 -07:00
{
2016-10-25 21:20:43 -07:00
bool success = false ;
2017-02-23 17:13:27 -08:00
string tempFile = Path . Combine ( Path . GetTempPath ( ) , "tmp" + Guid . NewGuid ( ) . ToString ( ) ) ;
2017-01-27 17:07:08 -08:00
// If either list of roms is null or empty, return
if ( inputFiles = = null | | roms = = null | | inputFiles . Count = = 0 | | roms . Count = = 0 )
{
return success ;
}
2016-10-25 21:20:43 -07:00
// 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
2017-01-27 16:53:29 -08:00
string archiveFileName = Path . Combine ( outDir , Style . RemovePathUnsafeCharacters ( roms [ 0 ] . Machine . Name ) + ( roms [ 0 ] . Machine . Name . EndsWith ( ".tar" ) ? "" : ".tar" ) ) ;
2016-10-25 21:20:43 -07:00
// Set internal variables
2017-01-27 17:07:08 -08:00
TarArchive oldTarFile = TarArchive . Create ( ) ;
TarArchive tarFile = TarArchive . Create ( ) ;
2016-10-25 21:20:43 -07:00
try
{
// If the full output path doesn't exist, create it
if ( ! Directory . Exists ( Path . GetDirectoryName ( archiveFileName ) ) )
{
Directory . CreateDirectory ( Path . GetDirectoryName ( archiveFileName ) ) ;
}
2017-01-27 17:07:08 -08:00
// If the archive doesn't exist, create it and put the single file
if ( ! File . Exists ( archiveFileName ) )
2016-10-25 21:20:43 -07:00
{
2017-01-27 17:07:08 -08:00
// 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 . TorrentZipStringCompare ) ;
// Now add all of the files in order
foreach ( string key in keys )
{
// Get the index mapped to the key
int index = inputIndexMap [ key ] ;
2017-01-27 16:40:58 -08:00
2017-01-27 17:07:08 -08:00
// Copy the input stream to the output
tarFile . AddEntry ( roms [ index ] . Name , inputFiles [ index ] ) ;
}
}
2017-01-27 16:40:58 -08:00
2017-01-27 17:07:08 -08:00
// Otherwise, sort the input files and write out in the correct order
else
2017-01-27 16:53:23 -08:00
{
2017-01-27 17:07:08 -08:00
// 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 . 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 )
{
// Copy the input file to the output
tarFile . AddEntry ( roms [ - index - 1 ] . Name , inputFiles [ - index - 1 ] ) ;
}
// Otherwise, copy the file from the old archive
else
{
// Get the stream from the original archive
2017-02-23 17:13:27 -08:00
string tempEntry = Path . Combine ( Path . GetTempPath ( ) , "tmp" + Guid . NewGuid ( ) . ToString ( ) ) ;
2017-01-27 17:07:08 -08:00
oldTarFile . Entries . Where ( e = > e . Key = = key ) . ToList ( ) [ 0 ] . WriteToFile ( tempEntry ) ;
// Copy the input stream to the output
tarFile . AddEntry ( key , tempEntry ) ;
}
}
2016-10-25 21:20:43 -07:00
}
2017-01-27 17:07:08 -08:00
// Close the output tar file
tarFile . SaveTo ( tempFile , new WriterOptions ( CompressionType . None ) ) ;
2016-10-25 21:20:43 -07:00
success = true ;
}
catch ( Exception ex )
{
2017-01-27 17:07:08 -08:00
Console . WriteLine ( ex ) ;
2016-10-25 21:20:43 -07:00
success = false ;
}
finally
{
2017-01-27 17:07:08 -08:00
tarFile . Dispose ( ) ;
oldTarFile . Dispose ( ) ;
}
// If the old file exists, delete it and replace
if ( File . Exists ( archiveFileName ) )
{
2017-03-15 20:07:28 -07:00
FileTools . TryDeleteFile ( archiveFileName ) ;
2016-10-25 21:20:43 -07:00
}
2017-01-27 17:07:08 -08:00
File . Move ( tempFile , archiveFileName ) ;
2016-10-25 21:20:43 -07:00
2017-01-27 17:07:08 -08:00
return true ;
2016-10-25 16:04:45 -07:00
}
/// <summary>
/// Write an input file to a torrent7z archive
/// </summary>
/// <param name="inputFile">Input filename to be moved</param>
/// <param name="outDir">Output directory to build to</param>
/// <param name="rom">RomData representing the new information</param>
/// <param name="date">True if the date from the DAT should be used if available, false otherwise (default)</param>
/// <returns>True if the archive was written properly, false otherwise</returns>
2017-03-01 21:26:27 -08:00
public static bool WriteTorrent7Zip ( string inputFile , string outDir , Rom rom , bool date = false )
2016-10-25 16:04:45 -07:00
{
// Wrap the individual inputs into lists
2017-03-01 20:28:32 -08:00
List < string > inputFiles = new List < string > ( ) { inputFile } ;
List < Rom > roms = new List < Rom > ( ) { rom } ;
2016-10-25 16:04:45 -07:00
2017-03-01 21:26:27 -08:00
return WriteTorrent7Zip ( inputFiles , outDir , roms , date : date ) ;
2016-10-25 16:04:45 -07:00
}
/// <summary>
2017-03-05 21:41:28 -08:00
/// Write a set of input files to a torrent7z archive (assuming the same output archive name)
2016-10-25 16:04:45 -07:00
/// </summary>
/// <param name="inputFile">Input filenames to be moved</param>
/// <param name="outDir">Output directory to build to</param>
/// <param name="rom">List of Rom representing the new information</param>
/// <param name="date">True if the date from the DAT should be used if available, false otherwise (default)</param>
/// <returns>True if the archive was written properly, false otherwise</returns>
2017-03-01 21:26:27 -08:00
public static bool WriteTorrent7Zip ( List < string > inputFiles , string outDir , List < Rom > roms , bool date = false )
2016-10-25 16:04:45 -07:00
{
2017-02-27 23:52:33 -08:00
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
2017-02-28 13:35:22 -08:00
SevenZipBase . SetLibraryPath ( "7za.dll" ) ;
2017-02-27 23:52:33 -08:00
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 ) )
{
2017-03-01 20:28:32 -08:00
zipFile = new SevenZipCompressor ( )
{
ArchiveFormat = OutArchiveFormat . SevenZip ,
CompressionLevel = SevenZip . CompressionLevel . Normal ,
} ;
2017-02-27 23:52:33 -08:00
// 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 . TorrentZipStringCompare ) ;
2017-02-28 13:35:22 -08:00
// Create the temp directory
string tempPath = Path . Combine ( Path . GetTempPath ( ) , new Guid ( ) . ToString ( ) ) ;
if ( ! Directory . Exists ( tempPath ) )
{
Directory . CreateDirectory ( tempPath ) ;
}
2017-02-27 23:52:33 -08:00
// Now add all of the files in order
2017-02-28 13:35:22 -08:00
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 ) ;
2017-03-15 20:07:28 -07:00
FileTools . TryDeleteDirectory ( tempPath ) ;
2017-02-27 23:52:33 -08:00
}
// Otherwise, sort the input files and write out in the correct order
else
{
// Open the old archive for reading
2017-03-15 20:07:28 -07:00
Stream oldZipFileStream = FileTools . TryOpenRead ( archiveFileName ) ;
2017-02-28 13:35:22 -08:00
oldZipFile = new SevenZipExtractor ( oldZipFileStream ) ;
2017-02-27 23:52:33 -08:00
// 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 ( 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
2017-03-01 20:28:32 -08:00
zipFile = new SevenZipCompressor ( )
{
ArchiveFormat = OutArchiveFormat . SevenZip ,
CompressionLevel = SevenZip . CompressionLevel . Normal ,
} ;
2017-03-15 20:07:28 -07:00
Stream zipFileStream = FileTools . TryOpenWrite ( tempFile ) ;
2017-02-27 23:52:33 -08:00
// 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 ] ;
// 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
{
2017-03-15 20:07:28 -07:00
Stream oldZipFileEntryStream = FileTools . TryOpenReadWrite ( inputFiles [ index ] ) ;
2017-02-28 13:35:22 -08:00
oldZipFile . ExtractFile ( index , oldZipFileEntryStream ) ;
zipFile . CompressFiles ( zipFileStream , inputFiles [ index ] ) ;
oldZipFileEntryStream . Dispose ( ) ;
2017-03-15 20:07:28 -07:00
FileTools . TryDeleteFile ( inputFiles [ index ] ) ;
2017-02-27 23:52:33 -08:00
}
}
2017-02-28 13:35:22 -08:00
zipFileStream . Dispose ( ) ;
oldZipFile . Dispose ( ) ;
2017-02-27 23:52:33 -08:00
}
success = true ;
}
catch ( Exception ex )
{
Console . WriteLine ( ex ) ;
success = false ;
}
// If the old file exists, delete it and replace
if ( File . Exists ( archiveFileName ) )
{
2017-03-15 20:07:28 -07:00
FileTools . TryDeleteFile ( archiveFileName ) ;
2017-02-27 23:52:33 -08:00
}
File . Move ( tempFile , archiveFileName ) ;
2017-02-28 13:35:22 -08:00
// Now make the file T7Z
// TODO: Add ACTUAL T7Z compatible code
2017-03-15 20:07:28 -07:00
BinaryWriter bw = new BinaryWriter ( FileTools . TryOpenReadWrite ( archiveFileName ) ) ;
2017-02-28 13:35:22 -08:00
bw . Seek ( 0 , SeekOrigin . Begin ) ;
bw . Write ( Constants . Torrent7ZipHeader ) ;
bw . Seek ( 0 , SeekOrigin . End ) ;
2017-03-15 20:07:28 -07:00
oldZipFile = new SevenZipExtractor ( FileTools . TryOpenReadWrite ( archiveFileName ) ) ;
2017-02-28 13:35:22 -08:00
// 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 ( ) ;
2017-02-27 23:52:33 -08:00
return true ;
2016-10-25 16:04:45 -07:00
}
/// <summary>
/// Write an input file to a torrent GZ file
/// </summary>
/// <param name="input">File to write from</param>
/// <param name="outDir">Directory to write archive to</param>
/// <param name="romba">True if files should be output in Romba depot folders, false otherwise</param>
/// <returns>True if the write was a success, false otherwise</returns>
/// <remarks>This works for now, but it can be sped up by using Ionic.Zip or another zlib wrapper that allows for header values built-in. See edc's code.</remarks>
2017-03-01 21:26:27 -08:00
public static bool WriteTorrentGZ ( string input , string outDir , bool romba )
2016-10-25 16:04:45 -07:00
{
// Check that the input file exists
if ( ! File . Exists ( input ) )
{
2017-03-01 21:26:27 -08:00
Globals . Logger . Warning ( "File " + input + " does not exist!" ) ;
2016-10-25 16:04:45 -07:00
return false ;
}
input = Path . GetFullPath ( input ) ;
// Make sure the output directory exists
if ( ! Directory . Exists ( outDir ) )
{
Directory . CreateDirectory ( outDir ) ;
}
outDir = Path . GetFullPath ( outDir ) ;
// Now get the Rom info for the file so we have hashes and size
2017-03-01 21:26:27 -08:00
Rom rom = FileTools . GetFileInfo ( input ) ;
2016-10-25 16:04:45 -07:00
// Get the output file name
2017-01-30 23:16:05 -08:00
string outfile = null ;
// If we have a romba output, add the romba path
if ( romba )
{
2017-02-27 21:27:43 -08:00
outfile = Path . Combine ( outDir , Style . GetRombaPath ( rom . SHA1 ) ) ; // TODO: When updating to SHA-256, this needs to update to SHA256
2017-02-11 12:40:12 -08:00
// Check to see if the folder needs to be created
if ( ! Directory . Exists ( Path . GetDirectoryName ( outfile ) ) )
{
Directory . CreateDirectory ( Path . GetDirectoryName ( outfile ) ) ;
}
2017-01-30 23:16:05 -08:00
}
// Otherwise, we're just rebuilding to the main directory
else
{
2017-02-27 21:27:43 -08:00
outfile = Path . Combine ( outDir , rom . SHA1 + ".gz" ) ; // TODO: When updating to SHA-256, this needs to update to SHA256
2017-01-30 23:16:05 -08:00
}
2016-10-25 16:04:45 -07:00
// If the output file exists, don't try to write again
if ( ! File . Exists ( outfile ) )
{
// Compress the input stream
2017-03-15 20:07:28 -07:00
FileStream inputStream = FileTools . TryOpenRead ( input ) ;
FileStream outputStream = FileTools . TryCreate ( outfile ) ;
2016-10-25 16:04:45 -07:00
// Open the output file for writing
BinaryWriter sw = new BinaryWriter ( outputStream ) ;
// Write standard header and TGZ info
byte [ ] data = Constants . TorrentGZHeader
. Concat ( Style . StringToByteArray ( rom . MD5 ) ) // MD5
. Concat ( Style . StringToByteArray ( rom . CRC ) ) // CRC
. ToArray ( ) ;
sw . Write ( data ) ;
sw . Write ( ( ulong ) rom . Size ) ; // Long size (Unsigned, Mirrored)
// Now create a deflatestream from the input file
2017-02-27 23:52:33 -08:00
DeflateStream ds = new DeflateStream ( outputStream , Ionic . Zlib . CompressionMode . Compress , Ionic . Zlib . CompressionLevel . BestCompression , true ) ;
2016-10-25 16:04:45 -07:00
// Copy the input stream to the output
byte [ ] ibuffer = new byte [ _bufferSize ] ;
int ilen ;
while ( ( ilen = inputStream . Read ( ibuffer , 0 , _bufferSize ) ) > 0 )
{
ds . Write ( ibuffer , 0 , ilen ) ;
ds . Flush ( ) ;
}
ds . Dispose ( ) ;
// Now write the standard footer
sw . Write ( Style . StringToByteArray ( rom . CRC ) . Reverse ( ) . ToArray ( ) ) ;
sw . Write ( ( uint ) rom . Size ) ;
// Dispose of everything
sw . Dispose ( ) ;
outputStream . Dispose ( ) ;
inputStream . Dispose ( ) ;
}
return true ;
}
/// <summary>
/// Write an input file to a torrentlrzip archive
/// </summary>
/// <param name="inputFile">Input filename to be moved</param>
/// <param name="outDir">Output directory to build to</param>
/// <param name="rom">RomData representing the new information</param>
/// <param name="date">True if the date from the DAT should be used if available, false otherwise (default)</param>
/// <returns>True if the archive was written properly, false otherwise</returns>
2017-03-01 21:26:27 -08:00
public static bool WriteTorrentLRZ ( string inputFile , string outDir , Rom rom , bool date = false )
2016-10-25 16:04:45 -07:00
{
// Wrap the individual inputs into lists
2017-03-01 20:28:32 -08:00
List < string > inputFiles = new List < string > ( ) { inputFile } ;
List < Rom > roms = new List < Rom > ( ) { rom } ;
2016-10-25 16:04:45 -07:00
2017-03-01 21:26:27 -08:00
return WriteTorrentLRZ ( inputFiles , outDir , roms , date : date ) ;
2016-10-25 16:04:45 -07:00
}
/// <summary>
2016-10-31 14:26:23 -07:00
/// (UNIMPLEMENTED) Write a set of input files to a torrentlrzip archive (assuming the same output archive name)
2016-10-25 16:04:45 -07:00
/// </summary>
/// <param name="inputFile">Input filenames to be moved</param>
/// <param name="outDir">Output directory to build to</param>
/// <param name="rom">List of Rom representing the new information</param>
/// <param name="date">True if the date from the DAT should be used if available, false otherwise (default)</param>
/// <returns>True if the archive was written properly, false otherwise</returns>
2017-03-01 21:26:27 -08:00
public static bool WriteTorrentLRZ ( List < string > inputFiles , string outDir , List < Rom > roms , bool date = false )
2016-10-25 16:04:45 -07:00
{
return false ;
}
/// <summary>
/// Write an input file to a torrentrar archive
/// </summary>
/// <param name="inputFile">Input filename to be moved</param>
/// <param name="outDir">Output directory to build to</param>
/// <param name="rom">RomData representing the new information</param>
/// <param name="date">True if the date from the DAT should be used if available, false otherwise (default)</param>
/// <returns>True if the archive was written properly, false otherwise</returns>
2017-03-01 21:26:27 -08:00
public static bool WriteTorrentRAR ( string inputFile , string outDir , Rom rom , bool date = false )
2016-10-25 16:04:45 -07:00
{
// Wrap the individual inputs into lists
2017-03-01 20:28:32 -08:00
List < string > inputFiles = new List < string > ( ) { inputFile } ;
List < Rom > roms = new List < Rom > ( ) { rom } ;
2016-10-25 16:04:45 -07:00
2017-03-01 21:26:27 -08:00
return WriteTorrentRAR ( inputFiles , outDir , roms , date : date ) ;
2016-10-25 16:04:45 -07:00
}
/// <summary>
2016-10-31 14:26:23 -07:00
/// (UNIMPLEMENTED) Write a set of input files to a torrentrar archive (assuming the same output archive name)
2016-10-25 16:04:45 -07:00
/// </summary>
/// <param name="inputFile">Input filenames to be moved</param>
/// <param name="outDir">Output directory to build to</param>
/// <param name="rom">List of Rom representing the new information</param>
/// <param name="date">True if the date from the DAT should be used if available, false otherwise (default)</param>
/// <returns>True if the archive was written properly, false otherwise</returns>
2017-03-01 21:26:27 -08:00
public static bool WriteTorrentRAR ( List < string > inputFiles , string outDir , List < Rom > roms , bool date = false )
2016-10-25 16:04:45 -07:00
{
return false ;
}
/// <summary>
/// Write an input file to a torrentxz archive
/// </summary>
/// <param name="inputFile">Input filename to be moved</param>
/// <param name="outDir">Output directory to build to</param>
/// <param name="rom">RomData representing the new information</param>
/// <param name="date">True if the date from the DAT should be used if available, false otherwise (default)</param>
/// <returns>True if the archive was written properly, false otherwise</returns>
2017-03-01 21:26:27 -08:00
public static bool WriteTorrentXZ ( string inputFile , string outDir , Rom rom , bool date = false )
2016-10-25 16:04:45 -07:00
{
// Wrap the individual inputs into lists
2017-03-01 20:28:32 -08:00
List < string > inputFiles = new List < string > ( ) { inputFile } ;
List < Rom > roms = new List < Rom > ( ) { rom } ;
2016-10-25 16:04:45 -07:00
2017-03-01 21:26:27 -08:00
return WriteTorrentXZ ( inputFiles , outDir , roms , date : date ) ;
2016-10-25 16:04:45 -07:00
}
/// <summary>
2017-03-05 21:41:28 -08:00
/// Write a set of input files to a torrentxz archive (assuming the same output archive name)
2016-10-25 16:04:45 -07:00
/// </summary>
/// <param name="inputFile">Input filenames to be moved</param>
/// <param name="outDir">Output directory to build to</param>
/// <param name="rom">List of Rom representing the new information</param>
/// <param name="date">True if the date from the DAT should be used if available, false otherwise (default)</param>
/// <returns>True if the archive was written properly, false otherwise</returns>
2017-03-01 21:26:27 -08:00
public static bool WriteTorrentXZ ( List < string > inputFiles , string outDir , List < Rom > roms , bool date = false )
2016-10-25 16:04:45 -07:00
{
2017-03-05 21:41:28 -08:00
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 < 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 . 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 ) ;
2017-03-15 20:07:28 -07:00
FileTools . TryDeleteDirectory ( tempPath ) ;
2017-03-05 21:41:28 -08:00
}
// Otherwise, sort the input files and write out in the correct order
else
{
// Open the old archive for reading
2017-03-15 20:07:28 -07:00
Stream oldZipFileStream = FileTools . TryOpenRead ( archiveFileName ) ;
2017-03-05 21:41:28 -08:00
oldZipFile = new SevenZipExtractor ( oldZipFileStream ) ;
// 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 ( 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 ,
} ;
2017-03-15 20:07:28 -07:00
Stream zipFileStream = FileTools . TryOpenWrite ( tempFile ) ;
2017-03-05 21:41:28 -08:00
// 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 ] ;
// 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
{
2017-03-15 20:07:28 -07:00
Stream oldZipFileEntryStream = FileTools . TryCreate ( inputFiles [ index ] ) ;
2017-03-05 21:41:28 -08:00
oldZipFile . ExtractFile ( index , oldZipFileEntryStream ) ;
zipFile . CompressFiles ( zipFileStream , inputFiles [ index ] ) ;
oldZipFileEntryStream . Dispose ( ) ;
2017-03-15 20:07:28 -07:00
FileTools . TryDeleteFile ( inputFiles [ index ] ) ;
2017-03-05 21:41:28 -08:00
}
}
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 ) )
{
2017-03-15 20:07:28 -07:00
FileTools . TryDeleteFile ( archiveFileName ) ;
2017-03-05 21:41:28 -08:00
}
File . Move ( tempFile , archiveFileName ) ;
// Now make the file TXZ
// TODO: Add ACTUAL TXZ compatible code (based on T7z)
2017-03-15 20:07:28 -07:00
BinaryWriter bw = new BinaryWriter ( FileTools . TryOpenReadWrite ( archiveFileName ) ) ;
2017-03-05 21:41:28 -08:00
bw . Seek ( 0 , SeekOrigin . Begin ) ;
bw . Write ( Constants . Torrent7ZipHeader ) ;
bw . Seek ( 0 , SeekOrigin . End ) ;
2017-03-15 20:07:28 -07:00
oldZipFile = new SevenZipExtractor ( FileTools . TryOpenReadWrite ( archiveFileName ) ) ;
2017-03-05 21:41:28 -08:00
// 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 ;
2016-10-25 16:04:45 -07:00
}
/// <summary>
/// Write an input file to a torrentzip archive
/// </summary>
/// <param name="inputFile">Input filename to be moved</param>
/// <param name="outDir">Output directory to build to</param>
/// <param name="rom">RomData representing the new information</param>
/// <param name="date">True if the date from the DAT should be used if available, false otherwise (default)</param>
/// <returns>True if the archive was written properly, false otherwise</returns>
2017-03-01 21:26:27 -08:00
public static bool WriteTorrentZip ( string inputFile , string outDir , Rom rom , bool date = false )
2016-10-25 16:04:45 -07:00
{
// Wrap the individual inputs into lists
2017-03-01 20:28:32 -08:00
List < string > inputFiles = new List < string > ( ) { inputFile } ;
List < Rom > roms = new List < Rom > ( ) { rom } ;
2016-10-25 16:04:45 -07:00
2017-03-01 21:26:27 -08:00
return WriteTorrentZip ( inputFiles , outDir , roms , date : date ) ;
2016-10-25 16:04:45 -07:00
}
/// <summary>
/// Write a set of input files to a torrentzip archive (assuming the same output archive name)
/// </summary>
/// <param name="inputFile">Input filenames to be moved</param>
/// <param name="outDir">Output directory to build to</param>
/// <param name="rom">List of Rom representing the new information</param>
/// <param name="date">True if the date from the DAT should be used if available, false otherwise (default)</param>
/// <returns>True if the archive was written properly, false otherwise</returns>
2017-03-01 21:26:27 -08:00
public static bool WriteTorrentZip ( List < string > inputFiles , string outDir , List < Rom > roms , bool date = false )
2016-09-22 21:00:18 -07:00
{
bool success = false ;
2017-02-26 22:42:38 -08:00
string tempFile = Path . Combine ( outDir , "tmp" + Guid . NewGuid ( ) . ToString ( ) ) ;
2016-09-22 21:00:18 -07:00
2016-10-28 17:53:27 -07:00
// If either list of roms is null or empty, return
if ( inputFiles = = null | | roms = = null | | inputFiles . Count = = 0 | | roms . Count = = 0 )
{
return success ;
}
2016-09-22 21:00:18 -07:00
// 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
2017-01-27 16:53:29 -08:00
string archiveFileName = Path . Combine ( outDir , Style . RemovePathUnsafeCharacters ( roms [ 0 ] . Machine . Name ) + ( roms [ 0 ] . Machine . Name . EndsWith ( ".zip" ) ? "" : ".zip" ) ) ;
2016-09-22 21:00:18 -07:00
// Set internal variables
Stream writeStream = null ;
ZipFile oldZipFile = new ZipFile ( ) ;
ZipFile zipFile = new ZipFile ( ) ;
ZipReturn zipReturn = ZipReturn . ZipGood ;
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 ) )
{
zipReturn = zipFile . Create ( tempFile ) ;
// Map all inputs to index
Dictionary < string , int > inputIndexMap = new Dictionary < string , int > ( ) ;
for ( int i = 0 ; i < inputFiles . Count ; i + + )
{
2016-10-07 14:06:44 -07:00
inputIndexMap . Add ( roms [ i ] . Name . Replace ( '\\' , '/' ) , i ) ;
2016-09-22 21:00:18 -07:00
}
// Sort the keys in TZIP order
List < string > keys = inputIndexMap . Keys . ToList ( ) ;
keys . Sort ( ZipFile . TorrentZipStringCompare ) ;
// Now add all of the files in order
foreach ( string key in keys )
{
2016-10-07 14:06:44 -07:00
// Get the index mapped to the key
int index = inputIndexMap [ key ] ;
2016-09-22 21:00:18 -07:00
// Open the input file for reading
2017-03-15 20:07:28 -07:00
Stream freadStream = FileTools . TryOpenRead ( inputFiles [ index ] ) ;
2016-10-07 14:06:44 -07:00
ulong istreamSize = ( ulong ) ( new FileInfo ( inputFiles [ index ] ) . Length ) ;
2016-10-14 16:58:15 -07:00
DateTime dt = DateTime . Now ;
if ( date & & ! String . IsNullOrEmpty ( roms [ index ] . Date ) & & DateTime . TryParse ( roms [ index ] . Date . Replace ( '\\' , '/' ) , out dt ) )
{
2016-10-14 17:20:29 -07:00
uint msDosDateTime = Style . ConvertDateTimeToMsDosTimeFormat ( dt ) ;
2016-10-14 16:58:15 -07:00
zipFile . OpenWriteStream ( false , false , roms [ index ] . Name . Replace ( '\\' , '/' ) , istreamSize ,
2017-02-27 23:52:33 -08:00
SabreTools . Helper . Data . CompressionMethod . Deflated , out writeStream , lastMod : msDosDateTime ) ;
2016-10-14 16:58:15 -07:00
}
else
{
2017-02-27 23:52:33 -08:00
zipFile . OpenWriteStream ( false , true , roms [ index ] . Name . Replace ( '\\' , '/' ) , istreamSize , SabreTools . Helper . Data . CompressionMethod . Deflated , out writeStream ) ;
2016-10-14 16:58:15 -07:00
}
2016-09-22 21:00:18 -07:00
// Copy the input stream to the output
2016-10-07 14:06:44 -07:00
byte [ ] ibuffer = new byte [ _bufferSize ] ;
int ilen ;
while ( ( ilen = freadStream . Read ( ibuffer , 0 , _bufferSize ) ) > 0 )
2016-09-22 21:00:18 -07:00
{
2016-10-07 14:06:44 -07:00
writeStream . Write ( ibuffer , 0 , ilen ) ;
2016-10-08 23:28:09 -07:00
writeStream . Flush ( ) ;
2016-09-22 21:00:18 -07:00
}
2016-10-07 14:06:44 -07:00
freadStream . Dispose ( ) ;
zipFile . CloseWriteStream ( Convert . ToUInt32 ( roms [ index ] . CRC , 16 ) ) ;
2016-09-22 21:00:18 -07:00
}
}
// Otherwise, sort the input files and write out in the correct order
else
{
// Open the old archive for reading
oldZipFile . Open ( archiveFileName , new FileInfo ( archiveFileName ) . LastWriteTime . Ticks , true ) ;
// 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
2016-10-07 14:06:44 -07:00
if ( oldZipFile . Contains ( roms [ i ] . Name . Replace ( '\\' , '/' ) ) )
2016-09-22 21:00:18 -07:00
{
continue ;
}
2016-10-07 14:06:44 -07:00
inputIndexMap . Add ( roms [ i ] . Name . Replace ( '\\' , '/' ) , - ( i + 1 ) ) ;
2016-09-22 21:00:18 -07:00
}
// Then add all of the old entries to it too
for ( int i = 0 ; i < oldZipFile . EntriesCount ; i + + )
{
inputIndexMap . Add ( oldZipFile . Filename ( i ) , i ) ;
}
// If the number of entries is the same as the old archive, skip out
if ( inputIndexMap . Keys . Count < = oldZipFile . EntriesCount )
{
success = true ;
return success ;
}
// Otherwise, process the old zipfile
zipFile . Create ( tempFile ) ;
// 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 )
{
2016-10-07 14:06:44 -07:00
// Get the index mapped to the key
2016-09-22 21:00:18 -07:00
int index = inputIndexMap [ key ] ;
// If we have the input file, add it now
if ( index < 0 )
{
// Open the input file for reading
2017-03-15 20:07:28 -07:00
Stream freadStream = FileTools . TryOpenRead ( inputFiles [ - index - 1 ] ) ;
2016-10-07 14:06:44 -07:00
ulong istreamSize = ( ulong ) ( new FileInfo ( inputFiles [ - index - 1 ] ) . Length ) ;
2016-10-14 16:58:15 -07:00
DateTime dt = DateTime . Now ;
if ( date & & ! String . IsNullOrEmpty ( roms [ - index - 1 ] . Date ) & & DateTime . TryParse ( roms [ - index - 1 ] . Date . Replace ( '\\' , '/' ) , out dt ) )
{
2016-10-14 17:20:29 -07:00
uint msDosDateTime = Style . ConvertDateTimeToMsDosTimeFormat ( dt ) ;
2016-10-14 16:58:15 -07:00
zipFile . OpenWriteStream ( false , false , roms [ - index - 1 ] . Name . Replace ( '\\' , '/' ) , istreamSize ,
2017-02-27 23:52:33 -08:00
SabreTools . Helper . Data . CompressionMethod . Deflated , out writeStream , lastMod : msDosDateTime ) ;
2016-10-14 16:58:15 -07:00
}
else
{
2017-02-27 23:52:33 -08:00
zipFile . OpenWriteStream ( false , true , roms [ - index - 1 ] . Name . Replace ( '\\' , '/' ) , istreamSize , SabreTools . Helper . Data . CompressionMethod . Deflated , out writeStream ) ;
2016-10-14 16:58:15 -07:00
}
2016-09-22 21:00:18 -07:00
// Copy the input stream to the output
2016-10-07 12:16:33 -07:00
byte [ ] ibuffer = new byte [ _bufferSize ] ;
2016-09-22 21:00:18 -07:00
int ilen ;
2016-10-07 12:16:33 -07:00
while ( ( ilen = freadStream . Read ( ibuffer , 0 , _bufferSize ) ) > 0 )
2016-09-22 21:00:18 -07:00
{
writeStream . Write ( ibuffer , 0 , ilen ) ;
2016-10-08 23:28:09 -07:00
writeStream . Flush ( ) ;
2016-09-22 21:00:18 -07:00
}
freadStream . Dispose ( ) ;
zipFile . CloseWriteStream ( Convert . ToUInt32 ( roms [ - index - 1 ] . CRC , 16 ) ) ;
}
// Otherwise, copy the file from the old archive
else
{
// Instantiate the streams
2017-02-27 23:52:33 -08:00
oldZipFile . OpenReadStream ( index , false , out Stream zreadStream , out ulong istreamSize , out SabreTools . Helper . Data . CompressionMethod icompressionMethod , out uint lastMod ) ;
2016-10-14 16:58:15 -07:00
zipFile . OpenWriteStream ( false , lastMod = = Constants . TorrentZipFileDateTime , oldZipFile . Filename ( index ) ,
2017-02-27 23:52:33 -08:00
istreamSize , SabreTools . Helper . Data . CompressionMethod . Deflated , out writeStream , lastMod : lastMod ) ;
2016-09-22 21:00:18 -07:00
// Copy the input stream to the output
2016-10-07 12:16:33 -07:00
byte [ ] ibuffer = new byte [ _bufferSize ] ;
2016-09-22 21:00:18 -07:00
int ilen ;
2016-10-07 12:16:33 -07:00
while ( ( ilen = zreadStream . Read ( ibuffer , 0 , _bufferSize ) ) > 0 )
2016-09-22 21:00:18 -07:00
{
writeStream . Write ( ibuffer , 0 , ilen ) ;
2016-10-08 23:28:09 -07:00
writeStream . Flush ( ) ;
2016-09-22 21:00:18 -07:00
}
zipFile . CloseWriteStream ( BitConverter . ToUInt32 ( oldZipFile . CRC32 ( index ) , 0 ) ) ;
}
}
}
// Close the output zip file
zipFile . Close ( ) ;
success = true ;
}
catch ( Exception ex )
{
Console . WriteLine ( ex ) ;
success = false ;
}
finally
{
zipFile . Dispose ( ) ;
oldZipFile . Dispose ( ) ;
}
// If the old file exists, delete it and replace
if ( File . Exists ( archiveFileName ) )
{
2017-03-15 20:07:28 -07:00
FileTools . TryDeleteFile ( archiveFileName ) ;
2016-09-22 21:00:18 -07:00
}
File . Move ( tempFile , archiveFileName ) ;
return true ;
}
#endregion
}
}