TODO cleanup, fix Logiqx, INI parser for RC

This commit is contained in:
Matt Nadareski
2020-06-11 21:54:08 -07:00
parent ec52981070
commit 9baa20c0a7
6 changed files with 388 additions and 146 deletions

View File

@@ -12,7 +12,6 @@ using Mono.Data.Sqlite;
namespace RombaSharp
{
// TODO: Do same overhaul here as in SabreTools.Help.cs
public partial class RombaSharp
{
#region Private Flag features

View File

@@ -1025,7 +1025,7 @@ namespace SabreTools.Library.DatFiles
xtw.WriteAttributeString("name", rom.GetField(Field.Name, ExcludeFields));
if (!ExcludeFields[(int)Field.Size] && rom.Size != -1)
xtw.WriteAttributeString("size", rom.Size.ToString());
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.MD5, ExcludeFields)))
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.CRC, ExcludeFields)))
xtw.WriteAttributeString("crc", rom.CRC.ToLowerInvariant());
if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.MD5, ExcludeFields)))
xtw.WriteAttributeString("md5", rom.MD5.ToLowerInvariant());

View File

@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using SabreTools.Library.Data;
@@ -45,149 +46,87 @@ namespace SabreTools.Library.DatFiles
bool clean,
bool remUnicode)
{
// Open a file reader
Encoding enc = Utilities.GetEncoding(filename);
StreamReader sr = new StreamReader(Utilities.TryOpenRead(filename), enc);
// Outsource the work of parsing the file to a helper
IniFile ini = new IniFile(filename);
string blocktype = string.Empty;
while (!sr.EndOfStream)
// CREDITS section
Author = string.IsNullOrWhiteSpace(Author) ? ini["CREDITS.author"] : Author;
Version = string.IsNullOrWhiteSpace(Version) ? ini["CREDITS.version"] : Version;
Email = string.IsNullOrWhiteSpace(Email) ? ini["CREDITS.email"] : Email;
Homepage = string.IsNullOrWhiteSpace(Homepage) ? ini["CREDITS.homepage"] : Homepage;
Url = string.IsNullOrWhiteSpace(Url) ? ini["CREDITS.url"] : Url;
Date = string.IsNullOrWhiteSpace(Date) ? ini["CREDITS.date"] : Date;
// DAT section
//RCVersion = string.IsNullOrWhiteSpace(RCVersion) ? ini["CREDITS.version"] : RCVersion;
//Plugin = string.IsNullOrWhiteSpace(Plugin) ? ini["CREDITS.plugin"] : Plugin;
if (ForceMerging == ForceMerging.None)
{
string line = sr.ReadLine();
// If the line is the start of the credits section
if (line.ToLowerInvariant().StartsWith("[credits]"))
{
blocktype = "credits";
}
// If the line is the start of the dat section
else if (line.ToLowerInvariant().StartsWith("[dat]"))
{
blocktype = "dat";
}
// If the line is the start of the emulator section
else if (line.ToLowerInvariant().StartsWith("[emulator]"))
{
blocktype = "emulator";
}
// If the line is the start of the game section
else if (line.ToLowerInvariant().StartsWith("[games]"))
{
blocktype = "games";
}
// Otherwise, it's not a section and it's data, so get out all data
else
{
// If we have an author
if (line.ToLowerInvariant().StartsWith("author="))
{
Author = (string.IsNullOrWhiteSpace(Author) ? line.Split('=')[1] : Author);
}
// If we have one of the three version tags
else if (line.ToLowerInvariant().StartsWith("version="))
{
switch (blocktype)
{
case "credits":
Version = (string.IsNullOrWhiteSpace(Version) ? line.Split('=')[1] : Version);
break;
case "emulator":
Description = (string.IsNullOrWhiteSpace(Description) ? line.Split('=')[1] : Description);
break;
}
}
// If we have a URL
else if (line.ToLowerInvariant().StartsWith("url="))
{
Url = (string.IsNullOrWhiteSpace(Url) ? line.Split('=')[1] : Url);
}
// If we have a comment
else if (line.ToLowerInvariant().StartsWith("comment="))
{
Comment = (string.IsNullOrWhiteSpace(Comment) ? line.Split('=')[1] : Comment);
}
// If we have the split flag
else if (line.ToLowerInvariant().StartsWith("split="))
{
if (Int32.TryParse(line.Split('=')[1], out int split))
{
if (split == 1 && ForceMerging == ForceMerging.None)
ForceMerging = ForceMerging.Split;
}
}
// If we have the merge tag
else if (line.ToLowerInvariant().StartsWith("merge="))
{
if (Int32.TryParse(line.Split('=')[1], out int merge))
{
if (merge == 1 && ForceMerging == ForceMerging.None)
ForceMerging = ForceMerging.Full;
}
}
// If we have the refname tag
else if (line.ToLowerInvariant().StartsWith("refname="))
{
Name = (string.IsNullOrWhiteSpace(Name) ? line.Split('=')[1] : Name);
}
// If we have a rom
else if (line.StartsWith("¬"))
{
// Some old RC DATs have this behavior
if (line.Contains("¬N¬O"))
line = line.Replace("¬N¬O", string.Empty) + "¬¬";
/*
The rominfo order is as follows:
1 - parent name
2 - parent description
3 - game name
4 - game description
5 - rom name
6 - rom crc
7 - rom size
8 - romof name
9 - merge name
*/
string[] rominfo = line.Split('¬');
// Try getting the size separately
if (!Int64.TryParse(rominfo[7], out long size))
size = 0;
Rom rom = new Rom
{
Name = rominfo[5],
Size = size,
CRC = Utilities.CleanHashData(rominfo[6], Constants.CRCLength),
ItemStatus = ItemStatus.None,
MachineName = rominfo[3],
MachineDescription = rominfo[4],
CloneOf = rominfo[1],
RomOf = rominfo[8],
SystemID = sysid,
SourceID = srcid,
};
// Now process and add the rom
ParseAddHelper(rom, clean, remUnicode);
}
}
if (ini["DAT.split"] == "1")
ForceMerging = ForceMerging.Split;
else if (ini["DAT.merge"] == "1")
ForceMerging = ForceMerging.Merged;
}
sr.Dispose();
// EMULATOR section
Name = string.IsNullOrWhiteSpace(Name) ? ini["EMULATOR.refname"] : Name;
Description = string.IsNullOrWhiteSpace(Description) ? ini["EMULATOR.version"] : Description;
// GAMES section
foreach (string game in ini.Where(kvp => kvp.Value == null).Select(kvp => kvp.Key))
{
// Get the line into a separate variable so it can be manipulated
string line = game;
// Remove INI prefixing
if (line.StartsWith("GAMES"))
line = line.Substring("GAMES.".Length);
// If we have a valid game
if (line.StartsWith("¬"))
{
// Some old RC DATs have this behavior
if (line.Contains("¬N¬O"))
line = game.Replace("¬N¬O", string.Empty) + "¬¬";
/*
The rominfo order is as follows:
1 - parent name
2 - parent description
3 - game name
4 - game description
5 - rom name
6 - rom crc
7 - rom size
8 - romof name
9 - merge name
*/
string[] rominfo = line.Split('¬');
// Try getting the size separately
if (!Int64.TryParse(rominfo[7], out long size))
size = 0;
Rom rom = new Rom
{
Name = rominfo[5],
Size = size,
CRC = Utilities.CleanHashData(rominfo[6], Constants.CRCLength),
ItemStatus = ItemStatus.None,
MachineName = rominfo[3],
MachineDescription = rominfo[4],
CloneOf = rominfo[1],
RomOf = rominfo[8],
SystemID = sysid,
SourceID = srcid,
};
// Now process and add the rom
ParseAddHelper(rom, clean, remUnicode);
}
}
}
/// <summary>

View File

@@ -0,0 +1,305 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace SabreTools.Library.Tools
{
public class IniFile : IDictionary<string, string>
{
private Dictionary<string, string> _keyValuePairs = new Dictionary<string, string>();
public string this[string key]
{
get
{
if (_keyValuePairs == null)
_keyValuePairs = new Dictionary<string, string>();
key = key.ToLowerInvariant();
if (_keyValuePairs.ContainsKey(key))
return _keyValuePairs[key];
return null;
}
set
{
if (_keyValuePairs == null)
_keyValuePairs = new Dictionary<string, string>();
key = key.ToLowerInvariant();
_keyValuePairs[key] = value;
}
}
/// <summary>
/// Create an empty INI file
/// </summary>
public IniFile()
{
}
/// <summary>
/// Populate an INI file from path
/// </summary>
public IniFile(string path)
{
this.Parse(path);
}
/// <summary>
/// Populate an INI file from stream
/// </summary>
public IniFile(Stream stream)
{
this.Parse(stream);
}
/// <summary>
/// Add or update a key and value to the INI file
/// </summary>
public void AddOrUpdate(string key, string value)
{
_keyValuePairs[key.ToLowerInvariant()] = value;
}
/// <summary>
/// Remove a key from the INI file
/// </summary>
public void Remove(string key)
{
_keyValuePairs.Remove(key.ToLowerInvariant());
}
/// <summary>
/// Read an INI file based on the path
/// </summary>
public bool Parse(string path)
{
// If we don't have a file, we can't read it
if (!File.Exists(path))
return false;
using (var fileStream = File.OpenRead(path))
{
return Parse(fileStream);
}
}
/// <summary>
/// Read an INI file from a stream
/// </summary>
public bool Parse(Stream stream)
{
// If the stream is invalid or unreadable, we can't process it
if (stream == null || !stream.CanRead || stream.Position >= stream.Length - 1)
return false;
// Keys are case-insensitive by default
try
{
using (StreamReader sr = new StreamReader(stream))
{
string section = string.Empty;
while (!sr.EndOfStream)
{
string line = sr.ReadLine().Trim();
// Comments start with ';'
if (line.StartsWith(";"))
{
// No-op, we don't process comments
}
// Section titles are surrounded by square brackets
else if (line.StartsWith("["))
{
section = line.TrimStart('[').TrimEnd(']');
}
// Valid INI lines are in the format key=value
else if (line.Contains("="))
{
// Split the line by '=' for key-value pairs
string[] data = line.Split('=');
// If the value field contains an '=', we need to put them back in
string key = data[0].Trim();
string value = string.Join("=", data.Skip(1)).Trim();
// Section names are prepended to the key with a '.' separating
if (!string.IsNullOrEmpty(section))
key = $"{section}.{key}";
// Set or overwrite keys in the returned dictionary
_keyValuePairs[key.ToLowerInvariant()] = value;
}
// Lines that aren't a section or key=value are assumed to be key=null
else
{
// Section names are prepended to the key with a '.' separating
if (!string.IsNullOrEmpty(section))
line = $"{section}.{line}";
// Note that these items are NOT normalized
_keyValuePairs[line] = null;
}
}
}
}
catch
{
// We don't care what the error was, just catch and return
return false;
}
return true;
}
/// <summary>
/// Write an INI file to a path
/// </summary>
public bool Write(string path)
{
// If we don't have a valid dictionary with values, we can't write out
if (_keyValuePairs == null || _keyValuePairs.Count == 0)
return false;
using (var fileStream = File.OpenWrite(path))
{
return Write(fileStream);
}
}
/// <summary>
/// Write an INI file to a stream
/// </summary>
public bool Write(Stream stream)
{
// If we don't have a valid dictionary with values, we can't write out
if (_keyValuePairs == null || _keyValuePairs.Count == 0)
return false;
// If the stream is invalid or unwritable, we can't output to it
if (stream == null || !stream.CanWrite || stream.Position >= stream.Length - 1)
return false;
try
{
using (StreamWriter sw = new StreamWriter(stream))
{
// Order the dictionary by keys to link sections together
var orderedKeyValuePairs = _keyValuePairs.OrderBy(kvp => kvp.Key);
string section = string.Empty;
foreach (var keyValuePair in orderedKeyValuePairs)
{
// Extract the key and value
string key = keyValuePair.Key;
string value = keyValuePair.Value;
// We assume '.' is a section name separator
if (key.Contains('.'))
{
// Split the key by '.'
string[] data = keyValuePair.Key.Split('.');
// If the key contains an '.', we need to put them back in
string newSection = data[0].Trim();
key = string.Join(".", data.Skip(1)).Trim();
// If we have a new section, write it out
if (!string.Equals(newSection, section, StringComparison.OrdinalIgnoreCase))
{
sw.WriteLine($"[{newSection}]");
section = newSection;
}
}
// Now write out the key and value in a standardized way
sw.WriteLine($"{key}={value}");
}
}
}
catch
{
// We don't care what the error was, just catch and return
return false;
}
return true;
}
#region IDictionary Impelementations
public ICollection<string> Keys => ((IDictionary<string, string>)_keyValuePairs).Keys;
public ICollection<string> Values => ((IDictionary<string, string>)_keyValuePairs).Values;
public int Count => ((ICollection<KeyValuePair<string, string>>)_keyValuePairs).Count;
public bool IsReadOnly => ((ICollection<KeyValuePair<string, string>>)_keyValuePairs).IsReadOnly;
public void Add(string key, string value)
{
((IDictionary<string, string>)_keyValuePairs).Add(key.ToLowerInvariant(), value);
}
bool IDictionary<string, string>.Remove(string key)
{
return ((IDictionary<string, string>)_keyValuePairs).Remove(key.ToLowerInvariant());
}
public bool TryGetValue(string key, out string value)
{
return ((IDictionary<string, string>)_keyValuePairs).TryGetValue(key.ToLowerInvariant(), out value);
}
public void Add(KeyValuePair<string, string> item)
{
var newItem = new KeyValuePair<string, string>(item.Key.ToLowerInvariant(), item.Value);
((ICollection<KeyValuePair<string, string>>)_keyValuePairs).Add(newItem);
}
public void Clear()
{
((ICollection<KeyValuePair<string, string>>)_keyValuePairs).Clear();
}
public bool Contains(KeyValuePair<string, string> item)
{
var newItem = new KeyValuePair<string, string>(item.Key.ToLowerInvariant(), item.Value);
return ((ICollection<KeyValuePair<string, string>>)_keyValuePairs).Contains(newItem);
}
public bool ContainsKey(string key)
{
return _keyValuePairs.ContainsKey(key.ToLowerInvariant());
}
public void CopyTo(KeyValuePair<string, string>[] array, int arrayIndex)
{
((ICollection<KeyValuePair<string, string>>)_keyValuePairs).CopyTo(array, arrayIndex);
}
public bool Remove(KeyValuePair<string, string> item)
{
var newItem = new KeyValuePair<string, string>(item.Key.ToLowerInvariant(), item.Value);
return ((ICollection<KeyValuePair<string, string>>)_keyValuePairs).Remove(newItem);
}
public IEnumerator<KeyValuePair<string, string>> GetEnumerator()
{
return ((IEnumerable<KeyValuePair<string, string>>)_keyValuePairs).GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return ((IEnumerable)_keyValuePairs).GetEnumerator();
}
#endregion
}
}

View File

@@ -875,7 +875,7 @@ namespace SabreTools
return new Feature(
SkipRipeMd160Value,
new List<string>() { "-nr160", "--skip-ripemd160" },
"Include RIPEMD160 in output", // TODO: Invert this later
"Include RIPEMD160 in output", // TODO: This needs to be inverted later
FeatureType.Flag,
longDescription: "This allows the user to skip calculating the RIPEMD160 for each of the files which will speed up the creation of the DAT.");
}
@@ -903,7 +903,7 @@ namespace SabreTools
return new Feature(
SkipSha256Value,
new List<string>() { "-ns256", "--skip-sha256" },
"Include SHA-256 in output", // TODO: Invert this later
"Include SHA-256 in output", // TODO: This needs to be inverted later
FeatureType.Flag,
longDescription: "This allows the user to skip calculating the SHA-256 for each of the files which will speed up the creation of the DAT.");
}
@@ -917,7 +917,7 @@ namespace SabreTools
return new Feature(
SkipSha384Value,
new List<string>() { "-ns384", "--skip-sha384" },
"Include SHA-384 in output", // TODO: Invert this later
"Include SHA-384 in output", // TODO: This needs to be inverted later
FeatureType.Flag,
longDescription: "This allows the user to skip calculating the SHA-384 for each of the files which will speed up the creation of the DAT.");
}
@@ -931,7 +931,7 @@ namespace SabreTools
return new Feature(
SkipSha512Value,
new List<string>() { "-ns512", "--skip-sha512" },
"Include SHA-512 in output", // TODO: Invert this later
"Include SHA-512 in output", // TODO: This needs to be inverted later
FeatureType.Flag,
longDescription: "This allows the user to skip calculating the SHA-512 for each of the files which will speed up the creation of the DAT.");
}

View File

@@ -9,7 +9,6 @@ namespace SabreTools
/// <summary>
/// Entry class for the DATabase application
/// </summary>
/// TODO: Look into async read/write to make things quicker. Ask edc for help?
public partial class SabreTools
{
// Private required variables