diff --git a/.gitignore b/.gitignore
index c608dca..21896b9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -404,4 +404,7 @@ ASALocalRun/
# Local History for Visual Studio
.localhistory/
gaseous-server/.DS_Store
+gaseous-server/wwwroot/.DS_Store
gaseous-server/wwwroot/emulators/EmulatorJS
+.devcontainer/.env
+.mono/
diff --git a/.vscode/launch.json b/.vscode/launch.json
index 82476c1..4ec8f9b 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -24,7 +24,8 @@
},
"sourceFileMap": {
"/Views": "${workspaceFolder}/Views"
- }
+ },
+ "enableStepFiltering": false
},
{
"name": ".NET Core Attach",
diff --git a/Gaseous.sln b/Gaseous.sln
index 8b88e80..9eca1ea 100644
--- a/Gaseous.sln
+++ b/Gaseous.sln
@@ -1,4 +1,4 @@
-
+
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 25.0.1704.4
@@ -27,30 +27,18 @@ Global
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {08699C93-15CD-4E39-9053-D3C8056CE938}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {08699C93-15CD-4E39-9053-D3C8056CE938}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {08699C93-15CD-4E39-9053-D3C8056CE938}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {08699C93-15CD-4E39-9053-D3C8056CE938}.Release|Any CPU.Build.0 = Release|Any CPU
- {FFCEC386-033F-4772-A45B-D33579F2E5EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {FFCEC386-033F-4772-A45B-D33579F2E5EE}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {FFCEC386-033F-4772-A45B-D33579F2E5EE}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {FFCEC386-033F-4772-A45B-D33579F2E5EE}.Release|Any CPU.Build.0 = Release|Any CPU
{A01D2EFF-C82E-473B-84D7-7C25E736F5D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A01D2EFF-C82E-473B-84D7-7C25E736F5D2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A01D2EFF-C82E-473B-84D7-7C25E736F5D2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A01D2EFF-C82E-473B-84D7-7C25E736F5D2}.Release|Any CPU.Build.0 = Release|Any CPU
- {B07A4655-A003-416B-A790-ADAA5B548E1A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {B07A4655-A003-416B-A790-ADAA5B548E1A}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {B07A4655-A003-416B-A790-ADAA5B548E1A}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {B07A4655-A003-416B-A790-ADAA5B548E1A}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
- GlobalSection(ExtensibilityGlobals) = postSolution
- SolutionGuid = {979BF092-AFBC-4F19-B55F-32E73959BD1A}
- EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{F1A847C7-57BC-4DA9-8F83-CD060A7F5122} = {17FA6F12-8532-420C-9489-CB8FDE42137C}
EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {979BF092-AFBC-4F19-B55F-32E73959BD1A}
+ EndGlobalSection
EndGlobal
diff --git a/LICENSE b/LICENSE
index 0f2028b..29ebfa5 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,5 +1,5 @@
- GNU GENERAL PUBLIC LICENSE
- Version 3, 29 June 2007
+ GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3, 19 November 2007
Copyright (C) 2007 Free Software Foundation, Inc.
Everyone is permitted to copy and distribute verbatim copies
@@ -7,17 +7,15 @@
Preamble
- The GNU General Public License is a free, copyleft license for
-software and other kinds of works.
+ The GNU Affero General Public License is a free, copyleft license for
+software and other kinds of works, specifically designed to ensure
+cooperation with the community in the case of network server software.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
-the GNU General Public License is intended to guarantee your freedom to
+our General Public Licenses are intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
-software for all its users. We, the Free Software Foundation, use the
-GNU General Public License for most of our software; it applies also to
-any other work released this way by its authors. You can apply it to
-your programs, too.
+software for all its users.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
@@ -26,44 +24,34 @@ them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
- To protect your rights, we need to prevent others from denying you
-these rights or asking you to surrender the rights. Therefore, you have
-certain responsibilities if you distribute copies of the software, or if
-you modify it: responsibilities to respect the freedom of others.
+ Developers that use our General Public Licenses protect your rights
+with two steps: (1) assert copyright on the software, and (2) offer
+you this License which gives you legal permission to copy, distribute
+and/or modify the software.
- For example, if you distribute copies of such a program, whether
-gratis or for a fee, you must pass on to the recipients the same
-freedoms that you received. You must make sure that they, too, receive
-or can get the source code. And you must show them these terms so they
-know their rights.
+ A secondary benefit of defending all users' freedom is that
+improvements made in alternate versions of the program, if they
+receive widespread use, become available for other developers to
+incorporate. Many developers of free software are heartened and
+encouraged by the resulting cooperation. However, in the case of
+software used on network servers, this result may fail to come about.
+The GNU General Public License permits making a modified version and
+letting the public access it on a server without ever releasing its
+source code to the public.
- Developers that use the GNU GPL protect your rights with two steps:
-(1) assert copyright on the software, and (2) offer you this License
-giving you legal permission to copy, distribute and/or modify it.
+ The GNU Affero General Public License is designed specifically to
+ensure that, in such cases, the modified source code becomes available
+to the community. It requires the operator of a network server to
+provide the source code of the modified version running there to the
+users of that server. Therefore, public use of a modified version, on
+a publicly accessible server, gives the public access to the source
+code of the modified version.
- For the developers' and authors' protection, the GPL clearly explains
-that there is no warranty for this free software. For both users' and
-authors' sake, the GPL requires that modified versions be marked as
-changed, so that their problems will not be attributed erroneously to
-authors of previous versions.
-
- Some devices are designed to deny users access to install or run
-modified versions of the software inside them, although the manufacturer
-can do so. This is fundamentally incompatible with the aim of
-protecting users' freedom to change the software. The systematic
-pattern of such abuse occurs in the area of products for individuals to
-use, which is precisely where it is most unacceptable. Therefore, we
-have designed this version of the GPL to prohibit the practice for those
-products. If such problems arise substantially in other domains, we
-stand ready to extend this provision to those domains in future versions
-of the GPL, as needed to protect the freedom of users.
-
- Finally, every program is threatened constantly by software patents.
-States should not allow patents to restrict development and use of
-software on general-purpose computers, but in those that do, we wish to
-avoid the special danger that patents applied to a free program could
-make it effectively proprietary. To prevent this, the GPL assures that
-patents cannot be used to render the program non-free.
+ An older license, called the Affero General Public License and
+published by Affero, was designed to accomplish similar goals. This is
+a different license, not a version of the Affero GPL, but Affero has
+released a new version of the Affero GPL which permits relicensing under
+this license.
The precise terms and conditions for copying, distribution and
modification follow.
@@ -72,7 +60,7 @@ modification follow.
0. Definitions.
- "This License" refers to version 3 of the GNU General Public License.
+ "This License" refers to version 3 of the GNU Affero General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
@@ -549,35 +537,45 @@ to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
- 13. Use with the GNU Affero General Public License.
+ 13. Remote Network Interaction; Use with the GNU General Public License.
+
+ Notwithstanding any other provision of this License, if you modify the
+Program, your modified version must prominently offer all users
+interacting with it remotely through a computer network (if your version
+supports such interaction) an opportunity to receive the Corresponding
+Source of your version by providing access to the Corresponding Source
+from a network server at no charge, through some standard or customary
+means of facilitating copying of software. This Corresponding Source
+shall include the Corresponding Source for any work covered by version 3
+of the GNU General Public License that is incorporated pursuant to the
+following paragraph.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
-under version 3 of the GNU Affero General Public License into a single
+under version 3 of the GNU General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
-but the special requirements of the GNU Affero General Public License,
-section 13, concerning interaction through a network will apply to the
-combination as such.
+but the work with which it is combined will remain governed by version
+3 of the GNU General Public License.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
-the GNU General Public License from time to time. Such new versions will
-be similar in spirit to the present version, but may differ in detail to
+the GNU Affero General Public License from time to time. Such new versions
+will be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
-Program specifies that a certain numbered version of the GNU General
+Program specifies that a certain numbered version of the GNU Affero General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
-GNU General Public License, you may choose any version ever published
+GNU Affero General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
-versions of the GNU General Public License can be used, that proxy's
+versions of the GNU Affero General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
@@ -631,44 +629,33 @@ to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
- Gaseous
- Copyright (C) 2023 Gaseous
+
+ Copyright (C)
This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
+ it under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
+ GNU Affero General Public License for more details.
- You should have received a copy of the GNU General Public License
+ You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
Also add information on how to contact you by electronic and paper mail.
- If the program does terminal interaction, make it output a short
-notice like this when it starts in an interactive mode:
-
- Copyright (C) 2023 Gaseous
- This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
- This is free software, and you are welcome to redistribute it
- under certain conditions; type `show c' for details.
-
-The hypothetical commands `show w' and `show c' should show the appropriate
-parts of the General Public License. Of course, your program's commands
-might be different; for a GUI interface, you would use an "about box".
+ If your software can interact with users remotely through a computer
+network, you should also make sure that it provides a way for users to
+get its source. For example, if your program is a web application, its
+interface could display a "Source" link that leads users to an archive
+of the code. There are many ways you could offer source, and different
+solutions will be better for different programs; see section 13 for the
+specific requirements.
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
-For more information on this, and how to apply and follow the GNU GPL, see
-.
-
- The GNU General Public License does not permit incorporating your program
-into proprietary programs. If your program is a subroutine library, you
-may consider it more useful to permit linking proprietary applications with
-the library. If this is what you want to do, use the GNU Lesser General
-Public License instead of this License. But first, please read
-.
+For more information on this, and how to apply and follow the GNU AGPL, see
+.
\ No newline at end of file
diff --git a/gaseous-server/Classes/Common.cs b/gaseous-server/Classes/Common.cs
index 4cb2cdf..bd6cf8e 100644
--- a/gaseous-server/Classes/Common.cs
+++ b/gaseous-server/Classes/Common.cs
@@ -1,5 +1,6 @@
-using System.Collections.Concurrent;
+using System.Collections.Concurrent;
using System.ComponentModel;
+using System.Data;
using System.IO.Compression;
using System.Reflection;
using System.Security.Cryptography;
diff --git a/gaseous-server/Classes/Config.cs b/gaseous-server/Classes/Config.cs
index 26f9963..9ce5cca 100644
--- a/gaseous-server/Classes/Config.cs
+++ b/gaseous-server/Classes/Config.cs
@@ -80,7 +80,8 @@ namespace gaseous_server.Classes
get
{
string logPath = Path.Combine(ConfigurationPath, "Logs");
- if (!Directory.Exists(logPath)) {
+ if (!Directory.Exists(logPath))
+ {
Directory.CreateDirectory(logPath);
}
return logPath;
@@ -92,7 +93,7 @@ namespace gaseous_server.Classes
get
{
string logFileExtension = "txt";
-
+
string logPathName = Path.Combine(LogPath, "Server Log " + DateTime.Now.ToUniversalTime().ToString("yyyyMMdd") + "." + logFileExtension);
return logPathName;
}
@@ -131,7 +132,7 @@ namespace gaseous_server.Classes
_config.DatabaseConfiguration.DatabaseName = (string)Common.GetEnvVar("dbname", _config.DatabaseConfiguration.DatabaseName);
_config.DatabaseConfiguration.Port = int.Parse((string)Common.GetEnvVar("dbport", _config.DatabaseConfiguration.Port.ToString()));
_config.MetadataConfiguration.MetadataSource = (HasheousClient.Models.MetadataModel.MetadataSources)Enum.Parse(typeof(HasheousClient.Models.MetadataModel.MetadataSources), (string)Common.GetEnvVar("metadatasource", _config.MetadataConfiguration.MetadataSource.ToString()));
- _config.MetadataConfiguration.SignatureSource = (HasheousClient.Models.MetadataModel.SignatureSources)Enum.Parse(typeof(HasheousClient.Models.MetadataModel.SignatureSources), (string)Common.GetEnvVar("signaturesource", _config.MetadataConfiguration.SignatureSource.ToString()));;
+ _config.MetadataConfiguration.SignatureSource = (HasheousClient.Models.MetadataModel.SignatureSources)Enum.Parse(typeof(HasheousClient.Models.MetadataModel.SignatureSources), (string)Common.GetEnvVar("signaturesource", _config.MetadataConfiguration.SignatureSource.ToString())); ;
_config.MetadataConfiguration.MaxLibraryScanWorkers = int.Parse((string)Common.GetEnvVar("maxlibraryscanworkers", _config.MetadataConfiguration.MaxLibraryScanWorkers.ToString()));
_config.MetadataConfiguration.HasheousHost = (string)Common.GetEnvVar("hasheoushost", _config.MetadataConfiguration.HasheousHost);
_config.IGDBConfiguration.ClientId = (string)Common.GetEnvVar("igdbclientid", _config.IGDBConfiguration.ClientId);
@@ -205,9 +206,9 @@ namespace gaseous_server.Classes
{
AppSettings.Remove(SettingName);
}
-
+
Logging.Log(Logging.LogType.Information, "Load Settings", "Loading setting " + SettingName + " from database");
-
+
try
{
if (Database.schema_version >= 1016)
@@ -275,7 +276,7 @@ namespace gaseous_server.Classes
if (Database.schema_version >= 1016)
{
sql = "SELECT Value, ValueDate FROM Settings WHERE Setting = @SettingName";
-
+
dbResponse = db.ExecuteCMD(sql, dbDict);
Type type = typeof(T);
if (dbResponse.Rows.Count == 0)
@@ -301,7 +302,7 @@ namespace gaseous_server.Classes
else
{
sql = "SELECT Value FROM Settings WHERE Setting = @SettingName";
-
+
dbResponse = db.ExecuteCMD(sql, dbDict);
Type type = typeof(T);
if (dbResponse.Rows.Count == 0)
@@ -355,7 +356,7 @@ namespace gaseous_server.Classes
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql;
Dictionary dbDict;
-
+
if (Database.schema_version >= 1016)
{
sql = "REPLACE INTO Settings (Setting, ValueType, Value, ValueDate) VALUES (@SettingName, @ValueType, @Value, @ValueDate)";
@@ -427,7 +428,8 @@ namespace gaseous_server.Classes
public class Database
{
- private static string _DefaultHostName {
+ private static string _DefaultHostName
+ {
get
{
if (!String.IsNullOrEmpty(Environment.GetEnvironmentVariable("dbhost")))
@@ -643,7 +645,7 @@ namespace gaseous_server.Classes
return MetadataPath;
}
- public string LibrarySignatureImportDirectory
+ public string LibrarySignaturesDirectory
{
get
{
@@ -651,6 +653,14 @@ namespace gaseous_server.Classes
}
}
+ public string LibrarySignaturesProcessedDirectory
+ {
+ get
+ {
+ return Path.Combine(LibraryRootDirectory, "Signatures - Processed");
+ }
+ }
+
public void InitLibrary()
{
if (!Directory.Exists(LibraryRootDirectory)) { Directory.CreateDirectory(LibraryRootDirectory); }
@@ -660,7 +670,8 @@ namespace gaseous_server.Classes
if (!Directory.Exists(LibraryMetadataDirectory)) { Directory.CreateDirectory(LibraryMetadataDirectory); }
if (!Directory.Exists(LibraryTempDirectory)) { Directory.CreateDirectory(LibraryTempDirectory); }
if (!Directory.Exists(LibraryCollectionsDirectory)) { Directory.CreateDirectory(LibraryCollectionsDirectory); }
- if (!Directory.Exists(LibrarySignatureImportDirectory)) { Directory.CreateDirectory(LibrarySignatureImportDirectory); }
+ if (!Directory.Exists(LibrarySignaturesDirectory)) { Directory.CreateDirectory(LibrarySignaturesDirectory); }
+ if (!Directory.Exists(LibrarySignaturesProcessedDirectory)) { Directory.CreateDirectory(LibrarySignaturesProcessedDirectory); }
}
}
@@ -696,6 +707,10 @@ namespace gaseous_server.Classes
}
}
+ private static bool _HasheousSubmitFixes { get; set; } = false;
+
+ private static string _HasheousAPIKey { get; set; } = "";
+
private static int _MaxLibraryScanWorkers
{
get
@@ -730,6 +745,10 @@ namespace gaseous_server.Classes
public HasheousClient.Models.MetadataModel.SignatureSources SignatureSource = _SignatureSource;
+ public bool HasheousSubmitFixes = _HasheousSubmitFixes;
+
+ public string HasheousAPIKey = _HasheousAPIKey;
+
public int MaxLibraryScanWorkers = _MaxLibraryScanWorkers;
public string HasheousHost = _HasheousHost;
diff --git a/gaseous-server/Classes/DatabaseMigration.cs b/gaseous-server/Classes/DatabaseMigration.cs
index c9e65de..b701076 100644
--- a/gaseous-server/Classes/DatabaseMigration.cs
+++ b/gaseous-server/Classes/DatabaseMigration.cs
@@ -1,14 +1,17 @@
using System;
using System.Data;
using System.Reflection;
+using gaseous_server.Classes.Metadata;
+using gaseous_server.Models;
+using IGDB.Models;
namespace gaseous_server.Classes
{
- public static class DatabaseMigration
- {
+ public static class DatabaseMigration
+ {
public static List BackgroundUpgradeTargetSchemaVersions = new List();
- public static void PreUpgradeScript(int TargetSchemaVersion, Database.databaseType? DatabaseType)
+ public static void PreUpgradeScript(int TargetSchemaVersion, Database.databaseType? DatabaseType)
{
// load resources
var assembly = Assembly.GetExecutingAssembly();
@@ -20,14 +23,14 @@ namespace gaseous_server.Classes
Logging.Log(Logging.LogType.Information, "Database", "Checking for pre-upgrade for schema version " + TargetSchemaVersion);
- switch(DatabaseType)
+ switch (DatabaseType)
{
case Database.databaseType.MySql:
switch (TargetSchemaVersion)
{
case 1005:
Logging.Log(Logging.LogType.Information, "Database", "Running pre-upgrade for schema version " + TargetSchemaVersion);
-
+
// there was a mistake at dbschema version 1004-1005
// the first preview release of v1.7 reused dbschema version 1004
// if table "Relation_Game_AgeRatings" exists - then we need to apply the gaseous-fix-1005.sql script before applying the standard 1005 script
@@ -62,14 +65,16 @@ namespace gaseous_server.Classes
}
}
- public static void PostUpgradeScript(int TargetSchemaVersion, Database.databaseType? DatabaseType)
+ public static void PostUpgradeScript(int TargetSchemaVersion, Database.databaseType? DatabaseType)
{
+ var assembly = Assembly.GetExecutingAssembly();
+
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "";
Dictionary dbDict = new Dictionary();
DataTable data;
- switch(DatabaseType)
+ switch (DatabaseType)
{
case Database.databaseType.MySql:
switch (TargetSchemaVersion)
@@ -78,7 +83,7 @@ namespace gaseous_server.Classes
// this is a safe background task
BackgroundUpgradeTargetSchemaVersions.Add(1002);
break;
-
+
case 1004:
// needs to run on start up
@@ -103,6 +108,51 @@ namespace gaseous_server.Classes
sql = "DELETE FROM Settings WHERE Setting LIKE 'LastRun_%';";
db.ExecuteNonQuery(sql);
break;
+
+ case 1022:
+ // load country list
+ Logging.Log(Logging.LogType.Information, "Database Upgrade", "Adding country look up table contents");
+
+ string countryResourceName = "gaseous_server.Support.Country.txt";
+ using (Stream stream = assembly.GetManifestResourceStream(countryResourceName))
+ using (StreamReader reader = new StreamReader(stream))
+ {
+ do
+ {
+ string[] line = reader.ReadLine().Split("|");
+
+ sql = "INSERT INTO Country (Code, Value) VALUES (@code, @value);";
+ dbDict = new Dictionary{
+ { "code", line[0] },
+ { "value", line[1] }
+ };
+ db.ExecuteNonQuery(sql, dbDict);
+ } while (reader.EndOfStream == false);
+ }
+
+ // load language list
+ Logging.Log(Logging.LogType.Information, "Database Upgrade", "Adding language look up table contents");
+
+ string languageResourceName = "gaseous_server.Support.Language.txt";
+ using (Stream stream = assembly.GetManifestResourceStream(languageResourceName))
+ using (StreamReader reader = new StreamReader(stream))
+ {
+ do
+ {
+ string[] line = reader.ReadLine().Split("|");
+
+ sql = "INSERT INTO Language (Code, Value) VALUES (@code, @value);";
+ dbDict = new Dictionary{
+ { "code", line[0] },
+ { "value", line[1] }
+ };
+ db.ExecuteNonQuery(sql, dbDict);
+ } while (reader.EndOfStream == false);
+ }
+
+ // this is a safe background task
+ BackgroundUpgradeTargetSchemaVersions.Add(1022);
+ break;
}
break;
}
@@ -117,11 +167,16 @@ namespace gaseous_server.Classes
case 1002:
MySql_1002_MigrateMetadataVersion();
break;
+
+ case 1022:
+ MySql_1022_MigrateMetadataVersion();
+ break;
}
}
}
- public static void MySql_1002_MigrateMetadataVersion() {
+ public static void MySql_1002_MigrateMetadataVersion()
+ {
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "";
Dictionary dbDict = new Dictionary();
@@ -134,7 +189,7 @@ namespace gaseous_server.Classes
Logging.Log(Logging.LogType.Information, "Signature Ingestor - Database Update", "Updating " + data.Rows.Count + " database entries");
int Counter = 0;
int LastCounterCheck = 0;
- foreach (DataRow row in data.Rows)
+ foreach (DataRow row in data.Rows)
{
List Flags = Newtonsoft.Json.JsonConvert.DeserializeObject>((string)Common.ReturnValueIfNull(row["flags"], "[]"));
List> Attributes = new List>();
@@ -207,7 +262,7 @@ namespace gaseous_server.Classes
dbDict.Add("id", (int)row["Id"]);
db.ExecuteCMD(updateSQL, dbDict);
- if ((Counter - LastCounterCheck) > 10)
+ if ((Counter - LastCounterCheck) > 10)
{
LastCounterCheck = Counter;
Logging.Log(Logging.LogType.Information, "Signature Ingestor - Database Update", "Updating " + Counter + " / " + data.Rows.Count + " database entries");
@@ -216,5 +271,36 @@ namespace gaseous_server.Classes
}
}
}
+
+ public static void MySql_1022_MigrateMetadataVersion()
+ {
+ FileSignature fileSignature = new FileSignature();
+
+ Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
+ string sql = "SELECT * FROM Games_Roms WHERE RomDataVersion = 1;";
+ DataTable data = db.ExecuteCMD(sql);
+ foreach (DataRow row in data.Rows)
+ {
+ Logging.Log(Logging.LogType.Information, "Database Migration", "Updating ROM table for ROM: " + (string)row["Name"]);
+
+ GameLibrary.LibraryItem library = GameLibrary.GetLibrary((int)row["LibraryId"]);
+ Common.hashObject hash = new Common.hashObject()
+ {
+ md5hash = (string)row["MD5"],
+ sha1hash = (string)row["SHA1"]
+ };
+ Signatures_Games signature = fileSignature.GetFileSignature(
+ library,
+ hash,
+ new FileInfo((string)row["Path"]),
+ (string)row["Path"]
+ );
+
+ Platform platform = Platforms.GetPlatform((long)row["PlatformId"], false);
+ Game game = Games.GetGame((long)row["GameId"], false, false, false);
+
+ ImportGame.StoreROM(library, hash, game, platform, signature, (string)row["Path"], (long)row["Id"]);
+ }
+ }
}
}
\ No newline at end of file
diff --git a/gaseous-server/Classes/FileSignature.cs b/gaseous-server/Classes/FileSignature.cs
index 2c78fc7..3df2ded 100644
--- a/gaseous-server/Classes/FileSignature.cs
+++ b/gaseous-server/Classes/FileSignature.cs
@@ -1,6 +1,8 @@
using System.Collections.Concurrent;
using System.IO.Compression;
+using gaseous_server.Classes.Metadata;
using HasheousClient.Models;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
using NuGet.Common;
using SevenZip;
using SharpCompress.Archives;
@@ -31,7 +33,7 @@ namespace gaseous_server.Classes
if (!Directory.Exists(ExtractPath)) { Directory.CreateDirectory(ExtractPath); }
try
{
- switch(ImportedFileExtension)
+ switch (ImportedFileExtension)
{
case ".zip":
Logging.Log(Logging.LogType.Information, "Get Signature", "Decompressing using zip");
@@ -105,31 +107,24 @@ namespace gaseous_server.Classes
}
break;
}
-
+
Logging.Log(Logging.LogType.Information, "Get Signature", "Processing decompressed files for signature matches");
// loop through contents until we find the first signature match
List archiveFiles = new List();
bool signatureFound = false;
+ bool signatureSelectorAlreadyApplied = false;
foreach (string file in Directory.GetFiles(ExtractPath, "*.*", SearchOption.AllDirectories))
{
+ bool signatureSelector = false;
if (File.Exists(file))
{
FileInfo zfi = new FileInfo(file);
Common.hashObject zhash = new Common.hashObject(file);
-
+
Logging.Log(Logging.LogType.Information, "Get Signature", "Checking signature of decompressed file " + file);
if (zfi != null)
{
- ArchiveData archiveData = new ArchiveData{
- FileName = Path.GetFileName(file),
- FilePath = zfi.Directory.FullName.Replace(ExtractPath, ""),
- Size = zfi.Length,
- MD5 = hash.md5hash,
- SHA1 = hash.sha1hash
- };
- archiveFiles.Add(archiveData);
-
if (signatureFound == false)
{
gaseous_server.Models.Signatures_Games zDiscoveredSignature = _GetFileSignature(zhash, zfi.Name, zfi.Extension, zfi.Length, file, true);
@@ -138,7 +133,7 @@ namespace gaseous_server.Classes
if (zDiscoveredSignature.Score > discoveredSignature.Score)
{
if (
- zDiscoveredSignature.Rom.SignatureSource == gaseous_server.Models.Signatures_Games.RomItem.SignatureSourceType.MAMEArcade ||
+ zDiscoveredSignature.Rom.SignatureSource == gaseous_server.Models.Signatures_Games.RomItem.SignatureSourceType.MAMEArcade ||
zDiscoveredSignature.Rom.SignatureSource == gaseous_server.Models.Signatures_Games.RomItem.SignatureSourceType.MAMEMess
)
{
@@ -151,15 +146,37 @@ namespace gaseous_server.Classes
discoveredSignature = zDiscoveredSignature;
signatureFound = true;
+
+ if (signatureSelectorAlreadyApplied == false)
+ {
+ signatureSelector = true;
+ signatureSelectorAlreadyApplied = true;
+ }
}
}
+
+ ArchiveData archiveData = new ArchiveData
+ {
+ FileName = Path.GetFileName(file),
+ FilePath = zfi.Directory.FullName.Replace(ExtractPath, ""),
+ Size = zfi.Length,
+ MD5 = zhash.md5hash,
+ SHA1 = zhash.sha1hash,
+ isSignatureSelector = signatureSelector
+ };
+ archiveFiles.Add(archiveData);
}
}
}
- discoveredSignature.Rom.Attributes.Add(new KeyValuePair(
+ if (discoveredSignature.Rom.Attributes == null)
+ {
+ discoveredSignature.Rom.Attributes = new Dictionary();
+ }
+
+ discoveredSignature.Rom.Attributes.Add(
"ZipContents", Newtonsoft.Json.JsonConvert.SerializeObject(archiveFiles)
- ));
+ );
}
catch (Exception ex)
{
@@ -195,7 +212,7 @@ namespace gaseous_server.Classes
{
// signature retrieved from Hasheous
Logging.Log(Logging.LogType.Information, "Import Game", "Signature retrieved from Hasheous for game: " + dbSignature.Game.Name);
-
+
discoveredSignature = dbSignature;
}
else
@@ -203,7 +220,7 @@ namespace gaseous_server.Classes
// construct a signature from file data
dbSignature = _GetFileSignatureFromFileData(hash, ImageName, ImageExtension, ImageSize, GameFileImportPath);
Logging.Log(Logging.LogType.Information, "Import Game", "Signature generated from provided file for game: " + dbSignature.Game.Name);
-
+
discoveredSignature = dbSignature;
}
}
@@ -216,8 +233,8 @@ namespace gaseous_server.Classes
return discoveredSignature;
}
- private gaseous_server.Models.Signatures_Games? _GetFileSignatureFromDatabase(Common.hashObject hash, string ImageName, string ImageExtension, long ImageSize, string GameFileImportPath)
- {
+ private gaseous_server.Models.Signatures_Games? _GetFileSignatureFromDatabase(Common.hashObject hash, string ImageName, string ImageExtension, long ImageSize, string GameFileImportPath)
+ {
Logging.Log(Logging.LogType.Information, "Get Signature", "Checking local database for MD5: " + hash.md5hash);
// check 1: do we have a signature for it?
@@ -378,6 +395,7 @@ namespace gaseous_server.Classes
public long Size { get; set; }
public string MD5 { get; set; }
public string SHA1 { get; set; }
+ public bool isSignatureSelector { get; set; } = false;
}
}
}
\ No newline at end of file
diff --git a/gaseous-server/Classes/ImportGames.cs b/gaseous-server/Classes/ImportGames.cs
index d8d9701..e4a1267 100644
--- a/gaseous-server/Classes/ImportGames.cs
+++ b/gaseous-server/Classes/ImportGames.cs
@@ -16,48 +16,49 @@ using HasheousClient.Models;
namespace gaseous_server.Classes
{
- public class ImportGame : QueueItemStatus
- {
+ public class ImportGame : QueueItemStatus
+ {
public void ProcessDirectory(string ImportPath)
{
if (Directory.Exists(ImportPath))
- {
- string[] importContents = Directory.GetFiles(ImportPath, "*.*", SearchOption.AllDirectories);
+ {
+ string[] importContents = Directory.GetFiles(ImportPath, "*.*", SearchOption.AllDirectories);
Logging.Log(Logging.LogType.Information, "Import Games", "Found " + importContents.Length + " files to process in import directory: " + ImportPath);
- // import files first
+ // import files first
int importCount = 1;
- foreach (string importContent in importContents) {
+ foreach (string importContent in importContents)
+ {
SetStatus(importCount, importContents.Length, "Importing file: " + importContent);
- ImportGameFile(importContent, null);
+ ImportGameFile(importContent, null);
importCount += 1;
- }
+ }
ClearStatus();
DeleteOrphanedDirectories(ImportPath);
}
- else
- {
- Logging.Log(Logging.LogType.Critical, "Import Games", "The import directory " + ImportPath + " does not exist.");
- throw new DirectoryNotFoundException("Invalid path: " + ImportPath);
- }
+ else
+ {
+ Logging.Log(Logging.LogType.Critical, "Import Games", "The import directory " + ImportPath + " does not exist.");
+ throw new DirectoryNotFoundException("Invalid path: " + ImportPath);
+ }
}
public void ImportGameFile(string GameFileImportPath, IGDB.Models.Platform? OverridePlatform)
- {
+ {
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
- string sql = "";
- Dictionary dbDict = new Dictionary();
+ string sql = "";
+ Dictionary dbDict = new Dictionary();
if (Common.SkippableFiles.Contains(Path.GetFileName(GameFileImportPath), StringComparer.OrdinalIgnoreCase))
- {
+ {
Logging.Log(Logging.LogType.Debug, "Import Game", "Skipping item " + GameFileImportPath);
}
- else
- {
+ else
+ {
FileInfo fi = new FileInfo(GameFileImportPath);
Common.hashObject hash = new Common.hashObject(GameFileImportPath);
@@ -87,7 +88,7 @@ namespace gaseous_server.Classes
}
File.Move(GameFileImportPath, targetPathWithFileName, true);
}
-
+
// import source was the upload directory
if (GameFileImportPath.StartsWith(Config.LibraryConfiguration.LibraryUploadDirectory))
{
@@ -146,8 +147,8 @@ namespace gaseous_server.Classes
}
}
}
- }
- }
+ }
+ }
public static IGDB.Models.Game SearchForGame(gaseous_server.Models.Signatures_Games Signature, long PlatformId, bool FullDownload)
{
@@ -173,7 +174,7 @@ namespace gaseous_server.Classes
string GameName = Signature.Game.Name;
List SearchCandidates = GetSearchCandidates(GameName);
-
+
foreach (string SearchCandidate in SearchCandidates)
{
bool GameFound = false;
@@ -197,8 +198,10 @@ namespace gaseous_server.Classes
Logging.Log(Logging.LogType.Information, "Import Game", " " + games.Length + " search results found");
// quite likely we've found sequels and alternate types
- foreach (Game game in games) {
- if (game.Name == SearchCandidate) {
+ foreach (Game game in games)
+ {
+ if (game.Name == SearchCandidate)
+ {
// found game title matches the search candidate
determinedGame = Metadata.Games.GetGame((long)games[0].Id, false, false, false);
Logging.Log(Logging.LogType.Information, "Import Game", "Found exact match!");
@@ -273,7 +276,8 @@ namespace gaseous_server.Classes
// assumption: no games have () in their titles so we'll remove them
int idx = GameName.IndexOf('(');
- if (idx >= 0) {
+ if (idx >= 0)
+ {
GameName = GameName.Substring(0, idx);
}
@@ -304,10 +308,11 @@ namespace gaseous_server.Classes
if (UpdateId == 0)
{
- sql = "INSERT INTO Games_Roms (PlatformId, GameId, Name, Size, CRC, MD5, SHA1, DevelopmentStatus, Attributes, RomType, RomTypeMedia, MediaLabel, Path, MetadataSource, MetadataGameName, MetadataVersion, LibraryId) VALUES (@platformid, @gameid, @name, @size, @crc, @md5, @sha1, @developmentstatus, @Attributes, @romtype, @romtypemedia, @medialabel, @path, @metadatasource, @metadatagamename, @metadataversion, @libraryid); SELECT CAST(LAST_INSERT_ID() AS SIGNED);";
- } else
+ sql = "INSERT INTO Games_Roms (PlatformId, GameId, Name, Size, CRC, MD5, SHA1, DevelopmentStatus, Attributes, RomType, RomTypeMedia, MediaLabel, Path, MetadataSource, MetadataGameName, MetadataVersion, LibraryId, RomDataVersion) VALUES (@platformid, @gameid, @name, @size, @crc, @md5, @sha1, @developmentstatus, @Attributes, @romtype, @romtypemedia, @medialabel, @path, @metadatasource, @metadatagamename, @metadataversion, @libraryid, @romdataversion); SELECT CAST(LAST_INSERT_ID() AS SIGNED);";
+ }
+ else
{
- sql = "UPDATE Games_Roms SET PlatformId=@platformid, GameId=@gameid, Name=@name, Size=@size, DevelopmentStatus=@developmentstatus, Attributes=@Attributes, RomType=@romtype, RomTypeMedia=@romtypemedia, MediaLabel=@medialabel, MetadataSource=@metadatasource, MetadataGameName=@metadatagamename, MetadataVersion=@metadataversion WHERE Id=@id;";
+ sql = "UPDATE Games_Roms SET PlatformId=@platformid, GameId=@gameid, Name=@name, Size=@size, DevelopmentStatus=@developmentstatus, Attributes=@Attributes, RomType=@romtype, RomTypeMedia=@romtypemedia, MediaLabel=@medialabel, MetadataSource=@metadatasource, MetadataGameName=@metadatagamename, MetadataVersion=@metadataversion, RomDataVersion=@romdataversion WHERE Id=@id;";
dbDict.Add("id", UpdateId);
}
dbDict.Add("platformid", Common.ReturnValueIfNull(determinedPlatform.Id, 0));
@@ -322,6 +327,7 @@ namespace gaseous_server.Classes
dbDict.Add("metadatagamename", discoveredSignature.Game.Name);
dbDict.Add("metadataversion", 2);
dbDict.Add("libraryid", library.Id);
+ dbDict.Add("romdataversion", 2);
if (discoveredSignature.Rom.Attributes != null)
{
@@ -348,7 +354,8 @@ namespace gaseous_server.Classes
if (UpdateId == 0)
{
romId = (long)romInsert.Rows[0][0];
- } else
+ }
+ else
{
romId = UpdateId;
}
@@ -362,73 +369,73 @@ namespace gaseous_server.Classes
return romId;
}
- public static string ComputeROMPath(long RomId)
- {
- Classes.Roms.GameRomItem rom = Classes.Roms.GetRom(RomId);
-
- // get metadata
- IGDB.Models.Platform platform = gaseous_server.Classes.Metadata.Platforms.GetPlatform(rom.PlatformId);
- IGDB.Models.Game game = gaseous_server.Classes.Metadata.Games.GetGame(rom.GameId, false, false, false);
-
- // build path
- string platformSlug = "Unknown Platform";
- if (platform != null)
- {
- platformSlug = platform.Slug;
- }
- string gameSlug = "Unknown Title";
- if (game != null)
- {
- gameSlug = game.Slug;
- }
- string DestinationPath = Path.Combine(GameLibrary.GetDefaultLibrary.Path, gameSlug, platformSlug);
- if (!Directory.Exists(DestinationPath))
- {
- Directory.CreateDirectory(DestinationPath);
- }
-
- string DestinationPathName = Path.Combine(DestinationPath, rom.Name);
-
- return DestinationPathName;
- }
-
- public static bool MoveGameFile(long RomId)
- {
+ public static string ComputeROMPath(long RomId)
+ {
Classes.Roms.GameRomItem rom = Classes.Roms.GetRom(RomId);
- string romPath = rom.Path;
+
+ // get metadata
+ IGDB.Models.Platform platform = gaseous_server.Classes.Metadata.Platforms.GetPlatform(rom.PlatformId);
+ IGDB.Models.Game game = gaseous_server.Classes.Metadata.Games.GetGame(rom.GameId, false, false, false);
+
+ // build path
+ string platformSlug = "Unknown Platform";
+ if (platform != null)
+ {
+ platformSlug = platform.Slug;
+ }
+ string gameSlug = "Unknown Title";
+ if (game != null)
+ {
+ gameSlug = game.Slug;
+ }
+ string DestinationPath = Path.Combine(GameLibrary.GetDefaultLibrary.Path, gameSlug, platformSlug);
+ if (!Directory.Exists(DestinationPath))
+ {
+ Directory.CreateDirectory(DestinationPath);
+ }
+
+ string DestinationPathName = Path.Combine(DestinationPath, rom.Name);
+
+ return DestinationPathName;
+ }
+
+ public static bool MoveGameFile(long RomId)
+ {
+ Classes.Roms.GameRomItem rom = Classes.Roms.GetRom(RomId);
+ string romPath = rom.Path;
if (File.Exists(romPath))
{
string DestinationPath = ComputeROMPath(RomId);
- if (romPath == DestinationPath)
- {
- Logging.Log(Logging.LogType.Debug, "Move Game ROM", "Destination path is the same as the current path - aborting");
+ if (romPath == DestinationPath)
+ {
+ Logging.Log(Logging.LogType.Debug, "Move Game ROM", "Destination path is the same as the current path - aborting");
return true;
- }
- else
- {
- Logging.Log(Logging.LogType.Information, "Move Game ROM", "Moving " + romPath + " to " + DestinationPath);
- if (File.Exists(DestinationPath))
- {
- Logging.Log(Logging.LogType.Information, "Move Game ROM", "A file with the same name exists at the destination - aborting");
+ }
+ else
+ {
+ Logging.Log(Logging.LogType.Information, "Move Game ROM", "Moving " + romPath + " to " + DestinationPath);
+ if (File.Exists(DestinationPath))
+ {
+ Logging.Log(Logging.LogType.Information, "Move Game ROM", "A file with the same name exists at the destination - aborting");
return false;
- }
- else
- {
- File.Move(romPath, DestinationPath);
+ }
+ else
+ {
+ File.Move(romPath, DestinationPath);
- // update the db
- Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
- string sql = "UPDATE Games_Roms SET Path=@path WHERE Id=@id";
- Dictionary dbDict = new Dictionary();
- dbDict.Add("id", RomId);
- dbDict.Add("path", DestinationPath);
- db.ExecuteCMD(sql, dbDict);
+ // update the db
+ Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
+ string sql = "UPDATE Games_Roms SET Path=@path WHERE Id=@id";
+ Dictionary dbDict = new Dictionary();
+ dbDict.Add("id", RomId);
+ dbDict.Add("path", DestinationPath);
+ db.ExecuteCMD(sql, dbDict);
return true;
- }
- }
+ }
+ }
}
else
{
@@ -437,8 +444,8 @@ namespace gaseous_server.Classes
}
}
- public void OrganiseLibrary()
- {
+ public void OrganiseLibrary()
+ {
Logging.Log(Logging.LogType.Information, "Organise Library", "Starting default library organisation");
GameLibrary.LibraryItem library = GameLibrary.GetDefaultLibrary;
@@ -451,19 +458,19 @@ namespace gaseous_server.Classes
DataTable romDT = db.ExecuteCMD(sql, dbDict);
if (romDT.Rows.Count > 0)
- {
- for (int i = 0; i < romDT.Rows.Count; i++)
- {
+ {
+ for (int i = 0; i < romDT.Rows.Count; i++)
+ {
SetStatus(i, romDT.Rows.Count, "Processing file " + romDT.Rows[i]["name"]);
- Logging.Log(Logging.LogType.Information, "Organise Library", "(" + i + "/" + romDT.Rows.Count + ") Processing ROM " + romDT.Rows[i]["name"]);
+ Logging.Log(Logging.LogType.Information, "Organise Library", "(" + i + "/" + romDT.Rows.Count + ") Processing ROM " + romDT.Rows[i]["name"]);
long RomId = (long)romDT.Rows[i]["id"];
- MoveGameFile(RomId);
- }
- }
+ MoveGameFile(RomId);
+ }
+ }
ClearStatus();
- // clean up empty directories
- DeleteOrphanedDirectories(GameLibrary.GetDefaultLibrary.Path);
+ // clean up empty directories
+ DeleteOrphanedDirectories(GameLibrary.GetDefaultLibrary.Path);
Logging.Log(Logging.LogType.Information, "Organise Library", "Finsihed default library organisation");
}
@@ -476,7 +483,7 @@ namespace gaseous_server.Classes
string[] files = Directory.GetFiles(directory);
string[] directories = Directory.GetDirectories(directory);
-
+
if (files.Length == 0 &&
directories.Length == 0)
{
@@ -563,7 +570,7 @@ namespace gaseous_server.Classes
public void LibrarySpecificScan(GameLibrary.LibraryItem library)
{
-
+
Logging.Log(Logging.LogType.Information, "Library Scan", "Starting scan of library: " + library.Name);
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
@@ -632,7 +639,7 @@ namespace gaseous_server.Classes
{
// file is not in database - process it
Logging.Log(Logging.LogType.Information, "Library Scan", "Orphaned file found in library: " + LibraryFile);
-
+
Common.hashObject hash = new Common.hashObject(LibraryFile);
FileInfo fi = new FileInfo(LibraryFile);
@@ -644,8 +651,8 @@ namespace gaseous_server.Classes
// get discovered platform
long PlatformId;
IGDB.Models.Platform determinedPlatform;
-
- if (sig.Flags.IGDBPlatformId == null || sig.Flags.IGDBPlatformId == 0 )
+
+ if (sig.Flags.IGDBPlatformId == null || sig.Flags.IGDBPlatformId == 0)
{
// no platform discovered in the signature
PlatformId = library.DefaultPlatformId;
@@ -770,8 +777,8 @@ namespace gaseous_server.Classes
// get discovered platform
long PlatformId;
IGDB.Models.Platform determinedPlatform;
-
- if (sig.Flags.IGDBPlatformId == null || sig.Flags.IGDBPlatformId == 0 )
+
+ if (sig.Flags.IGDBPlatformId == null || sig.Flags.IGDBPlatformId == 0)
{
// no platform discovered in the signature
PlatformId = library.DefaultPlatformId;
diff --git a/gaseous-server/Classes/Metadata/Games.cs b/gaseous-server/Classes/Metadata/Games.cs
index 7e1fbc4..329c3a2 100644
--- a/gaseous-server/Classes/Metadata/Games.cs
+++ b/gaseous-server/Classes/Metadata/Games.cs
@@ -78,15 +78,17 @@ namespace gaseous_server.Classes.Metadata
if (cacheStatus == Storage.CacheStatus.Current) { cacheStatus = Storage.CacheStatus.Expired; }
}
- // set up where clause
string WhereClause = "";
+ string searchField = "";
switch (searchUsing)
{
case SearchUsing.id:
WhereClause = "where id = " + searchValue;
+ searchField = "id";
break;
case SearchUsing.slug:
- WhereClause = "where slug = " + searchValue;
+ WhereClause = "where slug = \"" + searchValue + "\"";
+ searchField = "slug";
break;
default:
throw new Exception("Invalid search type");
@@ -110,11 +112,11 @@ namespace gaseous_server.Classes.Metadata
catch (Exception ex)
{
Logging.Log(Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
- returnValue = Storage.GetCacheValue(returnValue, "id", (long)searchValue);
+ returnValue = Storage.GetCacheValue(returnValue, searchField, searchValue);
}
return returnValue;
case Storage.CacheStatus.Current:
- returnValue = Storage.GetCacheValue(returnValue, "id", (long)searchValue);
+ returnValue = Storage.GetCacheValue(returnValue, searchField, searchValue);
UpdateSubClasses(returnValue, false, false, false);
return returnValue;
default:
diff --git a/gaseous-server/Classes/Metadata/Platforms.cs b/gaseous-server/Classes/Metadata/Platforms.cs
index 5bab6b6..e02ffda 100644
--- a/gaseous-server/Classes/Metadata/Platforms.cs
+++ b/gaseous-server/Classes/Metadata/Platforms.cs
@@ -78,13 +78,16 @@ namespace gaseous_server.Classes.Metadata
// set up where clause
string WhereClause = "";
+ string searchField = "";
switch (searchUsing)
{
case SearchUsing.id:
WhereClause = "where id = " + searchValue;
+ searchField = "id";
break;
case SearchUsing.slug:
- WhereClause = "where slug = " + searchValue;
+ WhereClause = "where slug = \"" + searchValue + "\"";
+ searchField = "slug";
break;
default:
throw new Exception("Invalid search type");
@@ -111,10 +114,10 @@ namespace gaseous_server.Classes.Metadata
catch (Exception ex)
{
Logging.Log(Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
- return Storage.GetCacheValue(returnValue, "id", (long)searchValue);
+ return Storage.GetCacheValue(returnValue, searchField, searchValue);
}
case Storage.CacheStatus.Current:
- return Storage.GetCacheValue(returnValue, "id", (long)searchValue);
+ return Storage.GetCacheValue(returnValue, searchField, searchValue);
default:
throw new Exception("How did you get here?");
}
diff --git a/gaseous-server/Classes/Roms.cs b/gaseous-server/Classes/Roms.cs
index 22aceec..77b54ca 100644
--- a/gaseous-server/Classes/Roms.cs
+++ b/gaseous-server/Classes/Roms.cs
@@ -4,6 +4,9 @@ using gaseous_signature_parser.models.RomSignatureObject;
using static gaseous_server.Classes.RomMediaGroup;
using gaseous_server.Classes.Metadata;
using IGDB.Models;
+using static HasheousClient.Models.FixMatchModel;
+using NuGet.Protocol.Core.Types;
+using static gaseous_server.Classes.FileSignature;
namespace gaseous_server.Classes
{
@@ -147,6 +150,53 @@ namespace gaseous_server.Classes
GameRomItem rom = GetRom(RomId);
+ // send update to Hasheous if enabled
+ if (PlatformId != 0 && GameId != 0)
+ {
+ if (Config.MetadataConfiguration.HasheousSubmitFixes == true)
+ {
+ if (
+ Config.MetadataConfiguration.SignatureSource == HasheousClient.Models.MetadataModel.SignatureSources.Hasheous &&
+ (
+ Config.MetadataConfiguration.HasheousAPIKey != null &&
+ Config.MetadataConfiguration.HasheousAPIKey != "")
+ )
+ {
+ try
+ {
+ // find signature used for identifing the rom
+ string md5String = rom.Md5;
+ string sha1String = rom.Sha1;
+ if (rom.Attributes.ContainsKey("ZipContents"))
+ {
+ bool selectorFound = false;
+ List archiveDataValues = Newtonsoft.Json.JsonConvert.DeserializeObject>(rom.Attributes["ZipContents"].ToString());
+ foreach (ArchiveData archiveData in archiveDataValues)
+ {
+ if (archiveData.isSignatureSelector == true)
+ {
+ md5String = archiveData.MD5;
+ sha1String = archiveData.SHA1;
+ selectorFound = true;
+ break;
+ }
+ }
+ }
+
+ HasheousClient.WebApp.HttpHelper.AddHeader("X-API-Key", Config.MetadataConfiguration.HasheousAPIKey);
+ HasheousClient.Hasheous hasheousClient = new HasheousClient.Hasheous();
+ List metadataMatchList = new List();
+ metadataMatchList.Add(new MetadataMatch(HasheousClient.Models.MetadataSources.IGDB, platform.Slug, game.Slug));
+ hasheousClient.FixMatch(new HasheousClient.Models.FixMatchModel(md5String, sha1String, metadataMatchList));
+ }
+ catch (Exception ex)
+ {
+ Logging.Log(Logging.LogType.Critical, "Fix Match", "An error occurred while sending a fixed match to Hasheous.", ex);
+ }
+ }
+ }
+ }
+
return rom;
}
@@ -179,6 +229,22 @@ namespace gaseous_server.Classes
}
}
+ Dictionary romAttributes = new Dictionary();
+ if (romDR["attributes"] != DBNull.Value)
+ {
+ try
+ {
+ if ((string)romDR["attributes"] != "[ ]")
+ {
+ romAttributes = Newtonsoft.Json.JsonConvert.DeserializeObject>((string)romDR["attributes"]);
+ }
+ }
+ catch (Exception ex)
+ {
+ Logging.Log(Logging.LogType.Warning, "Roms", "Error parsing rom attributes: " + ex.Message);
+ }
+ }
+
GameRomItem romItem = new GameRomItem
{
Id = (long)romDR["id"],
@@ -223,7 +289,7 @@ namespace gaseous_server.Classes
public int Count { get; set; }
}
- public class GameRomItem : HasheousClient.Models.LookupResponseModel.RomItem
+ public class GameRomItem : HasheousClient.Models.SignatureModel.RomItem
{
public long PlatformId { get; set; }
public string Platform { get; set; }
diff --git a/gaseous-server/Classes/SignatureIngestors/XML.cs b/gaseous-server/Classes/SignatureIngestors/XML.cs
index eda4fdc..ff5ee6f 100644
--- a/gaseous-server/Classes/SignatureIngestors/XML.cs
+++ b/gaseous-server/Classes/SignatureIngestors/XML.cs
@@ -8,21 +8,49 @@ namespace gaseous_server.SignatureIngestors.XML
{
public class XMLIngestor : QueueItemStatus
{
- public void Import(string SearchPath, gaseous_signature_parser.parser.SignatureParser XMLType)
+ public void Import(string SearchPath, string ProcessedDirectory, gaseous_signature_parser.parser.SignatureParser XMLType)
{
// connect to database
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
+ string? XMLDBSearchPath = null;
+ string? XMLDBProcessedDirectory = null;
+ if (XMLType == gaseous_signature_parser.parser.SignatureParser.NoIntro)
+ {
+ XMLDBSearchPath = Path.Combine(SearchPath, "DB");
+ XMLDBProcessedDirectory = Path.Combine(ProcessedDirectory, "DB");
+ SearchPath = Path.Combine(SearchPath, "DAT");
+ ProcessedDirectory = Path.Combine(ProcessedDirectory, "DAT");
+ }
+
// process provided files
- Logging.Log(Logging.LogType.Information, "Signature Ingestor - XML", "Importing from " + SearchPath);
if (!Directory.Exists(SearchPath))
{
Directory.CreateDirectory(SearchPath);
}
+ if (!Directory.Exists(ProcessedDirectory))
+ {
+ Directory.CreateDirectory(ProcessedDirectory);
+ }
string[] PathContents = Directory.GetFiles(SearchPath);
Array.Sort(PathContents);
+ string[]? DBPathContents = null;
+ if (XMLDBSearchPath != null)
+ {
+ if (!Directory.Exists(XMLDBSearchPath))
+ {
+ Directory.CreateDirectory(XMLDBSearchPath);
+ }
+ if (!Directory.Exists(XMLDBProcessedDirectory))
+ {
+ Directory.CreateDirectory(XMLDBProcessedDirectory);
+ }
+
+ DBPathContents = Directory.GetFiles(XMLDBSearchPath);
+ }
+
string sql = "";
Dictionary dbDict = new Dictionary();
System.Data.DataTable sigDB;
@@ -33,226 +61,380 @@ namespace gaseous_server.SignatureIngestors.XML
SetStatus(i + 1, PathContents.Length, "Processing signature file: " + XMLFile);
- if (Common.SkippableFiles.Contains(Path.GetFileName(XMLFile), StringComparer.OrdinalIgnoreCase))
- {
- Logging.Log(Logging.LogType.Information, "Signature Ingestor - XML", "Skipping file: " + XMLFile);
- }
- else
- {
- // check xml file md5
- Common.hashObject hashObject = new Common.hashObject(XMLFile);
- sql = "SELECT * FROM Signatures_Sources WHERE SourceMD5=@sourcemd5";
- dbDict = new Dictionary();
- dbDict.Add("sourcemd5", hashObject.md5hash);
- sigDB = db.ExecuteCMD(sql, dbDict);
+ Logging.Log(Logging.LogType.Information, "Signature Ingest", "(" + (i + 1) + " / " + PathContents.Length + ") Processing " + XMLType.ToString() + " DAT file: " + XMLFile);
- if (sigDB.Rows.Count == 0)
+ string? DBFile = null;
+ if (XMLDBSearchPath != null)
+ {
+ switch (XMLType)
{
- try
- {
- Logging.Log(Logging.LogType.Information, "Signature Ingestor - XML", "Importing file: " + XMLFile);
-
- // start parsing file
- gaseous_signature_parser.parser Parser = new gaseous_signature_parser.parser();
- RomSignatureObject Object = Parser.ParseSignatureDAT(XMLFile, XMLType);
-
- // store in database
- string[] flipNameAndDescription = {
- "MAMEArcade",
- "MAMEMess"
- };
-
- // store source object
- bool processGames = false;
- if (Object.SourceMd5 != null)
+ case gaseous_signature_parser.parser.SignatureParser.NoIntro:
+ for (UInt16 x = 0; x < DBPathContents.Length; x++)
{
- sql = "SELECT * FROM Signatures_Sources WHERE SourceMD5=@sourcemd5";
- dbDict = new Dictionary();
- string sourceUriStr = "";
- if (Object.Url != null)
+ string tempDBFileName = Path.GetFileNameWithoutExtension(DBPathContents[x].Replace(" (DB Export)", ""));
+ if (tempDBFileName == Path.GetFileNameWithoutExtension(XMLFile))
{
- sourceUriStr = Object.Url.ToString();
+ DBFile = DBPathContents[x];
+ Logging.Log(Logging.LogType.Information, "Signature Ingest", "Using DB file: " + DBFile);
+ break;
}
- dbDict.Add("name", Common.ReturnValueIfNull(Object.Name, ""));
- dbDict.Add("description", Common.ReturnValueIfNull(Object.Description, ""));
- dbDict.Add("category", Common.ReturnValueIfNull(Object.Category, ""));
- dbDict.Add("version", Common.ReturnValueIfNull(Object.Version, ""));
- dbDict.Add("author", Common.ReturnValueIfNull(Object.Author, ""));
- dbDict.Add("email", Common.ReturnValueIfNull(Object.Email, ""));
- dbDict.Add("homepage", Common.ReturnValueIfNull(Object.Homepage, ""));
- dbDict.Add("uri", sourceUriStr);
- dbDict.Add("sourcetype", Common.ReturnValueIfNull(Object.SourceType, ""));
- dbDict.Add("sourcemd5", Object.SourceMd5);
- dbDict.Add("sourcesha1", Object.SourceSHA1);
+ }
+ break;
+ }
+ }
+
+ // check xml file md5
+ Common.hashObject hashObject = new Common.hashObject(XMLFile);
+ sql = "SELECT * FROM Signatures_Sources WHERE SourceMD5=@sourcemd5";
+ dbDict = new Dictionary();
+ dbDict.Add("sourcemd5", hashObject.md5hash);
+ sigDB = db.ExecuteCMD(sql, dbDict);
+
+ if (sigDB.Rows.Count == 0)
+ {
+ try
+ {
+ // start parsing file
+ gaseous_signature_parser.parser Parser = new gaseous_signature_parser.parser();
+ RomSignatureObject Object = Parser.ParseSignatureDAT(XMLFile, DBFile, XMLType);
+
+ // store in database
+ string[] flipNameAndDescription = {
+ "MAMEArcade",
+ "MAMEMess"
+ };
+
+ // store source object
+ bool processGames = false;
+ if (Object.SourceMd5 != null)
+ {
+ int sourceId = 0;
+
+ sql = "SELECT * FROM Signatures_Sources WHERE `SourceMD5`=@sourcemd5";
+ dbDict = new Dictionary
+ {
+ { "name", Common.ReturnValueIfNull(Object.Name, "") },
+ { "description", Common.ReturnValueIfNull(Object.Description, "") },
+ { "category", Common.ReturnValueIfNull(Object.Category, "") },
+ { "version", Common.ReturnValueIfNull(Object.Version, "") },
+ { "author", Common.ReturnValueIfNull(Object.Author, "") },
+ { "email", Common.ReturnValueIfNull(Object.Email, "") },
+ { "homepage", Common.ReturnValueIfNull(Object.Homepage, "") }
+ };
+ if (Object.Url == null)
+ {
+ dbDict.Add("uri", "");
+ }
+ else
+ {
+ dbDict.Add("uri", Common.ReturnValueIfNull(Object.Url.ToString(), ""));
+ }
+ dbDict.Add("sourcetype", Common.ReturnValueIfNull(Object.SourceType, ""));
+ dbDict.Add("sourcemd5", Object.SourceMd5);
+ dbDict.Add("sourcesha1", Object.SourceSHA1);
+
+ sigDB = db.ExecuteCMD(sql, dbDict);
+ if (sigDB.Rows.Count == 0)
+ {
+ // entry not present, insert it
+ sql = "INSERT INTO Signatures_Sources (`Name`, `Description`, `Category`, `Version`, `Author`, `Email`, `Homepage`, `Url`, `SourceType`, `SourceMD5`, `SourceSHA1`) VALUES (@name, @description, @category, @version, @author, @email, @homepage, @uri, @sourcetype, @sourcemd5, @sourcesha1); SELECT CAST(LAST_INSERT_ID() AS SIGNED);";
sigDB = db.ExecuteCMD(sql, dbDict);
- if (sigDB.Rows.Count == 0)
+
+ sourceId = Convert.ToInt32(sigDB.Rows[0][0]);
+
+ processGames = true;
+ }
+
+ if (processGames == true)
+ {
+ for (int x = 0; x < Object.Games.Count; ++x)
{
- // entry not present, insert it
- sql = "INSERT INTO Signatures_Sources (Name, Description, Category, Version, Author, Email, Homepage, Url, SourceType, SourceMD5, SourceSHA1) VALUES (@name, @description, @category, @version, @author, @email, @homepage, @uri, @sourcetype, @sourcemd5, @sourcesha1)";
+ RomSignatureObject.Game gameObject = Object.Games[x];
- db.ExecuteCMD(sql, dbDict);
-
- processGames = true;
- }
-
- if (processGames == true)
- {
- for (int x = 0; x < Object.Games.Count; ++x)
+ // set up game dictionary
+ dbDict = new Dictionary();
+ if (flipNameAndDescription.Contains(Object.SourceType))
{
- RomSignatureObject.Game gameObject = Object.Games[x];
+ dbDict.Add("name", Common.ReturnValueIfNull(gameObject.Description, ""));
+ dbDict.Add("description", Common.ReturnValueIfNull(gameObject.Name, ""));
+ }
+ else
+ {
+ dbDict.Add("name", Common.ReturnValueIfNull(gameObject.Name, ""));
+ dbDict.Add("description", Common.ReturnValueIfNull(gameObject.Description, ""));
+ }
+ dbDict.Add("year", Common.ReturnValueIfNull(gameObject.Year, ""));
+ dbDict.Add("publisher", Common.ReturnValueIfNull(gameObject.Publisher, ""));
+ dbDict.Add("demo", (int)gameObject.Demo);
+ dbDict.Add("system", Common.ReturnValueIfNull(gameObject.System, ""));
+ dbDict.Add("platform", Common.ReturnValueIfNull(gameObject.System, ""));
+ dbDict.Add("systemvariant", Common.ReturnValueIfNull(gameObject.SystemVariant, ""));
+ dbDict.Add("video", Common.ReturnValueIfNull(gameObject.Video, ""));
- // set up game dictionary
- dbDict = new Dictionary();
- if (flipNameAndDescription.Contains(Object.SourceType))
+ List gameCountries = new List();
+ if (
+ gameObject.Country != null &&
+ gameObject.Country != "Unknown"
+ )
+ {
+ string[] countries = gameObject.Country.Split(",");
+ foreach (string country in countries)
{
- dbDict.Add("name", Common.ReturnValueIfNull(gameObject.Description, ""));
- dbDict.Add("description", Common.ReturnValueIfNull(gameObject.Name, ""));
- }
- else
- {
- dbDict.Add("name", Common.ReturnValueIfNull(gameObject.Name, ""));
- dbDict.Add("description", Common.ReturnValueIfNull(gameObject.Description, ""));
- }
- dbDict.Add("year", Common.ReturnValueIfNull(gameObject.Year, ""));
- dbDict.Add("publisher", Common.ReturnValueIfNull(gameObject.Publisher, ""));
- dbDict.Add("demo", (int)gameObject.Demo);
- dbDict.Add("system", Common.ReturnValueIfNull(gameObject.System, ""));
- dbDict.Add("platform", Common.ReturnValueIfNull(gameObject.System, ""));
- dbDict.Add("systemvariant", Common.ReturnValueIfNull(gameObject.SystemVariant, ""));
- dbDict.Add("video", Common.ReturnValueIfNull(gameObject.Video, ""));
- dbDict.Add("country", Common.ReturnValueIfNull(gameObject.Country, ""));
- dbDict.Add("language", Common.ReturnValueIfNull(gameObject.Language, ""));
- dbDict.Add("copyright", Common.ReturnValueIfNull(gameObject.Copyright, ""));
-
- // store platform
- int gameSystem = 0;
- if (gameObject.System != null)
- {
- sql = "SELECT Id FROM Signatures_Platforms WHERE Platform=@platform";
-
- sigDB = db.ExecuteCMD(sql, dbDict);
- if (sigDB.Rows.Count == 0)
+ int countryId = -1;
+ countryId = Common.GetLookupByCode(Common.LookupTypes.Country, (string)Common.ReturnValueIfNull(country.Trim(), ""));
+ if (countryId == -1)
{
- // entry not present, insert it
- sql = "INSERT INTO Signatures_Platforms (Platform) VALUES (@platform); SELECT CAST(LAST_INSERT_ID() AS SIGNED);";
- sigDB = db.ExecuteCMD(sql, dbDict);
+ countryId = Common.GetLookupByValue(Common.LookupTypes.Country, (string)Common.ReturnValueIfNull(country.Trim(), ""));
- gameSystem = Convert.ToInt32(sigDB.Rows[0][0]);
+ if (countryId == -1)
+ {
+ Logging.Log(Logging.LogType.Warning, "Signature Ingest", "Unable to locate country id for " + country.Trim());
+ sql = "INSERT INTO Country (`Code`, `Value`) VALUES (@code, @name); SELECT CAST(LAST_INSERT_ID() AS SIGNED);";
+ Dictionary countryDict = new Dictionary{
+ { "code", country.Trim() },
+ { "name", country.Trim() }
+ };
+ countryId = int.Parse(db.ExecuteCMD(sql, countryDict).Rows[0][0].ToString());
+ }
}
- else
+
+ if (countryId > 0)
{
- gameSystem = (int)sigDB.Rows[0][0];
+ gameCountries.Add(countryId);
}
}
- dbDict.Add("systemid", gameSystem);
+ }
- // store publisher
- int gamePublisher = 0;
- if (gameObject.Publisher != null)
+ List gameLanguages = new List();
+ if (
+ gameObject.Language != null &&
+ gameObject.Language != "nolang"
+ )
+ {
+ string[] languages = gameObject.Language.Split(",");
+ foreach (string language in languages)
{
- sql = "SELECT * FROM Signatures_Publishers WHERE Publisher=@publisher";
+ int languageId = -1;
+ languageId = Common.GetLookupByCode(Common.LookupTypes.Language, (string)Common.ReturnValueIfNull(language.Trim(), ""));
+ if (languageId == -1)
+ {
+ languageId = Common.GetLookupByValue(Common.LookupTypes.Language, (string)Common.ReturnValueIfNull(language.Trim(), ""));
- sigDB = db.ExecuteCMD(sql, dbDict);
- if (sigDB.Rows.Count == 0)
- {
- // entry not present, insert it
- sql = "INSERT INTO Signatures_Publishers (Publisher) VALUES (@publisher); SELECT CAST(LAST_INSERT_ID() AS SIGNED);";
- sigDB = db.ExecuteCMD(sql, dbDict);
- gamePublisher = Convert.ToInt32(sigDB.Rows[0][0]);
+ if (languageId == -1)
+ {
+ Logging.Log(Logging.LogType.Warning, "Signature Ingest", "Unable to locate language id for " + language.Trim());
+ sql = "INSERT INTO Language (`Code`, `Value`) VALUES (@code, @name); SELECT CAST(LAST_INSERT_ID() AS SIGNED);";
+ Dictionary langDict = new Dictionary{
+ { "code", language.Trim() },
+ { "name", language.Trim() }
+ };
+ languageId = int.Parse(db.ExecuteCMD(sql, langDict).Rows[0][0].ToString());
+ }
}
- else
+
+ if (languageId > 0)
{
- gamePublisher = (int)sigDB.Rows[0][0];
+ gameLanguages.Add(languageId);
}
}
- dbDict.Add("publisherid", gamePublisher);
+ }
- // store game
- int gameId = 0;
- sql = "SELECT * FROM Signatures_Games WHERE Name=@name AND Year=@year AND Publisherid=@publisher AND Systemid=@systemid AND Country=@country AND Language=@language";
+ dbDict.Add("copyright", Common.ReturnValueIfNull(gameObject.Copyright, ""));
+
+ // store platform
+ int gameSystem = 0;
+ if (gameObject.System != null)
+ {
+ sql = "SELECT `Id` FROM Signatures_Platforms WHERE `Platform`=@platform";
sigDB = db.ExecuteCMD(sql, dbDict);
if (sigDB.Rows.Count == 0)
{
// entry not present, insert it
- sql = "INSERT INTO Signatures_Games " +
- "(Name, Description, Year, PublisherId, Demo, SystemId, SystemVariant, Video, Country, Language, Copyright) VALUES " +
- "(@name, @description, @year, @publisherid, @demo, @systemid, @systemvariant, @video, @country, @language, @copyright); SELECT CAST(LAST_INSERT_ID() AS SIGNED);";
+ sql = "INSERT INTO Signatures_Platforms (`Platform`) VALUES (@platform); SELECT CAST(LAST_INSERT_ID() AS SIGNED);";
sigDB = db.ExecuteCMD(sql, dbDict);
- gameId = Convert.ToInt32(sigDB.Rows[0][0]);
+ gameSystem = Convert.ToInt32(sigDB.Rows[0][0]);
}
else
{
- gameId = (int)sigDB.Rows[0][0];
+ gameSystem = (int)sigDB.Rows[0][0];
}
+ }
+ dbDict.Add("systemid", gameSystem);
- // store rom
- foreach (RomSignatureObject.Game.Rom romObject in gameObject.Roms)
+ // store publisher
+ int gamePublisher = 0;
+ if (gameObject.Publisher != null)
+ {
+ sql = "SELECT * FROM Signatures_Publishers WHERE `Publisher`=@publisher";
+
+ sigDB = db.ExecuteCMD(sql, dbDict);
+ if (sigDB.Rows.Count == 0)
{
- if (romObject.Md5 != null || romObject.Sha1 != null)
+ // entry not present, insert it
+ sql = "INSERT INTO Signatures_Publishers (`Publisher`) VALUES (@publisher); SELECT CAST(LAST_INSERT_ID() AS SIGNED);";
+ sigDB = db.ExecuteCMD(sql, dbDict);
+ gamePublisher = Convert.ToInt32(sigDB.Rows[0][0]);
+ }
+ else
+ {
+ gamePublisher = (int)sigDB.Rows[0][0];
+ }
+ }
+ dbDict.Add("publisherid", gamePublisher);
+
+ // store game
+ long gameId = 0;
+ sql = "SELECT * FROM Signatures_Games WHERE `Name`=@name AND `Year`=@year AND `PublisherId`=@publisherid AND `SystemId`=@systemid";
+
+ sigDB = db.ExecuteCMD(sql, dbDict);
+ if (sigDB.Rows.Count == 0)
+ {
+ // entry not present, insert it
+ sql = "INSERT INTO Signatures_Games " +
+ "(`Name`, `Description`, `Year`, `PublisherId`, `Demo`, `SystemId`, `SystemVariant`, `Video`, `Copyright`) VALUES " +
+ "(@name, @description, @year, @publisherid, @demo, @systemid, @systemvariant, @video, @copyright); SELECT CAST(LAST_INSERT_ID() AS SIGNED);";
+ sigDB = db.ExecuteCMD(sql, dbDict);
+
+ gameId = Convert.ToInt32(sigDB.Rows[0][0]);
+ }
+ else
+ {
+ gameId = (int)sigDB.Rows[0][0];
+ }
+
+ // insert countries
+ foreach (int gameCountry in gameCountries)
+ {
+ try
+ {
+ sql = "SELECT * FROM Signatures_Games_Countries WHERE GameId = @gameid AND CountryId = @Countryid";
+ Dictionary countryDict = new Dictionary{
+ { "gameid", gameId },
+ { "Countryid", gameCountry }
+ };
+ if (db.ExecuteCMD(sql, countryDict).Rows.Count == 0)
{
- int romId = 0;
- sql = "SELECT * FROM Signatures_Roms WHERE GameId=@gameid AND MD5=@md5";
- dbDict = new Dictionary();
- dbDict.Add("gameid", gameId);
- dbDict.Add("name", Common.ReturnValueIfNull(romObject.Name, ""));
- dbDict.Add("size", Common.ReturnValueIfNull(romObject.Size, ""));
- dbDict.Add("crc", Common.ReturnValueIfNull(romObject.Crc, "").ToString().ToLower());
- dbDict.Add("md5", Common.ReturnValueIfNull(romObject.Md5, "").ToString().ToLower());
- dbDict.Add("sha1", Common.ReturnValueIfNull(romObject.Sha1, "").ToString().ToLower());
- dbDict.Add("developmentstatus", Common.ReturnValueIfNull(romObject.DevelopmentStatus, ""));
+ sql = "INSERT INTO Signatures_Games_Countries (GameId, CountryId) VALUES (@gameid, @Countryid)";
+ db.ExecuteCMD(sql, countryDict);
+ }
+ }
+ catch
+ {
+ Console.WriteLine("Game id: " + gameId + " with Country " + gameCountry);
+ }
+ }
- if (romObject.Attributes != null)
+ // insert languages
+ foreach (int gameLanguage in gameLanguages)
+ {
+ try
+ {
+ sql = "SELECT * FROM Signatures_Games_Languages WHERE GameId = @gameid AND LanguageId = @languageid";
+ Dictionary langDict = new Dictionary{
+ { "gameid", gameId },
+ { "languageid", gameLanguage }
+ };
+ if (db.ExecuteCMD(sql, langDict).Rows.Count == 0)
+ {
+ sql = "INSERT INTO Signatures_Games_Languages (GameId, LanguageId) VALUES (@gameid, @languageid)";
+ db.ExecuteCMD(sql, langDict);
+ }
+ }
+ catch
+ {
+ Console.WriteLine("Game id: " + gameId + " with language " + gameLanguage);
+ }
+ }
+
+ // store rom
+ foreach (RomSignatureObject.Game.Rom romObject in gameObject.Roms)
+ {
+ if (romObject.Md5 != null || romObject.Sha1 != null)
+ {
+ long romId = 0;
+ sql = "SELECT * FROM Signatures_Roms WHERE `GameId`=@gameid AND (`MD5`=@md5 OR `SHA1`=@sha1)";
+ dbDict = new Dictionary();
+ dbDict.Add("gameid", gameId);
+ dbDict.Add("name", Common.ReturnValueIfNull(romObject.Name, ""));
+ dbDict.Add("size", Common.ReturnValueIfNull(romObject.Size, ""));
+ dbDict.Add("crc", Common.ReturnValueIfNull(romObject.Crc, "").ToString().ToLower());
+ dbDict.Add("md5", Common.ReturnValueIfNull(romObject.Md5, "").ToString().ToLower());
+ dbDict.Add("sha1", Common.ReturnValueIfNull(romObject.Sha1, "").ToString().ToLower());
+ dbDict.Add("developmentstatus", Common.ReturnValueIfNull(romObject.DevelopmentStatus, ""));
+
+ if (romObject.Attributes != null)
+ {
+ if (romObject.Attributes.Count > 0)
{
- if (romObject.Attributes.Count > 0)
- {
- dbDict.Add("attributes", Newtonsoft.Json.JsonConvert.SerializeObject(romObject.Attributes));
- }
- else
- {
- dbDict.Add("attributes", "[ ]");
- }
+ dbDict.Add("attributes", Newtonsoft.Json.JsonConvert.SerializeObject(romObject.Attributes));
}
else
{
- dbDict.Add("attributes", "[ ]");
+ dbDict.Add("attributes", "");
}
- dbDict.Add("romtype", (int)romObject.RomType);
- dbDict.Add("romtypemedia", Common.ReturnValueIfNull(romObject.RomTypeMedia, ""));
- dbDict.Add("medialabel", Common.ReturnValueIfNull(romObject.MediaLabel, ""));
- dbDict.Add("metadatasource", romObject.SignatureSource);
- dbDict.Add("ingestorversion", 2);
+ }
+ else
+ {
+ dbDict.Add("attributes", "");
+ }
+ dbDict.Add("romtype", (int)romObject.RomType);
+ dbDict.Add("romtypemedia", Common.ReturnValueIfNull(romObject.RomTypeMedia, ""));
+ dbDict.Add("medialabel", Common.ReturnValueIfNull(romObject.MediaLabel, ""));
+ dbDict.Add("metadatasource", romObject.SignatureSource);
+ dbDict.Add("ingestorversion", 2);
+ sigDB = db.ExecuteCMD(sql, dbDict);
+ if (sigDB.Rows.Count == 0)
+ {
+ // entry not present, insert it
+ sql = "INSERT INTO Signatures_Roms (`GameId`, `Name`, `Size`, `CRC`, `MD5`, `SHA1`, `DevelopmentStatus`, `Attributes`, `RomType`, `RomTypeMedia`, `MediaLabel`, `MetadataSource`, `IngestorVersion`) VALUES (@gameid, @name, @size, @crc, @md5, @sha1, @developmentstatus, @attributes, @romtype, @romtypemedia, @medialabel, @metadatasource, @ingestorversion); SELECT CAST(LAST_INSERT_ID() AS SIGNED);";
sigDB = db.ExecuteCMD(sql, dbDict);
- if (sigDB.Rows.Count == 0)
- {
- // entry not present, insert it
- sql = "INSERT INTO Signatures_Roms (GameId, Name, Size, CRC, MD5, SHA1, DevelopmentStatus, Attributes, RomType, RomTypeMedia, MediaLabel, MetadataSource, IngestorVersion) VALUES (@gameid, @name, @size, @crc, @md5, @sha1, @developmentstatus, @attributes, @romtype, @romtypemedia, @medialabel, @metadatasource, @ingestorversion); SELECT CAST(LAST_INSERT_ID() AS SIGNED);";
- sigDB = db.ExecuteCMD(sql, dbDict);
+ romId = Convert.ToInt32(sigDB.Rows[0][0]);
+ }
+ else
+ {
+ romId = (int)sigDB.Rows[0][0];
+ }
- romId = Convert.ToInt32(sigDB.Rows[0][0]);
- }
- else
- {
- romId = (int)sigDB.Rows[0][0];
- }
+ // map the rom to the source
+ sql = "SELECT * FROM Signatures_RomToSource WHERE SourceId=@sourceid AND RomId=@romid;";
+ dbDict.Add("romid", romId);
+ dbDict.Add("sourceId", sourceId);
+
+ sigDB = db.ExecuteCMD(sql, dbDict);
+ if (sigDB.Rows.Count == 0)
+ {
+ sql = "INSERT INTO Signatures_RomToSource (`SourceId`, `RomId`) VALUES (@sourceid, @romid);";
+ db.ExecuteCMD(sql, dbDict);
}
}
}
}
}
}
- catch (Exception ex)
+
+ File.Move(XMLFile, Path.Combine(ProcessedDirectory, Path.GetFileName(XMLFile)));
+ if (DBFile != null)
{
- Logging.Log(Logging.LogType.Warning, "Signature Ingestor - XML", "Invalid import file: " + XMLFile, ex);
+ File.Move(DBFile, Path.Combine(XMLDBProcessedDirectory, Path.GetFileName(DBFile)));
}
}
- else
+ catch (Exception ex)
{
- Logging.Log(Logging.LogType.Debug, "Signature Ingestor - XML", "Rejecting already imported file: " + XMLFile);
+ Logging.Log(Logging.LogType.Warning, "Signature Ingest", "Error ingesting " + XMLType.ToString() + " file: " + XMLFile, ex);
+ }
+ }
+ else
+ {
+ Logging.Log(Logging.LogType.Information, "Signature Ingest", "Rejecting already imported " + XMLType.ToString() + " file: " + XMLFile);
+ File.Move(XMLFile, Path.Combine(ProcessedDirectory, Path.GetFileName(XMLFile)));
+ if (DBFile != null)
+ {
+ File.Move(DBFile, Path.Combine(XMLDBProcessedDirectory, Path.GetFileName(DBFile)));
}
}
}
diff --git a/gaseous-server/Classes/SignatureManagement.cs b/gaseous-server/Classes/SignatureManagement.cs
index 2ef5cde..cad5ebc 100644
--- a/gaseous-server/Classes/SignatureManagement.cs
+++ b/gaseous-server/Classes/SignatureManagement.cs
@@ -1,5 +1,6 @@
using System.Data;
using gaseous_signature_parser.models.RomSignatureObject;
+using static gaseous_server.Classes.Common;
namespace gaseous_server.Classes
{
@@ -10,7 +11,8 @@ namespace gaseous_server.Classes
if (md5.Length > 0)
{
return _GetSignature("Signatures_Roms.md5 = @searchstring", md5);
- } else
+ }
+ else
{
return _GetSignature("Signatures_Roms.sha1 = @searchstring", sha1);
}
@@ -21,7 +23,8 @@ namespace gaseous_server.Classes
if (TosecName.Length > 0)
{
return _GetSignature("Signatures_Roms.name = @searchstring", TosecName);
- } else
+ }
+ else
{
return null;
}
@@ -44,7 +47,7 @@ namespace gaseous_server.Classes
{
Game = new gaseous_server.Models.Signatures_Games.GameItem
{
- Id = (Int32)sigDbRow["Id"],
+ Id = (long)(int)sigDbRow["Id"],
Name = (string)sigDbRow["Name"],
Description = (string)sigDbRow["Description"],
Year = (string)sigDbRow["Year"],
@@ -53,20 +56,20 @@ namespace gaseous_server.Classes
System = (string)sigDbRow["Platform"],
SystemVariant = (string)sigDbRow["SystemVariant"],
Video = (string)sigDbRow["Video"],
- Country = (string)sigDbRow["Country"],
- Language = (string)sigDbRow["Language"],
+ Countries = new Dictionary(GetLookup(LookupTypes.Country, (long)(int)sigDbRow["Id"])),
+ Languages = new Dictionary(GetLookup(LookupTypes.Language, (long)(int)sigDbRow["Id"])),
Copyright = (string)sigDbRow["Copyright"]
},
Rom = new gaseous_server.Models.Signatures_Games.RomItem
{
- Id = (Int32)sigDbRow["romid"],
+ Id = (long)(int)sigDbRow["romid"],
Name = (string)sigDbRow["romname"],
Size = (Int64)sigDbRow["Size"],
Crc = (string)sigDbRow["CRC"],
Md5 = ((string)sigDbRow["MD5"]).ToLower(),
Sha1 = ((string)sigDbRow["SHA1"]).ToLower(),
DevelopmentStatus = (string)sigDbRow["DevelopmentStatus"],
- Attributes = Newtonsoft.Json.JsonConvert.DeserializeObject>>((string)Common.ReturnValueIfNull(sigDbRow["Attributes"], "[]")),
+ Attributes = Newtonsoft.Json.JsonConvert.DeserializeObject>((string)Common.ReturnValueIfNull(sigDbRow["Attributes"], "[]")),
RomType = (gaseous_server.Models.Signatures_Games.RomItem.RomTypes)(int)sigDbRow["RomType"],
RomTypeMedia = (string)sigDbRow["RomTypeMedia"],
MediaLabel = (string)sigDbRow["MediaLabel"],
@@ -77,5 +80,36 @@ namespace gaseous_server.Classes
}
return GamesList;
}
+
+ public Dictionary GetLookup(LookupTypes LookupType, long GameId)
+ {
+ string tableName = "";
+ switch (LookupType)
+ {
+ case LookupTypes.Country:
+ tableName = "Countries";
+ break;
+
+ case LookupTypes.Language:
+ tableName = "Languages";
+ break;
+
+ }
+
+ Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
+ string sql = "SELECT " + LookupType.ToString() + ".Code, " + LookupType.ToString() + ".Value FROM Signatures_Games_" + tableName + " JOIN " + LookupType.ToString() + " ON Signatures_Games_" + tableName + "." + LookupType.ToString() + "Id = " + LookupType.ToString() + ".Id WHERE Signatures_Games_" + tableName + ".GameId = @id;";
+ Dictionary dbDict = new Dictionary{
+ { "id", GameId }
+ };
+ DataTable data = db.ExecuteCMD(sql, dbDict);
+
+ Dictionary returnDict = new Dictionary();
+ foreach (DataRow row in data.Rows)
+ {
+ returnDict.Add((string)row["Code"], (string)row["Value"]);
+ }
+
+ return returnDict;
+ }
}
}
\ No newline at end of file
diff --git a/gaseous-server/Controllers/V1.0/SystemController.cs b/gaseous-server/Controllers/V1.0/SystemController.cs
index 10eb213..69b89a3 100644
--- a/gaseous-server/Controllers/V1.0/SystemController.cs
+++ b/gaseous-server/Controllers/V1.0/SystemController.cs
@@ -260,7 +260,14 @@ namespace gaseous_server.Controllers
{
AlwaysLogToDisk = Config.LoggingConfiguration.AlwaysLogToDisk,
MinimumLogRetentionPeriod = Config.LoggingConfiguration.LogRetention,
- EmulatorDebugMode = Boolean.Parse(Config.ReadSetting("emulatorDebugMode", false.ToString()))
+ EmulatorDebugMode = Boolean.Parse(Config.ReadSetting("emulatorDebugMode", false.ToString())),
+ SignatureSource = new SystemSettingsModel.SignatureSourceItem()
+ {
+ Source = Config.MetadataConfiguration.SignatureSource,
+ HasheousHost = Config.MetadataConfiguration.HasheousHost,
+ HasheousSubmitFixes = (bool)Config.MetadataConfiguration.HasheousSubmitFixes,
+ HasheousAPIKey = Config.MetadataConfiguration.HasheousAPIKey
+ }
};
return Ok(systemSettingsModel);
@@ -279,6 +286,10 @@ namespace gaseous_server.Controllers
Config.LoggingConfiguration.AlwaysLogToDisk = model.AlwaysLogToDisk;
Config.LoggingConfiguration.LogRetention = model.MinimumLogRetentionPeriod;
Config.SetSetting("emulatorDebugMode", model.EmulatorDebugMode.ToString());
+ Config.MetadataConfiguration.SignatureSource = model.SignatureSource.Source;
+ Config.MetadataConfiguration.HasheousHost = model.SignatureSource.HasheousHost;
+ Config.MetadataConfiguration.HasheousAPIKey = model.SignatureSource.HasheousAPIKey;
+ Config.MetadataConfiguration.HasheousSubmitFixes = model.SignatureSource.HasheousSubmitFixes;
Config.UpdateConfig();
}
@@ -719,5 +730,14 @@ namespace gaseous_server.Controllers
public bool AlwaysLogToDisk { get; set; }
public int MinimumLogRetentionPeriod { get; set; }
public bool EmulatorDebugMode { get; set; }
+ public SignatureSourceItem SignatureSource { get; set; }
+
+ public class SignatureSourceItem
+ {
+ public HasheousClient.Models.MetadataModel.SignatureSources Source { get; set; }
+ public string HasheousHost { get; set; }
+ public string HasheousAPIKey { get; set; }
+ public bool HasheousSubmitFixes { get; set; }
+ }
}
}
\ No newline at end of file
diff --git a/gaseous-server/Models/Signatures_Games.cs b/gaseous-server/Models/Signatures_Games.cs
index b2fe9b7..cc389cc 100644
--- a/gaseous-server/Models/Signatures_Games.cs
+++ b/gaseous-server/Models/Signatures_Games.cs
@@ -4,12 +4,36 @@ using gaseous_signature_parser.models.RomSignatureObject;
namespace gaseous_server.Models
{
- public class Signatures_Games : HasheousClient.Models.LookupResponseModel
+ public class Signatures_Games : HasheousClient.Models.SignatureModel
{
public Signatures_Games()
{
}
+ [JsonIgnore]
+ public int Score
+ {
+ get
+ {
+ int _score = 0;
+
+ if (Game != null)
+ {
+ _score = _score + Game.Score;
+ }
+
+ if (Rom != null)
+ {
+ _score = _score + Rom.Score;
+ }
+
+ return _score;
+ }
+ }
+
+ public GameItem Game = new GameItem();
+ public RomItem Rom = new RomItem();
+
public SignatureFlags Flags = new SignatureFlags();
public class SignatureFlags
@@ -18,6 +42,213 @@ namespace gaseous_server.Models
public string IGDBPlatformName { get; set; }
public long IGDBGameId { get; set; }
}
+
+ public class GameItem : HasheousClient.Models.SignatureModel.GameItem
+ {
+ public GameItem()
+ {
+
+ }
+
+ [JsonIgnore]
+ public int Score
+ {
+ get
+ {
+ // calculate a score based on the availablility of data
+ int _score = 0;
+ var properties = this.GetType().GetProperties();
+ foreach (var prop in properties)
+ {
+ if (prop.GetGetMethod() != null)
+ {
+ switch (prop.Name.ToLower())
+ {
+ case "id":
+ case "score":
+ break;
+ case "name":
+ case "year":
+ case "publisher":
+ case "system":
+ if (prop.PropertyType == typeof(string))
+ {
+ if (prop.GetValue(this) != null)
+ {
+ string propVal = prop.GetValue(this).ToString();
+ if (propVal.Length > 0)
+ {
+ _score = _score + 10;
+ }
+ }
+ }
+ break;
+ default:
+ if (prop.PropertyType == typeof(string))
+ {
+ if (prop.GetValue(this) != null)
+ {
+ string propVal = prop.GetValue(this).ToString();
+ if (propVal.Length > 0)
+ {
+ _score = _score + 1;
+ }
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ return _score;
+ }
+ }
+ }
+
+ public class RomItem : HasheousClient.Models.SignatureModel.RomItem
+ {
+ [JsonIgnore]
+ public int Score
+ {
+ get
+ {
+ // calculate a score based on the availablility of data
+ int _score = 0;
+ var properties = this.GetType().GetProperties();
+ foreach (var prop in properties)
+ {
+ if (prop.GetGetMethod() != null)
+ {
+ switch (prop.Name.ToLower())
+ {
+ case "name":
+ case "size":
+ case "crc":
+ case "developmentstatus":
+ case "flags":
+ case "attributes":
+ case "romtypemedia":
+ case "medialabel":
+ if (prop.PropertyType == typeof(string) || prop.PropertyType == typeof(Int64) || prop.PropertyType == typeof(List))
+ {
+ if (prop.GetValue(this) != null)
+ {
+ string propVal = prop.GetValue(this).ToString();
+ if (propVal.Length > 0)
+ {
+ _score = _score + 10;
+ }
+ }
+ }
+ break;
+ default:
+ if (prop.PropertyType == typeof(string))
+ {
+ if (prop.GetValue(this) != null)
+ {
+ string propVal = prop.GetValue(this).ToString();
+ if (propVal.Length > 0)
+ {
+ _score = _score + 1;
+ }
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ return _score;
+ }
+ }
+
+ public class MediaType
+ {
+ public MediaType(SignatureSourceType Source, string MediaTypeString)
+ {
+ switch (Source)
+ {
+ case RomItem.SignatureSourceType.TOSEC:
+ string[] typeString = MediaTypeString.Split(" ");
+
+ string inType = "";
+ foreach (string typeStringVal in typeString)
+ {
+ if (inType == "")
+ {
+ switch (typeStringVal.ToLower())
+ {
+ case "disk":
+ Media = RomItem.RomTypes.Disk;
+
+ inType = typeStringVal;
+ break;
+ case "disc":
+ Media = RomItem.RomTypes.Disc;
+
+ inType = typeStringVal;
+ break;
+ case "file":
+ Media = RomItem.RomTypes.File;
+
+ inType = typeStringVal;
+ break;
+ case "part":
+ Media = RomItem.RomTypes.Part;
+
+ inType = typeStringVal;
+ break;
+ case "tape":
+ Media = RomItem.RomTypes.Tape;
+
+ inType = typeStringVal;
+ break;
+ case "of":
+ inType = typeStringVal;
+ break;
+ case "side":
+ inType = typeStringVal;
+ break;
+ }
+ }
+ else {
+ switch (inType.ToLower())
+ {
+ case "disk":
+ case "disc":
+ case "file":
+ case "part":
+ case "tape":
+ Number = int.Parse(typeStringVal);
+ break;
+ case "of":
+ Count = int.Parse(typeStringVal);
+ break;
+ case "side":
+ Side = typeStringVal;
+ break;
+ }
+ inType = "";
+ }
+ }
+
+ break;
+
+ default:
+ break;
+
+ }
+ }
+
+ public RomItem.RomTypes? Media { get; set; }
+
+ public int? Number { get; set; }
+
+ public int? Count { get; set; }
+
+ public string? Side { get; set; }
+ }
+ }
}
}
diff --git a/gaseous-server/ProcessQueue.cs b/gaseous-server/ProcessQueue.cs
index 8b0f0d9..07dd941 100644
--- a/gaseous-server/ProcessQueue.cs
+++ b/gaseous-server/ProcessQueue.cs
@@ -245,15 +245,33 @@ namespace gaseous_server
CallingQueueItem = this
};
- Logging.Log(Logging.LogType.Debug, "Signature Import", "Processing TOSEC files");
- tIngest.Import(Path.Combine(Config.LibraryConfiguration.LibrarySignatureImportDirectory, "TOSEC"), gaseous_signature_parser.parser.SignatureParser.TOSEC);
-
- Logging.Log(Logging.LogType.Debug, "Signature Import", "Processing MAME Arcade files");
- tIngest.Import(Path.Combine(Config.LibraryConfiguration.LibrarySignatureImportDirectory, "MAME Arcade"), gaseous_signature_parser.parser.SignatureParser.MAMEArcade);
+ foreach (int i in Enum.GetValues(typeof(gaseous_signature_parser.parser.SignatureParser)))
+ {
+ gaseous_signature_parser.parser.SignatureParser parserType = (gaseous_signature_parser.parser.SignatureParser)i;
+ if (
+ parserType != gaseous_signature_parser.parser.SignatureParser.Auto &&
+ parserType != gaseous_signature_parser.parser.SignatureParser.Unknown
+ )
+ {
+ Logging.Log(Logging.LogType.Debug, "Signature Import", "Processing " + parserType + " files");
+
+ string SignaturePath = Path.Combine(Config.LibraryConfiguration.LibrarySignaturesDirectory, parserType.ToString());
+ string SignatureProcessedPath = Path.Combine(Config.LibraryConfiguration.LibrarySignaturesProcessedDirectory, parserType.ToString());
+
+ if (!Directory.Exists(SignaturePath))
+ {
+ Directory.CreateDirectory(SignaturePath);
+ }
+
+ if (!Directory.Exists(SignatureProcessedPath))
+ {
+ Directory.CreateDirectory(SignatureProcessedPath);
+ }
+
+ tIngest.Import(SignaturePath, SignatureProcessedPath, parserType);
+ }
+ }
- Logging.Log(Logging.LogType.Debug, "Signature Import", "Processing MAME MESS files");
- tIngest.Import(Path.Combine(Config.LibraryConfiguration.LibrarySignatureImportDirectory, "MAME MESS"), gaseous_signature_parser.parser.SignatureParser.MAMEMess);
-
_SaveLastRunTime = true;
break;
diff --git a/gaseous-server/Program.cs b/gaseous-server/Program.cs
index b623528..a4f7ab7 100644
--- a/gaseous-server/Program.cs
+++ b/gaseous-server/Program.cs
@@ -71,6 +71,7 @@ if (Directory.Exists(Config.LibraryConfiguration.LibraryUploadDirectory))
// kick off any delayed upgrade tasks
// run 1002 background updates in the background on every start
DatabaseMigration.BackgroundUpgradeTargetSchemaVersions.Add(1002);
+DatabaseMigration.BackgroundUpgradeTargetSchemaVersions.Add(1022);
// start the task
ProcessQueue.QueueItem queueItem = new ProcessQueue.QueueItem(
ProcessQueue.QueueItemType.BackgroundDatabaseUpgrade,
diff --git a/gaseous-server/Support/Country.txt b/gaseous-server/Support/Country.txt
new file mode 100644
index 0000000..5ee02a3
--- /dev/null
+++ b/gaseous-server/Support/Country.txt
@@ -0,0 +1,73 @@
+AE|United Arab Emirates
+AL|Albania
+AS|Asia
+AT|Austria
+AU|Australia
+BA|Bosnia and Herzegovina
+BE|Belgium
+BG|Bulgaria
+BR|Brazil
+CA|Canada
+CH|Switzerland
+CL|Chile
+CN|China
+CS|Serbia and Montenegro
+CY|Cyprus
+CZ|Czech Republic
+DE|Germany
+DK|Denmark
+EE|Estonia
+EG|Egypt
+ES|Spain
+EU|Europe
+FI|Finland
+FR|France
+GB|United Kingdom
+GR|Greece
+HK|Hong Kong
+HR|Croatia
+HU|Hungary
+ID|Indonesia
+IE|Ireland
+IL|Israel
+IN|India
+IR|Iran
+IS|Iceland
+IT|Italy
+JO|Jordan
+JP|Japan
+KR|Korea
+KR|South Korea
+LT|Lithuania
+LU|Luxembourg
+LV|Latvia
+MN|Mongolia
+MX|Mexico
+MY|Malaysia
+NL|Netherlands
+NO|Norway
+NP|Nepal
+NZ|New Zealand
+OM|Oman
+PE|Peru
+PH|Philippines
+PL|Poland
+PT|Portugal
+QA|Qatar
+RO|Romania
+RU|Russia
+SE|Sweden
+SG|Singapore
+SI|Slovenia
+SK|Slovakia
+TH|Thailand
+TR|Turkey
+TW|Taiwan
+US|United States
+USA|United States
+VN|Vietnam
+YU|Yugoslavia
+ZA|South Africa
+World|World
+Europe|Europe
+Asia|Asia
diff --git a/gaseous-server/Support/Database/MySQL/gaseous-1022.sql b/gaseous-server/Support/Database/MySQL/gaseous-1022.sql
new file mode 100644
index 0000000..b089e9c
--- /dev/null
+++ b/gaseous-server/Support/Database/MySQL/gaseous-1022.sql
@@ -0,0 +1,40 @@
+CREATE TABLE `Signatures_RomToSource` (
+ `SourceId` int NOT NULL,
+ `RomId` int NOT NULL,
+ PRIMARY KEY (`SourceId`, `RomId`)
+);
+
+CREATE TABLE `Signatures_Games_Countries` (
+ `GameId` INT NOT NULL,
+ `CountryId` INT NOT NULL,
+ PRIMARY KEY (`GameId`, `CountryId`),
+ CONSTRAINT `GameCountry` FOREIGN KEY (`GameId`) REFERENCES `Signatures_Games` (`Id`) ON DELETE CASCADE ON UPDATE NO ACTION
+);
+
+CREATE TABLE `Signatures_Games_Languages` (
+ `GameId` INT NOT NULL,
+ `LanguageId` INT NOT NULL,
+ PRIMARY KEY (`GameId`, `LanguageId`),
+ CONSTRAINT `GameLanguage` FOREIGN KEY (`GameId`) REFERENCES `Signatures_Games` (`Id`) ON DELETE CASCADE ON UPDATE NO ACTION
+);
+
+CREATE TABLE `Country` (
+ `Id` INT NOT NULL AUTO_INCREMENT,
+ `Code` VARCHAR(20) NULL,
+ `Value` VARCHAR(255) NULL,
+ PRIMARY KEY (`Id`),
+ INDEX `id_Code` (`Code` ASC) VISIBLE,
+ INDEX `id_Value` (`Value` ASC) VISIBLE
+);
+
+CREATE TABLE `Language` (
+ `Id` INT NOT NULL AUTO_INCREMENT,
+ `Code` VARCHAR(20) NULL,
+ `Value` VARCHAR(255) NULL,
+ PRIMARY KEY (`Id`),
+ INDEX `id_Code` (`Code` ASC) VISIBLE,
+ INDEX `id_Value` (`Value` ASC) VISIBLE
+);
+
+ALTER TABLE `Games_Roms`
+ADD COLUMN `RomDataVersion` INT DEFAULT 1;
\ No newline at end of file
diff --git a/gaseous-server/Support/Language.txt b/gaseous-server/Support/Language.txt
new file mode 100644
index 0000000..cdf301c
--- /dev/null
+++ b/gaseous-server/Support/Language.txt
@@ -0,0 +1,47 @@
+ar|Arabic
+bg|Bulgarian
+bs|Bosnian
+cs|Czech
+cy|Welsh
+da|Danish
+de|German
+el|Greek
+en|English
+eo|Esperanto
+es|Spanish
+et|Estonian
+fa|Persian
+fi|Finnish
+fr|French
+fr-ca|French Canadian
+ga|Irish
+gd|Gaelic
+gu|Gujarati
+he|Hebrew
+hi|Hindi
+hr|Croatian
+hu|Hungarian
+is|Icelandic
+it|Italian
+ja|Japanese
+ko|Korean
+lt|Lithuanian
+lv|Latvian
+ms|Malay
+nl|Dutch
+no|Norwegian
+pl|Polish
+pt|Portuguese
+ro|Romanian
+ru|Russian
+sk|Slovakian
+sl|Slovenian
+sq|Albanian
+sr|Serbian
+sv|Swedish
+th|Thai
+tr|Turkish
+ur|Urdu
+vi|Vietnamese
+yi|Yiddish
+zh|Chinese
diff --git a/gaseous-server/gaseous-server.csproj b/gaseous-server/gaseous-server.csproj
index 79071b8..a7d42af 100644
--- a/gaseous-server/gaseous-server.csproj
+++ b/gaseous-server/gaseous-server.csproj
@@ -16,9 +16,9 @@
bin\Release\net8.0\gaseous-server.xml
-
-
-
+
+
+
@@ -29,7 +29,7 @@
-
+
@@ -39,6 +39,8 @@
+
+
@@ -64,6 +66,7 @@
+
@@ -85,6 +88,8 @@
truePreserveNewest
+
+
@@ -108,5 +113,6 @@
+
\ No newline at end of file
diff --git a/gaseous-server/wwwroot/.DS_Store b/gaseous-server/wwwroot/.DS_Store
index f620c5d..d579713 100644
Binary files a/gaseous-server/wwwroot/.DS_Store and b/gaseous-server/wwwroot/.DS_Store differ
diff --git a/gaseous-server/wwwroot/images/unknowngame.png b/gaseous-server/wwwroot/images/unknowngame.png
index 78b8573..4eddb02 100644
Binary files a/gaseous-server/wwwroot/images/unknowngame.png and b/gaseous-server/wwwroot/images/unknowngame.png differ
diff --git a/gaseous-server/wwwroot/images/unknowngame.pxd b/gaseous-server/wwwroot/images/unknowngame.pxd
new file mode 100644
index 0000000..5dafd29
Binary files /dev/null and b/gaseous-server/wwwroot/images/unknowngame.pxd differ
diff --git a/gaseous-server/wwwroot/pages/dialogs/librarynew.html b/gaseous-server/wwwroot/pages/dialogs/librarynew.html
index 0f9c6d9..8525fce 100644
--- a/gaseous-server/wwwroot/pages/dialogs/librarynew.html
+++ b/gaseous-server/wwwroot/pages/dialogs/librarynew.html
@@ -39,7 +39,7 @@
},
processResults: function (data) {
var arr = [];
-
+
arr.push({
id: 0,
text: 'Any'
@@ -74,11 +74,11 @@
ajaxCall(
'/api/v1.1/Library?Name=' + encodeURIComponent(libName) + '&DefaultPlatformId=' + libPlatform[0].id + '&Path=' + encodeURIComponent(libPath),
'POST',
- function(result) {
+ function (result) {
drawLibrary();
- closeSubDialog();
+ closeDialog();
},
- function(error) {
+ function (error) {
alert('An error occurred while creating the library:\n\n' + JSON.stringify(error.responseText));
}
);
diff --git a/gaseous-server/wwwroot/pages/dialogs/rominfo.html b/gaseous-server/wwwroot/pages/dialogs/rominfo.html
index b5ce6e2..3e848b9 100644
--- a/gaseous-server/wwwroot/pages/dialogs/rominfo.html
+++ b/gaseous-server/wwwroot/pages/dialogs/rominfo.html
@@ -1,7 +1,9 @@
General
-
Archive Contents
-
Attributes
+
+ Archive Contents
+
Attributes
Title Match
@@ -59,7 +61,7 @@
-
+
@@ -78,7 +80,9 @@
-
+
@@ -144,7 +148,7 @@
document.getElementById('romDelete').style.display = 'none';
}
- if (result.attributes.length > 0) {
+ if (result.attributes) {
document.getElementById('properties_bodypanel_attributes').appendChild(BuildAttributesTable(result.attributes, result.source));
document.getElementById('properties_bodypanel_archive_content').appendChild(BuildArchiveTable(result.attributes, result.source));
}
@@ -276,8 +280,8 @@
var aTable = document.createElement('table');
aTable.style.width = '100%';
- for (var i = 0; i < attributes.length; i++) {
- if (attributes[i].key != "ZipContents") {
+ for (const [key, value] of Object.entries(attributes)) {
+ if (key != "ZipContents") {
// show attributes button
document.getElementById('properties_toc_attributes').style.display = '';
var aRow = document.createElement('tr');
@@ -285,15 +289,15 @@
var aTitleCell = document.createElement('th');
aTitleCell.width = "25%";
if (sourceName == "TOSEC") {
- aTitleCell.innerHTML = ConvertTOSECAttributeName(attributes[i].key);
+ aTitleCell.innerHTML = ConvertTOSECAttributeName(key);
} else {
- aTitleCell.innerHTML = attributes[i].key;
+ aTitleCell.innerHTML = key;
}
aRow.appendChild(aTitleCell);
var aValueCell = document.createElement('td');
aValueCell.width = "75%";
- aValueCell.innerHTML = attributes[i].value;
+ aValueCell.innerHTML = value;
aRow.appendChild(aValueCell);
aTable.appendChild(aRow);
@@ -304,13 +308,13 @@
}
function BuildArchiveTable(attributes, sourceName) {
- for (var i = 0; i < attributes.length; i++) {
- if (attributes[i].key == "ZipContents") {
- var archiveContent = JSON.parse(attributes[i].value);
-
+ for (const [key, value] of Object.entries(attributes)) {
+ if (key == "ZipContents") {
+ var archiveContent = JSON.parse(value);
+
// show archive button
document.getElementById('properties_toc_archive').style.display = '';
-
+
var aTable = document.createElement('table');
aTable.className = 'romtable';
aTable.setAttribute('cellspacing', 0);
@@ -343,6 +347,17 @@
hRow.appendChild(aHashCell);
aBody.appendChild(hRow);
+ if (archiveContent[r].isSignatureSelector == true) {
+ var sigRow = document.createElement('tr');
+
+ var sigCell = document.createElement('td');
+ sigCell.setAttribute('colspan', 2);
+ sigCell.style.paddingLeft = '20px';
+ sigCell.innerHTML = "Hash used to identify this archive";
+ sigRow.appendChild(sigCell);
+ aBody.appendChild(sigRow);
+ }
+
aTable.appendChild(aBody);
}
}
@@ -354,18 +369,18 @@
function ConvertTOSECAttributeName(attributeName) {
var tosecAttributeNames = {
"cr": "Cracked",
- "f" : "Fixed",
- "h" : "Hacked",
- "m" : "Modified",
- "p" : "Pirated",
- "t" : "Trained",
+ "f": "Fixed",
+ "h": "Hacked",
+ "m": "Modified",
+ "p": "Pirated",
+ "t": "Trained",
"tr": "Translated",
- "o" : "Over Dump",
- "u" : "Under Dump",
- "v" : "Virus",
- "b" : "Bad Dump",
- "a" : "Alternate",
- "!" : "Known Verified Dump"
+ "o": "Over Dump",
+ "u": "Under Dump",
+ "v": "Virus",
+ "b": "Bad Dump",
+ "a": "Alternate",
+ "!": "Known Verified Dump"
};
if (attributeName in tosecAttributeNames) {
@@ -378,4 +393,4 @@
SelectTab('general');
document.getElementById('romDelete').setAttribute("onclick", "showSubDialog('romdelete', " + modalVariables + ");");
-
+
\ No newline at end of file
diff --git a/gaseous-server/wwwroot/pages/settings.html b/gaseous-server/wwwroot/pages/settings.html
index 74cd83e..4b0b700 100644
--- a/gaseous-server/wwwroot/pages/settings.html
+++ b/gaseous-server/wwwroot/pages/settings.html
@@ -1,4 +1,5 @@
-
+
\ No newline at end of file
diff --git a/gaseous-server/wwwroot/pages/settings/libraries.html b/gaseous-server/wwwroot/pages/settings/libraries.html
new file mode 100644
index 0000000..d30f616
--- /dev/null
+++ b/gaseous-server/wwwroot/pages/settings/libraries.html
@@ -0,0 +1,63 @@
+
+
Libraries
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/gaseous-server/wwwroot/pages/settings/services.html b/gaseous-server/wwwroot/pages/settings/services.html
new file mode 100644
index 0000000..5d61e6c
--- /dev/null
+++ b/gaseous-server/wwwroot/pages/settings/services.html
@@ -0,0 +1,290 @@
+
+
Services
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/gaseous-server/wwwroot/pages/settings/settings.html b/gaseous-server/wwwroot/pages/settings/settings.html
index 93e6a06..aaa8a6e 100644
--- a/gaseous-server/wwwroot/pages/settings/settings.html
+++ b/gaseous-server/wwwroot/pages/settings/settings.html
@@ -2,37 +2,74 @@
Settings
-
Libraries
-
-
-
-
-
-
Advanced Settings
-
Warning Do not modify the below settings unless you know what you're doing.
-
Background Task Timers
-
-
-
-
-
-
System Settings
-
+
-
Logging
+
+
Metadata Sources
+
+
+
+
+ Signature Source
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Hasheous Host
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Hasheous API key
+
+
+
+
+
+
+
+
Logging
+
Write logs
-
+
-
+
@@ -47,10 +84,12 @@
-
Emulator
+
+
Emulator
+
-
Enable debug mode
+
@@ -61,339 +100,11 @@
\ No newline at end of file
diff --git a/gaseous-server/wwwroot/pages/settings/system.html b/gaseous-server/wwwroot/pages/settings/system.html
index 8e24b68..d7210e6 100644
--- a/gaseous-server/wwwroot/pages/settings/system.html
+++ b/gaseous-server/wwwroot/pages/settings/system.html
@@ -22,7 +22,7 @@