2016-06-22 14:17:27 -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 ;
using System.Text.RegularExpressions ;
2016-06-13 20:57:49 -07:00
namespace SabreTools.Helper
{
public class ArchiveTools
{
/// <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>
/// <param name="input">Input filename to be moved</param>
/// <param name="output">Output directory to build to</param>
/// <param name="rom">RomData representing the new information</param>
2016-06-16 18:57:34 -07:00
public static void WriteToArchive ( string input , string output , Rom rom )
2016-06-13 20:57:49 -07:00
{
2016-06-16 22:17:58 -07:00
string archiveFileName = Path . Combine ( output , rom . Game + ".zip" ) ;
2016-06-13 20:57:49 -07:00
2016-06-22 14:17:27 -07:00
System . IO . Compression . ZipArchive outarchive = null ;
2016-06-13 20:57:49 -07:00
try
{
2016-06-14 12:36:25 -07:00
if ( ! File . Exists ( archiveFileName ) )
{
2016-06-15 11:21:39 -07:00
outarchive = ZipFile . Open ( archiveFileName , ZipArchiveMode . Create ) ;
2016-06-14 12:36:25 -07:00
}
else
{
2016-06-15 11:21:39 -07:00
outarchive = ZipFile . Open ( archiveFileName , ZipArchiveMode . Update ) ;
2016-06-14 12:36:25 -07:00
}
2016-06-15 11:21:39 -07:00
if ( File . Exists ( input ) )
2016-06-14 12:36:25 -07:00
{
2016-06-15 11:21:39 -07:00
if ( outarchive . Mode = = ZipArchiveMode . Create | | outarchive . GetEntry ( rom . Name ) = = null )
{
outarchive . CreateEntryFromFile ( input , rom . Name , CompressionLevel . Optimal ) ;
}
2016-06-14 12:36:25 -07:00
}
2016-06-15 11:21:39 -07:00
else if ( Directory . Exists ( input ) )
2016-06-13 20:57:49 -07:00
{
2016-06-15 11:21:39 -07:00
foreach ( string file in Directory . EnumerateFiles ( input , "*" , SearchOption . AllDirectories ) )
{
if ( outarchive . Mode = = ZipArchiveMode . Create | | outarchive . GetEntry ( file ) = = null )
{
outarchive . CreateEntryFromFile ( file , file , CompressionLevel . Optimal ) ;
}
}
2016-06-13 20:57:49 -07:00
}
2016-06-14 12:36:25 -07:00
}
catch ( Exception ex )
{
Console . WriteLine ( ex ) ;
2016-06-13 20:57:49 -07:00
}
finally
{
outarchive ? . Dispose ( ) ;
}
}
2016-06-22 14:17:27 -07:00
/// <summary>
/// Copy a file to an output archive using SharpCompress
/// </summary>
/// <param name="input">Input filename to be moved</param>
/// <param name="output">Output directory to build to</param>
/// <param name="rom">RomData representing the new information</param>
public static void WriteToManagedArchive ( string input , string output , Rom rom )
{
string archiveFileName = Path . Combine ( output , rom . Game + ".zip" ) ;
// Delete an empty file first
if ( File . Exists ( archiveFileName ) & & new FileInfo ( archiveFileName ) . Length = = 0 )
{
File . Delete ( archiveFileName ) ;
}
// Get if the file should be written out
bool newfile = File . Exists ( archiveFileName ) & & new FileInfo ( archiveFileName ) . Length ! = 0 ;
using ( SharpCompress . Archive . Zip . ZipArchive archive = ( newfile
? ArchiveFactory . Open ( archiveFileName , Options . LookForHeader ) as SharpCompress . Archive . Zip . ZipArchive
: ArchiveFactory . Create ( ArchiveType . Zip ) as SharpCompress . Archive . Zip . ZipArchive ) )
{
try
{
if ( File . Exists ( input ) )
{
archive . AddEntry ( rom . Name , input ) ;
}
else if ( Directory . Exists ( input ) )
{
archive . AddAllFromDirectory ( input , "*" , SearchOption . AllDirectories ) ;
}
archive . SaveTo ( archiveFileName + ".tmp" , CompressionType . Deflate ) ;
}
2016-07-12 10:42:29 -07:00
catch ( Exception )
2016-06-22 14:17:27 -07:00
{
// Don't log archive write errors
}
}
if ( File . Exists ( archiveFileName + ".tmp" ) )
{
File . Delete ( archiveFileName ) ;
File . Move ( archiveFileName + ".tmp" , archiveFileName ) ;
}
}
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>
/// <param name="tempdir">Temporary directory for archive extraction</param>
/// <param name="logger">Logger object for file and console output</param>
/// <returns>True if the extraction was a success, false otherwise</returns>
public static bool ExtractArchive ( string input , string tempdir , Logger logger )
{
return ExtractArchive ( input , tempdir , ArchiveScanLevel . Both , ArchiveScanLevel . External , ArchiveScanLevel . External , ArchiveScanLevel . Both , logger ) ;
}
/// <summary>
/// Attempt to extract a file as an archive
/// </summary>
/// <param name="input">Name of the file to be extracted</param>
/// <param name="tempdir">Temporary directory for archive extraction</param>
/// <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>
public static bool ExtractArchive ( string input , string tempdir , int sevenzip ,
int gz , int rar , int zip , Logger logger )
{
return ExtractArchive ( input , tempdir , ( ArchiveScanLevel ) sevenzip , ( ArchiveScanLevel ) gz , ( ArchiveScanLevel ) rar , ( ArchiveScanLevel ) zip , logger ) ;
}
/// <summary>
/// Attempt to extract a file as an archive
/// </summary>
/// <param name="input">Name of the file to be extracted</param>
/// <param name="tempdir">Temporary directory for archive extraction</param>
/// <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>
public static bool ExtractArchive ( string input , string tempdir , ArchiveScanLevel sevenzip ,
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-15 14:29:34 -07:00
IReader reader = null ;
2016-08-18 15:48:45 -07:00
SevenZipArchive sza = null ;
2016-06-13 20:57:49 -07:00
try
{
2016-08-18 15:48:45 -07:00
if ( at = = ArchiveType . SevenZip & & sevenzip ! = ArchiveScanLevel . External )
2016-06-13 20:57:49 -07:00
{
2016-08-18 15:48:45 -07:00
sza = SevenZipArchive . Open ( File . OpenRead ( input ) ) ;
logger . Log ( "Found archive of type: " + at ) ;
2016-06-13 20:57:49 -07:00
// Create the temp directory
2016-06-15 14:29:34 -07:00
Directory . CreateDirectory ( tempdir ) ;
2016-06-13 20:57:49 -07:00
// Extract all files to the temp directory
2016-08-18 15:48:45 -07:00
sza . WriteToDirectory ( tempdir , ExtractOptions . ExtractFullPath | ExtractOptions . Overwrite ) ;
2016-06-15 14:29:34 -07:00
encounteredErrors = false ;
2016-06-13 20:57:49 -07:00
}
2016-08-18 15:50:57 -07:00
else if ( at = = ArchiveType . GZip & & gz ! = ArchiveScanLevel . External )
{
2016-08-18 15:52:14 -07:00
logger . Log ( "Found archive of type: " + at ) ;
2016-08-18 15:50:57 -07:00
// Create the temp directory
Directory . CreateDirectory ( tempdir ) ;
using ( FileStream itemstream = File . OpenRead ( input ) )
{
2016-08-25 16:34:15 -07:00
using ( FileStream outstream = File . Create ( Path . Combine ( tempdir , Path . GetFileNameWithoutExtension ( input ) ) ) )
2016-08-18 15:50:57 -07:00
{
using ( GZipStream gzstream = new GZipStream ( itemstream , CompressionMode . Decompress ) )
{
gzstream . CopyTo ( outstream ) ;
}
}
}
encounteredErrors = false ;
}
2016-08-18 15:48:45 -07:00
else
2016-06-13 20:57:49 -07:00
{
2016-08-18 15:48:45 -07:00
reader = ReaderFactory . Open ( File . OpenRead ( input ) ) ;
logger . Log ( "Found archive of type: " + at ) ;
2016-06-13 20:57:49 -07:00
2016-08-18 15:48:45 -07:00
if ( ( at = = ArchiveType . Zip & & zip ! = ArchiveScanLevel . External ) | |
( at = = ArchiveType . Rar & & rar ! = ArchiveScanLevel . External ) )
{
// Create the temp directory
Directory . CreateDirectory ( tempdir ) ;
2016-06-13 20:57:49 -07:00
2016-08-18 15:48:45 -07:00
// Extract all files to the temp directory
reader . WriteAllToDirectory ( tempdir , ExtractOptions . ExtractFullPath | ExtractOptions . Overwrite ) ;
encounteredErrors = false ;
}
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
}
finally
{
reader ? . Dispose ( ) ;
2016-08-18 15:48:45 -07:00
sza ? . Dispose ( ) ;
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>
/// <param name="tempdir">Temporary directory for archive extraction</param>
/// <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-06-15 16:37:15 -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
{
reader = ReaderFactory . Open ( File . OpenRead ( input ) ) ;
if ( at = = ArchiveType . Zip | | at = = ArchiveType . SevenZip | | at = = ArchiveType . Rar )
{
// Create the temp directory
Directory . CreateDirectory ( tempdir ) ;
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-06-20 20:20:17 -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 )
{
// Dispose the original reader
reader . Dispose ( ) ;
using ( FileStream itemstream = File . OpenRead ( input ) )
{
using ( FileStream outstream = File . Create ( Path . Combine ( tempdir , Path . GetFileNameWithoutExtension ( input ) ) ) )
{
using ( GZipStream gzstream = new GZipStream ( itemstream , CompressionMode . Decompress ) )
{
gzstream . CopyTo ( outstream ) ;
outfile = Path . GetFullPath ( Path . Combine ( tempdir , Path . GetFileNameWithoutExtension ( input ) ) ) ;
}
}
}
}
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-06-15 16:35:01 -07:00
/// <summary>
/// Attempt to copy a file between archives
/// </summary>
2016-06-21 10:44:17 -07:00
/// <param name="inputArchive">Source archive name</param>
/// <param name="outputArchive">Destination archive name</param>
/// <param name="sourceEntryName">Input entry name</param>
/// <param name="destEntryName">Output entry name</param>
2016-06-15 16:35:01 -07:00
/// <param name="logger">Logger object for file and console output</param>
/// <returns>True if the copy was a success, false otherwise</returns>
2016-06-21 10:44:17 -07:00
public static bool CopyFileBetweenArchives ( string inputArchive , string outputArchive ,
string sourceEntryName , string destEntryName , Logger logger )
2016-06-15 16:35:01 -07:00
{
bool success = false ;
// First get the archive types
2016-06-21 10:44:17 -07:00
ArchiveType ? iat = GetCurrentArchiveType ( inputArchive , logger ) ;
ArchiveType ? oat = ( File . Exists ( outputArchive ) ? GetCurrentArchiveType ( outputArchive , logger ) : ArchiveType . Zip ) ;
2016-06-15 16:35:01 -07:00
// If we got back null (or the output is not a Zipfile), then it's not an archive, so we we return
2016-06-21 10:44:17 -07:00
if ( iat = = null | | ( oat = = null | | oat ! = ArchiveType . Zip ) | | inputArchive = = outputArchive )
2016-06-15 16:35:01 -07:00
{
return success ;
}
IReader reader = null ;
2016-06-22 14:17:27 -07:00
System . IO . Compression . ZipArchive outarchive = null ;
2016-06-15 16:35:01 -07:00
try
{
2016-06-21 10:44:17 -07:00
reader = ReaderFactory . Open ( File . OpenRead ( inputArchive ) ) ;
2016-06-15 16:35:01 -07:00
if ( iat = = ArchiveType . Zip | | iat = = ArchiveType . SevenZip | | iat = = ArchiveType . Rar )
{
while ( reader . MoveToNextEntry ( ) )
{
logger . Log ( "Current entry name: '" + reader . Entry . Key + "'" ) ;
2016-06-21 10:44:17 -07:00
if ( reader . Entry ! = null & & reader . Entry . Key . Contains ( sourceEntryName ) )
2016-06-15 16:35:01 -07:00
{
2016-06-21 10:44:17 -07:00
if ( ! File . Exists ( outputArchive ) )
2016-06-15 16:35:01 -07:00
{
2016-06-21 10:44:17 -07:00
outarchive = ZipFile . Open ( outputArchive , ZipArchiveMode . Create ) ;
2016-06-15 16:35:01 -07:00
}
else
{
2016-06-21 10:44:17 -07:00
outarchive = ZipFile . Open ( outputArchive , ZipArchiveMode . Update ) ;
2016-06-15 16:35:01 -07:00
}
2016-06-21 10:44:17 -07:00
if ( outarchive . Mode = = ZipArchiveMode . Create | | outarchive . GetEntry ( destEntryName ) = = null )
2016-06-15 16:35:01 -07:00
{
2016-06-22 14:17:27 -07:00
System . IO . Compression . ZipArchiveEntry iae = outarchive . CreateEntry ( destEntryName , CompressionLevel . Optimal ) as System . IO . Compression . ZipArchiveEntry ;
2016-06-21 00:47:39 -07:00
using ( Stream iaestream = iae . Open ( ) )
2016-06-15 16:35:01 -07:00
{
2016-06-21 00:47:39 -07:00
reader . WriteEntryTo ( iaestream ) ;
2016-06-15 16:35:01 -07:00
}
}
success = true ;
}
}
}
}
catch ( Exception ex )
{
logger . Error ( ex . ToString ( ) ) ;
success = false ;
}
finally
{
reader ? . Dispose ( ) ;
outarchive ? . Dispose ( ) ;
}
return success ;
}
2016-06-22 14:17:27 -07:00
/// <summary>
/// Attempt to copy a file between archives using SharpCompress
/// </summary>
/// <param name="inputArchive">Source archive name</param>
/// <param name="outputArchive">Destination archive name</param>
/// <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>
public static bool CopyFileBetweenManagedArchives ( string inputArchive , string outputArchive ,
string sourceEntryName , string destEntryName , Logger logger )
{
bool success = false ;
// First get the archive types
ArchiveType ? iat = GetCurrentArchiveType ( inputArchive , logger ) ;
ArchiveType ? oat = ( File . Exists ( outputArchive ) ? GetCurrentArchiveType ( outputArchive , logger ) : ArchiveType . Zip ) ;
// If we got back null (or the output is not a Zipfile), then it's not an archive, so we we return
if ( iat = = null | | ( oat = = null | | oat ! = ArchiveType . Zip ) | | inputArchive = = outputArchive )
{
return success ;
}
try
{
using ( IReader reader = ReaderFactory . Open ( File . OpenRead ( inputArchive ) ) )
{
if ( iat = = ArchiveType . Zip | | iat = = ArchiveType . SevenZip | | iat = = ArchiveType . Rar )
{
while ( reader . MoveToNextEntry ( ) )
{
logger . Log ( "Current entry name: '" + reader . Entry . Key + "'" ) ;
if ( reader . Entry ! = null & & reader . Entry . Key . Contains ( sourceEntryName ) )
{
// Get if the file should be written out
bool newfile = File . Exists ( outputArchive ) & & new FileInfo ( outputArchive ) . Length ! = 0 ;
using ( SharpCompress . Archive . Zip . ZipArchive archive = ( newfile
? ArchiveFactory . Open ( outputArchive , Options . LookForHeader ) as SharpCompress . Archive . Zip . ZipArchive
: ArchiveFactory . Create ( ArchiveType . Zip ) as SharpCompress . Archive . Zip . ZipArchive ) )
{
try
{
Stream tempstream = new MemoryStream ( ) ;
reader . WriteEntryTo ( tempstream ) ;
archive . AddEntry ( destEntryName , tempstream ) ;
archive . SaveTo ( outputArchive + ".tmp" , CompressionType . Deflate ) ;
}
2016-07-12 10:42:29 -07:00
catch ( Exception )
2016-06-22 14:17:27 -07:00
{
// Don't log archive write errors
}
}
if ( File . Exists ( outputArchive + ".tmp" ) )
{
File . Delete ( outputArchive ) ;
File . Move ( outputArchive + ".tmp" , outputArchive ) ;
}
success = true ;
}
}
}
}
}
catch ( Exception ex )
{
logger . Error ( ex . ToString ( ) ) ;
success = false ;
}
return success ;
}
2016-06-15 14:55:06 -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>
/// <param name="logger">Logger object for file and console output</param>
/// <returns>List of RomData objects representing the found data</returns>
2016-06-16 18:57:34 -07:00
public static List < Rom > GetArchiveFileInfo ( string input , Logger logger )
2016-06-15 14:55:06 -07:00
{
2016-06-16 18:57:34 -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 )
{
Rom possibleTgz = GetTorrentGZFileInfo ( input , logger ) ;
// 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
using ( 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 ) ;
}
}
reader = ReaderFactory . Open ( File . OpenRead ( input ) ) ;
2016-06-15 14:55:06 -07:00
if ( at ! = ArchiveType . Tar )
{
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-06-16 18:57:34 -07:00
roms . Add ( new Rom
2016-06-15 15:44:11 -07:00
{
Type = "rom" ,
Name = reader . Entry . Key ,
Game = gamename ,
2016-08-29 13:05:32 -07:00
HashData = new HashData
{
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-06-16 18:57:34 -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-06-16 18:57:34 -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-06-16 18:57:34 -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-08-25 20:03:27 -07:00
using ( BinaryReader br = new BinaryReader ( File . OpenRead ( input ) ) )
2016-06-13 22:12:00 -07:00
{
2016-08-25 20:03:27 -07:00
header = br . ReadBytes ( 12 ) ;
headermd5 = br . ReadBytes ( 16 ) ;
headercrc = br . ReadBytes ( 4 ) ;
headersz = br . ReadBytes ( 8 ) ;
}
// If the header is not correct, return a blank rom
bool correct = true ;
for ( int i = 0 ; i < header . Length ; i + + )
{
2016-08-29 11:52:11 -07:00
correct & = ( header [ i ] = = Constants . TorrentGZHeader [ i ] ) ;
2016-08-25 20:03:27 -07:00
}
if ( ! correct )
{
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-08-25 20:03:27 -07:00
long extractedsize = ( long ) BitConverter . ToUInt64 ( headersz . Reverse ( ) . ToArray ( ) , 0 ) ;
2016-06-13 22:12:00 -07:00
2016-06-16 18:57:34 -07:00
Rom rom = new Rom
2016-06-13 22:12:00 -07:00
{
Type = "rom" ,
2016-08-25 20:03:27 -07:00
Game = Path . GetFileNameWithoutExtension ( input ) . ToLowerInvariant ( ) ,
Name = Path . GetFileNameWithoutExtension ( input ) . ToLowerInvariant ( ) ,
2016-08-29 13:05:32 -07:00
HashData = new HashData
{
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
2016-08-24 16:29:49 -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>
2016-08-25 10:26:52 -07:00
/// <param name="romba">True if files should be output in Romba depot folders, false otherwise</param>
2016-08-24 16:29:49 -07:00
/// <param name="logger">Logger object for file and console output</param>
/// <returns>True if the write was a success, false otherwise</returns>
2016-08-25 15:00:39 -07:00
/// <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-08-25 10:26:52 -07:00
public static bool WriteTorrentGZ ( string input , string outdir , bool romba , Logger logger )
2016-08-24 16:29:49 -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
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
Rom rom = RomTools . GetSingleFileInfo ( input ) ;
// If it doesn't exist, create the output file and then write
2016-08-29 13:05:32 -07:00
string outfile = Path . Combine ( outdir , rom . HashData . SHA1 + ".gz" ) ;
2016-08-25 11:57:52 -07:00
using ( FileStream inputstream = new FileStream ( input , FileMode . Open ) )
2016-08-24 20:33:35 -07:00
using ( GZipStream output = new GZipStream ( File . Open ( outfile , FileMode . Create , FileAccess . Write ) , CompressionMode . Compress ) )
2016-08-24 16:29:49 -07:00
{
inputstream . CopyTo ( output ) ;
}
2016-08-25 15:00:39 -07:00
// Now that it's ready, inject the header info
2016-08-24 20:33:35 -07:00
using ( BinaryWriter sw = new BinaryWriter ( new MemoryStream ( ) ) )
{
using ( BinaryReader br = new BinaryReader ( File . OpenRead ( outfile ) ) )
2016-08-24 16:29:49 -07:00
{
2016-08-29 11:52:11 -07:00
// Write standard header and TGZ info
byte [ ] data = Constants . TorrentGZHeader
2016-08-29 13:05:32 -07:00
. Concat ( StringToByteArray ( rom . HashData . MD5 ) ) // MD5
. Concat ( StringToByteArray ( rom . HashData . CRC ) ) // CRC
. Concat ( BitConverter . GetBytes ( rom . HashData . Size ) . Reverse ( ) . ToArray ( ) ) // Long size (Mirrored)
2016-08-24 20:33:35 -07:00
. 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 )
{
sw . Write ( br . ReadByte ( ) ) ;
i + + ;
}
2016-08-24 16:29:49 -07:00
}
2016-08-24 20:33:35 -07:00
using ( BinaryWriter bw = new BinaryWriter ( File . Open ( outfile , FileMode . Create ) ) )
{
// Now write the new file over the original
sw . BaseStream . Seek ( 0 , SeekOrigin . Begin ) ;
bw . BaseStream . Seek ( 0 , SeekOrigin . Begin ) ;
sw . BaseStream . CopyTo ( bw . BaseStream ) ;
}
2016-08-24 16:29:49 -07:00
}
2016-08-25 10:26:52 -07:00
// If we're in romba mode, create the subfolder and move the file
if ( romba )
{
2016-08-29 13:05:32 -07:00
string subfolder = Path . Combine ( rom . HashData . SHA1 . Substring ( 0 , 2 ) , rom . HashData . SHA1 . Substring ( 2 , 2 ) , rom . HashData . SHA1 . Substring ( 4 , 2 ) , rom . HashData . SHA1 . Substring ( 6 , 2 ) ) ;
2016-08-25 10:26:52 -07:00
outdir = Path . Combine ( outdir , subfolder ) ;
if ( ! Directory . Exists ( outdir ) )
{
Directory . CreateDirectory ( outdir ) ;
}
2016-08-25 11:14:46 -07:00
try
{
File . Move ( outfile , Path . Combine ( outdir , Path . GetFileName ( outfile ) ) ) ;
}
catch ( Exception ex )
{
2016-08-25 11:54:04 -07:00
logger . Warning ( ex . ToString ( ) ) ;
2016-08-25 11:14:46 -07:00
File . Delete ( outfile ) ;
}
2016-08-25 10:26:52 -07:00
}
2016-08-24 16:29:49 -07:00
return true ;
}
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-08-19 10:14:10 -07:00
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 ] ;
using ( BinaryReader br = new BinaryReader ( File . OpenRead ( input ) ) )
{
magic = br . ReadBytes ( 8 ) ;
}
// 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 ;
ArchiveType ? archiveType = ArchiveTools . GetCurrentArchiveType ( input , logger ) ;
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-08-24 16:29:49 -07:00
/// <summary>
/// http://stackoverflow.com/questions/311165/how-do-you-convert-byte-array-to-hexadecimal-string-and-vice-versa
/// </summary>
public static byte [ ] StringToByteArray ( String hex )
{
int NumberChars = hex . Length ;
byte [ ] bytes = new byte [ NumberChars / 2 ] ;
for ( int i = 0 ; i < NumberChars ; i + = 2 )
bytes [ i / 2 ] = Convert . ToByte ( hex . Substring ( i , 2 ) , 16 ) ;
return bytes ;
}
2016-06-13 20:57:49 -07:00
}
}