diff --git a/.gitignore b/.gitignore index 26ba31d3..4e1bef9d 100644 --- a/.gitignore +++ b/.gitignore @@ -11,9 +11,6 @@ /Deheader/obj /Deheader/obj/Debug /Deheader/obj/Release -/HashSplit/obj -/HashSplit/obj/Debug -/HashSplit/obj/Release /OfflineMerge/obj /OfflineMerge/obj/Debug /OfflineMerge/obj/Release diff --git a/DATFromDir/DATFromDir.cs b/DATFromDir/DATFromDir.cs index 071bb255..16ceabf0 100644 --- a/DATFromDir/DATFromDir.cs +++ b/DATFromDir/DATFromDir.cs @@ -21,63 +21,35 @@ namespace SabreTools private string _basePath; private string _tempDir; - // Extraction and listing related variables - private Dictionary> _dict; + // User specified inputs private List _inputs; - - // User specified flags + private DatData _datdata; private bool _noMD5; private bool _noSHA1; private bool _bare; - private bool _forceunpack; private bool _archivesAsFiles; - private bool _old; - private bool _superDat; - - // User specified strings - private string _name; - private string _desc; - private string _cat; - private string _version; - private string _author; // Other required variables - private string _date = DateTime.Now.ToString("yyyy-MM-dd"); private Logger _logger; /// /// Create a new DATFromDir object /// /// A List of Strings representing the files and folders to be DATted - /// Internal name of the DAT - /// Description and external name of the DAT - /// Category for the DAT - /// Version of the DAT - /// Author of the DAT + /// DatData object representing the requested output DAT /// True if MD5 hashes should be skipped over, false otherwise /// True if SHA-1 hashes should be skipped over, false otherwise /// True if the date should be omitted from the DAT, false otherwise - /// True if the forcepacking="unzip" tag is to be added, false otherwise - /// True if all archives should be treated like files, false otherwise - /// True if a old-style DAT should be output, false otherwise - /// True if SuperDAT mode is enabled, false otherwise + /// True if archives should be treated as files, false otherwise /// Logger object for console and file output - public DATFromDir(List inputs, string name, string desc, string cat, string version, string author, - bool noMD5, bool noSHA1, bool bare, bool forceunpack, bool archivesAsFiles, bool old, bool superDat, Logger logger) + public DATFromDir(List inputs, DatData datdata, bool noMD5, bool noSHA1, bool bare, bool archivesAsFiles, Logger logger) { _inputs = inputs; - _name = name; - _desc = desc; - _cat = cat; - _version = version; - _author = author; + _datdata = datdata; _noMD5 = noMD5; _noSHA1 = noSHA1; _bare = bare; - _forceunpack = forceunpack; _archivesAsFiles = archivesAsFiles; - _old = old; - _superDat = superDat; _logger = logger; } @@ -100,7 +72,7 @@ namespace SabreTools logger.Start(); // First things first, take care of all of the arguments that this could have - bool noMD5 = false, noSHA1 = false, forceunpack = false, archivesAsFiles = false, old = false, superDat = false, bare = false; + bool noMD5 = false, noSHA1 = false, forceunpack = false, archivesAsFiles = false, old = false, superDat = false, bare = false, romba = false; string name = "", desc = "", cat = "", version = "", author = ""; List inputs = new List(); foreach (string arg in args) @@ -137,6 +109,10 @@ namespace SabreTools case "--old": old = true; break; + case "-ro": + case "--romba": + romba = true; + break; case "-sd": case "--superdat": superDat = true; @@ -194,7 +170,21 @@ namespace SabreTools } // Create a new DATFromDir object and process the inputs - DATFromDir dfd = new DATFromDir(inputs, name, desc, cat, version, author, noMD5, noSHA1, bare, forceunpack, archivesAsFiles, old, superDat, logger); + DatData datdata = new DatData + { + Name = name, + Description = desc, + Category = cat, + Version = version, + Date = DateTime.Now.ToString("yyyy-MM-dd"), + Author = author, + ForcePacking = (forceunpack ? ForcePacking.Unzip : ForcePacking.None), + OutputFormat = (old ? OutputFormat.ClrMamePro : OutputFormat.Xml), + Romba = romba, + Type = (superDat ? "SuperDAT" : ""), + Roms = new Dictionary>(), + }; + DATFromDir dfd = new DATFromDir(inputs, datdata, noMD5, noSHA1, bare, archivesAsFiles, logger); bool success = dfd.Start(); // If we failed, show the help @@ -214,7 +204,7 @@ namespace SabreTools public bool Start() { // Create an output dictionary for all found items - _dict = new Dictionary>(); + _datdata.Roms = new Dictionary>(); // Loop over each of the found paths, if any foreach (string path in _inputs) @@ -242,7 +232,7 @@ namespace SabreTools string basePathBackup = _basePath; foreach (string item in Directory.EnumerateDirectories(_basePath)) { - if (!_superDat) + if (_datdata.Type != "SuperDAT") { _basePath = (File.Exists(item) ? item : item + Path.DirectorySeparatorChar); _basePath = Path.GetFullPath(_basePath); @@ -269,15 +259,15 @@ namespace SabreTools }; string key = rom.Size + "-" + rom.CRC; - if (_dict.ContainsKey(key)) + if (_datdata.Roms.ContainsKey(key)) { - _dict[key].Add(rom); + _datdata.Roms[key].Add(rom); } else { List temp = new List(); temp.Add(rom); - _dict.Add(key, temp); + _datdata.Roms.Add(key, temp); } } @@ -297,15 +287,15 @@ namespace SabreTools }; string key = rom.Size + "-" + rom.CRC; - if (_dict.ContainsKey(key)) + if (_datdata.Roms.ContainsKey(key)) { - _dict[key].Add(rom); + _datdata.Roms[key].Add(rom); } else { List temp = new List(); temp.Add(rom); - _dict.Add(key, temp); + _datdata.Roms.Add(key, temp); } } } @@ -320,17 +310,17 @@ namespace SabreTools } // If we found nothing (error state), exit - if (_dict.Count == 0) + if (_datdata.Roms.Count == 0) { return false; } // Double check to see what it needs to be named - if (_name == "") + if (_datdata.Name == "") { if (_inputs.Count > 1) { - _name = Environment.CurrentDirectory.Split(Path.DirectorySeparatorChar).Last(); + _datdata.Name = Environment.CurrentDirectory.Split(Path.DirectorySeparatorChar).Last(); } else { @@ -338,49 +328,35 @@ namespace SabreTools { _basePath = _basePath.Substring(0, _basePath.Length - 1); } - _name = _basePath.Split(Path.DirectorySeparatorChar).Last(); + _datdata.Name = _basePath.Split(Path.DirectorySeparatorChar).Last(); } } - _name = (_name == "" ? "Default" : _name); + _datdata.Name = (_datdata.Name == "" ? "Default" : _datdata.Name); // If we're in a superdat, append the folder name to all game names - if (_superDat) + if (_datdata.Type == "SuperDAT") { - List keys = _dict.Keys.ToList(); + List keys = _datdata.Roms.Keys.ToList(); foreach (string key in keys) { List newroms = new List(); - foreach (RomData rom in _dict[key]) + foreach (RomData rom in _datdata.Roms[key]) { RomData newrom = rom; - newrom.Game = _name + (newrom.Game != "" && + newrom.Game = _datdata.Name + (newrom.Game != "" && !newrom.Game.StartsWith(Path.DirectorySeparatorChar.ToString()) ? Path.DirectorySeparatorChar.ToString() : "") + newrom.Game; newroms.Add(newrom); } - _dict[key] = newroms; + _datdata.Roms[key] = newroms; } - _name = _name += " - SuperDAT"; + _datdata.Name = _datdata.Name += " - SuperDAT"; } - _desc = (_desc == "" ? _name + (_bare ? "" : " (" + _date + ")") : _desc); - - DatData datdata = new DatData - { - Name = _name, - Description = _desc, - Version = _version, - Date = _date, - Category = _cat, - Author = _author, - Type = (_superDat ? "SuperDAT" : ""), - ForcePacking = (_forceunpack ? ForcePacking.Unzip : ForcePacking.None), - OutputFormat = (_old ? OutputFormat.ClrMamePro : OutputFormat.Xml), - Roms = _dict, - }; + _datdata.Description = (_datdata.Description == "" ? _datdata.Name + (_bare ? "" : " (" + _datdata.Date + ")") : _datdata.Description); // Now write it all out as a DAT - Output.WriteDatfile(datdata, Environment.CurrentDirectory, _logger); + Output.WriteDatfile(_datdata, Environment.CurrentDirectory, _logger); return true; } @@ -401,7 +377,7 @@ namespace SabreTools ArchiveType at = archive.Type; _logger.Log("Found archive of type: " + at); - if (at == ArchiveType.Zip || at == ArchiveType.SevenZip || at == ArchiveType.Rar) + if (at == ArchiveType.Zip || at == ArchiveType.SevenZip || at == ArchiveType.Rar || (at == ArchiveType.GZip && _datdata.Romba)) { _tempDir = Environment.CurrentDirectory + Path.DirectorySeparatorChar + "temp" + DateTime.Now.ToString("yyyyMMddHHmmss") + Path.DirectorySeparatorChar; DirectoryInfo di = Directory.CreateDirectory(_tempDir); @@ -473,7 +449,7 @@ namespace SabreTools actualitem = entry.Remove(0, _tempDir.Length); // If we're in SuperDAT mode, make sure the added item is by itself - if (_superDat) + if (_datdata.Type == "SuperDAT") { actualroot = Path.GetDirectoryName(item.Remove(0, _basePath.Length)); actualroot = (actualroot == "" ? _basePath.Split(Path.DirectorySeparatorChar).Last() : actualroot); @@ -500,15 +476,15 @@ namespace SabreTools }; string key = rom.Size + "-" + rom.CRC; - if (_dict.ContainsKey(key)) + if (_datdata.Roms.ContainsKey(key)) { - _dict[key].Add(rom); + _datdata.Roms[key].Add(rom); } else { List temp = new List(); temp.Add(rom); - _dict.Add(key, temp); + _datdata.Roms.Add(key, temp); } _logger.User("File added: " + entry + Environment.NewLine); @@ -559,11 +535,11 @@ namespace SabreTools } string actualroot = (item == _basePath ? item.Split(Path.DirectorySeparatorChar).Last() : item.Remove(0, _basePath.Length).Split(Path.DirectorySeparatorChar)[0]); - actualroot = (actualroot == "" && !_superDat ? _basePath.Split(Path.DirectorySeparatorChar).Last() : actualroot); + actualroot = (actualroot == "" && _datdata.Type != "SuperDAT" ? _basePath.Split(Path.DirectorySeparatorChar).Last() : actualroot); string actualitem = (item == _basePath ? item : item.Remove(0, _basePath.Length + 1)); // If we're in SuperDAT mode, make sure the added item is by itself - if (_superDat) + if (_datdata.Type == "SuperDAT") { actualroot += (actualroot != "" ? Path.DirectorySeparatorChar.ToString() : "") + Path.GetDirectoryName(actualitem); actualroot = actualroot.TrimEnd(Path.DirectorySeparatorChar); @@ -590,15 +566,15 @@ namespace SabreTools }; string key = rom.Size + "-" + rom.CRC; - if (_dict.ContainsKey(key)) + if (_datdata.Roms.ContainsKey(key)) { - _dict[key].Add(rom); + _datdata.Roms[key].Add(rom); } else { List temp = new List(); temp.Add(rom); - _dict.Add(key, temp); + _datdata.Roms.Add(key, temp); } _logger.User("File added: " + actualitem + Environment.NewLine); diff --git a/DATabase/DATabase.cs b/DATabase/DATabase.cs index 28934c9c..3513af74 100644 --- a/DATabase/DATabase.cs +++ b/DATabase/DATabase.cs @@ -67,6 +67,7 @@ namespace SabreTools forceunpack = false, generate = false, genall = false, + hashsplit = false, import = false, listsrc = false, listsys = false, @@ -169,6 +170,10 @@ namespace SabreTools case "--game-prefix": gamename = true; break; + case "-hs": + case "--hash-split": + hashsplit = true; + break; case "-i": case "--import": import = true; @@ -321,7 +326,7 @@ namespace SabreTools // If more than one switch is enabled or help is set, show the help screen if (help || !(add ^ (convertMiss || romba) ^ convertCMP ^ convertRC ^ convertSD ^ convertXml ^ extsplit ^ generate ^ - genall ^ import ^ listsrc ^ listsys ^ (merge || diff) ^ rem ^ trim)) + genall ^ hashsplit ^ import ^ listsrc ^ listsys ^ (merge || diff) ^ rem ^ trim)) { Build.Help(); logger.Close(); @@ -329,7 +334,8 @@ namespace SabreTools } // If a switch that requires a filename is set and no file is, show the help screen - if (inputs.Count == 0 && ((convertMiss || romba) || convertCMP || convertRC || convertSD || convertXml || extsplit || import || (merge || diff) || trim)) + if (inputs.Count == 0 && ((convertMiss || romba) || convertCMP || convertRC || convertSD + || convertXml || extsplit || hashsplit || import || (merge || diff) || trim)) { Build.Help(); logger.Close(); @@ -474,6 +480,12 @@ namespace SabreTools InitMergeDiff(inputs, name, desc, cat, version, author, diff, dedup, bare, forceunpack, old, superdat, cascade); } + // Split a DAT by available hashes + else if (hashsplit) + { + InitHashSplit(inputs, outdir); + } + logger.Close(); return; } @@ -675,6 +687,7 @@ Make a selection: 3) Trim all entries in DAT and merge into a single game 4) Merge, diff, and/or dedup 2 or more DAT files 5) Split DAT using 2 extensions + 6) Split DATs by best available hash values B) Go back to the previous menu "); Console.Write("Enter selection: "); @@ -696,6 +709,9 @@ Make a selection: case "5": ExtSplitMenu(); break; + case "6": + HashSplitMenu(); + break; } } } @@ -1061,7 +1077,7 @@ Make a selection: case "4": Console.Clear(); Console.Write("Please enter the output directory: "); - exta = Console.ReadLine(); + outdir = Console.ReadLine(); break; case "5": Console.Clear(); @@ -1074,6 +1090,52 @@ Make a selection: } } + /// + /// Show the text-based HashSplit menu + /// + private static void HashSplitMenu() + { + string selection = "", input = "", outdir = ""; + while (selection.ToLowerInvariant() != "b") + { + Console.Clear(); + Build.Start("DATabase"); + Console.WriteLine(@"HASH SPLIT MENU +=========================== +Make a selection: + + 1) File or folder to split" + (input != "" ? ":\n\t" + input : "") + @" + 2) Output directory" + (outdir != "" ? ":\n\t" + outdir : "") + @" + 3) Split the file + B) Go back to the previous menu +"); + Console.Write("Enter selection: "); + selection = Console.ReadLine(); + switch (selection) + { + case "1": + Console.Clear(); + Console.Write("Please enter the file or folder name: "); + input = Console.ReadLine(); + break; + case "2": + Console.Clear(); + Console.Write("Please enter the output directory: "); + outdir = Console.ReadLine(); + break; + case "3": + Console.Clear(); + List inputs = new List(); + inputs.Add(input); + InitHashSplit(inputs, outdir); + Console.Write("\nPress any key to continue..."); + Console.ReadKey(); + input = ""; outdir = ""; + break; + } + } + } + /// /// Show the text-based add and remove menu /// @@ -1562,6 +1624,33 @@ Make a selection: } } + /// + /// Wrap splitting a DAT by best available hashes + /// + /// List of inputs to be used + /// Output directory for the split files + private static void InitHashSplit(List inputs, string outdir) + { + // Strip any quotations from the names + outdir = outdir.Replace("\"", ""); + + // Verify the input files + foreach (string input in inputs) + { + if (!File.Exists(input.Replace("\"", "")) && !Directory.Exists(input.Replace("\"", ""))) + { + logger.Error(input + " is not a valid file or folder!"); + Console.WriteLine(); + Build.Help(); + return; + } + } + + // If so, run the program + HashSplit hs = new HashSplit(inputs, outdir, logger); + hs.Split(); + } + /// /// Wrap adding a new source to the database /// diff --git a/DATabase/DATabase.csproj b/DATabase/DATabase.csproj index 0f857a05..6ea0bcf9 100644 --- a/DATabase/DATabase.csproj +++ b/DATabase/DATabase.csproj @@ -106,6 +106,7 @@ + diff --git a/HashSplit/HashSplit.cs b/DATabase/HashSplit.cs similarity index 82% rename from HashSplit/HashSplit.cs rename to DATabase/HashSplit.cs index 64800caf..ead49a5d 100644 --- a/HashSplit/HashSplit.cs +++ b/DATabase/HashSplit.cs @@ -29,78 +29,6 @@ namespace SabreTools _logger = logger; } - /// - /// Start help or use supplied parameters - /// - /// String array representing command line parameters - public static void Main(string[] args) - { - Console.Clear(); - - // Credits take precidence over all - if ((new List(args)).Contains("--credits")) - { - Build.Credits(); - return; - } - - Logger logger = new Logger(true, "hashsplit.log"); - logger.Start(); - - // First things first, take care of all of the arguments that this could have - string outdir = ""; - List inputs = new List(); - foreach (string arg in args) - { - switch (arg) - { - case "-h": - case "-?": - case "--help": - Build.Help(); - logger.Close(); - return; - default: - if (arg.StartsWith("-out=")) - { - outdir = arg.Split('=')[1]; - } - else - { - inputs.Add(arg); - } - break; - } - } - - // If there's no inputs, show the help - if (inputs.Count() == 0) - { - Build.Help(); - logger.Close(); - return; - } - - // Output the title - Build.Start("HashSplit"); - - // Verify the input file - foreach (string input in inputs) - { - if (!File.Exists(input.Replace("\"", "")) && !Directory.Exists(input.Replace("\"", ""))) - { - logger.Error(input + " is not a valid file!"); - Console.WriteLine(); - Build.Help(); - return; - } - } - - // If so, run the program - HashSplit hs = new HashSplit(inputs, outdir, logger); - hs.Split(); - } - /// /// Split the DAT into parts by best-available hash data /// diff --git a/HashSplit/App.config b/HashSplit/App.config deleted file mode 100644 index 88fa4027..00000000 --- a/HashSplit/App.config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/HashSplit/HashSplit.csproj b/HashSplit/HashSplit.csproj deleted file mode 100644 index 7c32e4f7..00000000 --- a/HashSplit/HashSplit.csproj +++ /dev/null @@ -1,103 +0,0 @@ - - - - - Debug - AnyCPU - {9E2D38DA-8D65-45B4-A853-6DBEE04B6BA4} - Exe - Properties - SabreTools - HashSplit - v4.5.2 - 512 - true - - - publish\ - true - Disk - false - Foreground - 7 - Days - false - false - true - 0 - 1.0.0.%2a - false - false - true - - - AnyCPU - true - full - false - ..\..\Debug\ - DEBUG;TRACE - prompt - 4 - - - AnyCPU - pdbonly - true - ..\..\Release\ - TRACE - prompt - 4 - - - true - ..\..\Debug-x64\ - DEBUG;TRACE - full - x64 - prompt - MinimumRecommendedRules.ruleset - true - - - ..\..\Release-x64\ - TRACE - true - pdbonly - x64 - prompt - MinimumRecommendedRules.ruleset - true - - - - - - - - - - - - - - - - - - - - - {225a1afd-0890-44e8-b779-7502665c23a5} - SabreHelper - - - - - \ No newline at end of file diff --git a/HashSplit/Properties/AssemblyInfo.cs b/HashSplit/Properties/AssemblyInfo.cs deleted file mode 100644 index c5556a1f..00000000 --- a/HashSplit/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("HashSplit")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("HashSplit")] -[assembly: AssemblyCopyright("Copyright © 2016")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("9e2d38da-8d65-45b4-a853-6dbee04b6ba4")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/SabreHelper/Build.cs b/SabreHelper/Build.cs index 33cdacde..656da896 100644 --- a/SabreHelper/Build.cs +++ b/SabreHelper/Build.cs @@ -118,6 +118,8 @@ Options: -out= Output directory -nr, --no-rename Don't auto-rename games -o, --old Output DAT in CMP format instead of XML + -hs, --hash-split Split a DAT or folder by best-available hashes + -out= Output directory -i, --import Start tool in import mode -lso, --list-sources List all sources (id <= name) -lsy, --list-systems List all systems (id <= name) @@ -168,6 +170,7 @@ Options: -u, --unzip Force unzipping in created DAT -f, --files Treat archives as files -o, --old Output DAT in CMP format instead of XML + -ro, --romba Allow reading of GZIP files for Romba -n=, --name= Set the internal name of the DAT -d=, --desc= Set the filename and description of the DAT -c=, --cat= Set the category of the DAT @@ -210,16 +213,6 @@ Options: -nr, --no-rename Don't auto-rename games by source/system -o, --old Output DAT in CMP format instead of XML -sys=, --system= System ID to generate from -"); - break; - case "HashSplit": - Console.WriteLine(@"HashSplit - Split a DAT by best-available hashes ------------------------------------------ -Usage: HashSplit [options] [filename|dirname] ... - -Options: - -h, -?, --help Show this help dialog - -out= Output directory "); break; default: diff --git a/SabreTools.sln b/SabreTools.sln index 023738cd..c8c8d42b 100644 --- a/SabreTools.sln +++ b/SabreTools.sln @@ -19,8 +19,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UncompressedSize", "Uncompr EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DATabaseTwo", "DATabaseTwo\DATabaseTwo.csproj", "{C7732A05-1F96-43ED-AC8C-0E388F37EBC1}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HashSplit", "HashSplit\HashSplit.csproj", "{9E2D38DA-8D65-45B4-A853-6DBEE04B6BA4}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -94,14 +92,6 @@ Global {C7732A05-1F96-43ED-AC8C-0E388F37EBC1}.Release|Any CPU.Build.0 = Release|Any CPU {C7732A05-1F96-43ED-AC8C-0E388F37EBC1}.Release|x64.ActiveCfg = Release|x64 {C7732A05-1F96-43ED-AC8C-0E388F37EBC1}.Release|x64.Build.0 = Release|x64 - {9E2D38DA-8D65-45B4-A853-6DBEE04B6BA4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {9E2D38DA-8D65-45B4-A853-6DBEE04B6BA4}.Debug|Any CPU.Build.0 = Debug|Any CPU - {9E2D38DA-8D65-45B4-A853-6DBEE04B6BA4}.Debug|x64.ActiveCfg = Debug|x64 - {9E2D38DA-8D65-45B4-A853-6DBEE04B6BA4}.Debug|x64.Build.0 = Debug|x64 - {9E2D38DA-8D65-45B4-A853-6DBEE04B6BA4}.Release|Any CPU.ActiveCfg = Release|Any CPU - {9E2D38DA-8D65-45B4-A853-6DBEE04B6BA4}.Release|Any CPU.Build.0 = Release|Any CPU - {9E2D38DA-8D65-45B4-A853-6DBEE04B6BA4}.Release|x64.ActiveCfg = Release|Any CPU - {9E2D38DA-8D65-45B4-A853-6DBEE04B6BA4}.Release|x64.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE