mirror of
https://github.com/claunia/SabreTools.git
synced 2025-12-16 19:14:27 +00:00
[SabreTools.Library] Update folder name
This commit is contained in:
3255
SabreTools.Library/Tools/ArchiveTools.cs
Normal file
3255
SabreTools.Library/Tools/ArchiveTools.cs
Normal file
File diff suppressed because it is too large
Load Diff
202
SabreTools.Library/Tools/DatabaseTools.cs
Normal file
202
SabreTools.Library/Tools/DatabaseTools.cs
Normal file
@@ -0,0 +1,202 @@
|
||||
using Mono.Data.Sqlite;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using SabreTools.Helper.Data;
|
||||
|
||||
#if MONO
|
||||
using System.IO;
|
||||
#else
|
||||
using Alphaleonis.Win32.Filesystem;
|
||||
#endif
|
||||
|
||||
namespace SabreTools.Helper.Tools
|
||||
{
|
||||
/// <summary>
|
||||
/// All general database operations
|
||||
/// </summary>
|
||||
public static class DatabaseTools
|
||||
{
|
||||
/// <summary>
|
||||
/// Add a header to the database
|
||||
/// </summary>
|
||||
/// <param name="header">String representing the header bytes</param>
|
||||
/// <param name="SHA1">SHA-1 of the deheadered file</param>
|
||||
/// <param name="type">Name of the source skipper file</param>
|
||||
public static void AddHeaderToDatabase(string header, string SHA1, string source)
|
||||
{
|
||||
bool exists = false;
|
||||
|
||||
// Ensure the database exists
|
||||
EnsureDatabase(Constants.HeadererDbSchema, Constants.HeadererFileName, Constants.HeadererConnectionString);
|
||||
|
||||
// Open the database connection
|
||||
SqliteConnection dbc = new SqliteConnection(Constants.HeadererConnectionString);
|
||||
dbc.Open();
|
||||
|
||||
string query = @"SELECT * FROM data WHERE sha1='" + SHA1 + "' AND header='" + header + "'";
|
||||
SqliteCommand slc = new SqliteCommand(query, dbc);
|
||||
SqliteDataReader sldr = slc.ExecuteReader();
|
||||
exists = sldr.HasRows;
|
||||
|
||||
if (!exists)
|
||||
{
|
||||
query = @"INSERT INTO data (sha1, header, type) VALUES ('" +
|
||||
SHA1 + "', " +
|
||||
"'" + header + "', " +
|
||||
"'" + source + "')";
|
||||
slc = new SqliteCommand(query, dbc);
|
||||
Globals.Logger.Verbose("Result of inserting header: " + slc.ExecuteNonQuery());
|
||||
}
|
||||
|
||||
// Dispose of database objects
|
||||
slc.Dispose();
|
||||
sldr.Dispose();
|
||||
dbc.Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensure that the databse exists and has the proper schema
|
||||
/// </summary>
|
||||
/// <param name="type">Schema type to use</param>
|
||||
/// <param name="db">Name of the databse</param>
|
||||
/// <param name="connectionString">Connection string for SQLite</param>
|
||||
public static void EnsureDatabase(string type, string db, string connectionString)
|
||||
{
|
||||
// Set the type to lowercase
|
||||
type = type.ToLowerInvariant();
|
||||
|
||||
// Make sure the file exists
|
||||
if (!File.Exists(db))
|
||||
{
|
||||
SqliteConnection.CreateFile(db);
|
||||
}
|
||||
|
||||
// Open the database connection
|
||||
SqliteConnection dbc = new SqliteConnection(connectionString);
|
||||
dbc.Open();
|
||||
|
||||
// Make sure the database has the correct schema
|
||||
try
|
||||
{
|
||||
if (type == "rombasharp")
|
||||
{
|
||||
string query = @"
|
||||
CREATE TABLE IF NOT EXISTS crc (
|
||||
'crc' TEXT NOT NULL,
|
||||
PRIMARY KEY (crc)
|
||||
)";
|
||||
SqliteCommand slc = new SqliteCommand(query, dbc);
|
||||
slc.ExecuteNonQuery();
|
||||
|
||||
query = @"
|
||||
CREATE TABLE IF NOT EXISTS md5 (
|
||||
'md5' TEXT NOT NULL,
|
||||
PRIMARY KEY (md5)
|
||||
)";
|
||||
slc = new SqliteCommand(query, dbc);
|
||||
slc.ExecuteNonQuery();
|
||||
|
||||
query = @"
|
||||
CREATE TABLE IF NOT EXISTS sha1 (
|
||||
'sha1' TEXT NOT NULL,
|
||||
'depot' TEXT,
|
||||
PRIMARY KEY (sha1)
|
||||
)";
|
||||
slc = new SqliteCommand(query, dbc);
|
||||
slc.ExecuteNonQuery();
|
||||
|
||||
query = @"
|
||||
CREATE TABLE IF NOT EXISTS crcsha1 (
|
||||
'crc' TEXT NOT NULL,
|
||||
'sha1' TEXT NOT NULL,
|
||||
PRIMARY KEY (crc, sha1)
|
||||
)";
|
||||
slc = new SqliteCommand(query, dbc);
|
||||
slc.ExecuteNonQuery();
|
||||
|
||||
query = @"
|
||||
CREATE TABLE IF NOT EXISTS md5sha1 (
|
||||
'md5' TEXT NOT NULL,
|
||||
'sha1' TEXT NOT NULL,
|
||||
PRIMARY KEY (md5, sha1)
|
||||
)";
|
||||
slc = new SqliteCommand(query, dbc);
|
||||
slc.ExecuteNonQuery();
|
||||
|
||||
query = @"
|
||||
CREATE TABLE IF NOT EXISTS dat (
|
||||
'hash' TEXT NOT NULL,
|
||||
PRIMARY KEY (hash)
|
||||
)";
|
||||
slc = new SqliteCommand(query, dbc);
|
||||
slc.ExecuteNonQuery();
|
||||
slc.Dispose();
|
||||
}
|
||||
else if (type == "headerer")
|
||||
{
|
||||
string query = @"
|
||||
CREATE TABLE IF NOT EXISTS data (
|
||||
'sha1' TEXT NOT NULL,
|
||||
'header' TEXT NOT NULL,
|
||||
'type' TEXT NOT NULL,
|
||||
PRIMARY KEY (sha1, header, type)
|
||||
)";
|
||||
SqliteCommand slc = new SqliteCommand(query, dbc);
|
||||
slc.ExecuteNonQuery();
|
||||
slc.Dispose();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine(ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
dbc.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve headers from the database
|
||||
/// </summary>
|
||||
/// <param name="SHA1">SHA-1 of the deheadered file</param>
|
||||
/// <returns>List of strings representing the headers to add</returns>
|
||||
public static List<string> RetrieveHeadersFromDatabase(string SHA1)
|
||||
{
|
||||
// Ensure the database exists
|
||||
EnsureDatabase(Constants.HeadererDbSchema, Constants.HeadererFileName, Constants.HeadererConnectionString);
|
||||
|
||||
// Open the database connection
|
||||
SqliteConnection dbc = new SqliteConnection(Constants.HeadererConnectionString);
|
||||
dbc.Open();
|
||||
|
||||
// Create the output list of headers
|
||||
List<string> headers = new List<string>();
|
||||
|
||||
string query = @"SELECT header, type FROM data WHERE sha1='" + SHA1 + "'";
|
||||
SqliteCommand slc = new SqliteCommand(query, dbc);
|
||||
SqliteDataReader sldr = slc.ExecuteReader();
|
||||
|
||||
if (sldr.HasRows)
|
||||
{
|
||||
while (sldr.Read())
|
||||
{
|
||||
Globals.Logger.Verbose("Found match with rom type " + sldr.GetString(1));
|
||||
headers.Add(sldr.GetString(0));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Globals.Logger.Warning("No matching header could be found!");
|
||||
}
|
||||
|
||||
// Dispose of database objects
|
||||
slc.Dispose();
|
||||
sldr.Dispose();
|
||||
dbc.Dispose();
|
||||
|
||||
return headers;
|
||||
}
|
||||
}
|
||||
}
|
||||
899
SabreTools.Library/Tools/FileTools.cs
Normal file
899
SabreTools.Library/Tools/FileTools.cs
Normal file
@@ -0,0 +1,899 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Xml;
|
||||
using System.Xml.Schema;
|
||||
|
||||
using SabreTools.Helper.Data;
|
||||
using SabreTools.Helper.Dats;
|
||||
using SabreTools.Helper.External;
|
||||
using SabreTools.Helper.Skippers;
|
||||
|
||||
#if MONO
|
||||
using System.IO;
|
||||
#else
|
||||
using Alphaleonis.Win32.Filesystem;
|
||||
|
||||
using BinaryReader = System.IO.BinaryReader;
|
||||
using BinaryWriter = System.IO.BinaryWriter;
|
||||
using FileAccess = System.IO.FileAccess;
|
||||
using FileMode = System.IO.FileMode;
|
||||
using FileShare = System.IO.FileShare;
|
||||
using FileStream = System.IO.FileStream;
|
||||
using IOException = System.IO.IOException;
|
||||
using MemoryStream = System.IO.MemoryStream;
|
||||
using PathTooLongException = System.IO.PathTooLongException;
|
||||
using SearchOption = System.IO.SearchOption;
|
||||
using SeekOrigin = System.IO.SeekOrigin;
|
||||
using Stream = System.IO.Stream;
|
||||
using StreamReader = System.IO.StreamReader;
|
||||
#endif
|
||||
using NaturalSort;
|
||||
using OCRC;
|
||||
|
||||
namespace SabreTools.Helper.Tools
|
||||
{
|
||||
public static class FileTools
|
||||
{
|
||||
#region File Information
|
||||
|
||||
/// <summary>
|
||||
/// Get what type of DAT the input file is
|
||||
/// </summary>
|
||||
/// <param name="filename">Name of the file to be parsed</param>
|
||||
/// <returns>The DatFormat corresponding to the DAT</returns>
|
||||
/// <remarks>There is currently no differentiation between XML and SabreDAT here</remarks>
|
||||
public static DatFormat GetDatFormat(string filename)
|
||||
{
|
||||
// Limit the output formats based on extension
|
||||
string ext = Path.GetExtension(filename).ToLowerInvariant();
|
||||
if (ext.StartsWith("."))
|
||||
{
|
||||
ext = ext.Substring(1);
|
||||
}
|
||||
if (ext != "csv" && ext != "dat" && ext != "md5" && ext != "sfv" && ext != "sha1"
|
||||
&& ext != "sha384" && ext != "sha512" && ext != "tsv" && ext != "txt" && ext != "xml")
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Read the input file, if possible
|
||||
Globals.Logger.Verbose("Attempting to read file to get format: \"" + filename + "\"");
|
||||
|
||||
// Check if file exists
|
||||
if (!File.Exists(filename))
|
||||
{
|
||||
Globals.Logger.Warning("File '" + filename + "' could not read from!");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Some formats only require the extension to know
|
||||
if (ext == "md5")
|
||||
{
|
||||
return DatFormat.RedumpMD5;
|
||||
}
|
||||
if (ext == "sfv")
|
||||
{
|
||||
return DatFormat.RedumpSFV;
|
||||
}
|
||||
if (ext == "sha1")
|
||||
{
|
||||
return DatFormat.RedumpSHA1;
|
||||
}
|
||||
if (ext == "sha256")
|
||||
{
|
||||
return DatFormat.RedumpSHA256;
|
||||
}
|
||||
if (ext == "sha384")
|
||||
{
|
||||
return DatFormat.RedumpSHA384;
|
||||
}
|
||||
if (ext == "sha512")
|
||||
{
|
||||
return DatFormat.RedumpSHA512;
|
||||
}
|
||||
|
||||
// For everything else, we need to read it
|
||||
try
|
||||
{
|
||||
// Get the first two lines to check
|
||||
StreamReader sr = File.OpenText(filename);
|
||||
string first = sr.ReadLine().ToLowerInvariant();
|
||||
string second = sr.ReadLine().ToLowerInvariant();
|
||||
sr.Dispose();
|
||||
|
||||
// If we have an XML-based DAT
|
||||
if (first.Contains("<?xml") && first.Contains("?>"))
|
||||
{
|
||||
if (second.StartsWith("<!doctype datafile"))
|
||||
{
|
||||
return DatFormat.Logiqx;
|
||||
}
|
||||
else if (second.StartsWith("<!doctype softwarelist"))
|
||||
{
|
||||
return DatFormat.SoftwareList;
|
||||
}
|
||||
else if (second.StartsWith("<!doctype sabredat"))
|
||||
{
|
||||
return DatFormat.SabreDat;
|
||||
}
|
||||
else if (second.StartsWith("<dat") && !second.StartsWith("<datafile"))
|
||||
{
|
||||
return DatFormat.OfflineList;
|
||||
}
|
||||
// Older and non-compliant DATs
|
||||
else
|
||||
{
|
||||
return DatFormat.Logiqx;
|
||||
}
|
||||
}
|
||||
|
||||
// If we have an INI-based DAT
|
||||
else if (first.Contains("[") && first.Contains("]"))
|
||||
{
|
||||
return DatFormat.RomCenter;
|
||||
}
|
||||
|
||||
// If we have a CMP-based DAT
|
||||
else if (first.Contains("clrmamepro"))
|
||||
{
|
||||
return DatFormat.ClrMamePro;
|
||||
}
|
||||
else if (first.Contains("romvault"))
|
||||
{
|
||||
return DatFormat.ClrMamePro;
|
||||
}
|
||||
else if (first.Contains("doscenter"))
|
||||
{
|
||||
return DatFormat.DOSCenter;
|
||||
}
|
||||
else if (first.Contains("#Name;Title;Emulator;CloneOf;Year;Manufacturer;Category;Players;Rotation;Control;Status;DisplayCount;DisplayType;AltRomname;AltTitle;Extra"))
|
||||
{
|
||||
return DatFormat.AttractMode;
|
||||
}
|
||||
else if (first.Contains("\"File Name\",\"Internal Name\",\"Description\""))
|
||||
{
|
||||
return DatFormat.CSV;
|
||||
}
|
||||
else if (first.Contains("\"File Name\"\t\"Internal Name\"\t\"Description\""))
|
||||
{
|
||||
return DatFormat.TSV;
|
||||
}
|
||||
else
|
||||
{
|
||||
return DatFormat.ClrMamePro;
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get all empty folders within a root folder
|
||||
/// </summary>
|
||||
/// <param name="root">Root directory to parse</param>
|
||||
/// <returns>IEumerable containing all directories that are empty, an empty enumerable if the root is empty, null otherwise</returns>
|
||||
public static IEnumerable<string> GetEmptyDirectories(string root)
|
||||
{
|
||||
// Check if the root exists first
|
||||
if (!Directory.Exists(root))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// If it does and it is empty, return a blank enumerable
|
||||
if (Directory.EnumerateFileSystemEntries(root, "*", SearchOption.AllDirectories).Count() == 0)
|
||||
{
|
||||
return new List<string>();
|
||||
}
|
||||
|
||||
// Otherwise, get the complete list
|
||||
return Directory.EnumerateDirectories(root, "*", SearchOption.AllDirectories)
|
||||
.Where(dir => Directory.EnumerateFileSystemEntries(dir, "*", SearchOption.AllDirectories).Count() == 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve file information for a single file
|
||||
/// </summary>
|
||||
/// <param name="input">Filename to get information from</param>
|
||||
/// <param name="omitFromScan">Hash flag saying what hashes should not be calculated (defaults to none)</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>
|
||||
/// <param name="header">Populated string representing the name of the skipper to use, a blank string to use the first available checker, null otherwise</param>
|
||||
/// <returns>Populated RomData object if success, empty one on error</returns>
|
||||
public static Rom GetFileInfo(string input, Hash omitFromScan = 0x0,
|
||||
long offset = 0, bool date = false, string header = null)
|
||||
{
|
||||
// Add safeguard if file doesn't exist
|
||||
if (!File.Exists(input))
|
||||
{
|
||||
return new Rom();
|
||||
}
|
||||
|
||||
// Get the information from the file stream
|
||||
Rom rom = new Rom();
|
||||
if (header != null)
|
||||
{
|
||||
SkipperRule rule = Skipper.GetMatchingRule(input, Path.GetFileNameWithoutExtension(header));
|
||||
|
||||
// If there's a match, get the new information from the stream
|
||||
if (rule.Tests != null && rule.Tests.Count != 0)
|
||||
{
|
||||
// Create the input and output streams
|
||||
MemoryStream outputStream = new MemoryStream();
|
||||
FileStream inputStream = FileTools.TryOpenRead(input);
|
||||
|
||||
// Transform the stream and get the information from it
|
||||
rule.TransformStream(inputStream, outputStream, keepReadOpen: false, keepWriteOpen: true);
|
||||
rom = GetStreamInfo(outputStream, outputStream.Length, omitFromScan: omitFromScan, keepReadOpen: false);
|
||||
|
||||
// Dispose of the streams
|
||||
outputStream.Dispose();
|
||||
inputStream.Dispose();
|
||||
}
|
||||
// Otherwise, just get the info
|
||||
else
|
||||
{
|
||||
long length = new FileInfo(input).Length;
|
||||
rom = GetStreamInfo(TryOpenRead(input), length, omitFromScan, offset, false);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
long length = new FileInfo(input).Length;
|
||||
rom = GetStreamInfo(TryOpenRead(input), length, omitFromScan, offset, false);
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve a list of files from a directory recursively in proper order
|
||||
/// </summary>
|
||||
/// <param name="directory">Directory to parse</param>
|
||||
/// <param name="infiles">List representing existing files</param>
|
||||
/// <returns>List with all new files</returns>
|
||||
public static List<string> RetrieveFiles(string directory, List<string> infiles)
|
||||
{
|
||||
// Take care of the files in the top directory
|
||||
List<string> toadd = Directory.EnumerateFiles(directory, "*", SearchOption.TopDirectoryOnly).ToList();
|
||||
toadd.Sort(new NaturalComparer());
|
||||
infiles.AddRange(toadd);
|
||||
|
||||
// Then recurse through and add from the directories
|
||||
List<string> dirs = Directory.EnumerateDirectories(directory, "*", SearchOption.TopDirectoryOnly).ToList();
|
||||
dirs = Style.OrderByAlphaNumeric(dirs, s => s).ToList();
|
||||
foreach (string dir in dirs)
|
||||
{
|
||||
infiles = RetrieveFiles(dir, infiles);
|
||||
}
|
||||
|
||||
// Return the new list
|
||||
return infiles;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region File Manipulation
|
||||
|
||||
/// <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;
|
||||
}
|
||||
|
||||
FileStream fsr = TryOpenRead(input);
|
||||
FileStream fsw = TryOpenWrite(output);
|
||||
|
||||
AppendBytesToStream(fsr, fsw, bytesToAddToHead, bytesToAddToTail);
|
||||
|
||||
fsr.Dispose();
|
||||
fsw.Dispose();
|
||||
}
|
||||
|
||||
/// <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))
|
||||
{
|
||||
TryDeleteFile(file);
|
||||
}
|
||||
foreach (string dir in Directory.EnumerateDirectories(dirname, "*", SearchOption.TopDirectoryOnly))
|
||||
{
|
||||
TryDeleteDirectory(dir);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Detect header skipper compliance and create an output file
|
||||
/// </summary>
|
||||
/// <param name="file">Name of the file to be parsed</param>
|
||||
/// <param name="outDir">Output directory to write the file to, empty means the same directory as the input file</param>
|
||||
/// <returns>True if the output file was created, false otherwise</returns>
|
||||
public static bool DetectSkipperAndTransform(string file, string outDir)
|
||||
{
|
||||
// Create the output directory if it doesn't exist
|
||||
if (outDir != "" && !Directory.Exists(outDir))
|
||||
{
|
||||
Directory.CreateDirectory(outDir);
|
||||
}
|
||||
|
||||
Globals.Logger.User("\nGetting skipper information for '" + file + "'");
|
||||
|
||||
// Get the skipper rule that matches the file, if any
|
||||
SkipperRule rule = Skipper.GetMatchingRule(file, "");
|
||||
|
||||
// If we have an empty rule, return false
|
||||
if (rule.Tests == null || rule.Tests.Count == 0 || rule.Operation != HeaderSkipOperation.None)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Globals.Logger.User("File has a valid copier header");
|
||||
|
||||
// Get the header bytes from the file first
|
||||
string hstr = string.Empty;
|
||||
BinaryReader br = new BinaryReader(TryOpenRead(file));
|
||||
|
||||
// Extract the header as a string for the database
|
||||
byte[] hbin = br.ReadBytes((int)rule.StartOffset);
|
||||
for (int i = 0; i < (int)rule.StartOffset; i++)
|
||||
{
|
||||
hstr += BitConverter.ToString(new byte[] { hbin[i] });
|
||||
}
|
||||
br.Dispose();
|
||||
|
||||
// Apply the rule to the file
|
||||
string newfile = (outDir == "" ? Path.GetFullPath(file) + ".new" : Path.Combine(outDir, Path.GetFileName(file)));
|
||||
rule.TransformFile(file, newfile);
|
||||
|
||||
// If the output file doesn't exist, return false
|
||||
if (!File.Exists(newfile))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Now add the information to the database if it's not already there
|
||||
Rom rom = GetFileInfo(newfile);
|
||||
DatabaseTools.AddHeaderToDatabase(hstr, rom.SHA1, rule.SourceFile);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve a list of just files from inputs
|
||||
/// </summary>
|
||||
/// <param name="inputs">List of strings representing directories and files</param>
|
||||
/// <param name="appendparent">True if the parent name should be appended after the special character "¬", false otherwise</param>
|
||||
/// <returns>List of strings representing just files from the inputs</returns>
|
||||
public static List<string> GetOnlyFilesFromInputs(List<string> inputs, bool appendparent = false)
|
||||
{
|
||||
List<string> outputs = new List<string>();
|
||||
foreach (string input in inputs)
|
||||
{
|
||||
if (Directory.Exists(input))
|
||||
{
|
||||
List<string> files = FileTools.RetrieveFiles(input, new List<string>());
|
||||
|
||||
// Make sure the files in the directory are ordered correctly
|
||||
files = Style.OrderByAlphaNumeric(files, s => s).ToList();
|
||||
foreach (string file in files)
|
||||
{
|
||||
try
|
||||
{
|
||||
outputs.Add(Path.GetFullPath(file) + (appendparent ? "¬" + Path.GetFullPath(input) : ""));
|
||||
}
|
||||
catch (PathTooLongException)
|
||||
{
|
||||
Globals.Logger.Warning("The path for " + file + " was too long");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Globals.Logger.Error(ex.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (File.Exists(input))
|
||||
{
|
||||
try
|
||||
{
|
||||
outputs.Add(Path.GetFullPath(input) + (appendparent ? "¬" + Path.GetFullPath(input) : ""));
|
||||
}
|
||||
catch (PathTooLongException)
|
||||
{
|
||||
Globals.Logger.Warning("The path for " + input + " was too long");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Globals.Logger.Error(ex.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return outputs;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the XmlTextReader associated with a file, if possible
|
||||
/// </summary>
|
||||
/// <param name="filename">Name of the file to be parsed</param>
|
||||
/// <returns>The XmlTextReader representing the (possibly converted) file, null otherwise</returns>
|
||||
public static XmlReader GetXmlTextReader(string filename)
|
||||
{
|
||||
Globals.Logger.Verbose("Attempting to read file: \"" + filename + "\"");
|
||||
|
||||
// Check if file exists
|
||||
if (!File.Exists(filename))
|
||||
{
|
||||
Globals.Logger.Warning("File '" + filename + "' could not read from!");
|
||||
return null;
|
||||
}
|
||||
|
||||
XmlReader xtr = XmlReader.Create(filename, new XmlReaderSettings
|
||||
{
|
||||
CheckCharacters = false,
|
||||
DtdProcessing = DtdProcessing.Ignore,
|
||||
IgnoreComments = true,
|
||||
IgnoreWhitespace = true,
|
||||
ValidationFlags = XmlSchemaValidationFlags.None,
|
||||
ValidationType = ValidationType.None,
|
||||
});
|
||||
return xtr;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Detect and replace header(s) to the given file
|
||||
/// </summary>
|
||||
/// <param name="file">Name of the file to be parsed</param>
|
||||
/// <param name="outDir">Output directory to write the file to, empty means the same directory as the input file</param>
|
||||
/// <returns>True if a header was found and appended, false otherwise</returns>
|
||||
public static bool RestoreHeader(string file, string outDir)
|
||||
{
|
||||
// Create the output directory if it doesn't exist
|
||||
if (outDir != "" && !Directory.Exists(outDir))
|
||||
{
|
||||
Directory.CreateDirectory(outDir);
|
||||
}
|
||||
|
||||
// First, get the SHA-1 hash of the file
|
||||
Rom rom = GetFileInfo(file);
|
||||
|
||||
// Retrieve a list of all related headers from the database
|
||||
List<string> headers = DatabaseTools.RetrieveHeadersFromDatabase(rom.SHA1);
|
||||
|
||||
// If we have nothing retrieved, we return false
|
||||
if (headers.Count == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Now loop through and create the reheadered files, if possible
|
||||
for (int i = 0; i < headers.Count; i++)
|
||||
{
|
||||
Globals.Logger.User("Creating reheadered file: " +
|
||||
(outDir == "" ? Path.GetFullPath(file) + ".new" : Path.Combine(outDir, Path.GetFileName(file))) + i);
|
||||
AppendBytesToFile(file,
|
||||
(outDir == "" ? Path.GetFullPath(file) + ".new" : Path.Combine(outDir, Path.GetFileName(file))) + i, headers[i], string.Empty);
|
||||
Globals.Logger.User("Reheadered file created!");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Try to create a file for write, optionally throwing the error
|
||||
/// </summary>
|
||||
/// <param name="file">Name of the file to create</param>
|
||||
/// <param name="throwOnError">True if the error that is thrown should be thrown back to the caller, false otherwise</param>
|
||||
/// <returns>An opened stream representing the file on success, null otherwise</returns>
|
||||
public static FileStream TryCreate(string file, bool throwOnError = false)
|
||||
{
|
||||
// Now wrap opening the file
|
||||
try
|
||||
{
|
||||
return File.Open(file, FileMode.Create, FileAccess.Write, FileShare.ReadWrite);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (throwOnError)
|
||||
{
|
||||
throw ex;
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Try to safely delete a directory, optionally throwing the error
|
||||
/// </summary>
|
||||
/// <param name="file">Name of the directory to delete</param>
|
||||
/// <param name="throwOnError">True if the error that is thrown should be thrown back to the caller, false otherwise</param>
|
||||
/// <returns>True if the file didn't exist or could be deleted, false otherwise</returns>
|
||||
public static bool TryDeleteDirectory(string file, bool throwOnError = false)
|
||||
{
|
||||
// Check if the file exists first
|
||||
if (!Directory.Exists(file))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Now wrap deleting the file
|
||||
try
|
||||
{
|
||||
Directory.Delete(file, true);
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (throwOnError)
|
||||
{
|
||||
throw ex;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Try to safely delete a file, optionally throwing the error
|
||||
/// </summary>
|
||||
/// <param name="file">Name of the file to delete</param>
|
||||
/// <param name="throwOnError">True if the error that is thrown should be thrown back to the caller, false otherwise</param>
|
||||
/// <returns>True if the file didn't exist or could be deleted, false otherwise</returns>
|
||||
public static bool TryDeleteFile(string file, bool throwOnError = false)
|
||||
{
|
||||
// Check if the file exists first
|
||||
if (!File.Exists(file))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Now wrap deleting the file
|
||||
try
|
||||
{
|
||||
File.Delete(file);
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (throwOnError)
|
||||
{
|
||||
throw ex;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Try to open a file for read, optionally throwing the error
|
||||
/// </summary>
|
||||
/// <param name="file">Name of the file to open</param>
|
||||
/// <param name="throwOnError">True if the error that is thrown should be thrown back to the caller, false otherwise</param>
|
||||
/// <returns>An opened stream representing the file on success, null otherwise</returns>
|
||||
public static FileStream TryOpenRead(string file, bool throwOnError = false)
|
||||
{
|
||||
// Check if the file exists first
|
||||
if (!File.Exists(file))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// Now wrap opening the file
|
||||
try
|
||||
{
|
||||
return File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (throwOnError)
|
||||
{
|
||||
throw ex;
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Try to open a file for read/write, optionally throwing the error
|
||||
/// </summary>
|
||||
/// <param name="file">Name of the file to open</param>
|
||||
/// <param name="throwOnError">True if the error that is thrown should be thrown back to the caller, false otherwise</param>
|
||||
/// <returns>An opened stream representing the file on success, null otherwise</returns>
|
||||
public static FileStream TryOpenReadWrite(string file, bool throwOnError = false)
|
||||
{
|
||||
// Check if the file exists first
|
||||
if (!File.Exists(file))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// Now wrap opening the file
|
||||
try
|
||||
{
|
||||
return File.Open(file, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (throwOnError)
|
||||
{
|
||||
throw ex;
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Try to open a file for write, optionally throwing the error
|
||||
/// </summary>
|
||||
/// <param name="file">Name of the file to open</param>
|
||||
/// <param name="throwOnError">True if the error that is thrown should be thrown back to the caller, false otherwise</param>
|
||||
/// <returns>An opened stream representing the file on success, null otherwise</returns>
|
||||
public static FileStream TryOpenWrite(string file, bool throwOnError = false)
|
||||
{
|
||||
// Check if the file exists first
|
||||
if (!File.Exists(file))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// Now wrap opening the file
|
||||
try
|
||||
{
|
||||
return File.Open(file, FileMode.Open, FileAccess.Write, FileShare.ReadWrite);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (throwOnError)
|
||||
{
|
||||
throw ex;
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Stream Information
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve file information for a single file
|
||||
/// </summary>
|
||||
/// <param name="input">Filename to get information from</param>
|
||||
/// <param name="size">Size of the input stream</param>
|
||||
/// <param name="omitFromScan">Hash flag saying what hashes should not be calculated (defaults to none)</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 GetStreamInfo(Stream input, long size, Hash omitFromScan = 0x0,
|
||||
long offset = 0, bool keepReadOpen = false)
|
||||
{
|
||||
Rom rom = new Rom
|
||||
{
|
||||
Type = ItemType.Rom,
|
||||
Size = size,
|
||||
CRC = string.Empty,
|
||||
MD5 = string.Empty,
|
||||
SHA1 = string.Empty,
|
||||
SHA256 = string.Empty,
|
||||
SHA384 = string.Empty,
|
||||
SHA512 = string.Empty,
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
// Initialize the hashers
|
||||
OptimizedCRC crc = new OptimizedCRC();
|
||||
MD5 md5 = MD5.Create();
|
||||
SHA1 sha1 = SHA1.Create();
|
||||
SHA256 sha256 = SHA256.Create();
|
||||
SHA384 sha384 = SHA384.Create();
|
||||
SHA512 sha512 = SHA512.Create();
|
||||
xxHash xxHash = new xxHash();
|
||||
xxHash.Init();
|
||||
|
||||
// Seek to the starting position, if one is set
|
||||
if (offset < 0)
|
||||
{
|
||||
input.Seek(offset, SeekOrigin.End);
|
||||
}
|
||||
else
|
||||
{
|
||||
input.Seek(offset, SeekOrigin.Begin);
|
||||
}
|
||||
|
||||
byte[] buffer = new byte[8 * 1024];
|
||||
int read;
|
||||
while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
|
||||
{
|
||||
crc.Update(buffer, 0, read);
|
||||
if ((omitFromScan & Hash.MD5) == 0)
|
||||
{
|
||||
md5.TransformBlock(buffer, 0, read, buffer, 0);
|
||||
}
|
||||
if ((omitFromScan & Hash.SHA1) == 0)
|
||||
{
|
||||
sha1.TransformBlock(buffer, 0, read, buffer, 0);
|
||||
}
|
||||
if ((omitFromScan & Hash.SHA256) == 0)
|
||||
{
|
||||
sha256.TransformBlock(buffer, 0, read, buffer, 0);
|
||||
}
|
||||
if ((omitFromScan & Hash.SHA384) == 0)
|
||||
{
|
||||
sha384.TransformBlock(buffer, 0, read, buffer, 0);
|
||||
}
|
||||
if ((omitFromScan & Hash.SHA512) == 0)
|
||||
{
|
||||
sha512.TransformBlock(buffer, 0, read, buffer, 0);
|
||||
}
|
||||
if ((omitFromScan & Hash.xxHash) == 0)
|
||||
{
|
||||
xxHash.Update(buffer, read);
|
||||
}
|
||||
}
|
||||
|
||||
crc.Update(buffer, 0, 0);
|
||||
rom.CRC = crc.Value.ToString("X8").ToLowerInvariant();
|
||||
|
||||
if ((omitFromScan & Hash.MD5) == 0)
|
||||
{
|
||||
md5.TransformFinalBlock(buffer, 0, 0);
|
||||
rom.MD5 = BitConverter.ToString(md5.Hash).Replace("-", "").ToLowerInvariant();
|
||||
}
|
||||
if ((omitFromScan & Hash.SHA1) == 0)
|
||||
{
|
||||
sha1.TransformFinalBlock(buffer, 0, 0);
|
||||
rom.SHA1 = BitConverter.ToString(sha1.Hash).Replace("-", "").ToLowerInvariant();
|
||||
}
|
||||
if ((omitFromScan & Hash.SHA256) == 0)
|
||||
{
|
||||
sha256.TransformFinalBlock(buffer, 0, 0);
|
||||
rom.SHA256 = BitConverter.ToString(sha256.Hash).Replace("-", "").ToLowerInvariant();
|
||||
}
|
||||
if ((omitFromScan & Hash.SHA384) == 0)
|
||||
{
|
||||
sha384.TransformFinalBlock(buffer, 0, 0);
|
||||
rom.SHA384 = BitConverter.ToString(sha384.Hash).Replace("-", "").ToLowerInvariant();
|
||||
}
|
||||
if ((omitFromScan & Hash.SHA512) == 0)
|
||||
{
|
||||
sha512.TransformFinalBlock(buffer, 0, 0);
|
||||
rom.SHA512 = BitConverter.ToString(sha512.Hash).Replace("-", "").ToLowerInvariant();
|
||||
}
|
||||
if ((omitFromScan & Hash.xxHash) == 0)
|
||||
{
|
||||
//rom.xxHash = xxHash.Digest().ToString("X8").ToLowerInvariant();
|
||||
}
|
||||
|
||||
// Dispose of the hashers
|
||||
crc.Dispose();
|
||||
md5.Dispose();
|
||||
sha1.Dispose();
|
||||
sha256.Dispose();
|
||||
sha384.Dispose();
|
||||
sha512.Dispose();
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
return new Rom();
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Seek to the beginning of the stream
|
||||
input.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
if (!keepReadOpen)
|
||||
{
|
||||
input.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
return rom;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Stream Manipulation
|
||||
|
||||
/// <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
|
||||
}
|
||||
}
|
||||
970
SabreTools.Library/Tools/Style.cs
Normal file
970
SabreTools.Library/Tools/Style.cs
Normal file
@@ -0,0 +1,970 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Web;
|
||||
|
||||
using SabreTools.Helper.Data;
|
||||
using SabreTools.Helper.Dats;
|
||||
|
||||
#if MONO
|
||||
using System.IO;
|
||||
#else
|
||||
using Alphaleonis.Win32.Filesystem;
|
||||
|
||||
using FileStream = System.IO.FileStream;
|
||||
#endif
|
||||
|
||||
namespace SabreTools.Helper.Tools
|
||||
{
|
||||
/// <summary>
|
||||
/// Include character normalization and replacement mappings
|
||||
/// </summary>
|
||||
public static class Style
|
||||
{
|
||||
#region DAT Cleaning
|
||||
|
||||
/// <summary>
|
||||
/// Clean a game (or rom) name to the WoD standard
|
||||
/// </summary>
|
||||
/// <param name="game">Name of the game to be cleaned</param>
|
||||
/// <returns>The cleaned name</returns>
|
||||
public static string CleanGameName(string game)
|
||||
{
|
||||
///Run the name through the filters to make sure that it's correct
|
||||
game = NormalizeChars(game);
|
||||
game = RussianToLatin(game);
|
||||
game = SearchPattern(game);
|
||||
|
||||
game = new Regex(@"(([[(].*[\)\]] )?([^([]+))").Match(game).Groups[1].Value;
|
||||
game = game.TrimStart().TrimEnd();
|
||||
return game;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clean a game (or rom) name to the WoD standard
|
||||
/// </summary>
|
||||
/// <param name="game">Array representing the path to be cleaned</param>
|
||||
/// <returns>The cleaned name</returns>
|
||||
public static string CleanGameName(string[] game)
|
||||
{
|
||||
game[game.Length - 1] = CleanGameName(game[game.Length - 1]);
|
||||
string outgame = String.Join(Path.DirectorySeparatorChar.ToString(), game);
|
||||
outgame = outgame.TrimStart().TrimEnd();
|
||||
return outgame;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clean a hash string and pad to the correct size
|
||||
/// </summary>
|
||||
/// <param name="hash">Hash string to sanitize</param>
|
||||
/// <param name="padding">Amount of characters to pad to</param>
|
||||
/// <returns>Cleaned string</returns>
|
||||
public static string CleanHashData(string hash, int padding)
|
||||
{
|
||||
// If we have a known blank hash, return blank
|
||||
if (string.IsNullOrEmpty(hash) || hash == "-" || hash == "_")
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
// Check to see if it's a "hex" hash
|
||||
hash = hash.Trim().Replace("0x", "");
|
||||
|
||||
// If we have a blank hash now, return blank
|
||||
if (string.IsNullOrEmpty(hash))
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
// If the hash shorter than the required length, pad it
|
||||
if (hash.Length < padding)
|
||||
{
|
||||
hash = hash.PadLeft(padding, '0');
|
||||
}
|
||||
// If the hash is longer than the required length, it's invalid
|
||||
else if (hash.Length > padding)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
// Now normalize the hash
|
||||
hash = hash.ToLowerInvariant();
|
||||
|
||||
// Otherwise, make sure that every character is a proper match
|
||||
for (int i = 0; i < hash.Length; i++)
|
||||
{
|
||||
if ((hash[i] < '0' || hash[i] > '9') && (hash[i] < 'a' || hash[i] > 'f'))
|
||||
{
|
||||
hash = "";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate a proper outfile name based on a DAT and output directory
|
||||
/// </summary>
|
||||
/// <param name="outDir">Output directory</param>
|
||||
/// <param name="datdata">DAT information</param>
|
||||
/// <param name="overwrite">True if we ignore existing files (default), false otherwise</param>
|
||||
/// <returns>Dictionary of output formats mapped to file names</returns>
|
||||
public static Dictionary<DatFormat, string> CreateOutfileNames(string outDir, DatFile datdata, bool overwrite = true)
|
||||
{
|
||||
// Create the output dictionary
|
||||
Dictionary<DatFormat, string> outfileNames = new Dictionary<DatFormat, string>();
|
||||
|
||||
// Double check the outDir for the end delim
|
||||
if (!outDir.EndsWith(Path.DirectorySeparatorChar.ToString()))
|
||||
{
|
||||
outDir += Path.DirectorySeparatorChar;
|
||||
}
|
||||
|
||||
// Get the extensions from the output type
|
||||
|
||||
// AttractMode
|
||||
if ((datdata.DatFormat & DatFormat.AttractMode) != 0)
|
||||
{
|
||||
outfileNames.Add(DatFormat.AttractMode, CreateOutfileNamesHelper(outDir, ".txt", datdata, overwrite));
|
||||
}
|
||||
|
||||
// ClrMamePro
|
||||
if ((datdata.DatFormat & DatFormat.ClrMamePro) != 0)
|
||||
{
|
||||
outfileNames.Add(DatFormat.ClrMamePro, CreateOutfileNamesHelper(outDir, ".dat", datdata, overwrite));
|
||||
};
|
||||
|
||||
// CSV
|
||||
if ((datdata.DatFormat & DatFormat.CSV) != 0)
|
||||
{
|
||||
outfileNames.Add(DatFormat.CSV, CreateOutfileNamesHelper(outDir, ".csv", datdata, overwrite));
|
||||
};
|
||||
|
||||
// DOSCenter
|
||||
if ((datdata.DatFormat & DatFormat.DOSCenter) != 0
|
||||
&& (datdata.DatFormat & DatFormat.ClrMamePro) == 0
|
||||
&& (datdata.DatFormat & DatFormat.RomCenter) == 0)
|
||||
{
|
||||
outfileNames.Add(DatFormat.DOSCenter, CreateOutfileNamesHelper(outDir, ".dat", datdata, overwrite));
|
||||
};
|
||||
if ((datdata.DatFormat & DatFormat.DOSCenter) != 0
|
||||
&& ((datdata.DatFormat & DatFormat.ClrMamePro) != 0
|
||||
|| (datdata.DatFormat & DatFormat.RomCenter) != 0))
|
||||
{
|
||||
outfileNames.Add(DatFormat.DOSCenter, CreateOutfileNamesHelper(outDir, ".dc.dat", datdata, overwrite));
|
||||
};
|
||||
|
||||
// Logiqx XML
|
||||
if ((datdata.DatFormat & DatFormat.Logiqx) != 0)
|
||||
{
|
||||
outfileNames.Add(DatFormat.Logiqx, CreateOutfileNamesHelper(outDir, ".xml", datdata, overwrite));
|
||||
};
|
||||
|
||||
// Missfile
|
||||
if ((datdata.DatFormat & DatFormat.MissFile) != 0
|
||||
&& (datdata.DatFormat & DatFormat.AttractMode) == 0)
|
||||
{
|
||||
outfileNames.Add(DatFormat.MissFile, CreateOutfileNamesHelper(outDir, ".txt", datdata, overwrite));
|
||||
};
|
||||
if ((datdata.DatFormat & DatFormat.MissFile) != 0
|
||||
&& (datdata.DatFormat & DatFormat.AttractMode) != 0)
|
||||
{
|
||||
outfileNames.Add(DatFormat.MissFile, CreateOutfileNamesHelper(outDir, ".miss.txt", datdata, overwrite));
|
||||
};
|
||||
|
||||
// OfflineList
|
||||
if (((datdata.DatFormat & DatFormat.OfflineList) != 0)
|
||||
&& (datdata.DatFormat & DatFormat.Logiqx) == 0
|
||||
&& (datdata.DatFormat & DatFormat.SabreDat) == 0
|
||||
&& (datdata.DatFormat & DatFormat.SoftwareList) == 0)
|
||||
{
|
||||
outfileNames.Add(DatFormat.OfflineList, CreateOutfileNamesHelper(outDir, ".xml", datdata, overwrite));
|
||||
}
|
||||
if (((datdata.DatFormat & DatFormat.OfflineList) != 0
|
||||
&& ((datdata.DatFormat & DatFormat.Logiqx) != 0
|
||||
|| (datdata.DatFormat & DatFormat.SabreDat) != 0
|
||||
|| (datdata.DatFormat & DatFormat.SoftwareList) != 0)))
|
||||
{
|
||||
outfileNames.Add(DatFormat.OfflineList, CreateOutfileNamesHelper(outDir, ".ol.xml", datdata, overwrite));
|
||||
}
|
||||
|
||||
// Redump MD5
|
||||
if ((datdata.DatFormat & DatFormat.RedumpMD5) != 0)
|
||||
{
|
||||
outfileNames.Add(DatFormat.RedumpMD5, CreateOutfileNamesHelper(outDir, ".md5", datdata, overwrite));
|
||||
};
|
||||
|
||||
// Redump SFV
|
||||
if ((datdata.DatFormat & DatFormat.RedumpSFV) != 0)
|
||||
{
|
||||
outfileNames.Add(DatFormat.RedumpSFV, CreateOutfileNamesHelper(outDir, ".sfv", datdata, overwrite));
|
||||
};
|
||||
|
||||
// Redump SHA-1
|
||||
if ((datdata.DatFormat & DatFormat.RedumpSHA1) != 0)
|
||||
{
|
||||
outfileNames.Add(DatFormat.RedumpSHA1, CreateOutfileNamesHelper(outDir, ".sha1", datdata, overwrite));
|
||||
};
|
||||
|
||||
// Redump SHA-256
|
||||
if ((datdata.DatFormat & DatFormat.RedumpSHA256) != 0)
|
||||
{
|
||||
outfileNames.Add(DatFormat.RedumpSHA256, CreateOutfileNamesHelper(outDir, ".sha256", datdata, overwrite));
|
||||
};
|
||||
|
||||
// RomCenter
|
||||
if ((datdata.DatFormat & DatFormat.RomCenter) != 0
|
||||
&& (datdata.DatFormat & DatFormat.ClrMamePro) == 0)
|
||||
{
|
||||
outfileNames.Add(DatFormat.RomCenter, CreateOutfileNamesHelper(outDir, ".dat", datdata, overwrite));
|
||||
};
|
||||
if ((datdata.DatFormat & DatFormat.RomCenter) != 0
|
||||
&& (datdata.DatFormat & DatFormat.ClrMamePro) != 0)
|
||||
{
|
||||
outfileNames.Add(DatFormat.RomCenter, CreateOutfileNamesHelper(outDir, ".rc.dat", datdata, overwrite));
|
||||
};
|
||||
|
||||
// SabreDAT
|
||||
if ((datdata.DatFormat & DatFormat.SabreDat) != 0 && (datdata.DatFormat & DatFormat.Logiqx) == 0)
|
||||
{
|
||||
outfileNames.Add(DatFormat.SabreDat, CreateOutfileNamesHelper(outDir, ".xml", datdata, overwrite));
|
||||
};
|
||||
if ((datdata.DatFormat & DatFormat.SabreDat) != 0 && (datdata.DatFormat & DatFormat.Logiqx) != 0)
|
||||
{
|
||||
outfileNames.Add(DatFormat.SabreDat, CreateOutfileNamesHelper(outDir, ".sd.xml", datdata, overwrite));
|
||||
};
|
||||
|
||||
// Software List
|
||||
if ((datdata.DatFormat & DatFormat.SoftwareList) != 0
|
||||
&& (datdata.DatFormat & DatFormat.Logiqx) == 0
|
||||
&& (datdata.DatFormat & DatFormat.SabreDat) == 0)
|
||||
{
|
||||
outfileNames.Add(DatFormat.SoftwareList, CreateOutfileNamesHelper(outDir, ".xml", datdata, overwrite));
|
||||
}
|
||||
if ((datdata.DatFormat & DatFormat.SoftwareList) != 0
|
||||
&& ((datdata.DatFormat & DatFormat.Logiqx) != 0
|
||||
|| (datdata.DatFormat & DatFormat.SabreDat) != 0))
|
||||
{
|
||||
outfileNames.Add(DatFormat.SoftwareList, CreateOutfileNamesHelper(outDir, ".sl.xml", datdata, overwrite));
|
||||
}
|
||||
|
||||
// TSV
|
||||
if ((datdata.DatFormat & DatFormat.TSV) != 0)
|
||||
{
|
||||
outfileNames.Add(DatFormat.TSV, CreateOutfileNamesHelper(outDir, ".tsv", datdata, overwrite));
|
||||
};
|
||||
|
||||
return outfileNames;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Help generating the outfile name
|
||||
/// </summary>
|
||||
/// <param name="outDir">Output directory</param>
|
||||
/// <param name="extension">Extension to use for the file</param>
|
||||
/// <param name="datdata">DAT information</param>
|
||||
/// <param name="overwrite">True if we ignore existing files, false otherwise</param>
|
||||
/// <returns>String containing the new filename</returns>
|
||||
private static string CreateOutfileNamesHelper(string outDir, string extension, DatFile datdata, bool overwrite)
|
||||
{
|
||||
string filename = (String.IsNullOrEmpty(datdata.FileName) ? datdata.Description : datdata.FileName);
|
||||
string outfile = outDir + filename + extension;
|
||||
outfile = (outfile.Contains(Path.DirectorySeparatorChar.ToString() + Path.DirectorySeparatorChar.ToString()) ?
|
||||
outfile.Replace(Path.DirectorySeparatorChar.ToString() + Path.DirectorySeparatorChar.ToString(), Path.DirectorySeparatorChar.ToString()) :
|
||||
outfile);
|
||||
if (!overwrite)
|
||||
{
|
||||
int i = 1;
|
||||
while (File.Exists(outfile))
|
||||
{
|
||||
outfile = outDir + filename + "_" + i + extension;
|
||||
outfile = (outfile.Contains(Path.DirectorySeparatorChar.ToString() + Path.DirectorySeparatorChar.ToString()) ?
|
||||
outfile.Replace(Path.DirectorySeparatorChar.ToString() + Path.DirectorySeparatorChar.ToString(), Path.DirectorySeparatorChar.ToString()) :
|
||||
outfile);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
return outfile;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region String Manipulation
|
||||
|
||||
/// <summary>
|
||||
/// Compare strings as numeric
|
||||
/// </summary>
|
||||
/// <param name="s1">First string to compare</param>
|
||||
/// <param name="s2">Second string to compare</param>
|
||||
/// <returns>-1 if s1 comes before s2, 0 if s1 and s2 are equal, 1 if s1 comes after s2</returns>
|
||||
/// <remarks>I want to be able to handle paths properly with no issue, can I do a recursive call based on separated by path separator?</remarks>
|
||||
public static int CompareNumeric(string s1, string s2)
|
||||
{
|
||||
// We want to normalize the strings, so we set both to lower case
|
||||
s1 = s1.ToLowerInvariant();
|
||||
s2 = s2.ToLowerInvariant();
|
||||
|
||||
// If the strings are the same exactly, return
|
||||
if (s1 == s2)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// If one is null, then say that's less than
|
||||
if (s1 == null)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
if (s2 == null)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Now split into path parts after converting AltDirSeparator to DirSeparator
|
||||
s1 = s1.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
|
||||
s2 = s2.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
|
||||
string[] s1parts = s1.Split(Path.DirectorySeparatorChar);
|
||||
string[] s2parts = s2.Split(Path.DirectorySeparatorChar);
|
||||
|
||||
// Then compare each part in turn
|
||||
for (int j = 0; j < s1parts.Length && j < s2parts.Length; j++)
|
||||
{
|
||||
int compared = CompareNumericPart(s1parts[j], s2parts[j]);
|
||||
if (compared != 0)
|
||||
{
|
||||
return compared;
|
||||
}
|
||||
}
|
||||
|
||||
// If we got out here, then it looped through at least one of the strings
|
||||
if (s1parts.Length > s2parts.Length)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
if (s1parts.Length < s2parts.Length)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper for CompareNumeric
|
||||
/// </summary>
|
||||
/// <param name="s1">First string to compare</param>
|
||||
/// <param name="s2">Second string to compare</param>
|
||||
/// <returns>-1 if s1 comes before s2, 0 if s1 and s2 are equal, 1 if s1 comes after s2</returns>
|
||||
private static int CompareNumericPart(string s1, string s2)
|
||||
{
|
||||
// Otherwise, loop through until we have an answer
|
||||
for (int i = 0; i < s1.Length && i < s2.Length; i++)
|
||||
{
|
||||
int s1c = s1[i];
|
||||
int s2c = s2[i];
|
||||
|
||||
// If the characters are the same, continue
|
||||
if (s1c == s2c)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// If they're different, check which one was larger
|
||||
if (s1c > s2c)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
if (s1c < s2c)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// If we got out here, then it looped through at least one of the strings
|
||||
if (s1.Length > s2.Length)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
if (s1.Length < s2.Length)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert all characters that are not considered XML-safe
|
||||
/// </summary>
|
||||
/// <param name="s">Input string to clean</param>
|
||||
/// <returns>Cleaned string</returns>
|
||||
public static string ConvertXMLUnsafeCharacters(string s)
|
||||
{
|
||||
return new String(s.Select(c =>
|
||||
(c == 0x9
|
||||
|| c == 0xA
|
||||
|| c == 0xD
|
||||
|| (c >= 0x20 && c <= 0xD77F)
|
||||
|| (c >= 0xE000 && c <= 0xFFFD)
|
||||
|| (c >= 0x10000 && c <= 0x10FFFF)
|
||||
? c
|
||||
: HttpUtility.HtmlEncode(c)[0]))
|
||||
.ToArray());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a proper romba sub path
|
||||
/// </summary>
|
||||
/// <param name="hash">SHA-1 hash to get the path for</param>
|
||||
/// <returns>Subfolder path for the given hash</returns>
|
||||
public static string GetRombaPath(string hash)
|
||||
{
|
||||
// If the hash isn't the right size, then we return null
|
||||
if (hash.Length != Constants.SHA1Length) // TODO: When updating to SHA-256, this needs to update to Constants.SHA256Length
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return Path.Combine(hash.Substring(0, 2), hash.Substring(2, 2), hash.Substring(4, 2), hash.Substring(6, 2), hash + ".gz");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the multiplier to be used with the size given
|
||||
/// </summary>
|
||||
/// <param name="sizestring">String with possible size with extension</param>
|
||||
/// <returns>Tuple of multiplier to use on final size and fixed size string</returns>
|
||||
public static long GetSizeFromString(string sizestring)
|
||||
{
|
||||
long size = 0;
|
||||
|
||||
// Make sure the string is in lower case
|
||||
sizestring = sizestring.ToLowerInvariant();
|
||||
|
||||
// Get any trailing size identifiers
|
||||
long multiplier = 1;
|
||||
if (sizestring.EndsWith("k") || sizestring.EndsWith("kb"))
|
||||
{
|
||||
multiplier = Constants.KiloByte;
|
||||
}
|
||||
else if (sizestring.EndsWith("ki") || sizestring.EndsWith("kib"))
|
||||
{
|
||||
multiplier = Constants.KibiByte;
|
||||
}
|
||||
else if (sizestring.EndsWith("m") || sizestring.EndsWith("mb"))
|
||||
{
|
||||
multiplier = Constants.MegaByte;
|
||||
}
|
||||
else if (sizestring.EndsWith("mi") || sizestring.EndsWith("mib"))
|
||||
{
|
||||
multiplier = Constants.MibiByte;
|
||||
}
|
||||
else if (sizestring.EndsWith("g") || sizestring.EndsWith("gb"))
|
||||
{
|
||||
multiplier = Constants.GigaByte;
|
||||
}
|
||||
else if (sizestring.EndsWith("gi") || sizestring.EndsWith("gib"))
|
||||
{
|
||||
multiplier = Constants.GibiByte;
|
||||
}
|
||||
else if (sizestring.EndsWith("t") || sizestring.EndsWith("tb"))
|
||||
{
|
||||
multiplier = Constants.TeraByte;
|
||||
}
|
||||
else if (sizestring.EndsWith("ti") || sizestring.EndsWith("tib"))
|
||||
{
|
||||
multiplier = Constants.TibiByte;
|
||||
}
|
||||
else if (sizestring.EndsWith("p") || sizestring.EndsWith("pb"))
|
||||
{
|
||||
multiplier = Constants.PetaByte;
|
||||
}
|
||||
else if (sizestring.EndsWith("pi") || sizestring.EndsWith("pib"))
|
||||
{
|
||||
multiplier = Constants.PibiByte;
|
||||
}
|
||||
|
||||
// Remove any trailing identifiers
|
||||
sizestring = sizestring.TrimEnd(new char[] { 'k', 'm', 'g', 't', 'p', 'i', 'b', ' ' });
|
||||
|
||||
// Now try to get the size from the string
|
||||
if (!Int64.TryParse(sizestring, out size))
|
||||
{
|
||||
size = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
size *= multiplier;
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get if a string contains Unicode characters
|
||||
/// </summary>
|
||||
/// <param name="s">Input string to test</param>
|
||||
/// <returns>True if the string contains at least one Unicode character, false otherwise</returns>
|
||||
public static bool IsUnicode(string s)
|
||||
{
|
||||
return (s.Any(c => c > 255));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove all chars that are considered path unsafe
|
||||
/// </summary>
|
||||
/// <param name="s">Input string to clean</param>
|
||||
/// <returns>Cleaned string</returns>
|
||||
public static string RemovePathUnsafeCharacters(string s)
|
||||
{
|
||||
List<char> invalidPath = Path.GetInvalidPathChars().ToList();
|
||||
return new string(s.Where(c => !invalidPath.Contains(c)).ToArray());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove all unicode-specific chars from a string
|
||||
/// </summary>
|
||||
/// <param name="s">Input string to clean</param>
|
||||
/// <returns>Cleaned string</returns>
|
||||
public static string RemoveUnicodeCharacters(string s)
|
||||
{
|
||||
return new string(s.Where(c => c <= 255).ToArray());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Split a line as if it were a CMP rom line
|
||||
/// </summary>
|
||||
/// <param name="s">Line to split</param>
|
||||
/// <returns>Line split</returns>
|
||||
/// <remarks>Uses code from http://stackoverflow.com/questions/554013/regular-expression-to-split-on-spaces-unless-in-quotes</remarks>
|
||||
public static string[] SplitLineAsCMP(string s)
|
||||
{
|
||||
// Preprocess the string
|
||||
s = s.Trim();
|
||||
s = Regex.Replace(s, @"^\S* \(", ""); // Remove item identifier and opening brace
|
||||
s = Regex.Replace(s, @"\)\S*#.*$", ""); // Remove trailing comments
|
||||
s = s.TrimEnd(')'); // Remove closing brace
|
||||
s = s.Trim(); // Remove leading and trailing whitespace
|
||||
|
||||
// Now we get each string, divided up as cleanly as possible
|
||||
string[] matches = Regex
|
||||
//.Matches(s, @"([^\s]*""[^""]+""[^\s]*)|[^""]?\w+[^""]?")
|
||||
.Matches(s, @"[^\s""]+|""[^""]*""")
|
||||
.Cast<Match>()
|
||||
.Select(m => m.Groups[0].Value)
|
||||
.ToArray();
|
||||
|
||||
return matches;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region System.IO.Path Replacements
|
||||
|
||||
/// <summary>
|
||||
/// Replacement for System.IO.Path.GetDirectoryName
|
||||
/// </summary>
|
||||
/// <param name="s">Path to get directory name out of</param>
|
||||
/// <returns>Directory name from path</returns>
|
||||
/// <see cref="System.IO.Path.GetDirectoryName(string)"/>
|
||||
public static string GetDirectoryName(string s)
|
||||
{
|
||||
if (s == null)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
if (s.Contains("/"))
|
||||
{
|
||||
string[] tempkey = s.Split('/');
|
||||
return String.Join("/", tempkey.Take(tempkey.Length - 1));
|
||||
}
|
||||
else if (s.Contains("\\"))
|
||||
{
|
||||
string[] tempkey = s.Split('\\');
|
||||
return String.Join("\\", tempkey.Take(tempkey.Length - 1));
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Replacement for System.IO.Path.GetFileName
|
||||
/// </summary>
|
||||
/// <param name="s">Path to get file name out of</param>
|
||||
/// <returns>File name from path</returns>
|
||||
/// <see cref="System.IO.Path.GetFileName(string)"/>
|
||||
public static string GetFileName(string s)
|
||||
{
|
||||
if (s == null)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
if (s.Contains("/"))
|
||||
{
|
||||
string[] tempkey = s.Split('/');
|
||||
return tempkey.Last();
|
||||
}
|
||||
else if (s.Contains("\\"))
|
||||
{
|
||||
string[] tempkey = s.Split('\\');
|
||||
return tempkey.Last();
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Replacement for System.IO.Path.GetFileNameWithoutExtension
|
||||
/// </summary>
|
||||
/// <param name="s">Path to get file name out of</param>
|
||||
/// <returns>File name without extension from path</returns>
|
||||
/// <see cref="System.IO.Path.GetFileNameWithoutExtension(string)"/>
|
||||
public static string GetFileNameWithoutExtension(string s)
|
||||
{
|
||||
s = GetFileName(s);
|
||||
string[] tempkey = s.Split('.');
|
||||
if (tempkey.Count() == 1)
|
||||
{
|
||||
return s;
|
||||
}
|
||||
return String.Join(".", tempkey.Take(tempkey.Length - 1));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region WoD-based String Cleaning
|
||||
|
||||
/// <summary>
|
||||
/// Replace accented characters
|
||||
/// </summary>
|
||||
/// <param name="input">String to be parsed</param>
|
||||
/// <returns>String with characters replaced</returns>
|
||||
public static string NormalizeChars(string input)
|
||||
{
|
||||
string[,] charmap = {
|
||||
{ "Á", "A" }, { "á", "a" },
|
||||
{ "À", "A" }, { "à", "a" },
|
||||
{ "Â", "A" }, { "â", "a" },
|
||||
{ "Ä", "Ae" }, { "ä", "ae" },
|
||||
{ "Ã", "A" }, { "ã", "a" },
|
||||
{ "Å", "A" }, { "å", "a" },
|
||||
{ "Æ", "Ae" }, { "æ", "ae" },
|
||||
{ "Ç", "C" }, { "ç", "c" },
|
||||
{ "Ð", "D" }, { "ð", "d" },
|
||||
{ "É", "E" }, { "é", "e" },
|
||||
{ "È", "E" }, { "è", "e" },
|
||||
{ "Ê", "E" }, { "ê", "e" },
|
||||
{ "Ë", "E" }, { "ë", "e" },
|
||||
{ "ƒ", "f" },
|
||||
{ "Í", "I" }, { "í", "i" },
|
||||
{ "Ì", "I" }, { "ì", "i" },
|
||||
{ "Î", "I" }, { "î", "i" },
|
||||
{ "Ï", "I" }, { "ï", "i" },
|
||||
{ "Ñ", "N" }, { "ñ", "n" },
|
||||
{ "Ó", "O" }, { "ó", "o" },
|
||||
{ "Ò", "O" }, { "ò", "o" },
|
||||
{ "Ô", "O" }, { "ô", "o" },
|
||||
{ "Ö", "Oe" }, { "ö", "oe" },
|
||||
{ "Õ", "O" }, { "õ", "o" },
|
||||
{ "Ø", "O" }, { "ø", "o" },
|
||||
{ "Š", "S" }, { "š", "s" },
|
||||
{ "ß", "ss" },
|
||||
{ "Þ", "B" }, { "þ", "b" },
|
||||
{ "Ú", "U" }, { "ú", "u" },
|
||||
{ "Ù", "U" }, { "ù", "u" },
|
||||
{ "Û", "U" }, { "û", "u" },
|
||||
{ "Ü", "Ue" }, { "ü", "ue" },
|
||||
{ "ÿ", "y" },
|
||||
{ "Ý", "Y" }, { "ý", "y" },
|
||||
{ "Ž", "Z" }, { "ž", "z" },
|
||||
};
|
||||
|
||||
for (int i = 0; i < charmap.GetLength(0); i++)
|
||||
{
|
||||
input = input.Replace(charmap[i, 0], charmap[i, 1]);
|
||||
}
|
||||
|
||||
return input;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert Cyrillic lettering to Latin lettering
|
||||
/// </summary>
|
||||
/// <param name="input">String to be parsed</param>
|
||||
/// <returns>String with characters replaced</returns>
|
||||
public static string RussianToLatin(string input)
|
||||
{
|
||||
string[,] charmap = {
|
||||
{ "А", "A" }, { "Б", "B" }, { "В", "V" }, { "Г", "G" }, { "Д", "D" },
|
||||
{ "Е", "E" }, { "Ё", "Yo" }, { "Ж", "Zh" }, { "З", "Z" }, { "И", "I" },
|
||||
{ "Й", "J" }, { "К", "K" }, { "Л", "L" }, { "М", "M" }, { "Н", "N" },
|
||||
{ "О", "O" }, { "П", "P" }, { "Р", "R" }, { "С", "S" }, { "Т", "T" },
|
||||
{ "У", "U" }, { "Ф", "f" }, { "Х", "Kh" }, { "Ц", "Ts" }, { "Ч", "Ch" },
|
||||
{ "Ш", "Sh" }, { "Щ", "Sch" }, { "Ъ", "" }, { "Ы", "y" }, { "Ь", "" },
|
||||
{ "Э", "e" }, { "Ю", "yu" }, { "Я", "ya" }, { "а", "a" }, { "б", "b" },
|
||||
{ "в", "v" }, { "г", "g" }, { "д", "d" }, { "е", "e" }, { "ё", "yo" },
|
||||
{ "ж", "zh" }, { "з", "z" }, { "и", "i" }, { "й", "j" }, { "к", "k" },
|
||||
{ "л", "l" }, { "м", "m" }, { "н", "n" }, { "о", "o" }, { "п", "p" },
|
||||
{ "р", "r" }, { "с", "s" }, { "т", "t" }, { "у", "u" }, { "ф", "f" },
|
||||
{ "х", "kh" }, { "ц", "ts" }, { "ч", "ch" }, { "ш", "sh" }, { "щ", "sch" },
|
||||
{ "ъ", "" }, { "ы", "y" }, { "ь", "" }, { "э", "e" }, { "ю", "yu" },
|
||||
{ "я", "ya" },
|
||||
};
|
||||
|
||||
for (int i = 0; i < charmap.GetLength(0); i++)
|
||||
{
|
||||
input = input.Replace(charmap[i, 0], charmap[i, 1]);
|
||||
}
|
||||
|
||||
return input;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Replace special characters and patterns
|
||||
/// </summary>
|
||||
/// <param name="input">String to be parsed</param>
|
||||
/// <returns>String with characters replaced</returns>
|
||||
public static string SearchPattern(string input)
|
||||
{
|
||||
string[,] charmap = {
|
||||
{ @"~", " - " },
|
||||
{ @"_", " " },
|
||||
{ @":", " " },
|
||||
{ @">", ")" },
|
||||
{ @"<", "(" },
|
||||
{ @"\|", "-" },
|
||||
{ "\"", "'" },
|
||||
{ @"\*", "." },
|
||||
{ @"\\", "-" },
|
||||
{ @"/", "-" },
|
||||
{ @"\?", " " },
|
||||
{ @"\(([^)(]*)\(([^)]*)\)([^)(]*)\)", " " },
|
||||
{ @"\(([^)]+)\)", " " },
|
||||
{ @"\[([^]]+)\]", " " },
|
||||
{ @"\{([^}]+)\}", " " },
|
||||
{ @"(ZZZJUNK|ZZZ-UNK-|ZZZ-UNK |zzz unknow |zzz unk |Copy of |[.][a-z]{3}[.][a-z]{3}[.]|[.][a-z]{3}[.])", " " },
|
||||
{ @" (r|rev|v|ver)\s*[\d\.]+[^\s]*", " " },
|
||||
{ @"(( )|(\A))(\d{6}|\d{8})(( )|(\Z))", " " },
|
||||
{ @"(( )|(\A))(\d{1,2})-(\d{1,2})-(\d{4}|\d{2})", " " },
|
||||
{ @"(( )|(\A))(\d{4}|\d{2})-(\d{1,2})-(\d{1,2})", " " },
|
||||
{ @"[-]+", "-" },
|
||||
{ @"\A\s*\)", " " },
|
||||
{ @"\A\s*(,|-)", " " },
|
||||
{ @"\s+", " " },
|
||||
{ @"\s+,", "," },
|
||||
{ @"\s*(,|-)\s*\Z", " " },
|
||||
};
|
||||
|
||||
for (int i = 0; i < charmap.GetLength(0); i++)
|
||||
{
|
||||
input = Regex.Replace(input, charmap[i, 0], charmap[i, 1]);
|
||||
}
|
||||
|
||||
return input;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Externally sourced methods
|
||||
|
||||
/// <summary>
|
||||
/// Returns the human-readable file size for an arbitrary, 64-bit file size
|
||||
/// The default format is "0.### XB", e.g. "4.2 KB" or "1.434 GB"
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <returns>Human-readable file size</returns>
|
||||
/// <link>http://www.somacon.com/p576.php</link>
|
||||
public static string GetBytesReadable(long input)
|
||||
{
|
||||
// Get absolute value
|
||||
long absolute_i = (input < 0 ? -input : input);
|
||||
// Determine the suffix and readable value
|
||||
string suffix;
|
||||
double readable;
|
||||
if (absolute_i >= 0x1000000000000000) // Exabyte
|
||||
{
|
||||
suffix = "EB";
|
||||
readable = (input >> 50);
|
||||
}
|
||||
else if (absolute_i >= 0x4000000000000) // Petabyte
|
||||
{
|
||||
suffix = "PB";
|
||||
readable = (input >> 40);
|
||||
}
|
||||
else if (absolute_i >= 0x10000000000) // Terabyte
|
||||
{
|
||||
suffix = "TB";
|
||||
readable = (input >> 30);
|
||||
}
|
||||
else if (absolute_i >= 0x40000000) // Gigabyte
|
||||
{
|
||||
suffix = "GB";
|
||||
readable = (input >> 20);
|
||||
}
|
||||
else if (absolute_i >= 0x100000) // Megabyte
|
||||
{
|
||||
suffix = "MB";
|
||||
readable = (input >> 10);
|
||||
}
|
||||
else if (absolute_i >= 0x400) // Kilobyte
|
||||
{
|
||||
suffix = "KB";
|
||||
readable = input;
|
||||
}
|
||||
else
|
||||
{
|
||||
return input.ToString("0 B"); // Byte
|
||||
}
|
||||
// Divide by 1024 to get fractional value
|
||||
readable = (readable / 1024);
|
||||
// Return formatted number with suffix
|
||||
return readable.ToString("0.### ") + suffix;
|
||||
}
|
||||
|
||||
/// <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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// http://stackoverflow.com/questions/5613279/c-sharp-hex-to-ascii
|
||||
/// </summary>
|
||||
public static string ConvertHexToAscii(string hexString)
|
||||
{
|
||||
if (hexString.Contains("-"))
|
||||
{
|
||||
hexString = hexString.Replace("-", "");
|
||||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
for (int i = 0; i < hexString.Length; i += 2)
|
||||
{
|
||||
String hs = hexString.Substring(i, 2);
|
||||
sb.Append(Convert.ToChar(Convert.ToUInt32(hs, 16)));
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// http://stackoverflow.com/questions/15920741/convert-from-string-ascii-to-string-hex
|
||||
/// </summary>
|
||||
public static string ConvertAsciiToHex(string asciiString)
|
||||
{
|
||||
string hexOutput = "";
|
||||
foreach (char _eachChar in asciiString.ToCharArray())
|
||||
{
|
||||
// Get the integral value of the character.
|
||||
int value = Convert.ToInt32(_eachChar);
|
||||
// Convert the decimal value to a hexadecimal value in string form.
|
||||
hexOutput += String.Format("{0:X2}", value).Remove(0, 2);
|
||||
// to make output as your eg
|
||||
// hexOutput +=" "+ String.Format("{0:X}", value);
|
||||
}
|
||||
|
||||
return hexOutput;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adapted from 7-zip Source Code: CPP/Windows/TimeUtils.cpp:FileTimeToDosTime
|
||||
/// </summary>
|
||||
public static uint ConvertDateTimeToMsDosTimeFormat(DateTime dateTime)
|
||||
{
|
||||
uint year = (uint)((dateTime.Year - 1980) % 128);
|
||||
uint mon = (uint)dateTime.Month;
|
||||
uint day = (uint)dateTime.Day;
|
||||
uint hour = (uint)dateTime.Hour;
|
||||
uint min = (uint)dateTime.Minute;
|
||||
uint sec = (uint)dateTime.Second;
|
||||
|
||||
return (year << 25) | (mon << 21) | (day << 16) | (hour << 11) | (min << 5) | (sec >> 1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adapted from 7-zip Source Code: CPP/Windows/TimeUtils.cpp:DosTimeToFileTime
|
||||
/// </summary>
|
||||
public static DateTime ConvertMsDosTimeFormatToDateTime(uint msDosDateTime)
|
||||
{
|
||||
return new DateTime((int)(1980 + (msDosDateTime >> 25)), (int)((msDosDateTime >> 21) & 0xF), (int)((msDosDateTime >> 16) & 0x1F),
|
||||
(int)((msDosDateTime >> 11) & 0x1F), (int)((msDosDateTime >> 5) & 0x3F), (int)((msDosDateTime & 0x1F) * 2));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines a text file's encoding by analyzing its byte order mark (BOM).
|
||||
/// Defaults to ASCII when detection of the text file's endianness fails.
|
||||
/// http://stackoverflow.com/questions/3825390/effective-way-to-find-any-files-encoding
|
||||
/// </summary>
|
||||
/// <param name="filename">The text file to analyze.</param>
|
||||
/// <returns>The detected encoding.</returns>
|
||||
public static Encoding GetEncoding(string filename)
|
||||
{
|
||||
// Read the BOM
|
||||
var bom = new byte[4];
|
||||
FileStream file = FileTools.TryOpenRead(filename);
|
||||
file.Read(bom, 0, 4);
|
||||
file.Dispose();
|
||||
|
||||
// Analyze the BOM
|
||||
if (bom[0] == 0x2b && bom[1] == 0x2f && bom[2] == 0x76) return Encoding.UTF7;
|
||||
if (bom[0] == 0xef && bom[1] == 0xbb && bom[2] == 0xbf) return Encoding.UTF8;
|
||||
if (bom[0] == 0xff && bom[1] == 0xfe) return Encoding.Unicode; //UTF-16LE
|
||||
if (bom[0] == 0xfe && bom[1] == 0xff) return Encoding.BigEndianUnicode; //UTF-16BE
|
||||
if (bom[0] == 0 && bom[1] == 0 && bom[2] == 0xfe && bom[3] == 0xff) return Encoding.UTF32;
|
||||
return Encoding.Default;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// http://stackoverflow.com/questions/1600962/displaying-the-build-date
|
||||
/// </summary>
|
||||
public static DateTime GetLinkerTime(this Assembly assembly, TimeZoneInfo target = null)
|
||||
{
|
||||
var filePath = assembly.Location;
|
||||
const int c_PeHeaderOffset = 60;
|
||||
const int c_LinkerTimestampOffset = 8;
|
||||
|
||||
var buffer = new byte[2048];
|
||||
|
||||
using (var stream = FileTools.TryOpenRead(filePath))
|
||||
stream.Read(buffer, 0, 2048);
|
||||
|
||||
var offset = BitConverter.ToInt32(buffer, c_PeHeaderOffset);
|
||||
var secondsSince1970 = BitConverter.ToInt32(buffer, offset + c_LinkerTimestampOffset);
|
||||
var epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
|
||||
|
||||
var linkTimeUtc = epoch.AddSeconds(secondsSince1970);
|
||||
|
||||
var tz = target ?? TimeZoneInfo.Local;
|
||||
var localTime = TimeZoneInfo.ConvertTimeFromUtc(linkTimeUtc, tz);
|
||||
|
||||
return localTime;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// http://stackoverflow.com/questions/248603/natural-sort-order-in-c-sharp
|
||||
/// </summary>
|
||||
public static IEnumerable<T> OrderByAlphaNumeric<T>(this IEnumerable<T> source, Func<T, string> selector)
|
||||
{
|
||||
int max = source
|
||||
.SelectMany(i => Regex.Matches(selector(i), @"\d+").Cast<Match>().Select(m => (int?)m.Value.Length))
|
||||
.Max() ?? 0;
|
||||
|
||||
return source.OrderBy(i => Regex.Replace(selector(i), @"\d+", m => m.Value.PadLeft(max, '0')));
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user