mirror of
https://github.com/claunia/SabreTools.git
synced 2025-12-16 19:14:27 +00:00
There are at least 2 separate changes that got intermingled on this one so I'm committing them all at the same time. The first is massive overhauls to the Romba portion of DATFromDir. This part is still a work in progress. The second is adding more items to the DatData struct to allow for better and more efficient stat collection.
805 lines
26 KiB
C#
805 lines
26 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.IO;
|
|
using System.IO.Compression;
|
|
using System.Security.Cryptography;
|
|
using System.Text;
|
|
using System.Text.RegularExpressions;
|
|
|
|
using SabreTools.Helper;
|
|
using DamienG.Security.Cryptography;
|
|
using SharpCompress.Archive;
|
|
using SharpCompress.Common;
|
|
using SharpCompress.Reader;
|
|
|
|
namespace SabreTools
|
|
{
|
|
/// <summary>
|
|
/// Create a DAT file from a specified file, directory, or set thereof
|
|
/// </summary>
|
|
public class DATFromDir
|
|
{
|
|
// Path-related variables
|
|
private string _basePath;
|
|
private string _tempDir;
|
|
|
|
// User specified inputs
|
|
private List<String> _inputs;
|
|
private DatData _datdata;
|
|
private bool _noMD5;
|
|
private bool _noSHA1;
|
|
private bool _bare;
|
|
private bool _archivesAsFiles;
|
|
private bool _enableGzip;
|
|
|
|
// Other required variables
|
|
private Logger _logger;
|
|
|
|
/// <summary>
|
|
/// Create a new DATFromDir object
|
|
/// </summary>
|
|
/// <param name="inputs">A List of Strings representing the files and folders to be DATted</param>
|
|
/// <param name="datdata">DatData object representing the requested output DAT</param>
|
|
/// <param name="noMD5">True if MD5 hashes should be skipped over, false otherwise</param>
|
|
/// <param name="noSHA1">True if SHA-1 hashes should be skipped over, false otherwise</param>
|
|
/// <param name="bare">True if the date should be omitted from the DAT, false otherwise</param>
|
|
/// <param name="archivesAsFiles">True if archives should be treated as files, false otherwise</param>
|
|
/// <param name="enableGzip">True if GZIP archives should be treated as files, false otherwise</param>>
|
|
/// <param name="logger">Logger object for console and file output</param>
|
|
/// <param name="tempDir">Name of the directory to create a temp folder in (blank is current directory)</param>
|
|
public DATFromDir(List<String> inputs, DatData datdata, bool noMD5, bool noSHA1, bool bare, bool archivesAsFiles, bool enableGzip, string tempDir, Logger logger)
|
|
{
|
|
_inputs = inputs;
|
|
_datdata = datdata;
|
|
_noMD5 = noMD5;
|
|
_noSHA1 = noSHA1;
|
|
_bare = bare;
|
|
_archivesAsFiles = archivesAsFiles;
|
|
_enableGzip = enableGzip;
|
|
_tempDir = tempDir;
|
|
_logger = logger;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Start help or use supplied parameters
|
|
/// </summary>
|
|
/// <param name="args">String array representing command line parameters</param>
|
|
public static void Main(string[] args)
|
|
{
|
|
Console.Clear();
|
|
|
|
// Credits take precidence over all
|
|
if ((new List<string>(args)).Contains("--credits"))
|
|
{
|
|
Build.Credits();
|
|
return;
|
|
}
|
|
|
|
Logger logger = new Logger(true, "datfromdir.log");
|
|
logger.Start();
|
|
|
|
// First things first, take care of all of the arguments that this could have
|
|
bool noMD5 = false, noSHA1 = false, forceunpack = false, archivesAsFiles = false, old = false, superDat = false, bare = false, romba = false, enableGzip = false;
|
|
string name = "", desc = "", cat = "", version = "", author = "", tempDir = "";
|
|
List<string> inputs = new List<string>();
|
|
foreach (string arg in args)
|
|
{
|
|
switch (arg)
|
|
{
|
|
case "-h":
|
|
case "-?":
|
|
case "--help":
|
|
Build.Help();
|
|
logger.Close();
|
|
return;
|
|
case "-b":
|
|
case "--bare":
|
|
bare = true;
|
|
break;
|
|
case "-f":
|
|
case "--files":
|
|
archivesAsFiles = true;
|
|
break;
|
|
case "-gz":
|
|
case "--gz-files":
|
|
enableGzip = true;
|
|
break;
|
|
case "-m":
|
|
case "--noMD5":
|
|
noMD5 = true;
|
|
break;
|
|
case "-o":
|
|
case "--old":
|
|
old = true;
|
|
break;
|
|
case "-ro":
|
|
case "--romba":
|
|
romba = true;
|
|
break;
|
|
case "-s":
|
|
case "--noSHA1":
|
|
noSHA1 = true;
|
|
break;
|
|
case "-sd":
|
|
case "--superdat":
|
|
superDat = true;
|
|
break;
|
|
case "-u":
|
|
case "--unzip":
|
|
forceunpack = true;
|
|
break;
|
|
default:
|
|
if (arg.StartsWith("-n=") || arg.StartsWith("--name="))
|
|
{
|
|
name = arg.Split('=')[1];
|
|
}
|
|
else if (arg.StartsWith("-d=") || arg.StartsWith("--desc="))
|
|
{
|
|
desc = arg.Split('=')[1];
|
|
}
|
|
else if (arg.StartsWith("-c=") || arg.StartsWith("--cat="))
|
|
{
|
|
cat = arg.Split('=')[1];
|
|
}
|
|
else if (arg.StartsWith("-au=") || arg.StartsWith("--author="))
|
|
{
|
|
author = arg.Split('=')[1];
|
|
}
|
|
else if (arg.StartsWith("-t=") || arg.StartsWith("--temp="))
|
|
{
|
|
tempDir = arg.Split('=')[1];
|
|
}
|
|
else if (arg.StartsWith("-v=") || arg.StartsWith("--version="))
|
|
{
|
|
version = arg.Split('=')[1];
|
|
}
|
|
else
|
|
{
|
|
inputs.Add(arg);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// If there's no inputs, show the help
|
|
if (inputs.Count == 0)
|
|
{
|
|
Build.Help();
|
|
logger.Close();
|
|
return;
|
|
}
|
|
|
|
// Output the title
|
|
Build.Start("DATFromDir");
|
|
|
|
// If any of the inputs are not valid, show the help
|
|
foreach (string input in inputs)
|
|
{
|
|
if (!File.Exists(input) && !Directory.Exists(input))
|
|
{
|
|
logger.Error(input + " is not a valid input!");
|
|
Console.WriteLine();
|
|
Build.Help();
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Create a new DATFromDir object and process the inputs
|
|
DatData datdata = new DatData
|
|
{
|
|
Name = name,
|
|
Description = desc,
|
|
Category = cat,
|
|
Version = version,
|
|
Date = DateTime.Now.ToString("yyyy-MM-dd"),
|
|
Author = author,
|
|
ForcePacking = (forceunpack ? ForcePacking.Unzip : ForcePacking.None),
|
|
OutputFormat = (old ? OutputFormat.ClrMamePro : OutputFormat.Xml),
|
|
Romba = romba,
|
|
Type = (superDat ? "SuperDAT" : ""),
|
|
Roms = new Dictionary<string, List<RomData>>(),
|
|
};
|
|
DATFromDir dfd = new DATFromDir(inputs, datdata, noMD5, noSHA1, bare, archivesAsFiles, enableGzip, tempDir, logger);
|
|
bool success = dfd.Start();
|
|
|
|
// If we failed, show the help
|
|
if (!success)
|
|
{
|
|
Console.WriteLine();
|
|
Build.Help();
|
|
}
|
|
|
|
logger.Close();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Process the file, folder, or list of some combination into a DAT file
|
|
/// </summary>
|
|
/// <returns>True if the DAT could be created, false otherwise</returns>
|
|
/// <remarks>Try to get the hashing multithreaded (either on a per-hash or per-file level)</remarks>
|
|
public bool Start()
|
|
{
|
|
// Double check to see what it needs to be named
|
|
_basePath = (_inputs.Count > 0 ? (File.Exists(_inputs[0]) ? _inputs[0] : _inputs[0] + Path.DirectorySeparatorChar) : "");
|
|
_basePath = (_basePath != "" ? Path.GetFullPath(_basePath) : "");
|
|
|
|
// If the description is defined but not the name, set the name from the description
|
|
if (String.IsNullOrEmpty(_datdata.Name) && !String.IsNullOrEmpty(_datdata.Description))
|
|
{
|
|
_datdata.Name = _datdata.Description;
|
|
}
|
|
|
|
// If the name is defined but not the description, set the description from the name
|
|
else if (!String.IsNullOrEmpty(_datdata.Name) && String.IsNullOrEmpty(_datdata.Description))
|
|
{
|
|
_datdata.Description = _datdata.Name + (_bare ? "" : " (" + _datdata.Date + ")");
|
|
}
|
|
|
|
// If neither the name or description are defined, set them from the automatic values
|
|
else if (String.IsNullOrEmpty(_datdata.Name) && String.IsNullOrEmpty(_datdata.Description))
|
|
{
|
|
if (_inputs.Count > 1)
|
|
{
|
|
_datdata.Name = Environment.CurrentDirectory.Split(Path.DirectorySeparatorChar).Last();
|
|
}
|
|
else
|
|
{
|
|
if (_basePath.EndsWith(Path.DirectorySeparatorChar.ToString()))
|
|
{
|
|
_basePath = _basePath.Substring(0, _basePath.Length - 1);
|
|
}
|
|
_datdata.Name = _basePath.Split(Path.DirectorySeparatorChar).Last();
|
|
}
|
|
|
|
// If the name is still somehow empty, populate it with defaults
|
|
_datdata.Name = (String.IsNullOrEmpty(_datdata.Name) ? "Default" : _datdata.Name);
|
|
_datdata.Description = _datdata.Name + (_bare ? "" : " (" + _datdata.Date + ")");
|
|
}
|
|
|
|
// Create and open the output file for writing
|
|
FileStream fs = File.Create(Style.CreateOutfileName(Environment.CurrentDirectory, _datdata));
|
|
StreamWriter sw = new StreamWriter(fs, Encoding.UTF8);
|
|
sw.AutoFlush = true;
|
|
|
|
// Write out the initial file header
|
|
Output.WriteHeader(sw, _datdata, _logger);
|
|
|
|
// Loop over each of the found paths, if any
|
|
string lastparent = null;
|
|
foreach (string path in _inputs)
|
|
{
|
|
// Set local paths and vars
|
|
_basePath = (File.Exists(path) ? path : path + Path.DirectorySeparatorChar);
|
|
_basePath = Path.GetFullPath(_basePath);
|
|
|
|
// This is where the main loop would go
|
|
if (File.Exists(_basePath))
|
|
{
|
|
lastparent = ProcessFile(_basePath, sw, lastparent);
|
|
}
|
|
else if (Directory.Exists(_basePath))
|
|
{
|
|
_logger.Log("Folder found: " + _basePath);
|
|
|
|
// Process the files in the base folder first
|
|
foreach (string item in Directory.EnumerateFiles(_basePath, "*", SearchOption.TopDirectoryOnly))
|
|
{
|
|
lastparent = ProcessFile(item, sw, lastparent);
|
|
}
|
|
|
|
// Then process each of the subfolders themselves
|
|
string basePathBackup = _basePath;
|
|
foreach (string item in Directory.EnumerateDirectories(_basePath))
|
|
{
|
|
if (_datdata.Type != "SuperDAT")
|
|
{
|
|
_basePath = (File.Exists(item) ? item : item + Path.DirectorySeparatorChar);
|
|
_basePath = Path.GetFullPath(_basePath);
|
|
}
|
|
|
|
bool items = false;
|
|
foreach (string subitem in Directory.EnumerateFiles(item, "*", SearchOption.AllDirectories))
|
|
{
|
|
items = true;
|
|
lastparent = ProcessFile(subitem, sw, lastparent);
|
|
}
|
|
|
|
// If there were no subitems, add a "blank" game to to the set
|
|
if (!items)
|
|
{
|
|
string actualroot = item.Remove(0, basePathBackup.Length);
|
|
RomData rom = new RomData
|
|
{
|
|
Name = "null",
|
|
Game = (_datdata.Type == "SuperDAT" ?
|
|
_datdata.Name + (actualroot != "" && !actualroot.StartsWith(Path.DirectorySeparatorChar.ToString()) ?
|
|
Path.DirectorySeparatorChar.ToString() :
|
|
"") + actualroot :
|
|
actualroot),
|
|
Size = -1,
|
|
CRC = "null",
|
|
MD5 = "null",
|
|
SHA1 = "null",
|
|
};
|
|
|
|
string key = rom.Size + "-" + rom.CRC;
|
|
if (_datdata.Roms.ContainsKey(key))
|
|
{
|
|
_datdata.Roms[key].Add(rom);
|
|
}
|
|
else
|
|
{
|
|
List<RomData> temp = new List<RomData>();
|
|
temp.Add(rom);
|
|
_datdata.Roms.Add(key, temp);
|
|
}
|
|
}
|
|
|
|
// Now scour subdirectories for empties and add those as well
|
|
foreach (string subdir in Directory.EnumerateDirectories(item, "*", SearchOption.AllDirectories))
|
|
{
|
|
if (Directory.EnumerateFiles(subdir, "*", SearchOption.AllDirectories).Count() == 0)
|
|
{
|
|
string actualroot = subdir.Remove(0, basePathBackup.Length);
|
|
RomData rom = new RomData
|
|
{
|
|
Name = "null",
|
|
Game = (_datdata.Type == "SuperDAT" ?
|
|
_datdata.Name + (actualroot != "" && !actualroot.StartsWith(Path.DirectorySeparatorChar.ToString()) ?
|
|
Path.DirectorySeparatorChar.ToString() :
|
|
"") + actualroot :
|
|
actualroot),
|
|
Size = -1,
|
|
CRC = "null",
|
|
MD5 = "null",
|
|
SHA1 = "null",
|
|
};
|
|
|
|
string key = rom.Size + "-" + rom.CRC;
|
|
if (_datdata.Roms.ContainsKey(key))
|
|
{
|
|
_datdata.Roms[key].Add(rom);
|
|
}
|
|
else
|
|
{
|
|
List<RomData> temp = new List<RomData>();
|
|
temp.Add(rom);
|
|
_datdata.Roms.Add(key, temp);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
_basePath = basePathBackup;
|
|
}
|
|
// If this somehow skips past the original sensors
|
|
else
|
|
{
|
|
_logger.Error(path + " is not a valid input!");
|
|
}
|
|
}
|
|
|
|
// Now output any empties to the stream
|
|
foreach (List<RomData> roms in _datdata.Roms.Values)
|
|
{
|
|
for (int i = 0; i < roms.Count; i++)
|
|
{
|
|
RomData rom = roms[i];
|
|
|
|
// If we're in a mode that doesn't allow for actual empty folders, add the blank info
|
|
if (_datdata.OutputFormat != OutputFormat.SabreDat && _datdata.OutputFormat != OutputFormat.MissFile)
|
|
{
|
|
rom.Type = "rom";
|
|
rom.Name = "-";
|
|
rom.Size = Constants.SizeZero;
|
|
rom.CRC = Constants.CRCZero;
|
|
rom.MD5 = Constants.MD5Zero;
|
|
rom.SHA1 = Constants.SHA1Zero;
|
|
}
|
|
|
|
// If we have a different game and we're not at the start of the list, output the end of last item
|
|
int last = 0;
|
|
if (lastparent != null && lastparent.ToLowerInvariant() != rom.Game.ToLowerInvariant())
|
|
{
|
|
Output.WriteEndGame(sw, rom, new List<string>(), new List<string>(), lastparent, _datdata, 0, out last, _logger);
|
|
}
|
|
|
|
// If we have a new game, output the beginning of the new item
|
|
if (lastparent == null || lastparent.ToLowerInvariant() != rom.Game.ToLowerInvariant())
|
|
{
|
|
Output.WriteStartGame(sw, rom, new List<string>(), lastparent, _datdata, 0, last, _logger);
|
|
}
|
|
|
|
// Write out the rom data
|
|
if (_datdata.OutputFormat != OutputFormat.SabreDat && _datdata.OutputFormat != OutputFormat.MissFile)
|
|
{
|
|
Output.WriteRomData(sw, rom, lastparent, _datdata, 0, _logger);
|
|
}
|
|
|
|
lastparent = rom.Game;
|
|
}
|
|
}
|
|
|
|
// If we had roms but not blanks (and not in Romba mode), create an artifical rom for the purposes of outputting
|
|
if (lastparent != null && _datdata.Roms.Count == 0 && !_datdata.Romba)
|
|
{
|
|
_datdata.Roms.Add("temp", new List<RomData>());
|
|
}
|
|
|
|
// Now write the final piece and close the output stream
|
|
Output.WriteFooter(sw, _datdata, 0, _logger);
|
|
sw.Close();
|
|
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Check a given file for hashes, based on current settings
|
|
/// </summary>
|
|
/// <param name="item">Filename of the item to be checked</param>
|
|
/// <param name="sw">StreamWriter representing the output file</param>
|
|
/// <param name="lastparent">Name of the last parent rom to make sure that everything is grouped as well as possible</param>
|
|
/// <returns>New parent to be used</returns>
|
|
private string ProcessFile(string item, StreamWriter sw, string lastparent)
|
|
{
|
|
// Define the temporary directory
|
|
string tempdir = (String.IsNullOrEmpty(_tempDir) ? Environment.CurrentDirectory : _tempDir);
|
|
tempdir += (tempdir.EndsWith(Path.DirectorySeparatorChar.ToString()) ? "" : Path.DirectorySeparatorChar.ToString());
|
|
tempdir += "temp" + DateTime.Now.ToString("yyyyMMddHHmmss") + Path.DirectorySeparatorChar;
|
|
|
|
// Special case for if we are in Romba mode (all names are supposed to be SHA-1 hashes)
|
|
if (_datdata.Romba)
|
|
{
|
|
int neededHeaderSize = 32;
|
|
string datum = Path.GetFileName(item).ToLowerInvariant();
|
|
long filesize = new FileInfo(item).Length;
|
|
|
|
// 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 + "'");
|
|
return "";
|
|
}
|
|
|
|
// Check if the file is at least the minimum length
|
|
if (filesize < neededHeaderSize)
|
|
{
|
|
_logger.Warning("Possibly corrupt file '" + item + "' with size " + Style.GetBytesReadable(filesize));
|
|
return "";
|
|
}
|
|
|
|
// Get the Romba-specific header data
|
|
byte[] header;
|
|
byte[] footer;
|
|
using (FileStream itemstream = File.OpenRead(item))
|
|
{
|
|
using (BinaryReader br = new BinaryReader(itemstream))
|
|
{
|
|
header = br.ReadBytes(neededHeaderSize);
|
|
br.BaseStream.Seek(-4, SeekOrigin.End);
|
|
footer = br.ReadBytes(4);
|
|
}
|
|
}
|
|
|
|
// Now convert the data and get the right positions
|
|
string headerstring = BitConverter.ToString(header).Replace("-", string.Empty);
|
|
string gzmd5 = headerstring.Substring(24, 32);
|
|
string gzcrc = headerstring.Substring(56, 8);
|
|
string gzsize = BitConverter.ToString(footer.Reverse().ToArray()).Replace("-", string.Empty);
|
|
long extractedsize = Convert.ToInt64(gzsize, 16);
|
|
|
|
// Only try to add if the file size is greater than 2.5 GiB
|
|
if (filesize >= (2.5 * 1024 * 1024 * 1024))
|
|
{
|
|
// ISIZE is mod 2^32, so we add that if the ISIZE is smaller than the filesize and header
|
|
if (extractedsize < (filesize - neededHeaderSize))
|
|
{
|
|
_logger.Log("Filename: '" + Path.GetFullPath(item) + "'\nExtracted file size: " +
|
|
extractedsize + ", " + Style.GetBytesReadable(extractedsize) + "\nArchive file size: " + filesize + ", " + Style.GetBytesReadable(filesize));
|
|
}
|
|
while (extractedsize < (filesize - neededHeaderSize))
|
|
{
|
|
extractedsize += (long)Math.Pow(2, 32);
|
|
}
|
|
_logger.Log("Final file size: " + extractedsize + "\nExtracted CRC: " + gzcrc +
|
|
"\nExtracted MD5: " + gzmd5 + "\nSHA-1: " + Path.GetFileNameWithoutExtension(item));
|
|
}
|
|
|
|
RomData rom = new RomData
|
|
{
|
|
Type = "rom",
|
|
Game = Path.GetFileNameWithoutExtension(item),
|
|
Name = Path.GetFileNameWithoutExtension(item),
|
|
Size = extractedsize,
|
|
CRC = gzcrc,
|
|
MD5 = gzmd5,
|
|
SHA1 = Path.GetFileNameWithoutExtension(item),
|
|
};
|
|
|
|
int last = 0;
|
|
Output.WriteStartGame(sw, rom, new List<string>(), "", _datdata, 0, 0, _logger);
|
|
Output.WriteRomData(sw, rom, "", _datdata, 0, _logger);
|
|
Output.WriteEndGame(sw, rom, new List<string>(), new List<string>(), "", _datdata, 0, out last, _logger);
|
|
|
|
_logger.User("File added: " + Path.GetFileNameWithoutExtension(item) + Environment.NewLine);
|
|
return "";
|
|
}
|
|
|
|
// Create the temporary output directory
|
|
bool encounteredErrors = true;
|
|
if (!_archivesAsFiles)
|
|
{
|
|
IArchive archive = null;
|
|
try
|
|
{
|
|
archive = ArchiveFactory.Open(item);
|
|
ArchiveType at = archive.Type;
|
|
_logger.Log("Found archive of type: " + at);
|
|
|
|
if (at == ArchiveType.Zip || at == ArchiveType.SevenZip || at == ArchiveType.Rar)
|
|
{
|
|
// Create the temp directory
|
|
DirectoryInfo di = Directory.CreateDirectory(tempdir);
|
|
|
|
// Extract all files to the temp directory
|
|
IReader reader = archive.ExtractAllEntries();
|
|
reader.WriteAllToDirectory(tempdir, ExtractOptions.ExtractFullPath);
|
|
encounteredErrors = false;
|
|
}
|
|
else if (at == ArchiveType.GZip && _enableGzip)
|
|
{
|
|
// Close the original archive handle
|
|
archive.Dispose();
|
|
|
|
// Create the temp directory
|
|
DirectoryInfo di = Directory.CreateDirectory(tempdir);
|
|
|
|
using (FileStream itemstream = File.OpenRead(item))
|
|
{
|
|
using (FileStream outstream = File.Create(tempdir + Path.GetFileNameWithoutExtension(item)))
|
|
{
|
|
using (GZipStream gz = new GZipStream(itemstream, CompressionMode.Decompress))
|
|
{
|
|
gz.CopyTo(outstream);
|
|
}
|
|
}
|
|
}
|
|
encounteredErrors = false;
|
|
}
|
|
archive.Dispose();
|
|
}
|
|
catch (InvalidOperationException)
|
|
{
|
|
encounteredErrors = true;
|
|
if (archive != null)
|
|
{
|
|
archive.Dispose();
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.Error(ex.ToString());
|
|
encounteredErrors = true;
|
|
if (archive != null)
|
|
{
|
|
archive.Dispose();
|
|
}
|
|
}
|
|
}
|
|
|
|
// Get a list of files including size and hashes
|
|
Crc32 crc = new Crc32();
|
|
MD5 md5 = MD5.Create();
|
|
SHA1 sha1 = SHA1.Create();
|
|
|
|
// If the file was an archive and was extracted successfully, check it
|
|
if (!encounteredErrors)
|
|
{
|
|
int last = 0;
|
|
|
|
_logger.Log(Path.GetFileName(item) + " treated like an archive");
|
|
foreach (string entry in Directory.EnumerateFiles(tempdir, "*", SearchOption.AllDirectories))
|
|
{
|
|
_logger.Log("Found file: " + entry);
|
|
string fileCRC = string.Empty;
|
|
string fileMD5 = string.Empty;
|
|
string fileSHA1 = string.Empty;
|
|
|
|
try
|
|
{
|
|
using (FileStream fs = File.Open(entry, FileMode.Open))
|
|
{
|
|
foreach (byte b in crc.ComputeHash(fs))
|
|
{
|
|
fileCRC += b.ToString("x2").ToLower();
|
|
}
|
|
}
|
|
if (!_noMD5)
|
|
{
|
|
using (FileStream fs = File.Open(entry, FileMode.Open))
|
|
{
|
|
fileMD5 = BitConverter.ToString(md5.ComputeHash(fs)).Replace("-", "");
|
|
}
|
|
}
|
|
if (!_noSHA1)
|
|
{
|
|
using (FileStream fs = File.Open(entry, FileMode.Open))
|
|
{
|
|
fileSHA1 = BitConverter.ToString(sha1.ComputeHash(fs)).Replace("-", "");
|
|
}
|
|
}
|
|
}
|
|
catch (IOException)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
string actualroot = "";
|
|
string actualitem = "";
|
|
|
|
actualitem = entry.Remove(0, tempdir.Length);
|
|
|
|
// If we're in SuperDAT mode, make sure the added item is by itself
|
|
if (_datdata.Type == "SuperDAT")
|
|
{
|
|
actualroot = Path.GetDirectoryName(item.Remove(0, _basePath.Length));
|
|
actualroot = (actualroot == "" ? _basePath.Split(Path.DirectorySeparatorChar).Last() : actualroot);
|
|
actualroot += Path.DirectorySeparatorChar + Path.GetFileNameWithoutExtension(item) + Path.DirectorySeparatorChar + Path.GetDirectoryName(actualitem);
|
|
actualroot = actualroot.TrimEnd(Path.DirectorySeparatorChar);
|
|
actualitem = Path.GetFileName(actualitem);
|
|
}
|
|
// Otherwise, set the correct root and such
|
|
else
|
|
{
|
|
actualroot = Path.GetFileNameWithoutExtension(item);
|
|
actualroot = actualroot.TrimEnd(Path.DirectorySeparatorChar);
|
|
}
|
|
|
|
RomData rom = new RomData
|
|
{
|
|
Type = "rom",
|
|
Game = (_datdata.Type == "SuperDAT" ?
|
|
_datdata.Name + (actualroot != "" && !actualroot.StartsWith(Path.DirectorySeparatorChar.ToString()) ?
|
|
Path.DirectorySeparatorChar.ToString() :
|
|
"") + actualroot :
|
|
actualroot),
|
|
Name = actualitem,
|
|
Size = (new FileInfo(entry)).Length,
|
|
CRC = fileCRC,
|
|
MD5 = fileMD5,
|
|
SHA1 = fileSHA1,
|
|
};
|
|
|
|
// If we have a different game and we're not at the start of the list, output the end of last item
|
|
if (lastparent != null && lastparent.ToLowerInvariant() != rom.Game.ToLowerInvariant())
|
|
{
|
|
Output.WriteEndGame(sw, rom, new List<string>(), new List<string>(), lastparent, _datdata, 0, out last, _logger);
|
|
}
|
|
// If we have a different game and we're not at the start of the list, output the end of the last item
|
|
else if (lastparent != null && lastparent.ToLowerInvariant() != rom.Game.ToLowerInvariant())
|
|
{
|
|
Output.WriteEndGame(sw, rom, new List<string>(), new List<string>(), lastparent, _datdata, 0, out last, _logger);
|
|
}
|
|
|
|
// If we have a new game, output the beginning of the new item
|
|
if (lastparent == null || lastparent.ToLowerInvariant() != rom.Game.ToLowerInvariant())
|
|
{
|
|
Output.WriteStartGame(sw, rom, new List<string>(), lastparent, _datdata, 0, last, _logger);
|
|
}
|
|
|
|
// Write out the rom data
|
|
Output.WriteRomData(sw, rom, lastparent, _datdata, 0, _logger);
|
|
_logger.User("File added: " + entry + Environment.NewLine);
|
|
|
|
lastparent = rom.Game;
|
|
}
|
|
|
|
// Delete the temp directory
|
|
if (Directory.Exists(tempdir))
|
|
{
|
|
Directory.Delete(tempdir, true);
|
|
}
|
|
}
|
|
// Otherwise, just get the info on the file itself
|
|
else if (!Directory.Exists(item) && File.Exists(item))
|
|
{
|
|
_logger.Log(Path.GetFileName(item) + " treated like a file");
|
|
|
|
string fileCRC = String.Empty;
|
|
string fileMD5 = String.Empty;
|
|
string fileSHA1 = String.Empty;
|
|
|
|
try
|
|
{
|
|
using (FileStream fs = File.Open(item, FileMode.Open))
|
|
{
|
|
foreach (byte b in crc.ComputeHash(fs))
|
|
{
|
|
fileCRC += b.ToString("x2").ToLower();
|
|
}
|
|
}
|
|
if (!_noMD5)
|
|
{
|
|
using (FileStream fs = File.Open(item, FileMode.Open))
|
|
{
|
|
fileMD5 = BitConverter.ToString(md5.ComputeHash(fs)).Replace("-", "");
|
|
}
|
|
}
|
|
if (!_noSHA1)
|
|
{
|
|
using (FileStream fs = File.Open(item, FileMode.Open))
|
|
{
|
|
fileSHA1 = BitConverter.ToString(sha1.ComputeHash(fs)).Replace("-", "");
|
|
}
|
|
}
|
|
|
|
if (_basePath.EndsWith(Path.DirectorySeparatorChar.ToString()))
|
|
{
|
|
_basePath = _basePath.Substring(0, _basePath.Length - 1);
|
|
}
|
|
|
|
string actualroot = (item == _basePath ? item.Split(Path.DirectorySeparatorChar).Last() : item.Remove(0, _basePath.Length).Split(Path.DirectorySeparatorChar)[0]);
|
|
actualroot = (actualroot == "" && _datdata.Type != "SuperDAT" ? _basePath.Split(Path.DirectorySeparatorChar).Last() : actualroot);
|
|
string actualitem = (item == _basePath ? item : item.Remove(0, _basePath.Length + 1));
|
|
|
|
// If we're in SuperDAT mode, make sure the added item is by itself
|
|
if (_datdata.Type == "SuperDAT")
|
|
{
|
|
actualroot += (actualroot != "" ? Path.DirectorySeparatorChar.ToString() : "") + Path.GetDirectoryName(actualitem);
|
|
actualroot = actualroot.TrimEnd(Path.DirectorySeparatorChar);
|
|
actualitem = Path.GetFileName(actualitem);
|
|
}
|
|
|
|
// Drag and drop is funny
|
|
if (actualitem == Path.GetFullPath(actualitem))
|
|
{
|
|
actualitem = Path.GetFileName(actualitem);
|
|
}
|
|
|
|
_logger.Log("Actual item added: " + actualitem);
|
|
|
|
RomData rom = new RomData
|
|
{
|
|
Type = "rom",
|
|
Game = (_datdata.Type == "SuperDAT" ?
|
|
_datdata.Name + (actualroot != "" && !actualroot.StartsWith(Path.DirectorySeparatorChar.ToString()) ?
|
|
Path.DirectorySeparatorChar.ToString() :
|
|
"") + actualroot :
|
|
actualroot),
|
|
Name = actualitem,
|
|
Size = (new FileInfo(item)).Length,
|
|
CRC = fileCRC,
|
|
MD5 = fileMD5,
|
|
SHA1 = fileSHA1,
|
|
};
|
|
|
|
// If we have a different game and we're not at the start of the list, output the end of last item
|
|
int last = 0;
|
|
if (lastparent != null && lastparent.ToLowerInvariant() != rom.Game.ToLowerInvariant())
|
|
{
|
|
Output.WriteEndGame(sw, rom, new List<string>(), new List<string>(), lastparent, _datdata, 0, out last, _logger);
|
|
}
|
|
|
|
// If we have a new game, output the beginning of the new item
|
|
if (lastparent == null || lastparent.ToLowerInvariant() != rom.Game.ToLowerInvariant())
|
|
{
|
|
Output.WriteStartGame(sw, rom, new List<string>(), lastparent, _datdata, 0, last, _logger);
|
|
}
|
|
|
|
// Write out the rom data
|
|
Output.WriteRomData(sw, rom, lastparent, _datdata, 0, _logger);
|
|
_logger.User("File added: " + actualitem + Environment.NewLine);
|
|
|
|
return rom.Game;
|
|
}
|
|
catch (IOException ex)
|
|
{
|
|
_logger.Error(ex.ToString());
|
|
return null;
|
|
}
|
|
}
|
|
return lastparent;
|
|
}
|
|
}
|
|
}
|