mirror of
https://github.com/claunia/SabreTools.git
synced 2025-12-16 19:14:27 +00:00
[SabreHelper] Rename logical folder
This commit is contained in:
119
SabreHelper/Tools/CRC32.cs
Normal file
119
SabreHelper/Tools/CRC32.cs
Normal file
@@ -0,0 +1,119 @@
|
||||
// Copyright (c) Damien Guard. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
// Originally published at http://damieng.com/blog/2006/08/08/calculating_crc32_in_c_and_net
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
namespace DamienG.Security.Cryptography
|
||||
{
|
||||
/// <summary>
|
||||
/// Implements a 32-bit CRC hash algorithm compatible with Zip etc.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Crc32 should only be used for backward compatibility with older file formats
|
||||
/// and algorithms. It is not secure enough for new applications.
|
||||
/// If you need to call multiple times for the same data either use the HashAlgorithm
|
||||
/// interface or remember that the result of one Compute call needs to be ~ (XOR) before
|
||||
/// being passed in as the seed for the next Compute call.
|
||||
/// </remarks>
|
||||
public sealed class Crc32 : HashAlgorithm
|
||||
{
|
||||
public const UInt32 DefaultPolynomial = 0xedb88320u;
|
||||
public const UInt32 DefaultSeed = 0xffffffffu;
|
||||
|
||||
static UInt32[] defaultTable;
|
||||
|
||||
readonly UInt32 seed;
|
||||
readonly UInt32[] table;
|
||||
UInt32 hash;
|
||||
|
||||
public Crc32()
|
||||
: this(DefaultPolynomial, DefaultSeed)
|
||||
{
|
||||
}
|
||||
|
||||
public Crc32(UInt32 polynomial, UInt32 seed)
|
||||
{
|
||||
table = InitializeTable(polynomial);
|
||||
this.seed = hash = seed;
|
||||
}
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
hash = seed;
|
||||
}
|
||||
|
||||
protected override void HashCore(byte[] array, int ibStart, int cbSize)
|
||||
{
|
||||
hash = CalculateHash(table, hash, array, ibStart, cbSize);
|
||||
}
|
||||
|
||||
protected override byte[] HashFinal()
|
||||
{
|
||||
var hashBuffer = UInt32ToBigEndianBytes(~hash);
|
||||
HashValue = hashBuffer;
|
||||
return hashBuffer;
|
||||
}
|
||||
|
||||
public override int HashSize { get { return 32; } }
|
||||
|
||||
public static UInt32 Compute(byte[] buffer)
|
||||
{
|
||||
return Compute(DefaultSeed, buffer);
|
||||
}
|
||||
|
||||
public static UInt32 Compute(UInt32 seed, byte[] buffer)
|
||||
{
|
||||
return Compute(DefaultPolynomial, seed, buffer);
|
||||
}
|
||||
|
||||
public static UInt32 Compute(UInt32 polynomial, UInt32 seed, byte[] buffer)
|
||||
{
|
||||
return ~CalculateHash(InitializeTable(polynomial), seed, buffer, 0, buffer.Length);
|
||||
}
|
||||
|
||||
static UInt32[] InitializeTable(UInt32 polynomial)
|
||||
{
|
||||
if (polynomial == DefaultPolynomial && defaultTable != null)
|
||||
return defaultTable;
|
||||
|
||||
var createTable = new UInt32[256];
|
||||
for (var i = 0; i < 256; i++)
|
||||
{
|
||||
var entry = (UInt32)i;
|
||||
for (var j = 0; j < 8; j++)
|
||||
if ((entry & 1) == 1)
|
||||
entry = (entry >> 1) ^ polynomial;
|
||||
else
|
||||
entry = entry >> 1;
|
||||
createTable[i] = entry;
|
||||
}
|
||||
|
||||
if (polynomial == DefaultPolynomial)
|
||||
defaultTable = createTable;
|
||||
|
||||
return createTable;
|
||||
}
|
||||
|
||||
static UInt32 CalculateHash(UInt32[] table, UInt32 seed, IList<byte> buffer, int start, int size)
|
||||
{
|
||||
var crc = seed;
|
||||
for (var i = start; i < size - start; i++)
|
||||
crc = (crc >> 8) ^ table[buffer[i] ^ crc & 0xff];
|
||||
return crc;
|
||||
}
|
||||
|
||||
static byte[] UInt32ToBigEndianBytes(UInt32 uint32)
|
||||
{
|
||||
var result = BitConverter.GetBytes(uint32);
|
||||
|
||||
if (BitConverter.IsLittleEndian)
|
||||
Array.Reverse(result);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
1931
SabreHelper/Tools/DatTools.cs
Normal file
1931
SabreHelper/Tools/DatTools.cs
Normal file
File diff suppressed because it is too large
Load Diff
564
SabreHelper/Tools/Output.cs
Normal file
564
SabreHelper/Tools/Output.cs
Normal file
@@ -0,0 +1,564 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Web;
|
||||
|
||||
namespace SabreTools.Helper
|
||||
{
|
||||
public class Output
|
||||
{
|
||||
/// <summary>
|
||||
/// Create and open an output file for writing direct from a dictionary
|
||||
/// </summary>
|
||||
/// <param name="datdata">All information for creating the datfile header</param>
|
||||
/// <param name="outDir">Set the output directory</param>
|
||||
/// <param name="logger">Logger object for console and/or file output</param>
|
||||
/// <param name="norename">True if games should only be compared on game and file name (default), false if system and source are counted</param>
|
||||
/// <param name="stats">True if DAT statistics should be output on write, false otherwise (default)</param>
|
||||
/// <returns>True if the DAT was written correctly, false otherwise</returns>
|
||||
/// <remarks>
|
||||
/// The following features have been requested for file output:
|
||||
/// - Have the ability to strip special (non-ASCII) characters from rom information
|
||||
/// - Add a flag for ignoring roms with blank sizes
|
||||
/// </remarks>
|
||||
public static bool WriteDatfile(DatData datdata, string outDir, Logger logger, bool norename = true, bool stats = false)
|
||||
{
|
||||
// Output initial statistics, for kicks
|
||||
if (stats)
|
||||
{
|
||||
Stats.OutputStats(datdata, logger, (datdata.RomCount + datdata.DiskCount == 0));
|
||||
}
|
||||
|
||||
// Bucket roms by game name and optionally dedupe
|
||||
SortedDictionary<string, List<RomData>> sortable = DatTools.BucketByGame(datdata.Roms, datdata.MergeRoms, norename, logger);
|
||||
|
||||
// Now write out to file
|
||||
// If it's empty, use the current folder
|
||||
if (outDir.Trim() == "")
|
||||
{
|
||||
outDir = Environment.CurrentDirectory;
|
||||
}
|
||||
|
||||
// Create the output directory if it doesn't already exist
|
||||
Directory.CreateDirectory(outDir);
|
||||
|
||||
// Get the outfile name
|
||||
string outfile = Style.CreateOutfileName(outDir, datdata);
|
||||
|
||||
logger.User("Opening file for writing: " + outfile);
|
||||
|
||||
try
|
||||
{
|
||||
FileStream fs = File.Create(outfile);
|
||||
StreamWriter sw = new StreamWriter(fs, Encoding.UTF8);
|
||||
|
||||
// Write out the header
|
||||
WriteHeader(sw, datdata, logger);
|
||||
|
||||
// Write out each of the machines and roms
|
||||
int depth = 2, last = -1;
|
||||
string lastgame = null;
|
||||
List<string> splitpath = new List<string>();
|
||||
foreach (List<RomData> roms in sortable.Values)
|
||||
{
|
||||
for (int index = 0; index < roms.Count; index++)
|
||||
{
|
||||
RomData rom = roms[index];
|
||||
List<string> newsplit = rom.Game.Split('\\').ToList();
|
||||
|
||||
// If we have a different game and we're not at the start of the list, output the end of last item
|
||||
if (lastgame != null && lastgame.ToLowerInvariant() != rom.Game.ToLowerInvariant())
|
||||
{
|
||||
depth = WriteEndGame(sw, rom, splitpath, newsplit, lastgame, datdata, depth, out last, logger);
|
||||
}
|
||||
|
||||
// If we have a new game, output the beginning of the new item
|
||||
if (lastgame == null || lastgame.ToLowerInvariant() != rom.Game.ToLowerInvariant())
|
||||
{
|
||||
depth = WriteStartGame(sw, rom, newsplit, lastgame, datdata, depth, last, logger);
|
||||
}
|
||||
|
||||
// If we have a "null" game (created by DATFromDir or something similar), log it to file
|
||||
if (rom.Name == "null" && rom.Size == -1 && rom.CRC == "null" && rom.MD5 == "null" && rom.SHA1 == "null")
|
||||
{
|
||||
logger.Log("Empty folder found: " + rom.Game);
|
||||
|
||||
// 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.Name = "-";
|
||||
rom.Size = Constants.SizeZero;
|
||||
rom.CRC = Constants.CRCZero;
|
||||
rom.MD5 = Constants.MD5Zero;
|
||||
rom.SHA1 = Constants.SHA1Zero;
|
||||
}
|
||||
|
||||
// Otherwise, set the new path and such, write out, and continue
|
||||
else
|
||||
{
|
||||
splitpath = newsplit;
|
||||
lastgame = rom.Game;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Now, output the rom data
|
||||
WriteRomData(sw, rom, lastgame, datdata, depth, logger);
|
||||
|
||||
// Set the new data to compare against
|
||||
splitpath = newsplit;
|
||||
lastgame = rom.Game;
|
||||
}
|
||||
}
|
||||
|
||||
// Write the file footer out
|
||||
WriteFooter(sw, datdata, depth, logger);
|
||||
|
||||
logger.Log("File written!" + Environment.NewLine);
|
||||
sw.Close();
|
||||
fs.Close();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.Error(ex.ToString());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write out DAT header using the supplied StreamWriter
|
||||
/// </summary>
|
||||
/// <param name="sw">StreamWriter to output to</param>
|
||||
/// <param name="datdata">DatData object representing DAT information</param>
|
||||
/// <param name="logger">Logger object for file and console output</param>
|
||||
/// <returns>True if the data was written, false on error</returns>
|
||||
public static bool WriteHeader(StreamWriter sw, DatData datdata, Logger logger)
|
||||
{
|
||||
try
|
||||
{
|
||||
string header = "";
|
||||
switch (datdata.OutputFormat)
|
||||
{
|
||||
case OutputFormat.ClrMamePro:
|
||||
header = "clrmamepro (\n" +
|
||||
"\tname \"" + datdata.Name + "\"\n" +
|
||||
"\tdescription \"" + datdata.Description + "\"\n" +
|
||||
"\tcategory \"" + datdata.Category + "\"\n" +
|
||||
"\tversion \"" + datdata.Version + "\"\n" +
|
||||
"\tdate \"" + datdata.Date + "\"\n" +
|
||||
"\tauthor \"" + datdata.Author + "\"\n" +
|
||||
"\temail \"" + datdata.Email + "\"\n" +
|
||||
"\thomepage \"" + datdata.Homepage + "\"\n" +
|
||||
"\turl \"" + datdata.Url + "\"\n" +
|
||||
"\tcomment \"" + datdata.Comment + "\"\n" +
|
||||
(datdata.ForcePacking == ForcePacking.Unzip ? "\tforcezipping no\n" : "") +
|
||||
")\n";
|
||||
break;
|
||||
case OutputFormat.MissFile:
|
||||
if (datdata.TSV)
|
||||
{
|
||||
header = "File Name\tInternal Name\tDescription\tGame Name\tGame Description\tType\t" +
|
||||
"Rom Name\tDisk Name\tSize\tCRC\tMD5\tSHA1\tNodump\n";
|
||||
}
|
||||
break;
|
||||
case OutputFormat.RomCenter:
|
||||
header = "[CREDITS]\n" +
|
||||
"author=" + datdata.Author + "\n" +
|
||||
"version=" + datdata.Version + "\n" +
|
||||
"comment=" + datdata.Comment + "\n" +
|
||||
"[DAT]\n" +
|
||||
"version=2.50\n" +
|
||||
"split=" + (datdata.ForceMerging == ForceMerging.Split ? "1" : "0") + "\n" +
|
||||
"merge=" + (datdata.ForceMerging == ForceMerging.Full ? "1" : "0") + "\n" +
|
||||
"[EMULATOR]\n" +
|
||||
"refname=" + datdata.Name + "\n" +
|
||||
"version=" + datdata.Description + "\n" +
|
||||
"[GAMES]\n";
|
||||
break;
|
||||
case OutputFormat.SabreDat:
|
||||
header = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
|
||||
"<!DOCTYPE datafile PUBLIC \"-//Logiqx//DTD ROM Management Datafile//EN\" \"http://www.logiqx.com/Dats/datafile.dtd\">\n\n" +
|
||||
"<datafile>\n" +
|
||||
"\t<header>\n" +
|
||||
"\t\t<name>" + HttpUtility.HtmlEncode(datdata.Name) + "</name>\n" +
|
||||
"\t\t<description>" + HttpUtility.HtmlEncode(datdata.Description) + "</description>\n" +
|
||||
"\t\t<category>" + HttpUtility.HtmlEncode(datdata.Category) + "</category>\n" +
|
||||
"\t\t<version>" + HttpUtility.HtmlEncode(datdata.Version) + "</version>\n" +
|
||||
"\t\t<date>" + HttpUtility.HtmlEncode(datdata.Date) + "</date>\n" +
|
||||
"\t\t<author>" + HttpUtility.HtmlEncode(datdata.Author) + "</author>\n" +
|
||||
"\t\t<comment>" + HttpUtility.HtmlEncode(datdata.Comment) + "</comment>\n" +
|
||||
(!String.IsNullOrEmpty(datdata.Type) && datdata.ForcePacking != ForcePacking.Unzip ?
|
||||
"\t\t<flags>\n" +
|
||||
(!String.IsNullOrEmpty(datdata.Type) ? "\t\t\t<flag name=\"type\" value=\"" + datdata.Type + "\"/>\n" : "") +
|
||||
(datdata.ForcePacking == ForcePacking.Unzip ? "\t\t\t<flag name=\"forcepacking\" value=\"unzip\"/>\n" : "") +
|
||||
"\t\t</flags>\n" : "") +
|
||||
"\t</header>\n" +
|
||||
"\t<data>\n";
|
||||
break;
|
||||
case OutputFormat.Xml:
|
||||
header = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
|
||||
"<!DOCTYPE datafile PUBLIC \"-//Logiqx//DTD ROM Management Datafile//EN\" \"http://www.logiqx.com/Dats/datafile.dtd\">\n\n" +
|
||||
"<datafile>\n" +
|
||||
"\t<header>\n" +
|
||||
"\t\t<name>" + HttpUtility.HtmlEncode(datdata.Name) + "</name>\n" +
|
||||
"\t\t<description>" + HttpUtility.HtmlEncode(datdata.Description) + "</description>\n" +
|
||||
"\t\t<category>" + HttpUtility.HtmlEncode(datdata.Category) + "</category>\n" +
|
||||
"\t\t<version>" + HttpUtility.HtmlEncode(datdata.Version) + "</version>\n" +
|
||||
"\t\t<date>" + HttpUtility.HtmlEncode(datdata.Date) + "</date>\n" +
|
||||
"\t\t<author>" + HttpUtility.HtmlEncode(datdata.Author) + "</author>\n" +
|
||||
"\t\t<email>" + HttpUtility.HtmlEncode(datdata.Email) + "</email>\n" +
|
||||
"\t\t<homepage>" + HttpUtility.HtmlEncode(datdata.Homepage) + "</homepage>\n" +
|
||||
"\t\t<url>" + HttpUtility.HtmlEncode(datdata.Url) + "</url>\n" +
|
||||
"\t\t<comment>" + HttpUtility.HtmlEncode(datdata.Comment) + "</comment>\n" +
|
||||
(!String.IsNullOrEmpty(datdata.Type) ? "\t\t<type>" + datdata.Type + "</type>\n" : "") +
|
||||
(datdata.ForcePacking == ForcePacking.Unzip ? "\t\t<clrmamepro forcepacking=\"unzip\" />\n" : "") +
|
||||
"\t</header>\n";
|
||||
break;
|
||||
}
|
||||
|
||||
// Write the header out
|
||||
sw.Write(header);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.Error(ex.ToString());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write out Game start using the supplied StreamWriter
|
||||
/// </summary>
|
||||
/// <param name="sw">StreamWriter to output to</param>
|
||||
/// <param name="rom">RomData object to be output</param>
|
||||
/// <param name="newsplit">Split path representing the parent game (SabreDAT only)</param>
|
||||
/// <param name="lastgame">The name of the last game to be output</param>
|
||||
/// <param name="datdata">DatData object representing DAT information</param>
|
||||
/// <param name="depth">Current depth to output file at (SabreDAT only)</param>
|
||||
/// <param name="last">Last known depth to cycle back from (SabreDAT only)</param>
|
||||
/// <param name="logger">Logger object for file and console output</param>
|
||||
/// <returns>The new depth of the tag</returns>
|
||||
public static int WriteStartGame(StreamWriter sw, RomData rom, List<string> newsplit, string lastgame, DatData datdata, int depth, int last, Logger logger)
|
||||
{
|
||||
try
|
||||
{
|
||||
// No game should start with a path separator
|
||||
if (rom.Game.StartsWith(Path.DirectorySeparatorChar.ToString()))
|
||||
{
|
||||
rom.Game = rom.Game.Substring(1);
|
||||
}
|
||||
|
||||
string state = "";
|
||||
switch (datdata.OutputFormat)
|
||||
{
|
||||
case OutputFormat.ClrMamePro:
|
||||
state += "game (\n\tname \"" + rom.Game + "\"\n" +
|
||||
"\tdescription \"" + rom.Game + "\"\n";
|
||||
break;
|
||||
case OutputFormat.SabreDat:
|
||||
for (int i = (last == -1 ? 0 : last); i < newsplit.Count; i++)
|
||||
{
|
||||
for (int j = 0; j < depth - last + i - (lastgame == null ? 1 : 0); j++)
|
||||
{
|
||||
state += "\t";
|
||||
}
|
||||
state += "<directory name=\"" + HttpUtility.HtmlEncode(newsplit[i]) + "\" description=\"" +
|
||||
HttpUtility.HtmlEncode(newsplit[i]) + "\">\n";
|
||||
}
|
||||
depth = depth - (last == -1 ? 0 : last) + newsplit.Count;
|
||||
break;
|
||||
case OutputFormat.Xml:
|
||||
state += "\t<machine name=\"" + HttpUtility.HtmlEncode(rom.Game) + "\">\n" +
|
||||
"\t\t<description>" + HttpUtility.HtmlEncode(rom.Game) + "</description>\n";
|
||||
break;
|
||||
}
|
||||
|
||||
sw.Write(state);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.Error(ex.ToString());
|
||||
return depth;
|
||||
}
|
||||
|
||||
return depth;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write out Game start using the supplied StreamWriter
|
||||
/// </summary>
|
||||
/// <param name="sw">StreamWriter to output to</param>
|
||||
/// <param name="rom">RomData object to be output</param>
|
||||
/// <param name="splitpath">Split path representing last kwown parent game (SabreDAT only)</param>
|
||||
/// <param name="newsplit">Split path representing the parent game (SabreDAT only)</param>
|
||||
/// <param name="lastgame">The name of the last game to be output</param>
|
||||
/// <param name="datdata">DatData object representing DAT information</param>
|
||||
/// <param name="depth">Current depth to output file at (SabreDAT only)</param>
|
||||
/// <param name="last">Last known depth to cycle back from (SabreDAT only)</param>
|
||||
/// <param name="logger">Logger object for file and console output</param>
|
||||
/// <returns>The new depth of the tag</returns>
|
||||
public static int WriteEndGame(StreamWriter sw, RomData rom, List<string> splitpath, List<string> newsplit, string lastgame, DatData datdata, int depth, out int last, Logger logger)
|
||||
{
|
||||
last = 0;
|
||||
|
||||
try
|
||||
{
|
||||
string state = "";
|
||||
|
||||
switch (datdata.OutputFormat)
|
||||
{
|
||||
case OutputFormat.ClrMamePro:
|
||||
state += ")\n";
|
||||
break;
|
||||
case OutputFormat.SabreDat:
|
||||
if (splitpath != null)
|
||||
{
|
||||
for (int i = 0; i < newsplit.Count && i < splitpath.Count; i++)
|
||||
{
|
||||
// Always keep track of the last seen item
|
||||
last = i;
|
||||
|
||||
// If we find a difference, break
|
||||
if (newsplit[i] != splitpath[i])
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Now that we have the last known position, take down all open folders
|
||||
for (int i = depth - 1; i > last + 1; i--)
|
||||
{
|
||||
// Print out the number of tabs and the end folder
|
||||
for (int j = 0; j < i; j++)
|
||||
{
|
||||
state += "\t";
|
||||
}
|
||||
state += "</directory>\n";
|
||||
}
|
||||
|
||||
// Reset the current depth
|
||||
depth = 2 + last;
|
||||
}
|
||||
break;
|
||||
case OutputFormat.Xml:
|
||||
state += "\t</machine>\n";
|
||||
break;
|
||||
}
|
||||
|
||||
sw.Write(state);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.Error(ex.ToString());
|
||||
return depth;
|
||||
}
|
||||
|
||||
return depth;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write out RomData using the supplied StreamWriter
|
||||
/// </summary>
|
||||
/// <param name="sw">StreamWriter to output to</param>
|
||||
/// <param name="rom">RomData object to be output</param>
|
||||
/// <param name="lastgame">The name of the last game to be output</param>
|
||||
/// <param name="datdata">DatData object representing DAT information</param>
|
||||
/// <param name="depth">Current depth to output file at (SabreDAT only)</param>
|
||||
/// <param name="logger">Logger object for file and console output</param>
|
||||
/// <returns>True if the data was written, false on error</returns>
|
||||
public static bool WriteRomData(StreamWriter sw, RomData rom, string lastgame, DatData datdata, int depth, Logger logger)
|
||||
{
|
||||
try
|
||||
{
|
||||
string state = "";
|
||||
switch (datdata.OutputFormat)
|
||||
{
|
||||
case OutputFormat.ClrMamePro:
|
||||
state += "\t" + rom.Type + " ( name \"" + rom.Name + "\"" +
|
||||
(rom.Size != -1 ? " size " + rom.Size : "") +
|
||||
(!String.IsNullOrEmpty(rom.CRC) ? " crc " + rom.CRC.ToLowerInvariant() : "") +
|
||||
(!String.IsNullOrEmpty(rom.MD5) ? " md5 " + rom.MD5.ToLowerInvariant() : "") +
|
||||
(!String.IsNullOrEmpty(rom.SHA1) ? " sha1 " + rom.SHA1.ToLowerInvariant() : "") +
|
||||
(!String.IsNullOrEmpty(rom.Date) ? " date \"" + rom.Date + "\"" : "") +
|
||||
(rom.Nodump ? " flags nodump" : "") +
|
||||
" )\n";
|
||||
break;
|
||||
case OutputFormat.MissFile:
|
||||
string pre = datdata.Prefix + (datdata.Quotes || datdata.TSV ? "\"" : "");
|
||||
string post = (datdata.Quotes || datdata.TSV ? "\"" : "") + datdata.Postfix;
|
||||
|
||||
// Check for special strings in prefix and postfix
|
||||
pre = pre.Replace("%crc%", rom.CRC).Replace("%md5%", rom.MD5).Replace("%sha1%", rom.SHA1).Replace("%size%", rom.Size.ToString());
|
||||
post = post.Replace("%crc%", rom.CRC).Replace("%md5%", rom.MD5).Replace("%sha1%", rom.SHA1).Replace("%size%", rom.Size.ToString());
|
||||
|
||||
// If we're in Romba mode, the state is consistent
|
||||
if (datdata.Romba)
|
||||
{
|
||||
// We can only write out if there's a SHA-1
|
||||
if (rom.SHA1 != "")
|
||||
{
|
||||
string name = rom.SHA1.Substring(0, 2) + "/" + rom.SHA1.Substring(2, 2) + "/" + rom.SHA1.Substring(4, 2) + "/" +
|
||||
rom.SHA1.Substring(6, 2) + "/" + rom.SHA1 + ".gz";
|
||||
state += pre + name + post + "\n";
|
||||
}
|
||||
}
|
||||
// If we're in TSV mode, similarly the state is consistent
|
||||
else if (datdata.TSV)
|
||||
{
|
||||
string inline = datdata.FileName + "\t" + datdata.Name + "\t" + datdata.Description + "\t" + rom.Game + "\t" + rom.Game + "\t" +
|
||||
rom.Type + "\t" + (rom.Type == "rom" ? rom.Name : "") + "\t" + (rom.Type == "disk" ? rom.Name : "") + "\t" + rom.Size + "\t" +
|
||||
rom.CRC + "\t" + rom.MD5 + "\t" + rom.SHA1 + "\t" + (rom.Nodump ? "Nodump" : "");
|
||||
state += pre + inline + post + "\n";
|
||||
}
|
||||
// Otherwise, use any flags
|
||||
else
|
||||
{
|
||||
string name = (datdata.UseGame ? rom.Game : rom.Name);
|
||||
if (datdata.RepExt != "")
|
||||
{
|
||||
string dir = Path.GetDirectoryName(name);
|
||||
dir = (dir.EndsWith(Path.DirectorySeparatorChar.ToString()) ? dir : dir + Path.DirectorySeparatorChar);
|
||||
dir = (dir.StartsWith(Path.DirectorySeparatorChar.ToString()) ? dir.Remove(0, 1) : dir);
|
||||
name = dir + Path.GetFileNameWithoutExtension(name) + datdata.RepExt;
|
||||
}
|
||||
if (datdata.AddExt != "")
|
||||
{
|
||||
name += datdata.AddExt;
|
||||
}
|
||||
if (!datdata.UseGame && datdata.GameName)
|
||||
{
|
||||
name = (rom.Game.EndsWith(Path.DirectorySeparatorChar.ToString()) ? rom.Game : rom.Game + Path.DirectorySeparatorChar) + name;
|
||||
}
|
||||
|
||||
if (datdata.UseGame && rom.Game != lastgame)
|
||||
{
|
||||
state += pre + name + post + "\n";
|
||||
lastgame = rom.Game;
|
||||
}
|
||||
else if (!datdata.UseGame)
|
||||
{
|
||||
state += pre + name + post + "\n";
|
||||
}
|
||||
}
|
||||
break;
|
||||
case OutputFormat.RomCenter:
|
||||
state += "¬¬¬" + HttpUtility.HtmlEncode(rom.Game) +
|
||||
"¬" + HttpUtility.HtmlEncode(rom.Game) +
|
||||
"¬" + HttpUtility.HtmlEncode(rom.Name) +
|
||||
"¬" + rom.CRC.ToLowerInvariant() +
|
||||
"¬" + (rom.Size != -1 ? rom.Size.ToString() : "") + "¬¬¬\n";
|
||||
break;
|
||||
case OutputFormat.SabreDat:
|
||||
string prefix = "";
|
||||
for (int i = 0; i < depth; i++)
|
||||
{
|
||||
prefix += "\t";
|
||||
}
|
||||
|
||||
state += prefix;
|
||||
state += "<file type=\"" + rom.Type + "\" name=\"" + HttpUtility.HtmlEncode(rom.Name) + "\"" +
|
||||
(rom.Size != -1 ? " size=\"" + rom.Size + "\"" : "") +
|
||||
(!String.IsNullOrEmpty(rom.CRC) ? " crc=\"" + rom.CRC.ToLowerInvariant() + "\"" : "") +
|
||||
(!String.IsNullOrEmpty(rom.MD5) ? " md5=\"" + rom.MD5.ToLowerInvariant() + "\"" : "") +
|
||||
(!String.IsNullOrEmpty(rom.SHA1) ? " sha1=\"" + rom.SHA1.ToLowerInvariant() + "\"" : "") +
|
||||
(!String.IsNullOrEmpty(rom.Date) ? " date=\"" + rom.Date + "\"" : "") +
|
||||
(rom.Nodump ? prefix + "/>\n" + prefix + "\t<flags>\n" +
|
||||
prefix + "\t\t<flag name=\"status\" value=\"nodump\"/>\n" +
|
||||
prefix + "\t</flags>\n" +
|
||||
prefix + "</file>\n" :
|
||||
"/>\n");
|
||||
break;
|
||||
case OutputFormat.Xml:
|
||||
state += "\t\t<" + rom.Type + " name=\"" + HttpUtility.HtmlEncode(rom.Name) + "\"" +
|
||||
(rom.Size != -1 ? " size=\"" + rom.Size + "\"" : "") +
|
||||
(!String.IsNullOrEmpty(rom.CRC) ? " crc=\"" + rom.CRC.ToLowerInvariant() + "\"" : "") +
|
||||
(!String.IsNullOrEmpty(rom.MD5) ? " md5=\"" + rom.MD5.ToLowerInvariant() + "\"" : "") +
|
||||
(!String.IsNullOrEmpty(rom.SHA1) ? " sha1=\"" + rom.SHA1.ToLowerInvariant() + "\"" : "") +
|
||||
(!String.IsNullOrEmpty(rom.Date) ? " date=\"" + rom.Date + "\"" : "") +
|
||||
(rom.Nodump ? " status=\"nodump\"" : "") +
|
||||
"/>\n";
|
||||
break;
|
||||
}
|
||||
|
||||
sw.Write(state);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.Error(ex.ToString());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write out DAT footer using the supplied StreamWriter
|
||||
/// </summary>
|
||||
/// <param name="sw">StreamWriter to output to</param>
|
||||
/// <param name="datdata">DatData object representing DAT information</param>
|
||||
/// /// <param name="depth">Current depth to output file at (SabreDAT only)</param>
|
||||
/// <param name="logger">Logger object for file and console output</param>
|
||||
/// <returns>True if the data was written, false on error</returns>
|
||||
public static bool WriteFooter(StreamWriter sw, DatData datdata, int depth, Logger logger)
|
||||
{
|
||||
try
|
||||
{
|
||||
string footer = "";
|
||||
|
||||
// If we have roms, output the full footer
|
||||
if (datdata.Roms != null && datdata.Roms.Count > 0)
|
||||
{
|
||||
switch (datdata.OutputFormat)
|
||||
{
|
||||
case OutputFormat.ClrMamePro:
|
||||
footer = ")";
|
||||
break;
|
||||
case OutputFormat.SabreDat:
|
||||
for (int i = depth - 1; i >= 2; i--)
|
||||
{
|
||||
// Print out the number of tabs and the end folder
|
||||
for (int j = 0; j < i; j++)
|
||||
{
|
||||
footer += "\t";
|
||||
}
|
||||
footer += "</directory>\n";
|
||||
}
|
||||
footer += "\t</data>\n</datafile>";
|
||||
break;
|
||||
case OutputFormat.Xml:
|
||||
footer = "\t</machine>\n</datafile>";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, output the abbreviated form
|
||||
else
|
||||
{
|
||||
switch (datdata.OutputFormat)
|
||||
{
|
||||
case OutputFormat.SabreDat:
|
||||
case OutputFormat.Xml:
|
||||
footer = "</datafile>";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Write the footer out
|
||||
sw.Write(footer);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.Error(ex.ToString());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
147
SabreHelper/Tools/Stats.cs
Normal file
147
SabreHelper/Tools/Stats.cs
Normal file
@@ -0,0 +1,147 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using SabreTools.Helper;
|
||||
|
||||
namespace SabreTools
|
||||
{
|
||||
/// <summary>
|
||||
/// Get statistics on one or more DAT files
|
||||
/// </summary>
|
||||
public class Stats
|
||||
{
|
||||
// Private instance variables
|
||||
private List<String> _inputs;
|
||||
private bool _single;
|
||||
private Logger _logger;
|
||||
|
||||
/// <summary>
|
||||
/// Create a new UncompressedSize object
|
||||
/// </summary>
|
||||
/// <param name="inputs">List of files and folders to parse</param>
|
||||
/// <param name="single">True if single DAT stats are output, false otherwise</param>
|
||||
/// <param name="logger">Logger object for file and console output</param>
|
||||
public Stats(List<String> inputs, bool single, Logger logger)
|
||||
{
|
||||
_inputs = inputs;
|
||||
_single = single;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Output all requested statistics
|
||||
/// </summary>
|
||||
/// <returns>True if output succeeded, false otherwise</returns>
|
||||
public bool Process()
|
||||
{
|
||||
// Init all total variables
|
||||
long totalSize = 0;
|
||||
long totalGame = 0;
|
||||
long totalRom = 0;
|
||||
long totalDisk = 0;
|
||||
long totalCRC = 0;
|
||||
long totalMD5 = 0;
|
||||
long totalSHA1 = 0;
|
||||
long totalNodump = 0;
|
||||
|
||||
/// Now process each of the input files
|
||||
foreach (string filename in _inputs)
|
||||
{
|
||||
_logger.User("Beginning stat collection for '" + filename + "'");
|
||||
List<String> games = new List<String>();
|
||||
DatData datdata = new DatData();
|
||||
datdata = DatTools.Parse(filename, 0, 0, datdata, _logger);
|
||||
SortedDictionary<string, List<RomData>> newroms = DatTools.BucketByGame(datdata.Roms, false, true, _logger);
|
||||
|
||||
// Output single DAT stats (if asked)
|
||||
if (_single)
|
||||
{
|
||||
_logger.User(@"\nFor file '" + filename + @"':
|
||||
--------------------------------------------------");
|
||||
OutputStats(datdata, _logger);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.User("\nAdding stats for file '" + filename + "'");
|
||||
}
|
||||
|
||||
// Add single DAT stats to totals
|
||||
totalSize += datdata.TotalSize;
|
||||
totalGame += newroms.Count;
|
||||
totalRom += datdata.RomCount;
|
||||
totalDisk += datdata.DiskCount;
|
||||
totalCRC += datdata.CRCCount;
|
||||
totalMD5 += datdata.MD5Count;
|
||||
totalSHA1 += datdata.SHA1Count;
|
||||
totalNodump += datdata.NodumpCount;
|
||||
}
|
||||
|
||||
// Output total DAT stats
|
||||
if (!_single) { _logger.User(""); }
|
||||
DatData totaldata = new DatData
|
||||
{
|
||||
TotalSize = totalSize,
|
||||
RomCount = totalRom,
|
||||
DiskCount = totalDisk,
|
||||
CRCCount = totalCRC,
|
||||
MD5Count = totalMD5,
|
||||
SHA1Count = totalSHA1,
|
||||
NodumpCount = totalNodump,
|
||||
};
|
||||
_logger.User(@"For ALL DATs found
|
||||
--------------------------------------------------");
|
||||
OutputStats(totaldata, _logger);
|
||||
_logger.User(@"
|
||||
Please check the log folder if the stats scrolled offscreen");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Output the stats in a human-readable format
|
||||
/// </summary>
|
||||
/// <param name="datdata">DatData object to read stats from</param>
|
||||
/// <param name="logger">Logger object for file and console writing</param>
|
||||
/// <param name="recalculate">True if numbers should be recalculated for the DAT, false otherwise (default)</param>
|
||||
public static void OutputStats(DatData datdata, Logger logger, bool recalculate = false)
|
||||
{
|
||||
if (recalculate)
|
||||
{
|
||||
// Wipe out any stats already there
|
||||
datdata.RomCount = 0;
|
||||
datdata.DiskCount = 0;
|
||||
datdata.TotalSize = 0;
|
||||
datdata.CRCCount = 0;
|
||||
datdata.MD5Count = 0;
|
||||
datdata.SHA1Count = 0;
|
||||
datdata.NodumpCount = 0;
|
||||
|
||||
// Loop through and add
|
||||
foreach (List<RomData> roms in datdata.Roms.Values)
|
||||
{
|
||||
foreach (RomData rom in roms)
|
||||
{
|
||||
datdata.RomCount += (rom.Type == "rom" ? 1 : 0);
|
||||
datdata.DiskCount += (rom.Type == "disk" ? 1 : 0);
|
||||
datdata.TotalSize += rom.Size;
|
||||
datdata.CRCCount += (String.IsNullOrEmpty(rom.CRC) ? 0 : 1);
|
||||
datdata.MD5Count += (String.IsNullOrEmpty(rom.MD5) ? 0 : 1);
|
||||
datdata.SHA1Count += (String.IsNullOrEmpty(rom.SHA1) ? 0 : 1);
|
||||
datdata.NodumpCount += (rom.Nodump ? 1 : 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SortedDictionary<string, List<RomData>> newroms = DatTools.BucketByGame(datdata.Roms, false, true, logger);
|
||||
logger.User(@" Uncompressed size: " + Style.GetBytesReadable(datdata.TotalSize) + @"
|
||||
Games found: " + newroms.Count + @"
|
||||
Roms found: " + datdata.RomCount + @"
|
||||
Disks found: " + datdata.DiskCount + @"
|
||||
Roms with CRC: " + datdata.CRCCount + @"
|
||||
Roms with MD5: " + datdata.MD5Count + @"
|
||||
Roms with SHA-1: " + datdata.SHA1Count + @"
|
||||
Roms with Nodump status: " + datdata.NodumpCount + @"
|
||||
");
|
||||
}
|
||||
}
|
||||
}
|
||||
256
SabreHelper/Tools/Style.cs
Normal file
256
SabreHelper/Tools/Style.cs
Normal file
@@ -0,0 +1,256 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Xml;
|
||||
|
||||
namespace SabreTools.Helper
|
||||
{
|
||||
/// <summary>
|
||||
/// Include character normalization and replacement mappings
|
||||
/// </summary>
|
||||
public class Style
|
||||
{
|
||||
/// <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>
|
||||
/// 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;
|
||||
}
|
||||
|
||||
/// <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>
|
||||
/// 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>
|
||||
/// <returns>String representing the proper name</returns>
|
||||
public static string CreateOutfileName(string outDir, DatData datdata)
|
||||
{
|
||||
// Double check the outdir for the end delim
|
||||
if (!outDir.EndsWith(Path.DirectorySeparatorChar.ToString()))
|
||||
{
|
||||
outDir += Path.DirectorySeparatorChar;
|
||||
}
|
||||
|
||||
// Get the extension from the output type
|
||||
string extension = "";
|
||||
switch(datdata.OutputFormat)
|
||||
{
|
||||
case OutputFormat.ClrMamePro:
|
||||
case OutputFormat.DOSCenter:
|
||||
case OutputFormat.RomCenter:
|
||||
extension = ".dat";
|
||||
break;
|
||||
case OutputFormat.MissFile:
|
||||
extension = ".txt";
|
||||
break;
|
||||
case OutputFormat.SabreDat:
|
||||
case OutputFormat.Xml:
|
||||
extension = ".xml";
|
||||
break;
|
||||
}
|
||||
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);
|
||||
|
||||
Console.WriteLine(outfile);
|
||||
|
||||
return outfile;
|
||||
}
|
||||
|
||||
/// <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>
|
||||
/// Converts a string to sentence case.
|
||||
/// </summary>
|
||||
/// <param name="input">The string to convert.</param>
|
||||
/// <returns>A string representing a sentence case string</returns>
|
||||
/// <remarks>http://stackoverflow.com/questions/3141426/net-method-to-convert-a-string-to-sentence-case</remarks>
|
||||
public static string SentenceCase(string input)
|
||||
{
|
||||
if (input.Length < 1)
|
||||
{
|
||||
return input;
|
||||
}
|
||||
|
||||
string sentence = input.ToLower();
|
||||
return sentence[0].ToString().ToUpper() + sentence.Substring(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user