2016-09-22 10:49:37 -07:00
using OCRC ;
2016-08-29 16:55:55 -07:00
using SharpCompress.Archive ;
2016-08-18 15:48:45 -07:00
using SharpCompress.Archive.SevenZip ;
2016-06-22 14:17:27 -07:00
using SharpCompress.Common ;
2016-06-13 23:54:13 -07:00
using SharpCompress.Reader ;
using System ;
2016-06-15 14:55:06 -07:00
using System.Collections.Generic ;
2016-06-13 20:57:49 -07:00
using System.IO ;
using System.IO.Compression ;
2016-06-13 22:12:00 -07:00
using System.Linq ;
2016-08-29 16:55:55 -07:00
using System.Security.Cryptography ;
2016-06-13 22:12:00 -07:00
using System.Text.RegularExpressions ;
2016-09-22 09:42:45 -07:00
using System.Threading.Tasks ;
2016-06-13 20:57:49 -07:00
namespace SabreTools.Helper
{
2016-08-29 16:52:55 -07:00
public class FileTools
2016-06-13 20:57:49 -07:00
{
2016-08-29 16:51:47 -07:00
#region Archive Writing
2016-06-13 20:57:49 -07:00
/// <summary>
2016-06-22 14:17:27 -07:00
/// Copy a file to an output archive
2016-06-13 20:57:49 -07:00
/// </summary>
2016-09-13 16:04:24 -07:00
/// <param name="inputFile">Input filename to be moved</param>
2016-09-16 16:35:58 -07:00
/// <param name="outDir">Output directory to build to</param>
2016-06-13 20:57:49 -07:00
/// <param name="rom">RomData representing the new information</param>
2016-09-13 16:04:24 -07:00
/// <returns>True if the archive was written properly, false otherwise</returns>
2016-09-16 16:35:58 -07:00
public static bool WriteToArchive ( string inputFile , string outDir , Rom rom )
2016-09-17 23:24:44 -07:00
{
// Wrap the individual inputs into lists
List < string > inputFiles = new List < string > ( ) ;
inputFiles . Add ( inputFile ) ;
List < Rom > roms = new List < Rom > ( ) ;
roms . Add ( rom ) ;
return WriteToArchive ( inputFiles , outDir , roms ) ;
}
/// <summary>
/// Copy a set of files to an output archive (assuming the same output archive name)
/// </summary>
/// <param name="inputFiles">Input filenames to be moved</param>
/// <param name="outDir">Output directory to build to</param>
/// <param name="roms">List of Rom representing the new information</param>
/// <returns>True if the archive was written properly, false otherwise</returns>
public static bool WriteToArchive ( List < string > inputFiles , string outDir , List < Rom > roms )
2016-06-13 20:57:49 -07:00
{
2016-09-13 16:04:24 -07:00
bool success = false ;
2016-09-17 23:24:44 -07:00
// If the number of inputs is less than the number of available roms, return
if ( inputFiles . Count < roms . Count )
2016-09-13 16:04:24 -07:00
{
return success ;
}
2016-09-17 23:24:44 -07:00
// If one of the files doesn't exist, return
foreach ( string file in inputFiles )
{
if ( ! File . Exists ( file ) )
{
return success ;
}
}
2016-06-13 20:57:49 -07:00
2016-09-17 23:24:44 -07:00
// Get the output archive name from the first rebuild rom
2016-09-19 18:04:24 -07:00
string archiveFileName = Path . Combine ( outDir , roms [ 0 ] . MachineName + ".zip" ) ;
2016-09-17 23:24:44 -07:00
// First, open the archive
2016-08-29 16:33:07 -07:00
ZipArchive outarchive = null ;
2016-06-13 20:57:49 -07:00
try
{
2016-09-14 10:06:31 -07:00
// If the full output path doesn't exist, create it
if ( ! Directory . Exists ( Path . GetDirectoryName ( archiveFileName ) ) )
{
Directory . CreateDirectory ( Path . GetDirectoryName ( archiveFileName ) ) ;
}
2016-09-13 16:04:24 -07:00
// If the archive doesn't exist, create it
2016-08-29 16:33:07 -07:00
if ( ! File . Exists ( archiveFileName ) )
2016-06-14 12:36:25 -07:00
{
2016-09-15 15:57:37 -07:00
outarchive = System . IO . Compression . ZipFile . Open ( archiveFileName , ZipArchiveMode . Create ) ;
2016-09-13 16:04:24 -07:00
outarchive . Dispose ( ) ;
2016-06-14 12:36:25 -07:00
}
2016-09-13 16:04:24 -07:00
// Open the archive for writing
2016-09-22 15:36:02 -07:00
outarchive = System . IO . Compression . ZipFile . Open ( archiveFileName , ZipArchiveMode . Update ) ;
// Now loop through and add all files
for ( int i = 0 ; i < inputFiles . Count ; i + + )
2016-06-14 12:36:25 -07:00
{
2016-09-22 15:36:02 -07:00
string inputFile = inputFiles [ i ] ;
Rom rom = roms [ i ] ;
2016-09-13 16:04:24 -07:00
2016-09-22 15:36:02 -07:00
// If the archive doesn't already contain the entry, add it
if ( outarchive . GetEntry ( rom . Name ) = = null )
{
outarchive . CreateEntryFromFile ( inputFile , rom . Name , CompressionLevel . Optimal ) ;
}
2016-09-17 23:24:44 -07:00
2016-09-22 15:36:02 -07:00
// If there's a Date attached to the rom, change the entry to that Date
if ( ! string . IsNullOrEmpty ( rom . Date ) )
{
DateTimeOffset dto = DateTimeOffset . Now ;
if ( DateTimeOffset . TryParse ( rom . Date , out dto ) )
2016-09-17 23:24:44 -07:00
{
2016-09-22 15:36:02 -07:00
outarchive . GetEntry ( rom . Name ) . LastWriteTime = dto ;
2016-06-15 11:21:39 -07:00
}
}
2016-06-13 20:57:49 -07:00
}
2016-09-13 16:04:24 -07:00
2016-09-22 15:36:02 -07:00
// Dispose of the streams
outarchive . Dispose ( ) ;
2016-09-13 16:04:24 -07:00
success = true ;
2016-06-14 12:36:25 -07:00
}
catch ( Exception ex )
{
Console . WriteLine ( ex ) ;
2016-09-13 16:04:24 -07:00
success = false ;
2016-06-13 20:57:49 -07:00
}
finally
{
outarchive ? . Dispose ( ) ;
}
2016-09-13 16:04:24 -07:00
return success ;
2016-06-22 14:17:27 -07:00
}
2016-09-15 12:16:33 -07:00
/// <summary>
2016-09-15 16:58:45 -07:00
/// Copy a file to an output torrentzip archive
2016-09-15 12:16:33 -07:00
/// </summary>
/// <param name="inputFile">Input filename to be moved</param>
2016-09-16 16:35:58 -07:00
/// <param name="outDir">Output directory to build to</param>
2016-09-15 12:16:33 -07:00
/// <param name="rom">RomData representing the new information</param>
2016-09-15 16:58:45 -07:00
/// <param name="logger">Logger object for file and console output</param>
2016-09-15 12:16:33 -07:00
/// <returns>True if the archive was written properly, false otherwise</returns>
2016-09-16 16:35:58 -07:00
public static bool WriteTorrentZip ( string inputFile , string outDir , Rom rom , Logger logger )
2016-09-17 23:24:44 -07:00
{
// Wrap the individual inputs into lists
List < string > inputFiles = new List < string > ( ) ;
inputFiles . Add ( inputFile ) ;
List < Rom > roms = new List < Rom > ( ) ;
roms . Add ( rom ) ;
return WriteTorrentZip ( inputFiles , outDir , roms , logger ) ;
}
/// <summary>
/// Copy a set of files to an output 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="logger">Logger object for file and console output</param>
/// <returns>True if the archive was written properly, false otherwise</returns>
public static bool WriteTorrentZip ( List < string > inputFiles , string outDir , List < Rom > roms , Logger logger )
2016-09-15 12:16:33 -07:00
{
2016-09-15 16:58:45 -07:00
bool success = false ;
2016-09-16 15:51:37 -07:00
string tempFile = Path . GetTempFileName ( ) ;
2016-09-15 12:16:33 -07:00
2016-09-17 23:24:44 -07:00
// If the number of inputs is less than the number of available roms, return
if ( inputFiles . Count < roms . Count )
2016-09-15 16:58:45 -07:00
{
return success ;
}
2016-09-17 23:24:44 -07:00
// If one of the files doesn't exist, return
foreach ( string file in inputFiles )
2016-09-15 16:58:45 -07:00
{
2016-09-17 23:24:44 -07:00
if ( ! File . Exists ( file ) )
{
return success ;
}
2016-09-15 16:58:45 -07:00
}
2016-09-17 23:24:44 -07:00
// Get the output archive name from the first rebuild rom
2016-09-19 18:04:24 -07:00
string archiveFileName = Path . Combine ( outDir , roms [ 0 ] . MachineName + ".zip" ) ;
2016-09-17 23:24:44 -07:00
2016-09-15 16:58:45 -07:00
// Set internal variables
Stream writeStream = null ;
2016-09-15 21:25:58 -07:00
ZipFile oldZipFile = new ZipFile ( ) ;
2016-09-15 16:58:45 -07:00
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 ) ) ;
}
2016-09-16 15:51:37 -07:00
// If the archive doesn't exist, create it and put the single file
2016-09-15 16:58:45 -07:00
if ( ! File . Exists ( archiveFileName ) )
{
2016-09-16 15:51:37 -07:00
zipReturn = zipFile . Create ( tempFile ) ;
2016-09-17 23:24:44 -07:00
// Map all inputs to index
Dictionary < string , int > inputIndexMap = new Dictionary < string , int > ( ) ;
for ( int i = 0 ; i < inputFiles . Count ; i + + )
{
inputIndexMap . Add ( inputFiles [ i ] , i ) ;
}
2016-09-16 15:51:37 -07:00
2016-09-17 23:24:44 -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-09-16 15:51:37 -07:00
{
2016-09-17 23:24:44 -07:00
string inputFile = key ;
Rom rom = roms [ inputIndexMap [ key ] ] ;
// Open the input file for reading
2016-09-22 15:36:02 -07:00
Stream fs = File . Open ( inputFile , FileMode . Open , FileAccess . Read , FileShare . ReadWrite ) ;
2016-09-17 23:24:44 -07:00
2016-09-22 15:36:02 -07:00
ulong streamSize = ( ulong ) ( new FileInfo ( inputFile ) . Length ) ;
zipReturn = zipFile . OpenWriteStream ( false , true , rom . Name , streamSize , CompressionMethod . Deflated , out writeStream ) ;
// Copy the input stream to the output
byte [ ] buffer = new byte [ 8 * 1024 ] ;
int len ;
while ( ( len = fs . Read ( buffer , 0 , buffer . Length ) ) > 0 )
{
writeStream . Write ( buffer , 0 , len ) ;
2016-09-17 23:24:44 -07:00
}
2016-09-22 15:36:02 -07:00
writeStream . Flush ( ) ;
zipFile . CloseWriteStream ( Convert . ToUInt32 ( rom . CRC , 16 ) ) ;
//Dispose of the file stream
fs . Dispose ( ) ;
2016-09-16 15:51:37 -07:00
}
2016-09-15 16:58:45 -07:00
}
2016-09-16 15:51:37 -07:00
// Otherwise, sort the input files and write out in the correct order
2016-09-15 16:58:45 -07:00
else
{
2016-09-15 21:25:58 -07:00
// Open the old archive for reading
2016-09-16 11:37:55 -07:00
oldZipFile . Open ( archiveFileName , new FileInfo ( archiveFileName ) . LastWriteTime . Ticks , true ) ;
2016-09-15 21:25:58 -07:00
2016-09-17 23:24:44 -07: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 . Contains ( roms [ i ] . Name ) )
{
continue ;
}
inputIndexMap . Add ( inputFiles [ i ] , - ( i + 1 ) ) ;
}
// 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 )
2016-09-15 21:25:58 -07:00
{
2016-09-16 11:37:55 -07:00
success = true ;
2016-09-16 15:51:37 -07:00
return success ;
2016-09-16 11:37:55 -07:00
}
2016-09-16 15:51:37 -07:00
2016-09-16 11:37:55 -07:00
// Otherwise, process the old zipfile
2016-09-16 15:51:37 -07:00
zipFile . Create ( tempFile ) ;
// Get the order for the entries with the new file
2016-09-17 23:24:44 -07:00
List < string > keys = inputIndexMap . Keys . ToList ( ) ;
keys . Sort ( ZipFile . TorrentZipStringCompare ) ;
2016-09-16 15:51:37 -07:00
// Copy over all files to the new archive
2016-09-17 23:24:44 -07:00
foreach ( string key in keys )
2016-09-16 11:37:55 -07:00
{
2016-09-16 15:51:37 -07:00
// Get the index mapped to they key
2016-09-17 23:24:44 -07:00
int index = inputIndexMap [ key ] ;
2016-09-16 11:37:55 -07:00
2016-09-16 15:51:37 -07:00
// If we have the input file, add it now
2016-09-17 23:24:44 -07:00
if ( index < 0 )
2016-09-16 15:51:37 -07:00
{
// Open the input file for reading
2016-09-18 12:29:33 -07:00
Stream freadStream = File . Open ( key , FileMode . Open , FileAccess . Read , FileShare . ReadWrite ) ;
2016-09-17 23:24:44 -07:00
ulong istreamSize = ( ulong ) ( new FileInfo ( key ) . Length ) ;
zipFile . OpenWriteStream ( false , true , roms [ - index - 1 ] . Name , istreamSize , CompressionMethod . Deflated , out writeStream ) ;
2016-09-16 15:51:37 -07:00
// Copy the input stream to the output
byte [ ] ibuffer = new byte [ 8 * 1024 ] ;
int ilen ;
while ( ( ilen = freadStream . Read ( ibuffer , 0 , ibuffer . Length ) ) > 0 )
{
writeStream . Write ( ibuffer , 0 , ilen ) ;
}
freadStream . Dispose ( ) ;
2016-09-19 20:36:12 -07:00
zipFile . CloseWriteStream ( Convert . ToUInt32 ( roms [ - index - 1 ] . CRC , 16 ) ) ;
2016-09-16 15:51:37 -07:00
}
// Otherwise, copy the file from the old archive
else
2016-09-15 21:25:58 -07:00
{
2016-09-16 11:37:55 -07:00
// Instantiate the streams
CompressionMethod icompressionMethod = CompressionMethod . Stored ;
ulong istreamSize = 0 ;
2016-09-16 15:51:37 -07:00
Stream zreadStream ;
oldZipFile . OpenReadStream ( index , false , out zreadStream , out istreamSize , out icompressionMethod ) ;
zipFile . OpenWriteStream ( false , true , oldZipFile . Filename ( index ) , istreamSize , CompressionMethod . Deflated , out writeStream ) ;
2016-09-16 11:37:55 -07:00
// Copy the input stream to the output
byte [ ] ibuffer = new byte [ 8 * 1024 ] ;
int ilen ;
2016-09-16 15:51:37 -07:00
while ( ( ilen = zreadStream . Read ( ibuffer , 0 , ibuffer . Length ) ) > 0 )
2016-09-16 11:37:55 -07:00
{
writeStream . Write ( ibuffer , 0 , ilen ) ;
}
2016-09-16 15:51:37 -07:00
zipFile . CloseWriteStream ( BitConverter . ToUInt32 ( oldZipFile . CRC32 ( index ) , 0 ) ) ;
2016-09-15 21:25:58 -07:00
}
}
2016-09-15 16:58:45 -07:00
}
2016-09-16 15:51:37 -07:00
// Close the output zip file
2016-09-15 20:32:18 -07:00
zipFile . Close ( ) ;
2016-09-15 16:58:45 -07:00
success = true ;
}
catch ( Exception ex )
{
Console . WriteLine ( ex ) ;
success = false ;
}
finally
{
zipFile . Dispose ( ) ;
2016-09-15 21:25:58 -07:00
oldZipFile . Dispose ( ) ;
}
// If the old file exists, delete it and replace
if ( File . Exists ( archiveFileName ) )
{
File . Delete ( archiveFileName ) ;
2016-09-15 16:58:45 -07:00
}
2016-09-16 15:51:37 -07:00
File . Move ( tempFile , archiveFileName ) ;
2016-09-15 16:58:45 -07:00
2016-09-17 23:24:44 -07:00
return true ;
2016-09-15 12:16:33 -07:00
}
2016-08-29 16:51:47 -07:00
/// <summary>
/// Write an input file to a torrent GZ file
/// </summary>
/// <param name="input">File to write from</param>
2016-09-16 16:35:58 -07:00
/// <param name="outDir">Directory to write archive to</param>
2016-08-29 16:51:47 -07:00
/// <param name="romba">True if files should be output in Romba depot folders, false otherwise</param>
/// <param name="logger">Logger object for file and console output</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>
2016-09-16 16:35:58 -07:00
public static bool WriteTorrentGZ ( string input , string outDir , bool romba , Logger logger )
2016-08-29 16:51:47 -07:00
{
// Check that the input file exists
if ( ! File . Exists ( input ) )
{
logger . Warning ( "File " + input + " does not exist!" ) ;
return false ;
}
input = Path . GetFullPath ( input ) ;
// Make sure the output directory exists
2016-09-16 16:35:58 -07:00
if ( ! Directory . Exists ( outDir ) )
2016-08-29 16:51:47 -07:00
{
2016-09-16 16:35:58 -07:00
Directory . CreateDirectory ( outDir ) ;
2016-08-29 16:51:47 -07:00
}
2016-09-16 16:35:58 -07:00
outDir = Path . GetFullPath ( outDir ) ;
2016-08-29 16:51:47 -07:00
// Now get the Rom info for the file so we have hashes and size
2016-09-22 15:36:02 -07:00
Rom rom = GetSingleFileInfo ( input ) ;
2016-08-29 16:51:47 -07:00
// If it doesn't exist, create the output file and then write
2016-09-19 20:36:12 -07:00
string outfile = Path . Combine ( outDir , rom . SHA1 + ".gz" ) ;
2016-09-22 15:36:02 -07:00
// Compress the input stream
FileStream inputStream = File . OpenRead ( input ) ;
GZipStream outputStream = new GZipStream ( File . Open ( outfile , FileMode . Create , FileAccess . Write ) , CompressionMode . Compress ) ;
inputStream . CopyTo ( outputStream ) ;
// Dispose of the streams
inputStream . Dispose ( ) ;
outputStream . Dispose ( ) ;
2016-08-29 16:51:47 -07:00
// Now that it's ready, inject the header info
2016-09-22 15:36:02 -07:00
BinaryWriter sw = new BinaryWriter ( new MemoryStream ( ) ) ;
BinaryReader br = new BinaryReader ( File . OpenRead ( outfile ) ) ;
// Write standard header and TGZ info
byte [ ] data = Constants . TorrentGZHeader
. Concat ( Style . StringToByteArray ( rom . MD5 ) ) // MD5
. Concat ( Style . StringToByteArray ( rom . CRC ) ) // CRC
. Concat ( BitConverter . GetBytes ( rom . Size ) . Reverse ( ) . ToArray ( ) ) // Long size (Mirrored)
. ToArray ( ) ;
sw . Write ( data ) ;
// Finally, copy the rest of the data from the original file
br . BaseStream . Seek ( 10 , SeekOrigin . Begin ) ;
int i = 10 ;
while ( br . BaseStream . Position < br . BaseStream . Length )
2016-08-29 16:51:47 -07:00
{
2016-09-22 15:36:02 -07:00
sw . Write ( br . ReadByte ( ) ) ;
i + + ;
2016-08-29 16:51:47 -07:00
}
2016-09-22 15:36:02 -07:00
// Dispose of the stream
br . Dispose ( ) ;
// Now write the new file over the original
BinaryWriter bw = new BinaryWriter ( File . Open ( outfile , FileMode . Create ) ) ;
sw . BaseStream . Seek ( 0 , SeekOrigin . Begin ) ;
bw . BaseStream . Seek ( 0 , SeekOrigin . Begin ) ;
sw . BaseStream . CopyTo ( bw . BaseStream ) ;
// Dispose of the streams
bw . Dispose ( ) ;
sw . Dispose ( ) ;
2016-08-29 16:51:47 -07:00
// If we're in romba mode, create the subfolder and move the file
if ( romba )
{
2016-09-19 20:36:12 -07:00
string subfolder = Path . Combine ( rom . SHA1 . Substring ( 0 , 2 ) , rom . SHA1 . Substring ( 2 , 2 ) , rom . SHA1 . Substring ( 4 , 2 ) , rom . SHA1 . Substring ( 6 , 2 ) ) ;
2016-09-16 16:35:58 -07:00
outDir = Path . Combine ( outDir , subfolder ) ;
if ( ! Directory . Exists ( outDir ) )
2016-08-29 16:51:47 -07:00
{
2016-09-16 16:35:58 -07:00
Directory . CreateDirectory ( outDir ) ;
2016-08-29 16:51:47 -07:00
}
try
{
2016-09-16 16:35:58 -07:00
File . Move ( outfile , Path . Combine ( outDir , Path . GetFileName ( outfile ) ) ) ;
2016-08-29 16:51:47 -07:00
}
catch ( Exception ex )
{
logger . Warning ( ex . ToString ( ) ) ;
File . Delete ( outfile ) ;
}
}
return true ;
}
#endregion
#region Archive Extraction
2016-06-13 20:57:49 -07:00
/// <summary>
/// Attempt to extract a file as an archive
/// </summary>
/// <param name="input">Name of the file to be extracted</param>
2016-09-16 16:35:58 -07:00
/// <param name="tempDir">Temporary directory for archive extraction</param>
2016-06-13 20:57:49 -07:00
/// <param name="logger">Logger object for file and console output</param>
/// <returns>True if the extraction was a success, false otherwise</returns>
2016-09-16 16:35:58 -07:00
public static bool ExtractArchive ( string input , string tempDir , Logger logger )
2016-06-13 20:57:49 -07:00
{
2016-09-16 16:35:58 -07:00
return ExtractArchive ( input , tempDir , ArchiveScanLevel . Both , ArchiveScanLevel . External , ArchiveScanLevel . External , ArchiveScanLevel . Both , logger ) ;
2016-06-13 20:57:49 -07:00
}
/// <summary>
/// Attempt to extract a file as an archive
/// </summary>
/// <param name="input">Name of the file to be extracted</param>
2016-09-16 16:35:58 -07:00
/// <param name="tempDir">Temporary directory for archive extraction</param>
2016-06-13 20:57:49 -07:00
/// <param name="sevenzip">Integer representing the archive handling level for 7z</param>
/// <param name="gz">Integer representing the archive handling level for GZip</param>
/// <param name="rar">Integer representing the archive handling level for RAR</param>
/// <param name="zip">Integer representing the archive handling level for Zip</param>
/// <param name="logger">Logger object for file and console output</param>
/// <returns>True if the extraction was a success, false otherwise</returns>
2016-09-22 16:16:48 -07:00
public static bool ExtractArchive ( string input , string tempDir , int sevenzip , int gz , int rar , int zip , Logger logger )
2016-06-13 20:57:49 -07:00
{
2016-09-16 16:35:58 -07:00
return ExtractArchive ( input , tempDir , ( ArchiveScanLevel ) sevenzip , ( ArchiveScanLevel ) gz , ( ArchiveScanLevel ) rar , ( ArchiveScanLevel ) zip , logger ) ;
2016-06-13 20:57:49 -07:00
}
/// <summary>
/// Attempt to extract a file as an archive
/// </summary>
/// <param name="input">Name of the file to be extracted</param>
2016-09-16 16:35:58 -07:00
/// <param name="tempDir">Temporary directory for archive extraction</param>
2016-06-13 20:57:49 -07:00
/// <param name="sevenzip">Archive handling level for 7z</param>
/// <param name="gz">Archive handling level for GZip</param>
/// <param name="rar">Archive handling level for RAR</param>
/// <param name="zip">Archive handling level for Zip</param>
/// <param name="logger">Logger object for file and console output</param>
/// <returns>True if the extraction was a success, false otherwise</returns>
2016-09-16 16:35:58 -07:00
public static bool ExtractArchive ( string input , string tempDir , ArchiveScanLevel sevenzip ,
2016-06-13 20:57:49 -07:00
ArchiveScanLevel gz , ArchiveScanLevel rar , ArchiveScanLevel zip , Logger logger )
{
bool encounteredErrors = true ;
2016-06-15 14:43:05 -07:00
// First get the archive type
ArchiveType ? at = GetCurrentArchiveType ( input , logger ) ;
// If we got back null, then it's not an archive, so we we return
if ( at = = null )
{
return encounteredErrors ;
}
2016-06-13 20:57:49 -07:00
try
{
2016-09-22 14:45:28 -07:00
if ( at = = ArchiveType . SevenZip & & sevenzip ! = ArchiveScanLevel . External )
{
2016-09-22 15:36:02 -07:00
logger . Log ( "Found archive of type: " + at ) ;
2016-08-18 15:52:14 -07:00
2016-09-22 15:36:02 -07:00
// Create the temp directory
Directory . CreateDirectory ( tempDir ) ;
2016-08-18 15:50:57 -07:00
2016-09-22 15:36:02 -07:00
// Extract all files to the temp directory
SevenZipArchive sza = SevenZipArchive . Open ( File . OpenRead ( input ) ) ;
foreach ( IArchiveEntry iae in sza . Entries )
{
iae . WriteToDirectory ( tempDir , ExtractOptions . PreserveFileTime | ExtractOptions . ExtractFullPath | ExtractOptions . Overwrite ) ;
2016-08-18 15:50:57 -07:00
}
2016-09-22 15:36:02 -07:00
encounteredErrors = false ;
sza . Dispose ( ) ;
2016-09-22 14:45:28 -07:00
}
else if ( at = = ArchiveType . GZip & & gz ! = ArchiveScanLevel . External )
{
logger . Log ( "Found archive of type: " + at ) ;
// Create the temp directory
Directory . CreateDirectory ( tempDir ) ;
2016-09-22 15:36:02 -07:00
// Decompress the input stream
FileStream outstream = File . Create ( Path . Combine ( tempDir , Path . GetFileNameWithoutExtension ( input ) ) ) ;
GZipStream gzstream = new GZipStream ( File . OpenRead ( input ) , CompressionMode . Decompress ) ;
gzstream . CopyTo ( outstream ) ;
// Dispose of the streams
outstream . Dispose ( ) ;
gzstream . Dispose ( ) ;
2016-09-22 14:45:28 -07:00
encounteredErrors = false ;
}
2016-09-22 15:36:02 -07:00
else if ( ( at = = ArchiveType . Zip & & zip ! = ArchiveScanLevel . External )
| | ( at = = ArchiveType . Rar & & rar ! = ArchiveScanLevel . External ) )
2016-09-22 14:45:28 -07:00
{
2016-09-22 15:36:02 -07:00
logger . Log ( "Found archive of type: " + at ) ;
2016-09-22 11:17:10 -07:00
2016-09-22 15:36:02 -07:00
// Create the temp directory
Directory . CreateDirectory ( tempDir ) ;
2016-09-22 14:45:28 -07:00
2016-09-22 15:36:02 -07:00
// Extract all files to the temp directory
IReader reader = ReaderFactory . Open ( File . OpenRead ( input ) ) ;
bool succeeded = reader . MoveToNextEntry ( ) ;
while ( succeeded )
{
reader . WriteEntryToDirectory ( tempDir , ExtractOptions . PreserveFileTime | ExtractOptions . ExtractFullPath | ExtractOptions . Overwrite ) ;
succeeded = reader . MoveToNextEntry ( ) ;
2016-08-18 15:48:45 -07:00
}
2016-09-22 15:36:02 -07:00
encounteredErrors = false ;
reader . Dispose ( ) ;
2016-06-13 20:57:49 -07:00
}
}
2016-06-15 14:29:34 -07:00
catch ( EndOfStreamException )
{
// Catch this but don't count it as an error because SharpCompress is unsafe
}
2016-06-13 20:57:49 -07:00
catch ( InvalidOperationException )
{
encounteredErrors = true ;
}
2016-06-17 11:47:30 -07:00
catch ( Exception )
2016-06-13 20:57:49 -07:00
{
2016-06-17 11:23:23 -07:00
// Don't log file open errors
2016-06-13 20:57:49 -07:00
encounteredErrors = true ;
2016-06-15 15:09:57 -07:00
}
2016-08-17 18:01:07 -07:00
return encounteredErrors ;
2016-06-15 15:09:57 -07:00
}
/// <summary>
/// Attempt to extract a file from an archive
/// </summary>
/// <param name="input">Name of the archive to be extracted</param>
/// <param name="entryname">Name of the entry to be extracted</param>
2016-09-16 16:35:58 -07:00
/// <param name="tempDir">Temporary directory for archive extraction</param>
2016-06-15 15:09:57 -07:00
/// <param name="logger">Logger object for file and console output</param>
2016-06-15 15:13:19 -07:00
/// <returns>Name of the extracted file, null on error</returns>
2016-09-22 16:16:48 -07:00
public static string ExtractSingleItemFromArchive ( string input , string entryname , string tempDir , Logger logger )
2016-06-15 15:09:57 -07:00
{
2016-06-15 15:13:19 -07:00
string outfile = null ;
2016-06-15 15:09:57 -07:00
// First get the archive type
ArchiveType ? at = GetCurrentArchiveType ( input , logger ) ;
// If we got back null, then it's not an archive, so we we return
if ( at = = null )
{
2016-06-15 15:13:19 -07:00
return outfile ;
2016-06-15 15:09:57 -07:00
}
IReader reader = null ;
try
{
if ( at = = ArchiveType . Zip | | at = = ArchiveType . SevenZip | | at = = ArchiveType . Rar )
{
// Create the temp directory
2016-09-16 16:35:58 -07:00
Directory . CreateDirectory ( tempDir ) ;
2016-06-15 15:09:57 -07:00
2016-09-22 15:36:02 -07:00
reader = ReaderFactory . Open ( File . OpenRead ( input ) ) ;
2016-06-15 15:44:11 -07:00
while ( reader . MoveToNextEntry ( ) )
2016-06-15 15:09:57 -07:00
{
2016-06-15 15:44:11 -07:00
logger . Log ( "Current entry name: '" + reader . Entry . Key + "'" ) ;
if ( reader . Entry ! = null & & reader . Entry . Key . Contains ( entryname ) )
2016-06-15 15:09:57 -07:00
{
2016-09-16 16:35:58 -07:00
outfile = Path . GetFullPath ( Path . Combine ( tempDir , reader . Entry . Key ) ) ;
2016-06-19 20:51:32 -07:00
if ( ! Directory . Exists ( Path . GetDirectoryName ( outfile ) ) )
{
Directory . CreateDirectory ( Path . GetDirectoryName ( outfile ) ) ;
}
2016-06-17 11:20:59 -07:00
reader . WriteEntryToFile ( outfile , ExtractOptions . Overwrite ) ;
2016-06-15 15:09:57 -07:00
}
}
}
2016-08-25 20:03:27 -07:00
else if ( at = = ArchiveType . GZip )
{
2016-09-22 15:36:02 -07:00
// Decompress the input stream
FileStream outstream = File . Create ( Path . Combine ( tempDir , Path . GetFileNameWithoutExtension ( input ) ) ) ;
GZipStream gzstream = new GZipStream ( File . OpenRead ( input ) , CompressionMode . Decompress ) ;
gzstream . CopyTo ( outstream ) ;
outfile = Path . GetFullPath ( Path . Combine ( tempDir , Path . GetFileNameWithoutExtension ( input ) ) ) ;
// Dispose of the streams
outstream . Dispose ( ) ;
gzstream . Dispose ( ) ;
2016-08-25 20:03:27 -07:00
}
2016-06-15 15:09:57 -07:00
}
catch ( Exception ex )
{
logger . Error ( ex . ToString ( ) ) ;
2016-06-15 15:13:19 -07:00
outfile = null ;
2016-06-15 11:21:39 -07:00
}
finally
{
2016-06-15 14:29:34 -07:00
reader ? . Dispose ( ) ;
2016-06-13 20:57:49 -07:00
}
2016-06-15 15:13:19 -07:00
return outfile ;
2016-06-13 20:57:49 -07:00
}
2016-06-13 22:12:00 -07:00
2016-08-29 16:51:47 -07:00
#endregion
#region Archive - to - Archive Handling
2016-06-22 14:17:27 -07:00
/// <summary>
2016-09-13 16:04:24 -07:00
/// Attempt to copy a file between archives
2016-06-22 14:17:27 -07:00
/// </summary>
/// <param name="inputArchive">Source archive name</param>
2016-09-16 16:35:58 -07:00
/// <param name="outDir">Destination archive name</param>
2016-06-22 14:17:27 -07:00
/// <param name="sourceEntryName">Input entry name</param>
/// <param name="destEntryName">Output entry name</param>
/// <param name="logger">Logger object for file and console output</param>
/// <returns>True if the copy was a success, false otherwise</returns>
2016-09-16 16:35:58 -07:00
public static bool CopyFileBetweenArchives ( string inputArchive , string outDir ,
2016-09-13 16:04:24 -07:00
string sourceEntryName , Rom destEntry , Logger logger )
2016-06-22 14:17:27 -07:00
{
2016-09-13 16:04:24 -07:00
string tempfile = ExtractSingleItemFromArchive ( inputArchive , sourceEntryName , Path . GetTempPath ( ) , logger ) ;
2016-09-16 16:35:58 -07:00
return WriteToArchive ( tempfile , outDir , destEntry ) ;
2016-06-22 14:17:27 -07:00
}
2016-08-29 16:51:47 -07:00
#endregion
2016-09-01 20:38:41 -07:00
#region File Information
2016-08-29 16:51:47 -07:00
2016-06-15 14:55:06 -07:00
/// <summary>
2016-08-29 16:55:55 -07:00
/// Retrieve file information for a single file
/// </summary>
/// <param name="input">Filename to get information from</param>
2016-09-14 10:25:01 -07:00
/// <param name="noMD5">True if MD5 hashes should not be calculated, false otherwise (default)</param>
/// <param name="noSHA1">True if SHA-1 hashes should not be calcluated, false otherwise (default)</param>
/// <param name="offset">Set a >0 number for getting hash for part of the file, 0 otherwise (default)</param>
/// <param name="date">True if the file Date should be included, false otherwise (default)</param>
2016-08-29 16:55:55 -07:00
/// <returns>Populated RomData object if success, empty one on error</returns>
2016-09-14 10:25:01 -07:00
public static Rom GetSingleFileInfo ( string input , bool noMD5 = false , bool noSHA1 = false , long offset = 0 , bool date = false )
2016-08-29 16:55:55 -07:00
{
// Add safeguard if file doesn't exist
if ( ! File . Exists ( input ) )
{
return new Rom ( ) ;
}
2016-09-17 18:15:53 -07:00
// Get the information from the file stream
2016-09-22 11:17:10 -07:00
Rom rom = GetSingleStreamInfo ( File . OpenRead ( input ) , noMD5 , noSHA1 , offset , false ) ;
2016-09-17 18:15:53 -07:00
// Add unique data from the file
rom . Name = Path . GetFileName ( input ) ;
rom . Date = ( date ? new FileInfo ( input ) . LastWriteTime . ToString ( "yyyy/MM/dd HH:mm:ss" ) : "" ) ;
return rom ;
}
2016-08-29 16:55:55 -07:00
/// <summary>
2016-06-15 14:55:06 -07:00
/// Generate a list of RomData objects from the header values in an archive
/// </summary>
/// <param name="input">Input file to get data from</param>
/// <param name="logger">Logger object for file and console output</param>
/// <returns>List of RomData objects representing the found data</returns>
2016-08-29 13:57:46 -07:00
public static List < Rom > GetArchiveFileInfo ( string input , Logger logger )
2016-06-15 14:55:06 -07:00
{
2016-08-29 13:57:46 -07:00
List < Rom > roms = new List < Rom > ( ) ;
2016-06-15 14:55:06 -07:00
string gamename = Path . GetFileNameWithoutExtension ( input ) ;
// First get the archive type
ArchiveType ? at = GetCurrentArchiveType ( input , logger ) ;
// If we got back null, then it's not an archive, so we we return
if ( at = = null )
{
return roms ;
}
2016-08-25 16:00:49 -07:00
// If we got back GZip, try to get TGZ info first
else if ( at = = ArchiveType . GZip )
{
2016-08-29 13:57:46 -07:00
Rom possibleTgz = GetTorrentGZFileInfo ( input , logger ) ;
2016-08-25 16:00:49 -07:00
// If it was, then add it to the outputs and continue
if ( possibleTgz . Name ! = null )
{
roms . Add ( possibleTgz ) ;
return roms ;
}
}
2016-06-15 14:55:06 -07:00
IReader reader = null ;
try
{
logger . Log ( "Found archive of type: " + at ) ;
2016-08-25 20:03:27 -07:00
long size = 0 ;
string crc = "" ;
// If we have a gzip file, get the crc directly
if ( at = = ArchiveType . GZip )
{
// Get the CRC and size from the file
2016-09-22 15:36:02 -07:00
BinaryReader br = new BinaryReader ( File . OpenRead ( input ) ) ;
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 ( ) ;
2016-08-25 20:03:27 -07:00
}
2016-06-15 14:55:06 -07:00
if ( at ! = ArchiveType . Tar )
{
2016-09-22 15:36:02 -07:00
reader = ReaderFactory . Open ( File . OpenRead ( input ) ) ;
2016-06-15 15:44:11 -07:00
while ( reader . MoveToNextEntry ( ) )
2016-06-15 14:55:06 -07:00
{
2016-06-15 15:44:11 -07:00
if ( reader . Entry ! = null & & ! reader . Entry . IsDirectory )
2016-06-15 14:55:06 -07:00
{
2016-08-25 20:03:27 -07:00
logger . Log ( "Entry found: '" + reader . Entry . Key + "': "
+ ( size = = 0 ? reader . Entry . Size : size ) + ", "
+ ( crc = = "" ? reader . Entry . Crc . ToString ( "X" ) . ToLowerInvariant ( ) : crc ) ) ;
2016-06-15 15:44:11 -07:00
2016-08-29 13:57:46 -07:00
roms . Add ( new Rom
2016-06-15 15:44:11 -07:00
{
2016-08-29 13:50:55 -07:00
Type = ItemType . Rom ,
2016-06-15 15:44:11 -07:00
Name = reader . Entry . Key ,
2016-09-19 18:04:24 -07:00
MachineName = gamename ,
2016-09-19 20:36:12 -07:00
Size = ( size = = 0 ? reader . Entry . Size : size ) ,
CRC = ( crc = = "" ? reader . Entry . Crc . ToString ( "X" ) . ToLowerInvariant ( ) : crc ) ,
2016-06-15 15:44:11 -07:00
} ) ;
}
2016-06-15 14:55:06 -07:00
}
}
}
catch ( Exception ex )
{
logger . Error ( ex . ToString ( ) ) ;
}
finally
{
reader ? . Dispose ( ) ;
}
return roms ;
}
2016-06-13 22:12:00 -07:00
/// <summary>
/// Retrieve file information for a single torrent GZ file
/// </summary>
/// <param name="input">Filename to get information from</param>
2016-06-15 14:43:05 -07:00
/// <param name="logger">Logger object for file and console output</param>
2016-06-13 22:12:00 -07:00
/// <returns>Populated RomData object if success, empty one on error</returns>
2016-08-29 13:57:46 -07:00
public static Rom GetTorrentGZFileInfo ( string input , Logger logger )
2016-06-13 22:12:00 -07:00
{
string datum = Path . GetFileName ( input ) . ToLowerInvariant ( ) ;
long filesize = new FileInfo ( input ) . Length ;
2016-08-17 16:17:10 -07:00
2016-06-13 22:12:00 -07:00
// Check if the name is the right length
if ( ! Regex . IsMatch ( datum , @"^[0-9a-f]{40}\.gz" ) )
{
logger . Warning ( "Non SHA-1 filename found, skipping: '" + datum + "'" ) ;
2016-08-29 13:57:46 -07:00
return new Rom ( ) ;
2016-06-13 22:12:00 -07:00
}
// Check if the file is at least the minimum length
2016-08-17 16:17:10 -07:00
if ( filesize < 40 /* bytes */ )
2016-06-13 22:12:00 -07:00
{
logger . Warning ( "Possibly corrupt file '" + input + "' with size " + Style . GetBytesReadable ( filesize ) ) ;
2016-08-29 13:57:46 -07:00
return new Rom ( ) ;
2016-06-13 22:12:00 -07:00
}
// Get the Romba-specific header data
2016-08-25 20:03:27 -07:00
byte [ ] header ; // Get preamble header for checking
2016-08-17 16:17:10 -07:00
byte [ ] headermd5 ; // MD5
byte [ ] headercrc ; // CRC
byte [ ] headersz ; // Int64 size
2016-09-22 15:36:02 -07:00
BinaryReader br = new BinaryReader ( File . OpenRead ( input ) ) ;
header = br . ReadBytes ( 12 ) ;
headermd5 = br . ReadBytes ( 16 ) ;
headercrc = br . ReadBytes ( 4 ) ;
headersz = br . ReadBytes ( 8 ) ;
br . Dispose ( ) ;
2016-08-25 20:03:27 -07:00
// If the header is not correct, return a blank rom
bool correct = true ;
for ( int i = 0 ; i < header . Length ; i + + )
{
2016-09-09 12:37:13 -07:00
// 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 ;
}
2016-08-29 11:52:11 -07:00
correct & = ( header [ i ] = = Constants . TorrentGZHeader [ i ] ) ;
2016-08-25 20:03:27 -07:00
}
if ( ! correct )
{
2016-08-29 13:57:46 -07:00
return new Rom ( ) ;
2016-06-13 22:12:00 -07:00
}
2016-08-17 16:17:10 -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-09-06 14:08:53 -07:00
long extractedsize = BitConverter . ToInt64 ( headersz , 0 ) ;
2016-06-13 22:12:00 -07:00
2016-08-29 13:57:46 -07:00
Rom rom = new Rom
2016-06-13 22:12:00 -07:00
{
2016-08-29 13:50:55 -07:00
Type = ItemType . Rom ,
2016-09-19 18:04:24 -07:00
MachineName = Path . GetFileNameWithoutExtension ( input ) . ToLowerInvariant ( ) ,
2016-08-25 20:03:27 -07:00
Name = Path . GetFileNameWithoutExtension ( input ) . ToLowerInvariant ( ) ,
2016-09-19 20:36:12 -07:00
Size = extractedsize ,
CRC = gzcrc . ToLowerInvariant ( ) ,
MD5 = gzmd5 . ToLowerInvariant ( ) ,
SHA1 = Path . GetFileNameWithoutExtension ( input ) . ToLowerInvariant ( ) ,
2016-06-13 22:12:00 -07:00
} ;
return rom ;
}
2016-06-15 14:43:05 -07:00
/// <summary>
/// Returns the archive type of an input file
/// </summary>
/// <param name="input">Input file to check</param>
/// <param name="logger">Logger object for file and console output</param>
/// <returns>ArchiveType of inputted file (null on error)</returns>
public static ArchiveType ? GetCurrentArchiveType ( string input , Logger logger )
{
ArchiveType ? outtype = null ;
2016-08-18 19:54:37 -07:00
// First line of defense is going to be the extension, for better or worse
string ext = Path . GetExtension ( input ) . ToLowerInvariant ( ) ;
2016-09-09 13:39:01 -07:00
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" )
2016-08-18 19:54:37 -07:00
{
return outtype ;
}
2016-08-18 20:01:50 -07:00
// Read the first bytes of the file and get the magic number
2016-06-15 14:43:05 -07:00
try
{
2016-06-17 20:03:07 -07:00
byte [ ] magic = new byte [ 8 ] ;
2016-09-22 15:36:02 -07:00
BinaryReader br = new BinaryReader ( File . OpenRead ( input ) ) ;
magic = br . ReadBytes ( 8 ) ;
br . Dispose ( ) ;
2016-06-17 20:03:07 -07:00
// 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 ;
}
2016-06-15 14:43:05 -07:00
}
2016-06-16 12:51:35 -07:00
catch ( Exception )
2016-06-15 14:43:05 -07:00
{
2016-06-17 20:03:07 -07:00
// Don't log file open errors
2016-06-15 14:43:05 -07:00
}
return outtype ;
}
2016-08-24 16:29:49 -07:00
2016-08-29 12:23:02 -07:00
/// <summary>
/// Get if the current file should be scanned internally and externally
/// </summary>
/// <param name="input">Name of the input file to check</param>
/// <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>
/// <param name="logger">Logger object for file and console output</param>
/// <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>
public static void GetInternalExternalProcess ( string input , ArchiveScanLevel sevenzip , ArchiveScanLevel gzip ,
ArchiveScanLevel rar , ArchiveScanLevel zip , Logger logger , out bool shouldExternalProcess , out bool shouldInternalProcess )
{
shouldExternalProcess = true ;
shouldInternalProcess = true ;
2016-08-29 16:52:55 -07:00
ArchiveType ? archiveType = FileTools . GetCurrentArchiveType ( input , logger ) ;
2016-08-29 12:23:02 -07:00
switch ( archiveType )
{
case null :
shouldExternalProcess = true ;
shouldInternalProcess = false ;
break ;
case ArchiveType . GZip :
shouldExternalProcess = ( gzip ! = ArchiveScanLevel . Internal ) ;
shouldInternalProcess = ( gzip ! = ArchiveScanLevel . External ) ;
break ;
case ArchiveType . Rar :
shouldExternalProcess = ( rar ! = ArchiveScanLevel . Internal ) ;
shouldInternalProcess = ( rar ! = ArchiveScanLevel . External ) ;
break ;
case ArchiveType . SevenZip :
shouldExternalProcess = ( sevenzip ! = ArchiveScanLevel . Internal ) ;
shouldInternalProcess = ( sevenzip ! = ArchiveScanLevel . External ) ;
break ;
case ArchiveType . Zip :
shouldExternalProcess = ( zip ! = ArchiveScanLevel . Internal ) ;
shouldInternalProcess = ( zip ! = ArchiveScanLevel . External ) ;
break ;
}
}
2016-09-13 22:45:58 -07:00
/// <summary>
/// Read the information from an input 7z file
/// </summary>
/// <param name="input">Name of the input file to check</param>
/// <param name="logger">Logger object for file and console output</param>
/// <remarks>http://cpansearch.perl.org/src/BJOERN/Compress-Deflate7-1.0/7zip/DOC/7zFormat.txt</remarks>
2016-09-22 15:36:02 -07:00
public static void GetSevenZipFileInfo ( string input , Logger logger )
2016-09-13 22:45:58 -07:00
{
2016-09-22 15:36:02 -07:00
BinaryReader br = new BinaryReader ( File . OpenRead ( input ) ) ;
br . ReadBytes ( 6 ) ; // BYTE kSignature[6] = {'7', 'z', 0xBC, 0xAF, 0x27, 0x1C};
logger . User ( "ArchiveVersion (Major.Minor): " + br . ReadByte ( ) + "." + br . ReadByte ( ) ) ;
logger . User ( "StartHeaderCRC: " + br . ReadUInt32 ( ) ) ;
logger . User ( "StartHeader (NextHeaderOffset, NextHeaderSize, NextHeaderCRC)" + br . ReadUInt64 ( ) + ", " + br . ReadUInt64 ( ) + ", " + br . ReadUInt32 ( ) ) ;
2016-09-13 22:45:58 -07:00
}
2016-08-29 16:51:47 -07:00
#endregion
2016-08-29 16:33:07 -07:00
2016-09-22 16:16:48 -07:00
#region Stream Information
/// <summary>
/// Retrieve file information for a single file
/// </summary>
/// <param name="input">Filename to get information from</param>
/// <param name="noMD5">True if MD5 hashes should not be calculated, false otherwise (default)</param>
/// <param name="noSHA1">True if SHA-1 hashes should not be calcluated, false otherwise (default)</param>
/// <param name="offset">Set a >0 number for getting hash for part of the file, 0 otherwise (default)</param>
/// <param name="keepReadOpen">True if the underlying read stream should be kept open, false otherwise</param>
/// <returns>Populated RomData object if success, empty one on error</returns>
public static Rom GetSingleStreamInfo ( Stream input , bool noMD5 = false , bool noSHA1 = false , long offset = 0 , bool keepReadOpen = false )
{
// If we have a negative offset, zero it out since we don't support it yet
if ( offset < 0 )
{
offset = 0 ;
}
Rom rom = new Rom
{
Type = ItemType . Rom ,
Size = input . Length - Math . Abs ( offset ) ,
CRC = string . Empty ,
MD5 = string . Empty ,
SHA1 = string . Empty ,
} ;
try
{
// Initialize the hashers
OptimizedCRC crc = new OptimizedCRC ( ) ;
MD5 md5 = MD5 . Create ( ) ;
SHA1 sha1 = SHA1 . Create ( ) ;
// Seek to the starting position, if one is set
if ( offset > 0 )
{
input . Seek ( offset , SeekOrigin . Begin ) ;
}
byte [ ] buffer = new byte [ 1024 ] ;
int read ;
while ( ( read = input . Read ( buffer , 0 , buffer . Length ) ) > 0 )
{
crc . Update ( buffer , 0 , read ) ;
if ( ! noMD5 )
{
md5 . TransformBlock ( buffer , 0 , read , buffer , 0 ) ;
}
if ( ! noSHA1 )
{
sha1 . TransformBlock ( buffer , 0 , read , buffer , 0 ) ;
}
}
crc . Update ( buffer , 0 , 0 ) ;
rom . CRC = crc . Value . ToString ( "X8" ) . ToLowerInvariant ( ) ;
if ( ! noMD5 )
{
md5 . TransformFinalBlock ( buffer , 0 , 0 ) ;
rom . MD5 = BitConverter . ToString ( md5 . Hash ) . Replace ( "-" , "" ) . ToLowerInvariant ( ) ;
}
if ( ! noSHA1 )
{
sha1 . TransformFinalBlock ( buffer , 0 , 0 ) ;
rom . SHA1 = BitConverter . ToString ( sha1 . Hash ) . Replace ( "-" , "" ) . ToLowerInvariant ( ) ;
}
// Dispose of the hashers
crc . Dispose ( ) ;
md5 . Dispose ( ) ;
sha1 . Dispose ( ) ;
}
catch ( IOException )
{
return new Rom ( ) ;
}
finally
{
if ( ! keepReadOpen )
{
input . Dispose ( ) ;
}
}
return rom ;
}
#endregion
2016-09-01 20:38:41 -07:00
#region File Manipulation
/// <summary>
/// Remove an arbitrary number of bytes from the inputted file
/// </summary>
/// <param name="input">File to be cropped</param>
/// <param name="output">Outputted file</param>
/// <param name="bytesToRemoveFromHead">Bytes to be removed from head of file</param>
/// <param name="bytesToRemoveFromTail">Bytes to be removed from tail of file</param>
public static void RemoveBytesFromFile ( string input , string output , long bytesToRemoveFromHead , long bytesToRemoveFromTail )
{
// If any of the inputs are invalid, skip
if ( ! File . Exists ( input ) | | new FileInfo ( input ) . Length < = ( bytesToRemoveFromHead + bytesToRemoveFromTail ) )
{
return ;
}
2016-09-22 16:16:48 -07:00
// Get the streams
FileStream fsr = File . OpenRead ( input ) ;
FileStream fsw = File . OpenWrite ( output ) ;
2016-09-01 20:38:41 -07:00
2016-09-22 16:16:48 -07:00
RemoveBytesFromStream ( fsr , fsw , bytesToRemoveFromHead , bytesToRemoveFromTail ) ;
2016-09-01 20:38:41 -07:00
2016-09-22 16:16:48 -07:00
fsr . Dispose ( ) ;
fsw . Dispose ( ) ;
2016-09-01 20:38:41 -07:00
}
/// <summary>
/// Add an aribtrary number of bytes to the inputted file
/// </summary>
/// <param name="input">File to be appended to</param>
/// <param name="output">Outputted file</param>
/// <param name="bytesToAddToHead">String representing bytes to be added to head of file</param>
/// <param name="bytesToAddToTail">String representing bytes to be added to tail of file</param>
public static void AppendBytesToFile ( string input , string output , string bytesToAddToHead , string bytesToAddToTail )
{
// Source: http://stackoverflow.com/questions/311165/how-do-you-convert-byte-array-to-hexadecimal-string-and-vice-versa
byte [ ] bytesToAddToHeadArray = new byte [ bytesToAddToHead . Length / 2 ] ;
for ( int i = 0 ; i < bytesToAddToHead . Length ; i + = 2 )
{
bytesToAddToHeadArray [ i / 2 ] = Convert . ToByte ( bytesToAddToHead . Substring ( i , 2 ) , 16 ) ;
}
byte [ ] bytesToAddToTailArray = new byte [ bytesToAddToTail . Length / 2 ] ;
for ( int i = 0 ; i < bytesToAddToTail . Length ; i + = 2 )
{
bytesToAddToTailArray [ i / 2 ] = Convert . ToByte ( bytesToAddToTail . Substring ( i , 2 ) , 16 ) ;
}
AppendBytesToFile ( input , output , bytesToAddToHeadArray , bytesToAddToTailArray ) ;
}
/// <summary>
/// Add an aribtrary number of bytes to the inputted file
/// </summary>
/// <param name="input">File to be appended to</param>
/// <param name="output">Outputted file</param>
/// <param name="bytesToAddToHead">Bytes to be added to head of file</param>
/// <param name="bytesToAddToTail">Bytes to be added to tail of file</param>
public static void AppendBytesToFile ( string input , string output , byte [ ] bytesToAddToHead , byte [ ] bytesToAddToTail )
{
// If any of the inputs are invalid, skip
if ( ! File . Exists ( input ) )
{
return ;
}
2016-09-22 16:16:48 -07:00
FileStream fsr = File . OpenRead ( input ) ;
FileStream fsw = File . OpenWrite ( output ) ;
2016-09-22 15:36:02 -07:00
2016-09-22 16:16:48 -07:00
AppendBytesToStream ( fsr , fsw , bytesToAddToHead , bytesToAddToTail ) ;
2016-09-22 15:36:02 -07:00
2016-09-22 16:16:48 -07:00
fsr . Dispose ( ) ;
fsw . Dispose ( ) ;
2016-09-01 20:38:41 -07:00
}
/// <summary>
/// Copy a file to a new location, creating directories as needed
/// </summary>
/// <param name="input">Input filename</param>
/// <param name="output">Output filename</param>
public static void CopyFileToNewLocation ( string input , string output )
{
if ( File . Exists ( input ) & & ! File . Exists ( output ) )
{
if ( ! Directory . Exists ( Path . GetDirectoryName ( output ) ) )
{
Directory . CreateDirectory ( Path . GetDirectoryName ( output ) ) ;
}
File . Copy ( input , output ) ;
}
}
/// <summary>
/// Cleans out the temporary directory
/// </summary>
/// <param name="dirname">Name of the directory to clean out</param>
public static void CleanDirectory ( string dirname )
{
foreach ( string file in Directory . EnumerateFiles ( dirname , "*" , SearchOption . TopDirectoryOnly ) )
{
try
{
File . Delete ( file ) ;
}
catch { }
}
foreach ( string dir in Directory . EnumerateDirectories ( dirname , "*" , SearchOption . TopDirectoryOnly ) )
{
try
{
Directory . Delete ( dir , true ) ;
}
catch { }
}
}
2016-09-22 09:42:45 -07:00
/// <summary>
/// Delete a file asynchronously
/// </summary>
/// <param name="filename">Name of the file to be deleted</param>
public async static void DeleteFile ( string filename )
{
2016-09-22 11:17:10 -07:00
if ( ! File . Exists ( filename ) )
{
return ;
}
2016-09-22 10:07:49 -07:00
File . SetAttributes ( filename , 0 ) ;
2016-09-22 09:42:45 -07:00
FileInfo fi = new FileInfo ( filename ) ;
2016-09-22 10:07:49 -07:00
fi . IsReadOnly = false ;
2016-09-22 09:42:45 -07:00
await Task . Factory . StartNew ( ( ) = >
{
while ( fi . Exists )
{
try
{
fi . Delete ( ) ;
}
2016-09-22 10:49:37 -07:00
catch { }
2016-09-22 09:42:45 -07:00
}
} ) ;
}
2016-09-22 11:17:10 -07:00
/// <summary>
/// Delete a directory asynchronously
/// </summary>
/// <param name="dirname">Name of the directory to be deleted</param>
public async static void DeleteDirectory ( string dirname )
{
if ( ! Directory . Exists ( dirname ) )
{
return ;
}
DirectoryInfo di = new DirectoryInfo ( dirname ) ;
await Task . Factory . StartNew ( ( ) = >
{
while ( di . Exists )
{
try
{
di . Delete ( true ) ;
}
catch { }
}
} ) ;
}
2016-09-01 20:38:41 -07:00
#endregion
2016-09-22 16:16:48 -07:00
#region Stream Manipulation
// <summary>
/// Remove an arbitrary number of bytes from the inputted stream
/// </summary>
/// <param name="input">Stream to be cropped</param>
/// <param name="output">Stream to output to</param>
/// <param name="bytesToRemoveFromHead">Bytes to be removed from head of stream</param>
/// <param name="bytesToRemoveFromTail">Bytes to be removed from tail of stream</param>
public static void RemoveBytesFromStream ( Stream input , Stream output , long bytesToRemoveFromHead , long bytesToRemoveFromTail )
{
// Read the input file and write to the fail
BinaryReader br = new BinaryReader ( input ) ;
BinaryWriter bw = new BinaryWriter ( output ) ;
int bufferSize = 1024 ;
long adjustedLength = br . BaseStream . Length - bytesToRemoveFromTail ;
// Seek to the correct position
br . BaseStream . Seek ( ( bytesToRemoveFromHead < 0 ? 0 : bytesToRemoveFromHead ) , SeekOrigin . Begin ) ;
// Now read the file in chunks and write out
byte [ ] buffer = new byte [ bufferSize ] ;
while ( br . BaseStream . Position < = ( adjustedLength - bufferSize ) )
{
buffer = br . ReadBytes ( bufferSize ) ;
bw . Write ( buffer ) ;
}
// For the final chunk, if any, write out only that number of bytes
int length = ( int ) ( adjustedLength - br . BaseStream . Position ) ;
buffer = new byte [ length ] ;
buffer = br . ReadBytes ( length ) ;
bw . Write ( buffer ) ;
}
/// <summary>
/// Add an aribtrary number of bytes to the inputted stream
/// </summary>
/// <param name="input">Stream to be appended to</param>
/// <param name="output">Outputted stream</param>
/// <param name="bytesToAddToHead">String representing bytes to be added to head of stream</param>
/// <param name="bytesToAddToTail">String representing bytes to be added to tail of stream</param>
public static void AppendBytesToStream ( Stream input , Stream output , string bytesToAddToHead , string bytesToAddToTail )
{
// Source: http://stackoverflow.com/questions/311165/how-do-you-convert-byte-array-to-hexadecimal-string-and-vice-versa
byte [ ] bytesToAddToHeadArray = new byte [ bytesToAddToHead . Length / 2 ] ;
for ( int i = 0 ; i < bytesToAddToHead . Length ; i + = 2 )
{
bytesToAddToHeadArray [ i / 2 ] = Convert . ToByte ( bytesToAddToHead . Substring ( i , 2 ) , 16 ) ;
}
byte [ ] bytesToAddToTailArray = new byte [ bytesToAddToTail . Length / 2 ] ;
for ( int i = 0 ; i < bytesToAddToTail . Length ; i + = 2 )
{
bytesToAddToTailArray [ i / 2 ] = Convert . ToByte ( bytesToAddToTail . Substring ( i , 2 ) , 16 ) ;
}
AppendBytesToStream ( input , output , bytesToAddToHeadArray , bytesToAddToTailArray ) ;
}
/// <summary>
/// Add an aribtrary number of bytes to the inputted stream
/// </summary>
/// <param name="input">Stream to be appended to</param>
/// <param name="output">Outputted stream</param>
/// <param name="bytesToAddToHead">Bytes to be added to head of stream</param>
/// <param name="bytesToAddToTail">Bytes to be added to tail of stream</param>
public static void AppendBytesToStream ( Stream input , Stream output , byte [ ] bytesToAddToHead , byte [ ] bytesToAddToTail )
{
BinaryReader br = new BinaryReader ( input ) ;
BinaryWriter bw = new BinaryWriter ( output ) ;
if ( bytesToAddToHead . Count ( ) > 0 )
{
bw . Write ( bytesToAddToHead ) ;
}
int bufferSize = 1024 ;
// Now read the file in chunks and write out
byte [ ] buffer = new byte [ bufferSize ] ;
while ( br . BaseStream . Position < = ( br . BaseStream . Length - bufferSize ) )
{
buffer = br . ReadBytes ( bufferSize ) ;
bw . Write ( buffer ) ;
}
// For the final chunk, if any, write out only that number of bytes
int length = ( int ) ( br . BaseStream . Length - br . BaseStream . Position ) ;
buffer = new byte [ length ] ;
buffer = br . ReadBytes ( length ) ;
bw . Write ( buffer ) ;
if ( bytesToAddToTail . Count ( ) > 0 )
{
bw . Write ( bytesToAddToTail ) ;
}
}
#endregion
2016-06-13 20:57:49 -07:00
}
}