Merge branch 'main' into branch-v1.7.0

This commit is contained in:
Michael Green
2024-06-30 00:31:06 +10:00
committed by GitHub
33 changed files with 1817 additions and 839 deletions

3
.gitignore vendored
View File

@@ -404,4 +404,7 @@ ASALocalRun/
# Local History for Visual Studio # Local History for Visual Studio
.localhistory/ .localhistory/
gaseous-server/.DS_Store gaseous-server/.DS_Store
gaseous-server/wwwroot/.DS_Store
gaseous-server/wwwroot/emulators/EmulatorJS gaseous-server/wwwroot/emulators/EmulatorJS
.devcontainer/.env
.mono/

3
.vscode/launch.json vendored
View File

@@ -24,7 +24,8 @@
}, },
"sourceFileMap": { "sourceFileMap": {
"/Views": "${workspaceFolder}/Views" "/Views": "${workspaceFolder}/Views"
} },
"enableStepFiltering": false
}, },
{ {
"name": ".NET Core Attach", "name": ".NET Core Attach",

View File

@@ -1,4 +1,4 @@

Microsoft Visual Studio Solution File, Format Version 12.00 Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16 # Visual Studio Version 16
VisualStudioVersion = 25.0.1704.4 VisualStudioVersion = 25.0.1704.4
@@ -27,30 +27,18 @@ Global
Release|Any CPU = Release|Any CPU Release|Any CPU = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution 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.ActiveCfg = Debug|Any CPU
{A01D2EFF-C82E-473B-84D7-7C25E736F5D2}.Debug|Any CPU.Build.0 = 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.ActiveCfg = Release|Any CPU
{A01D2EFF-C82E-473B-84D7-7C25E736F5D2}.Release|Any CPU.Build.0 = 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 EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
EndGlobalSection EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {979BF092-AFBC-4F19-B55F-32E73959BD1A}
EndGlobalSection
GlobalSection(NestedProjects) = preSolution GlobalSection(NestedProjects) = preSolution
{F1A847C7-57BC-4DA9-8F83-CD060A7F5122} = {17FA6F12-8532-420C-9489-CB8FDE42137C} {F1A847C7-57BC-4DA9-8F83-CD060A7F5122} = {17FA6F12-8532-420C-9489-CB8FDE42137C}
EndGlobalSection EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {979BF092-AFBC-4F19-B55F-32E73959BD1A}
EndGlobalSection
EndGlobal EndGlobal

149
LICENSE
View File

@@ -1,5 +1,5 @@
GNU GENERAL PUBLIC LICENSE GNU AFFERO GENERAL PUBLIC LICENSE
Version 3, 29 June 2007 Version 3, 19 November 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/> Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies Everyone is permitted to copy and distribute verbatim copies
@@ -7,17 +7,15 @@
Preamble Preamble
The GNU General Public License is a free, copyleft license for The GNU Affero General Public License is a free, copyleft license for
software and other kinds of works. 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 The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast, 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 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 software for all its users.
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.
When we speak of free software, we are referring to freedom, not When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you 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 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. free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you Developers that use our General Public Licenses protect your rights
these rights or asking you to surrender the rights. Therefore, you have with two steps: (1) assert copyright on the software, and (2) offer
certain responsibilities if you distribute copies of the software, or if you this License which gives you legal permission to copy, distribute
you modify it: responsibilities to respect the freedom of others. and/or modify the software.
For example, if you distribute copies of such a program, whether A secondary benefit of defending all users' freedom is that
gratis or for a fee, you must pass on to the recipients the same improvements made in alternate versions of the program, if they
freedoms that you received. You must make sure that they, too, receive receive widespread use, become available for other developers to
or can get the source code. And you must show them these terms so they incorporate. Many developers of free software are heartened and
know their rights. 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: The GNU Affero General Public License is designed specifically to
(1) assert copyright on the software, and (2) offer you this License ensure that, in such cases, the modified source code becomes available
giving you legal permission to copy, distribute and/or modify it. 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 An older license, called the Affero General Public License and
that there is no warranty for this free software. For both users' and published by Affero, was designed to accomplish similar goals. This is
authors' sake, the GPL requires that modified versions be marked as a different license, not a version of the Affero GPL, but Affero has
changed, so that their problems will not be attributed erroneously to released a new version of the Affero GPL which permits relicensing under
authors of previous versions. this license.
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.
The precise terms and conditions for copying, distribution and The precise terms and conditions for copying, distribution and
modification follow. modification follow.
@@ -72,7 +60,7 @@ modification follow.
0. Definitions. 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 "Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks. 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 the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program. 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 Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed 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 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, License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License, but the work with which it is combined will remain governed by version
section 13, concerning interaction through a network will apply to the 3 of the GNU General Public License.
combination as such.
14. Revised Versions of this License. 14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of 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 the GNU Affero General Public License from time to time. Such new versions
be similar in spirit to the present version, but may differ in detail to will be similar in spirit to the present version, but may differ in detail to
address new problems or concerns. address new problems or concerns.
Each version is given a distinguishing version number. If the 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 Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the 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. by the Free Software Foundation.
If the Program specifies that a proxy can decide which future 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 public statement of acceptance of a version permanently authorizes you
to choose that version for the Program. 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 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. the "copyright" line and a pointer to where the full notice is found.
Gaseous <one line to give the program's name and a brief idea of what it does.>
Copyright (C) 2023 Gaseous Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU Affero General Public License as published
the Free Software Foundation, either version 3 of the License, or by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
This program is distributed in the hope that it will be useful, This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 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 <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail. Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short If your software can interact with users remotely through a computer
notice like this when it starts in an interactive mode: 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
<program> Copyright (C) 2023 Gaseous interface could display a "Source" link that leads users to an archive
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. of the code. There are many ways you could offer source, and different
This is free software, and you are welcome to redistribute it solutions will be better for different programs; see section 13 for the
under certain conditions; type `show c' for details. specific requirements.
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".
You should also get your employer (if you work as a programmer) or school, 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. 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 For more information on this, and how to apply and follow the GNU AGPL, see
<https://www.gnu.org/licenses/>. <https://www.gnu.org/licenses/>.
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
<https://www.gnu.org/licenses/why-not-lgpl.html>.

View File

@@ -1,5 +1,6 @@
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.ComponentModel; using System.ComponentModel;
using System.Data;
using System.IO.Compression; using System.IO.Compression;
using System.Reflection; using System.Reflection;
using System.Security.Cryptography; using System.Security.Cryptography;

View File

@@ -80,7 +80,8 @@ namespace gaseous_server.Classes
get get
{ {
string logPath = Path.Combine(ConfigurationPath, "Logs"); string logPath = Path.Combine(ConfigurationPath, "Logs");
if (!Directory.Exists(logPath)) { if (!Directory.Exists(logPath))
{
Directory.CreateDirectory(logPath); Directory.CreateDirectory(logPath);
} }
return logPath; return logPath;
@@ -92,7 +93,7 @@ namespace gaseous_server.Classes
get get
{ {
string logFileExtension = "txt"; string logFileExtension = "txt";
string logPathName = Path.Combine(LogPath, "Server Log " + DateTime.Now.ToUniversalTime().ToString("yyyyMMdd") + "." + logFileExtension); string logPathName = Path.Combine(LogPath, "Server Log " + DateTime.Now.ToUniversalTime().ToString("yyyyMMdd") + "." + logFileExtension);
return logPathName; return logPathName;
} }
@@ -131,7 +132,7 @@ namespace gaseous_server.Classes
_config.DatabaseConfiguration.DatabaseName = (string)Common.GetEnvVar("dbname", _config.DatabaseConfiguration.DatabaseName); _config.DatabaseConfiguration.DatabaseName = (string)Common.GetEnvVar("dbname", _config.DatabaseConfiguration.DatabaseName);
_config.DatabaseConfiguration.Port = int.Parse((string)Common.GetEnvVar("dbport", _config.DatabaseConfiguration.Port.ToString())); _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.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.MaxLibraryScanWorkers = int.Parse((string)Common.GetEnvVar("maxlibraryscanworkers", _config.MetadataConfiguration.MaxLibraryScanWorkers.ToString()));
_config.MetadataConfiguration.HasheousHost = (string)Common.GetEnvVar("hasheoushost", _config.MetadataConfiguration.HasheousHost); _config.MetadataConfiguration.HasheousHost = (string)Common.GetEnvVar("hasheoushost", _config.MetadataConfiguration.HasheousHost);
_config.IGDBConfiguration.ClientId = (string)Common.GetEnvVar("igdbclientid", _config.IGDBConfiguration.ClientId); _config.IGDBConfiguration.ClientId = (string)Common.GetEnvVar("igdbclientid", _config.IGDBConfiguration.ClientId);
@@ -205,9 +206,9 @@ namespace gaseous_server.Classes
{ {
AppSettings.Remove(SettingName); AppSettings.Remove(SettingName);
} }
Logging.Log(Logging.LogType.Information, "Load Settings", "Loading setting " + SettingName + " from database"); Logging.Log(Logging.LogType.Information, "Load Settings", "Loading setting " + SettingName + " from database");
try try
{ {
if (Database.schema_version >= 1016) if (Database.schema_version >= 1016)
@@ -275,7 +276,7 @@ namespace gaseous_server.Classes
if (Database.schema_version >= 1016) if (Database.schema_version >= 1016)
{ {
sql = "SELECT Value, ValueDate FROM Settings WHERE Setting = @SettingName"; sql = "SELECT Value, ValueDate FROM Settings WHERE Setting = @SettingName";
dbResponse = db.ExecuteCMD(sql, dbDict); dbResponse = db.ExecuteCMD(sql, dbDict);
Type type = typeof(T); Type type = typeof(T);
if (dbResponse.Rows.Count == 0) if (dbResponse.Rows.Count == 0)
@@ -301,7 +302,7 @@ namespace gaseous_server.Classes
else else
{ {
sql = "SELECT Value FROM Settings WHERE Setting = @SettingName"; sql = "SELECT Value FROM Settings WHERE Setting = @SettingName";
dbResponse = db.ExecuteCMD(sql, dbDict); dbResponse = db.ExecuteCMD(sql, dbDict);
Type type = typeof(T); Type type = typeof(T);
if (dbResponse.Rows.Count == 0) if (dbResponse.Rows.Count == 0)
@@ -355,7 +356,7 @@ namespace gaseous_server.Classes
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql; string sql;
Dictionary<string, object> dbDict; Dictionary<string, object> dbDict;
if (Database.schema_version >= 1016) if (Database.schema_version >= 1016)
{ {
sql = "REPLACE INTO Settings (Setting, ValueType, Value, ValueDate) VALUES (@SettingName, @ValueType, @Value, @ValueDate)"; sql = "REPLACE INTO Settings (Setting, ValueType, Value, ValueDate) VALUES (@SettingName, @ValueType, @Value, @ValueDate)";
@@ -427,7 +428,8 @@ namespace gaseous_server.Classes
public class Database public class Database
{ {
private static string _DefaultHostName { private static string _DefaultHostName
{
get get
{ {
if (!String.IsNullOrEmpty(Environment.GetEnvironmentVariable("dbhost"))) if (!String.IsNullOrEmpty(Environment.GetEnvironmentVariable("dbhost")))
@@ -643,7 +645,7 @@ namespace gaseous_server.Classes
return MetadataPath; return MetadataPath;
} }
public string LibrarySignatureImportDirectory public string LibrarySignaturesDirectory
{ {
get get
{ {
@@ -651,6 +653,14 @@ namespace gaseous_server.Classes
} }
} }
public string LibrarySignaturesProcessedDirectory
{
get
{
return Path.Combine(LibraryRootDirectory, "Signatures - Processed");
}
}
public void InitLibrary() public void InitLibrary()
{ {
if (!Directory.Exists(LibraryRootDirectory)) { Directory.CreateDirectory(LibraryRootDirectory); } 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(LibraryMetadataDirectory)) { Directory.CreateDirectory(LibraryMetadataDirectory); }
if (!Directory.Exists(LibraryTempDirectory)) { Directory.CreateDirectory(LibraryTempDirectory); } if (!Directory.Exists(LibraryTempDirectory)) { Directory.CreateDirectory(LibraryTempDirectory); }
if (!Directory.Exists(LibraryCollectionsDirectory)) { Directory.CreateDirectory(LibraryCollectionsDirectory); } 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 private static int _MaxLibraryScanWorkers
{ {
get get
@@ -730,6 +745,10 @@ namespace gaseous_server.Classes
public HasheousClient.Models.MetadataModel.SignatureSources SignatureSource = _SignatureSource; public HasheousClient.Models.MetadataModel.SignatureSources SignatureSource = _SignatureSource;
public bool HasheousSubmitFixes = _HasheousSubmitFixes;
public string HasheousAPIKey = _HasheousAPIKey;
public int MaxLibraryScanWorkers = _MaxLibraryScanWorkers; public int MaxLibraryScanWorkers = _MaxLibraryScanWorkers;
public string HasheousHost = _HasheousHost; public string HasheousHost = _HasheousHost;

View File

@@ -1,14 +1,17 @@
using System; using System;
using System.Data; using System.Data;
using System.Reflection; using System.Reflection;
using gaseous_server.Classes.Metadata;
using gaseous_server.Models;
using IGDB.Models;
namespace gaseous_server.Classes namespace gaseous_server.Classes
{ {
public static class DatabaseMigration public static class DatabaseMigration
{ {
public static List<int> BackgroundUpgradeTargetSchemaVersions = new List<int>(); public static List<int> BackgroundUpgradeTargetSchemaVersions = new List<int>();
public static void PreUpgradeScript(int TargetSchemaVersion, Database.databaseType? DatabaseType) public static void PreUpgradeScript(int TargetSchemaVersion, Database.databaseType? DatabaseType)
{ {
// load resources // load resources
var assembly = Assembly.GetExecutingAssembly(); 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); Logging.Log(Logging.LogType.Information, "Database", "Checking for pre-upgrade for schema version " + TargetSchemaVersion);
switch(DatabaseType) switch (DatabaseType)
{ {
case Database.databaseType.MySql: case Database.databaseType.MySql:
switch (TargetSchemaVersion) switch (TargetSchemaVersion)
{ {
case 1005: case 1005:
Logging.Log(Logging.LogType.Information, "Database", "Running pre-upgrade for schema version " + TargetSchemaVersion); Logging.Log(Logging.LogType.Information, "Database", "Running pre-upgrade for schema version " + TargetSchemaVersion);
// there was a mistake at dbschema version 1004-1005 // there was a mistake at dbschema version 1004-1005
// the first preview release of v1.7 reused dbschema version 1004 // 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 // 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); Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = ""; string sql = "";
Dictionary<string, object> dbDict = new Dictionary<string, object>(); Dictionary<string, object> dbDict = new Dictionary<string, object>();
DataTable data; DataTable data;
switch(DatabaseType) switch (DatabaseType)
{ {
case Database.databaseType.MySql: case Database.databaseType.MySql:
switch (TargetSchemaVersion) switch (TargetSchemaVersion)
@@ -78,7 +83,7 @@ namespace gaseous_server.Classes
// this is a safe background task // this is a safe background task
BackgroundUpgradeTargetSchemaVersions.Add(1002); BackgroundUpgradeTargetSchemaVersions.Add(1002);
break; break;
case 1004: case 1004:
// needs to run on start up // needs to run on start up
@@ -103,6 +108,51 @@ namespace gaseous_server.Classes
sql = "DELETE FROM Settings WHERE Setting LIKE 'LastRun_%';"; sql = "DELETE FROM Settings WHERE Setting LIKE 'LastRun_%';";
db.ExecuteNonQuery(sql); db.ExecuteNonQuery(sql);
break; 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<string, object>{
{ "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<string, object>{
{ "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; break;
} }
@@ -117,11 +167,16 @@ namespace gaseous_server.Classes
case 1002: case 1002:
MySql_1002_MigrateMetadataVersion(); MySql_1002_MigrateMetadataVersion();
break; 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); Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = ""; string sql = "";
Dictionary<string, object> dbDict = new Dictionary<string, object>(); Dictionary<string, object> dbDict = new Dictionary<string, object>();
@@ -134,7 +189,7 @@ namespace gaseous_server.Classes
Logging.Log(Logging.LogType.Information, "Signature Ingestor - Database Update", "Updating " + data.Rows.Count + " database entries"); Logging.Log(Logging.LogType.Information, "Signature Ingestor - Database Update", "Updating " + data.Rows.Count + " database entries");
int Counter = 0; int Counter = 0;
int LastCounterCheck = 0; int LastCounterCheck = 0;
foreach (DataRow row in data.Rows) foreach (DataRow row in data.Rows)
{ {
List<string> Flags = Newtonsoft.Json.JsonConvert.DeserializeObject<List<string>>((string)Common.ReturnValueIfNull(row["flags"], "[]")); List<string> Flags = Newtonsoft.Json.JsonConvert.DeserializeObject<List<string>>((string)Common.ReturnValueIfNull(row["flags"], "[]"));
List<KeyValuePair<string, object>> Attributes = new List<KeyValuePair<string, object>>(); List<KeyValuePair<string, object>> Attributes = new List<KeyValuePair<string, object>>();
@@ -207,7 +262,7 @@ namespace gaseous_server.Classes
dbDict.Add("id", (int)row["Id"]); dbDict.Add("id", (int)row["Id"]);
db.ExecuteCMD(updateSQL, dbDict); db.ExecuteCMD(updateSQL, dbDict);
if ((Counter - LastCounterCheck) > 10) if ((Counter - LastCounterCheck) > 10)
{ {
LastCounterCheck = Counter; LastCounterCheck = Counter;
Logging.Log(Logging.LogType.Information, "Signature Ingestor - Database Update", "Updating " + Counter + " / " + data.Rows.Count + " database entries"); 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"]);
}
}
} }
} }

View File

@@ -1,6 +1,8 @@
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.IO.Compression; using System.IO.Compression;
using gaseous_server.Classes.Metadata;
using HasheousClient.Models; using HasheousClient.Models;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using NuGet.Common; using NuGet.Common;
using SevenZip; using SevenZip;
using SharpCompress.Archives; using SharpCompress.Archives;
@@ -31,7 +33,7 @@ namespace gaseous_server.Classes
if (!Directory.Exists(ExtractPath)) { Directory.CreateDirectory(ExtractPath); } if (!Directory.Exists(ExtractPath)) { Directory.CreateDirectory(ExtractPath); }
try try
{ {
switch(ImportedFileExtension) switch (ImportedFileExtension)
{ {
case ".zip": case ".zip":
Logging.Log(Logging.LogType.Information, "Get Signature", "Decompressing using zip"); Logging.Log(Logging.LogType.Information, "Get Signature", "Decompressing using zip");
@@ -105,31 +107,24 @@ namespace gaseous_server.Classes
} }
break; break;
} }
Logging.Log(Logging.LogType.Information, "Get Signature", "Processing decompressed files for signature matches"); Logging.Log(Logging.LogType.Information, "Get Signature", "Processing decompressed files for signature matches");
// loop through contents until we find the first signature match // loop through contents until we find the first signature match
List<ArchiveData> archiveFiles = new List<ArchiveData>(); List<ArchiveData> archiveFiles = new List<ArchiveData>();
bool signatureFound = false; bool signatureFound = false;
bool signatureSelectorAlreadyApplied = false;
foreach (string file in Directory.GetFiles(ExtractPath, "*.*", SearchOption.AllDirectories)) foreach (string file in Directory.GetFiles(ExtractPath, "*.*", SearchOption.AllDirectories))
{ {
bool signatureSelector = false;
if (File.Exists(file)) if (File.Exists(file))
{ {
FileInfo zfi = new FileInfo(file); FileInfo zfi = new FileInfo(file);
Common.hashObject zhash = new Common.hashObject(file); Common.hashObject zhash = new Common.hashObject(file);
Logging.Log(Logging.LogType.Information, "Get Signature", "Checking signature of decompressed file " + file); Logging.Log(Logging.LogType.Information, "Get Signature", "Checking signature of decompressed file " + file);
if (zfi != null) 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) if (signatureFound == false)
{ {
gaseous_server.Models.Signatures_Games zDiscoveredSignature = _GetFileSignature(zhash, zfi.Name, zfi.Extension, zfi.Length, file, true); 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.Score > discoveredSignature.Score)
{ {
if ( 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 zDiscoveredSignature.Rom.SignatureSource == gaseous_server.Models.Signatures_Games.RomItem.SignatureSourceType.MAMEMess
) )
{ {
@@ -151,15 +146,37 @@ namespace gaseous_server.Classes
discoveredSignature = zDiscoveredSignature; discoveredSignature = zDiscoveredSignature;
signatureFound = true; 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<string, object>( if (discoveredSignature.Rom.Attributes == null)
{
discoveredSignature.Rom.Attributes = new Dictionary<string, object>();
}
discoveredSignature.Rom.Attributes.Add(
"ZipContents", Newtonsoft.Json.JsonConvert.SerializeObject(archiveFiles) "ZipContents", Newtonsoft.Json.JsonConvert.SerializeObject(archiveFiles)
)); );
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -195,7 +212,7 @@ namespace gaseous_server.Classes
{ {
// signature retrieved from Hasheous // signature retrieved from Hasheous
Logging.Log(Logging.LogType.Information, "Import Game", "Signature retrieved from Hasheous for game: " + dbSignature.Game.Name); Logging.Log(Logging.LogType.Information, "Import Game", "Signature retrieved from Hasheous for game: " + dbSignature.Game.Name);
discoveredSignature = dbSignature; discoveredSignature = dbSignature;
} }
else else
@@ -203,7 +220,7 @@ namespace gaseous_server.Classes
// construct a signature from file data // construct a signature from file data
dbSignature = _GetFileSignatureFromFileData(hash, ImageName, ImageExtension, ImageSize, GameFileImportPath); dbSignature = _GetFileSignatureFromFileData(hash, ImageName, ImageExtension, ImageSize, GameFileImportPath);
Logging.Log(Logging.LogType.Information, "Import Game", "Signature generated from provided file for game: " + dbSignature.Game.Name); Logging.Log(Logging.LogType.Information, "Import Game", "Signature generated from provided file for game: " + dbSignature.Game.Name);
discoveredSignature = dbSignature; discoveredSignature = dbSignature;
} }
} }
@@ -216,8 +233,8 @@ namespace gaseous_server.Classes
return discoveredSignature; 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); Logging.Log(Logging.LogType.Information, "Get Signature", "Checking local database for MD5: " + hash.md5hash);
// check 1: do we have a signature for it? // check 1: do we have a signature for it?
@@ -378,6 +395,7 @@ namespace gaseous_server.Classes
public long Size { get; set; } public long Size { get; set; }
public string MD5 { get; set; } public string MD5 { get; set; }
public string SHA1 { get; set; } public string SHA1 { get; set; }
public bool isSignatureSelector { get; set; } = false;
} }
} }
} }

View File

@@ -16,48 +16,49 @@ using HasheousClient.Models;
namespace gaseous_server.Classes namespace gaseous_server.Classes
{ {
public class ImportGame : QueueItemStatus public class ImportGame : QueueItemStatus
{ {
public void ProcessDirectory(string ImportPath) public void ProcessDirectory(string ImportPath)
{ {
if (Directory.Exists(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); 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; int importCount = 1;
foreach (string importContent in importContents) { foreach (string importContent in importContents)
{
SetStatus(importCount, importContents.Length, "Importing file: " + importContent); SetStatus(importCount, importContents.Length, "Importing file: " + importContent);
ImportGameFile(importContent, null); ImportGameFile(importContent, null);
importCount += 1; importCount += 1;
} }
ClearStatus(); ClearStatus();
DeleteOrphanedDirectories(ImportPath); DeleteOrphanedDirectories(ImportPath);
} }
else else
{ {
Logging.Log(Logging.LogType.Critical, "Import Games", "The import directory " + ImportPath + " does not exist."); Logging.Log(Logging.LogType.Critical, "Import Games", "The import directory " + ImportPath + " does not exist.");
throw new DirectoryNotFoundException("Invalid path: " + ImportPath); throw new DirectoryNotFoundException("Invalid path: " + ImportPath);
} }
} }
public void ImportGameFile(string GameFileImportPath, IGDB.Models.Platform? OverridePlatform) public void ImportGameFile(string GameFileImportPath, IGDB.Models.Platform? OverridePlatform)
{ {
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = ""; string sql = "";
Dictionary<string, object> dbDict = new Dictionary<string, object>(); Dictionary<string, object> dbDict = new Dictionary<string, object>();
if (Common.SkippableFiles.Contains<string>(Path.GetFileName(GameFileImportPath), StringComparer.OrdinalIgnoreCase)) if (Common.SkippableFiles.Contains<string>(Path.GetFileName(GameFileImportPath), StringComparer.OrdinalIgnoreCase))
{ {
Logging.Log(Logging.LogType.Debug, "Import Game", "Skipping item " + GameFileImportPath); Logging.Log(Logging.LogType.Debug, "Import Game", "Skipping item " + GameFileImportPath);
} }
else else
{ {
FileInfo fi = new FileInfo(GameFileImportPath); FileInfo fi = new FileInfo(GameFileImportPath);
Common.hashObject hash = new Common.hashObject(GameFileImportPath); Common.hashObject hash = new Common.hashObject(GameFileImportPath);
@@ -87,7 +88,7 @@ namespace gaseous_server.Classes
} }
File.Move(GameFileImportPath, targetPathWithFileName, true); File.Move(GameFileImportPath, targetPathWithFileName, true);
} }
// import source was the upload directory // import source was the upload directory
if (GameFileImportPath.StartsWith(Config.LibraryConfiguration.LibraryUploadDirectory)) 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) 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; string GameName = Signature.Game.Name;
List<string> SearchCandidates = GetSearchCandidates(GameName); List<string> SearchCandidates = GetSearchCandidates(GameName);
foreach (string SearchCandidate in SearchCandidates) foreach (string SearchCandidate in SearchCandidates)
{ {
bool GameFound = false; bool GameFound = false;
@@ -197,8 +198,10 @@ namespace gaseous_server.Classes
Logging.Log(Logging.LogType.Information, "Import Game", " " + games.Length + " search results found"); Logging.Log(Logging.LogType.Information, "Import Game", " " + games.Length + " search results found");
// quite likely we've found sequels and alternate types // quite likely we've found sequels and alternate types
foreach (Game game in games) { foreach (Game game in games)
if (game.Name == SearchCandidate) { {
if (game.Name == SearchCandidate)
{
// found game title matches the search candidate // found game title matches the search candidate
determinedGame = Metadata.Games.GetGame((long)games[0].Id, false, false, false); determinedGame = Metadata.Games.GetGame((long)games[0].Id, false, false, false);
Logging.Log(Logging.LogType.Information, "Import Game", "Found exact match!"); 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 // assumption: no games have () in their titles so we'll remove them
int idx = GameName.IndexOf('('); int idx = GameName.IndexOf('(');
if (idx >= 0) { if (idx >= 0)
{
GameName = GameName.Substring(0, idx); GameName = GameName.Substring(0, idx);
} }
@@ -304,10 +308,11 @@ namespace gaseous_server.Classes
if (UpdateId == 0) 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);"; 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 }
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("id", UpdateId);
} }
dbDict.Add("platformid", Common.ReturnValueIfNull(determinedPlatform.Id, 0)); dbDict.Add("platformid", Common.ReturnValueIfNull(determinedPlatform.Id, 0));
@@ -322,6 +327,7 @@ namespace gaseous_server.Classes
dbDict.Add("metadatagamename", discoveredSignature.Game.Name); dbDict.Add("metadatagamename", discoveredSignature.Game.Name);
dbDict.Add("metadataversion", 2); dbDict.Add("metadataversion", 2);
dbDict.Add("libraryid", library.Id); dbDict.Add("libraryid", library.Id);
dbDict.Add("romdataversion", 2);
if (discoveredSignature.Rom.Attributes != null) if (discoveredSignature.Rom.Attributes != null)
{ {
@@ -348,7 +354,8 @@ namespace gaseous_server.Classes
if (UpdateId == 0) if (UpdateId == 0)
{ {
romId = (long)romInsert.Rows[0][0]; romId = (long)romInsert.Rows[0][0];
} else }
else
{ {
romId = UpdateId; romId = UpdateId;
} }
@@ -362,73 +369,73 @@ namespace gaseous_server.Classes
return romId; return romId;
} }
public static string ComputeROMPath(long 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)
{
Classes.Roms.GameRomItem rom = Classes.Roms.GetRom(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)) if (File.Exists(romPath))
{ {
string DestinationPath = ComputeROMPath(RomId); string DestinationPath = ComputeROMPath(RomId);
if (romPath == DestinationPath) if (romPath == DestinationPath)
{ {
Logging.Log(Logging.LogType.Debug, "Move Game ROM", "Destination path is the same as the current path - aborting"); Logging.Log(Logging.LogType.Debug, "Move Game ROM", "Destination path is the same as the current path - aborting");
return true; return true;
} }
else else
{ {
Logging.Log(Logging.LogType.Information, "Move Game ROM", "Moving " + romPath + " to " + DestinationPath); Logging.Log(Logging.LogType.Information, "Move Game ROM", "Moving " + romPath + " to " + DestinationPath);
if (File.Exists(DestinationPath)) if (File.Exists(DestinationPath))
{ {
Logging.Log(Logging.LogType.Information, "Move Game ROM", "A file with the same name exists at the destination - aborting"); Logging.Log(Logging.LogType.Information, "Move Game ROM", "A file with the same name exists at the destination - aborting");
return false; return false;
} }
else else
{ {
File.Move(romPath, DestinationPath); File.Move(romPath, DestinationPath);
// update the db // update the db
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "UPDATE Games_Roms SET Path=@path WHERE Id=@id"; string sql = "UPDATE Games_Roms SET Path=@path WHERE Id=@id";
Dictionary<string, object> dbDict = new Dictionary<string, object>(); Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("id", RomId); dbDict.Add("id", RomId);
dbDict.Add("path", DestinationPath); dbDict.Add("path", DestinationPath);
db.ExecuteCMD(sql, dbDict); db.ExecuteCMD(sql, dbDict);
return true; return true;
} }
} }
} }
else 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"); Logging.Log(Logging.LogType.Information, "Organise Library", "Starting default library organisation");
GameLibrary.LibraryItem library = GameLibrary.GetDefaultLibrary; GameLibrary.LibraryItem library = GameLibrary.GetDefaultLibrary;
@@ -451,19 +458,19 @@ namespace gaseous_server.Classes
DataTable romDT = db.ExecuteCMD(sql, dbDict); DataTable romDT = db.ExecuteCMD(sql, dbDict);
if (romDT.Rows.Count > 0) 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"]); 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"]; long RomId = (long)romDT.Rows[i]["id"];
MoveGameFile(RomId); MoveGameFile(RomId);
} }
} }
ClearStatus(); ClearStatus();
// clean up empty directories // clean up empty directories
DeleteOrphanedDirectories(GameLibrary.GetDefaultLibrary.Path); DeleteOrphanedDirectories(GameLibrary.GetDefaultLibrary.Path);
Logging.Log(Logging.LogType.Information, "Organise Library", "Finsihed default library organisation"); 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[] files = Directory.GetFiles(directory);
string[] directories = Directory.GetDirectories(directory); string[] directories = Directory.GetDirectories(directory);
if (files.Length == 0 && if (files.Length == 0 &&
directories.Length == 0) directories.Length == 0)
{ {
@@ -563,7 +570,7 @@ namespace gaseous_server.Classes
public void LibrarySpecificScan(GameLibrary.LibraryItem library) public void LibrarySpecificScan(GameLibrary.LibraryItem library)
{ {
Logging.Log(Logging.LogType.Information, "Library Scan", "Starting scan of library: " + library.Name); Logging.Log(Logging.LogType.Information, "Library Scan", "Starting scan of library: " + library.Name);
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); 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 // file is not in database - process it
Logging.Log(Logging.LogType.Information, "Library Scan", "Orphaned file found in library: " + LibraryFile); Logging.Log(Logging.LogType.Information, "Library Scan", "Orphaned file found in library: " + LibraryFile);
Common.hashObject hash = new Common.hashObject(LibraryFile); Common.hashObject hash = new Common.hashObject(LibraryFile);
FileInfo fi = new FileInfo(LibraryFile); FileInfo fi = new FileInfo(LibraryFile);
@@ -644,8 +651,8 @@ namespace gaseous_server.Classes
// get discovered platform // get discovered platform
long PlatformId; long PlatformId;
IGDB.Models.Platform determinedPlatform; 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 // no platform discovered in the signature
PlatformId = library.DefaultPlatformId; PlatformId = library.DefaultPlatformId;
@@ -770,8 +777,8 @@ namespace gaseous_server.Classes
// get discovered platform // get discovered platform
long PlatformId; long PlatformId;
IGDB.Models.Platform determinedPlatform; 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 // no platform discovered in the signature
PlatformId = library.DefaultPlatformId; PlatformId = library.DefaultPlatformId;

View File

@@ -78,15 +78,17 @@ namespace gaseous_server.Classes.Metadata
if (cacheStatus == Storage.CacheStatus.Current) { cacheStatus = Storage.CacheStatus.Expired; } if (cacheStatus == Storage.CacheStatus.Current) { cacheStatus = Storage.CacheStatus.Expired; }
} }
// set up where clause
string WhereClause = ""; string WhereClause = "";
string searchField = "";
switch (searchUsing) switch (searchUsing)
{ {
case SearchUsing.id: case SearchUsing.id:
WhereClause = "where id = " + searchValue; WhereClause = "where id = " + searchValue;
searchField = "id";
break; break;
case SearchUsing.slug: case SearchUsing.slug:
WhereClause = "where slug = " + searchValue; WhereClause = "where slug = \"" + searchValue + "\"";
searchField = "slug";
break; break;
default: default:
throw new Exception("Invalid search type"); throw new Exception("Invalid search type");
@@ -110,11 +112,11 @@ namespace gaseous_server.Classes.Metadata
catch (Exception ex) catch (Exception ex)
{ {
Logging.Log(Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex); Logging.Log(Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
returnValue = Storage.GetCacheValue<Game>(returnValue, "id", (long)searchValue); returnValue = Storage.GetCacheValue<Game>(returnValue, searchField, searchValue);
} }
return returnValue; return returnValue;
case Storage.CacheStatus.Current: case Storage.CacheStatus.Current:
returnValue = Storage.GetCacheValue<Game>(returnValue, "id", (long)searchValue); returnValue = Storage.GetCacheValue<Game>(returnValue, searchField, searchValue);
UpdateSubClasses(returnValue, false, false, false); UpdateSubClasses(returnValue, false, false, false);
return returnValue; return returnValue;
default: default:

View File

@@ -78,13 +78,16 @@ namespace gaseous_server.Classes.Metadata
// set up where clause // set up where clause
string WhereClause = ""; string WhereClause = "";
string searchField = "";
switch (searchUsing) switch (searchUsing)
{ {
case SearchUsing.id: case SearchUsing.id:
WhereClause = "where id = " + searchValue; WhereClause = "where id = " + searchValue;
searchField = "id";
break; break;
case SearchUsing.slug: case SearchUsing.slug:
WhereClause = "where slug = " + searchValue; WhereClause = "where slug = \"" + searchValue + "\"";
searchField = "slug";
break; break;
default: default:
throw new Exception("Invalid search type"); throw new Exception("Invalid search type");
@@ -111,10 +114,10 @@ namespace gaseous_server.Classes.Metadata
catch (Exception ex) catch (Exception ex)
{ {
Logging.Log(Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex); Logging.Log(Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
return Storage.GetCacheValue<Platform>(returnValue, "id", (long)searchValue); return Storage.GetCacheValue<Platform>(returnValue, searchField, searchValue);
} }
case Storage.CacheStatus.Current: case Storage.CacheStatus.Current:
return Storage.GetCacheValue<Platform>(returnValue, "id", (long)searchValue); return Storage.GetCacheValue<Platform>(returnValue, searchField, searchValue);
default: default:
throw new Exception("How did you get here?"); throw new Exception("How did you get here?");
} }

View File

@@ -4,6 +4,9 @@ using gaseous_signature_parser.models.RomSignatureObject;
using static gaseous_server.Classes.RomMediaGroup; using static gaseous_server.Classes.RomMediaGroup;
using gaseous_server.Classes.Metadata; using gaseous_server.Classes.Metadata;
using IGDB.Models; using IGDB.Models;
using static HasheousClient.Models.FixMatchModel;
using NuGet.Protocol.Core.Types;
using static gaseous_server.Classes.FileSignature;
namespace gaseous_server.Classes namespace gaseous_server.Classes
{ {
@@ -147,6 +150,53 @@ namespace gaseous_server.Classes
GameRomItem rom = GetRom(RomId); 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<ArchiveData> archiveDataValues = Newtonsoft.Json.JsonConvert.DeserializeObject<List<ArchiveData>>(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<MetadataMatch> metadataMatchList = new List<MetadataMatch>();
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; return rom;
} }
@@ -179,6 +229,22 @@ namespace gaseous_server.Classes
} }
} }
Dictionary<string, object> romAttributes = new Dictionary<string, object>();
if (romDR["attributes"] != DBNull.Value)
{
try
{
if ((string)romDR["attributes"] != "[ ]")
{
romAttributes = Newtonsoft.Json.JsonConvert.DeserializeObject<Dictionary<string, object>>((string)romDR["attributes"]);
}
}
catch (Exception ex)
{
Logging.Log(Logging.LogType.Warning, "Roms", "Error parsing rom attributes: " + ex.Message);
}
}
GameRomItem romItem = new GameRomItem GameRomItem romItem = new GameRomItem
{ {
Id = (long)romDR["id"], Id = (long)romDR["id"],
@@ -223,7 +289,7 @@ namespace gaseous_server.Classes
public int Count { get; set; } 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 long PlatformId { get; set; }
public string Platform { get; set; } public string Platform { get; set; }

View File

@@ -8,21 +8,49 @@ namespace gaseous_server.SignatureIngestors.XML
{ {
public class XMLIngestor : QueueItemStatus 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 // connect to database
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); 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 // process provided files
Logging.Log(Logging.LogType.Information, "Signature Ingestor - XML", "Importing from " + SearchPath);
if (!Directory.Exists(SearchPath)) if (!Directory.Exists(SearchPath))
{ {
Directory.CreateDirectory(SearchPath); Directory.CreateDirectory(SearchPath);
} }
if (!Directory.Exists(ProcessedDirectory))
{
Directory.CreateDirectory(ProcessedDirectory);
}
string[] PathContents = Directory.GetFiles(SearchPath); string[] PathContents = Directory.GetFiles(SearchPath);
Array.Sort(PathContents); 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 = ""; string sql = "";
Dictionary<string, object> dbDict = new Dictionary<string, object>(); Dictionary<string, object> dbDict = new Dictionary<string, object>();
System.Data.DataTable sigDB; System.Data.DataTable sigDB;
@@ -33,226 +61,380 @@ namespace gaseous_server.SignatureIngestors.XML
SetStatus(i + 1, PathContents.Length, "Processing signature file: " + XMLFile); SetStatus(i + 1, PathContents.Length, "Processing signature file: " + XMLFile);
if (Common.SkippableFiles.Contains(Path.GetFileName(XMLFile), StringComparer.OrdinalIgnoreCase)) Logging.Log(Logging.LogType.Information, "Signature Ingest", "(" + (i + 1) + " / " + PathContents.Length + ") Processing " + XMLType.ToString() + " DAT file: " + XMLFile);
{
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<string, object>();
dbDict.Add("sourcemd5", hashObject.md5hash);
sigDB = db.ExecuteCMD(sql, dbDict);
if (sigDB.Rows.Count == 0) string? DBFile = null;
if (XMLDBSearchPath != null)
{
switch (XMLType)
{ {
try case gaseous_signature_parser.parser.SignatureParser.NoIntro:
{ for (UInt16 x = 0; x < DBPathContents.Length; x++)
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)
{ {
sql = "SELECT * FROM Signatures_Sources WHERE SourceMD5=@sourcemd5"; string tempDBFileName = Path.GetFileNameWithoutExtension(DBPathContents[x].Replace(" (DB Export)", ""));
dbDict = new Dictionary<string, object>(); if (tempDBFileName == Path.GetFileNameWithoutExtension(XMLFile))
string sourceUriStr = "";
if (Object.Url != null)
{ {
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, "")); break;
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, "")); // check xml file md5
dbDict.Add("homepage", Common.ReturnValueIfNull(Object.Homepage, "")); Common.hashObject hashObject = new Common.hashObject(XMLFile);
dbDict.Add("uri", sourceUriStr); sql = "SELECT * FROM Signatures_Sources WHERE SourceMD5=@sourcemd5";
dbDict.Add("sourcetype", Common.ReturnValueIfNull(Object.SourceType, "")); dbDict = new Dictionary<string, object>();
dbDict.Add("sourcemd5", Object.SourceMd5); dbDict.Add("sourcemd5", hashObject.md5hash);
dbDict.Add("sourcesha1", Object.SourceSHA1); 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<string, object>
{
{ "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); 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 RomSignatureObject.Game gameObject = Object.Games[x];
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)";
db.ExecuteCMD(sql, dbDict); // set up game dictionary
dbDict = new Dictionary<string, object>();
processGames = true; if (flipNameAndDescription.Contains(Object.SourceType))
}
if (processGames == true)
{
for (int x = 0; x < Object.Games.Count; ++x)
{ {
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 List<int> gameCountries = new List<int>();
dbDict = new Dictionary<string, object>(); if (
if (flipNameAndDescription.Contains(Object.SourceType)) gameObject.Country != null &&
gameObject.Country != "Unknown"
)
{
string[] countries = gameObject.Country.Split(",");
foreach (string country in countries)
{ {
dbDict.Add("name", Common.ReturnValueIfNull(gameObject.Description, "")); int countryId = -1;
dbDict.Add("description", Common.ReturnValueIfNull(gameObject.Name, "")); countryId = Common.GetLookupByCode(Common.LookupTypes.Country, (string)Common.ReturnValueIfNull(country.Trim(), ""));
} if (countryId == -1)
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)
{ {
// entry not present, insert it countryId = Common.GetLookupByValue(Common.LookupTypes.Country, (string)Common.ReturnValueIfNull(country.Trim(), ""));
sql = "INSERT INTO Signatures_Platforms (Platform) VALUES (@platform); SELECT CAST(LAST_INSERT_ID() AS SIGNED);";
sigDB = db.ExecuteCMD(sql, dbDict);
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<string, object> countryDict = new Dictionary<string, object>{
{ "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 List<int> gameLanguages = new List<int>();
int gamePublisher = 0; if (
if (gameObject.Publisher != null) 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 (languageId == -1)
if (sigDB.Rows.Count == 0) {
{ Logging.Log(Logging.LogType.Warning, "Signature Ingest", "Unable to locate language id for " + language.Trim());
// entry not present, insert it sql = "INSERT INTO Language (`Code`, `Value`) VALUES (@code, @name); SELECT CAST(LAST_INSERT_ID() AS SIGNED);";
sql = "INSERT INTO Signatures_Publishers (Publisher) VALUES (@publisher); SELECT CAST(LAST_INSERT_ID() AS SIGNED);"; Dictionary<string, object> langDict = new Dictionary<string, object>{
sigDB = db.ExecuteCMD(sql, dbDict); { "code", language.Trim() },
gamePublisher = Convert.ToInt32(sigDB.Rows[0][0]); { "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 dbDict.Add("copyright", Common.ReturnValueIfNull(gameObject.Copyright, ""));
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"; // store platform
int gameSystem = 0;
if (gameObject.System != null)
{
sql = "SELECT `Id` FROM Signatures_Platforms WHERE `Platform`=@platform";
sigDB = db.ExecuteCMD(sql, dbDict); sigDB = db.ExecuteCMD(sql, dbDict);
if (sigDB.Rows.Count == 0) if (sigDB.Rows.Count == 0)
{ {
// entry not present, insert it // entry not present, insert it
sql = "INSERT INTO Signatures_Games " + sql = "INSERT INTO Signatures_Platforms (`Platform`) VALUES (@platform); SELECT CAST(LAST_INSERT_ID() AS SIGNED);";
"(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);";
sigDB = db.ExecuteCMD(sql, dbDict); sigDB = db.ExecuteCMD(sql, dbDict);
gameId = Convert.ToInt32(sigDB.Rows[0][0]); gameSystem = Convert.ToInt32(sigDB.Rows[0][0]);
} }
else else
{ {
gameId = (int)sigDB.Rows[0][0]; gameSystem = (int)sigDB.Rows[0][0];
} }
}
dbDict.Add("systemid", gameSystem);
// store rom // store publisher
foreach (RomSignatureObject.Game.Rom romObject in gameObject.Roms) 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<string, object> countryDict = new Dictionary<string, object>{
{ "gameid", gameId },
{ "Countryid", gameCountry }
};
if (db.ExecuteCMD(sql, countryDict).Rows.Count == 0)
{ {
int romId = 0; sql = "INSERT INTO Signatures_Games_Countries (GameId, CountryId) VALUES (@gameid, @Countryid)";
sql = "SELECT * FROM Signatures_Roms WHERE GameId=@gameid AND MD5=@md5"; db.ExecuteCMD(sql, countryDict);
dbDict = new Dictionary<string, object>(); }
dbDict.Add("gameid", gameId); }
dbDict.Add("name", Common.ReturnValueIfNull(romObject.Name, "")); catch
dbDict.Add("size", Common.ReturnValueIfNull(romObject.Size, "")); {
dbDict.Add("crc", Common.ReturnValueIfNull(romObject.Crc, "").ToString().ToLower()); Console.WriteLine("Game id: " + gameId + " with Country " + gameCountry);
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) // insert languages
foreach (int gameLanguage in gameLanguages)
{
try
{
sql = "SELECT * FROM Signatures_Games_Languages WHERE GameId = @gameid AND LanguageId = @languageid";
Dictionary<string, object> langDict = new Dictionary<string, object>{
{ "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<string, object>();
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));
{
dbDict.Add("attributes", Newtonsoft.Json.JsonConvert.SerializeObject(romObject.Attributes));
}
else
{
dbDict.Add("attributes", "[ ]");
}
} }
else else
{ {
dbDict.Add("attributes", "[ ]"); dbDict.Add("attributes", "");
} }
dbDict.Add("romtype", (int)romObject.RomType); }
dbDict.Add("romtypemedia", Common.ReturnValueIfNull(romObject.RomTypeMedia, "")); else
dbDict.Add("medialabel", Common.ReturnValueIfNull(romObject.MediaLabel, "")); {
dbDict.Add("metadatasource", romObject.SignatureSource); dbDict.Add("attributes", "");
dbDict.Add("ingestorversion", 2); }
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); 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]); // map the rom to the source
} sql = "SELECT * FROM Signatures_RomToSource WHERE SourceId=@sourceid AND RomId=@romid;";
else dbDict.Add("romid", romId);
{ dbDict.Add("sourceId", sourceId);
romId = (int)sigDB.Rows[0][0];
} 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)));
} }
} }
} }

View File

@@ -1,5 +1,6 @@
using System.Data; using System.Data;
using gaseous_signature_parser.models.RomSignatureObject; using gaseous_signature_parser.models.RomSignatureObject;
using static gaseous_server.Classes.Common;
namespace gaseous_server.Classes namespace gaseous_server.Classes
{ {
@@ -10,7 +11,8 @@ namespace gaseous_server.Classes
if (md5.Length > 0) if (md5.Length > 0)
{ {
return _GetSignature("Signatures_Roms.md5 = @searchstring", md5); return _GetSignature("Signatures_Roms.md5 = @searchstring", md5);
} else }
else
{ {
return _GetSignature("Signatures_Roms.sha1 = @searchstring", sha1); return _GetSignature("Signatures_Roms.sha1 = @searchstring", sha1);
} }
@@ -21,7 +23,8 @@ namespace gaseous_server.Classes
if (TosecName.Length > 0) if (TosecName.Length > 0)
{ {
return _GetSignature("Signatures_Roms.name = @searchstring", TosecName); return _GetSignature("Signatures_Roms.name = @searchstring", TosecName);
} else }
else
{ {
return null; return null;
} }
@@ -44,7 +47,7 @@ namespace gaseous_server.Classes
{ {
Game = new gaseous_server.Models.Signatures_Games.GameItem Game = new gaseous_server.Models.Signatures_Games.GameItem
{ {
Id = (Int32)sigDbRow["Id"], Id = (long)(int)sigDbRow["Id"],
Name = (string)sigDbRow["Name"], Name = (string)sigDbRow["Name"],
Description = (string)sigDbRow["Description"], Description = (string)sigDbRow["Description"],
Year = (string)sigDbRow["Year"], Year = (string)sigDbRow["Year"],
@@ -53,20 +56,20 @@ namespace gaseous_server.Classes
System = (string)sigDbRow["Platform"], System = (string)sigDbRow["Platform"],
SystemVariant = (string)sigDbRow["SystemVariant"], SystemVariant = (string)sigDbRow["SystemVariant"],
Video = (string)sigDbRow["Video"], Video = (string)sigDbRow["Video"],
Country = (string)sigDbRow["Country"], Countries = new Dictionary<string, string>(GetLookup(LookupTypes.Country, (long)(int)sigDbRow["Id"])),
Language = (string)sigDbRow["Language"], Languages = new Dictionary<string, string>(GetLookup(LookupTypes.Language, (long)(int)sigDbRow["Id"])),
Copyright = (string)sigDbRow["Copyright"] Copyright = (string)sigDbRow["Copyright"]
}, },
Rom = new gaseous_server.Models.Signatures_Games.RomItem Rom = new gaseous_server.Models.Signatures_Games.RomItem
{ {
Id = (Int32)sigDbRow["romid"], Id = (long)(int)sigDbRow["romid"],
Name = (string)sigDbRow["romname"], Name = (string)sigDbRow["romname"],
Size = (Int64)sigDbRow["Size"], Size = (Int64)sigDbRow["Size"],
Crc = (string)sigDbRow["CRC"], Crc = (string)sigDbRow["CRC"],
Md5 = ((string)sigDbRow["MD5"]).ToLower(), Md5 = ((string)sigDbRow["MD5"]).ToLower(),
Sha1 = ((string)sigDbRow["SHA1"]).ToLower(), Sha1 = ((string)sigDbRow["SHA1"]).ToLower(),
DevelopmentStatus = (string)sigDbRow["DevelopmentStatus"], DevelopmentStatus = (string)sigDbRow["DevelopmentStatus"],
Attributes = Newtonsoft.Json.JsonConvert.DeserializeObject<List<KeyValuePair<string, object>>>((string)Common.ReturnValueIfNull(sigDbRow["Attributes"], "[]")), Attributes = Newtonsoft.Json.JsonConvert.DeserializeObject<Dictionary<string, object>>((string)Common.ReturnValueIfNull(sigDbRow["Attributes"], "[]")),
RomType = (gaseous_server.Models.Signatures_Games.RomItem.RomTypes)(int)sigDbRow["RomType"], RomType = (gaseous_server.Models.Signatures_Games.RomItem.RomTypes)(int)sigDbRow["RomType"],
RomTypeMedia = (string)sigDbRow["RomTypeMedia"], RomTypeMedia = (string)sigDbRow["RomTypeMedia"],
MediaLabel = (string)sigDbRow["MediaLabel"], MediaLabel = (string)sigDbRow["MediaLabel"],
@@ -77,5 +80,36 @@ namespace gaseous_server.Classes
} }
return GamesList; return GamesList;
} }
public Dictionary<string, string> 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<string, object> dbDict = new Dictionary<string, object>{
{ "id", GameId }
};
DataTable data = db.ExecuteCMD(sql, dbDict);
Dictionary<string, string> returnDict = new Dictionary<string, string>();
foreach (DataRow row in data.Rows)
{
returnDict.Add((string)row["Code"], (string)row["Value"]);
}
return returnDict;
}
} }
} }

View File

@@ -260,7 +260,14 @@ namespace gaseous_server.Controllers
{ {
AlwaysLogToDisk = Config.LoggingConfiguration.AlwaysLogToDisk, AlwaysLogToDisk = Config.LoggingConfiguration.AlwaysLogToDisk,
MinimumLogRetentionPeriod = Config.LoggingConfiguration.LogRetention, MinimumLogRetentionPeriod = Config.LoggingConfiguration.LogRetention,
EmulatorDebugMode = Boolean.Parse(Config.ReadSetting<string>("emulatorDebugMode", false.ToString())) EmulatorDebugMode = Boolean.Parse(Config.ReadSetting<string>("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); return Ok(systemSettingsModel);
@@ -279,6 +286,10 @@ namespace gaseous_server.Controllers
Config.LoggingConfiguration.AlwaysLogToDisk = model.AlwaysLogToDisk; Config.LoggingConfiguration.AlwaysLogToDisk = model.AlwaysLogToDisk;
Config.LoggingConfiguration.LogRetention = model.MinimumLogRetentionPeriod; Config.LoggingConfiguration.LogRetention = model.MinimumLogRetentionPeriod;
Config.SetSetting<string>("emulatorDebugMode", model.EmulatorDebugMode.ToString()); Config.SetSetting<string>("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(); Config.UpdateConfig();
} }
@@ -719,5 +730,14 @@ namespace gaseous_server.Controllers
public bool AlwaysLogToDisk { get; set; } public bool AlwaysLogToDisk { get; set; }
public int MinimumLogRetentionPeriod { get; set; } public int MinimumLogRetentionPeriod { get; set; }
public bool EmulatorDebugMode { 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; }
}
} }
} }

View File

@@ -4,12 +4,36 @@ using gaseous_signature_parser.models.RomSignatureObject;
namespace gaseous_server.Models namespace gaseous_server.Models
{ {
public class Signatures_Games : HasheousClient.Models.LookupResponseModel public class Signatures_Games : HasheousClient.Models.SignatureModel
{ {
public Signatures_Games() 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 SignatureFlags Flags = new SignatureFlags();
public class SignatureFlags public class SignatureFlags
@@ -18,6 +42,213 @@ namespace gaseous_server.Models
public string IGDBPlatformName { get; set; } public string IGDBPlatformName { get; set; }
public long IGDBGameId { 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<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 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; }
}
}
} }
} }

View File

@@ -245,15 +245,33 @@ namespace gaseous_server
CallingQueueItem = this CallingQueueItem = this
}; };
Logging.Log(Logging.LogType.Debug, "Signature Import", "Processing TOSEC files"); foreach (int i in Enum.GetValues(typeof(gaseous_signature_parser.parser.SignatureParser)))
tIngest.Import(Path.Combine(Config.LibraryConfiguration.LibrarySignatureImportDirectory, "TOSEC"), gaseous_signature_parser.parser.SignatureParser.TOSEC); {
gaseous_signature_parser.parser.SignatureParser parserType = (gaseous_signature_parser.parser.SignatureParser)i;
Logging.Log(Logging.LogType.Debug, "Signature Import", "Processing MAME Arcade files"); if (
tIngest.Import(Path.Combine(Config.LibraryConfiguration.LibrarySignatureImportDirectory, "MAME Arcade"), gaseous_signature_parser.parser.SignatureParser.MAMEArcade); 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; _SaveLastRunTime = true;
break; break;

View File

@@ -71,6 +71,7 @@ if (Directory.Exists(Config.LibraryConfiguration.LibraryUploadDirectory))
// kick off any delayed upgrade tasks // kick off any delayed upgrade tasks
// run 1002 background updates in the background on every start // run 1002 background updates in the background on every start
DatabaseMigration.BackgroundUpgradeTargetSchemaVersions.Add(1002); DatabaseMigration.BackgroundUpgradeTargetSchemaVersions.Add(1002);
DatabaseMigration.BackgroundUpgradeTargetSchemaVersions.Add(1022);
// start the task // start the task
ProcessQueue.QueueItem queueItem = new ProcessQueue.QueueItem( ProcessQueue.QueueItem queueItem = new ProcessQueue.QueueItem(
ProcessQueue.QueueItemType.BackgroundDatabaseUpgrade, ProcessQueue.QueueItemType.BackgroundDatabaseUpgrade,

View File

@@ -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

View File

@@ -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;

View File

@@ -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

View File

@@ -16,9 +16,9 @@
<DocumentationFile>bin\Release\net8.0\gaseous-server.xml</DocumentationFile> <DocumentationFile>bin\Release\net8.0\gaseous-server.xml</DocumentationFile>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Asp.Versioning.Mvc" Version="8.0.0" /> <PackageReference Include="Asp.Versioning.Mvc" Version="8.1.0" />
<PackageReference Include="Asp.Versioning.Mvc.ApiExplorer" Version="8.0.0" /> <PackageReference Include="Asp.Versioning.Mvc.ApiExplorer" Version="8.1.0" />
<PackageReference Include="gaseous-signature-parser" Version="2.1.0" /> <PackageReference Include="gaseous-signature-parser" Version="2.2.1" />
<PackageReference Include="gaseous.IGDB" Version="1.0.2" /> <PackageReference Include="gaseous.IGDB" Version="1.0.2" />
<PackageReference Include="hasheous-client" Version="0.1.0" /> <PackageReference Include="hasheous-client" Version="0.1.0" />
<PackageReference Include="Magick.NET-Q8-AnyCPU" Version="13.5.0" /> <PackageReference Include="Magick.NET-Q8-AnyCPU" Version="13.5.0" />
@@ -29,7 +29,7 @@
<PackageReference Include="Microsoft.AspNetCore.Identity.UI" Version="8.0.1" /> <PackageReference Include="Microsoft.AspNetCore.Identity.UI" Version="8.0.1" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.1" /> <PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.1" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" /> <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="8.0.0" /> <PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="8.0.2" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
@@ -39,6 +39,8 @@
<None Remove="Classes\" /> <None Remove="Classes\" />
<None Remove="Classes\SignatureIngestors\" /> <None Remove="Classes\SignatureIngestors\" />
<None Remove="Support\" /> <None Remove="Support\" />
<None Remove="Support\Country.txt" />
<None Remove="Support\Language.txt" />
<None Remove="Support\Database\" /> <None Remove="Support\Database\" />
<None Remove="Support\Database\MySQL\" /> <None Remove="Support\Database\MySQL\" />
<None Remove="Support\Database\MySQL\gaseous-1000.sql" /> <None Remove="Support\Database\MySQL\gaseous-1000.sql" />
@@ -64,6 +66,7 @@
<None Remove="Support\Database\MySQL\gaseous-1019.sql" /> <None Remove="Support\Database\MySQL\gaseous-1019.sql" />
<None Remove="Support\Database\MySQL\gaseous-1020.sql" /> <None Remove="Support\Database\MySQL\gaseous-1020.sql" />
<None Remove="Support\Database\MySQL\gaseous-1021.sql" /> <None Remove="Support\Database\MySQL\gaseous-1021.sql" />
<None Remove="Support\Database\MySQL\gaseous-1022.sql" />
<None Remove="Classes\Metadata\" /> <None Remove="Classes\Metadata\" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
@@ -85,6 +88,8 @@
<ExcludeFromSingleFile>true</ExcludeFromSingleFile> <ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory> <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</EmbeddedResource> </EmbeddedResource>
<EmbeddedResource Include="Support\Country.txt" />
<EmbeddedResource Include="Support\Language.txt" />
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1000.sql" /> <EmbeddedResource Include="Support\Database\MySQL\gaseous-1000.sql" />
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1001.sql" /> <EmbeddedResource Include="Support\Database\MySQL\gaseous-1001.sql" />
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1002.sql" /> <EmbeddedResource Include="Support\Database\MySQL\gaseous-1002.sql" />
@@ -108,5 +113,6 @@
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1019.sql" /> <EmbeddedResource Include="Support\Database\MySQL\gaseous-1019.sql" />
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1020.sql" /> <EmbeddedResource Include="Support\Database\MySQL\gaseous-1020.sql" />
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1021.sql" /> <EmbeddedResource Include="Support\Database\MySQL\gaseous-1021.sql" />
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1022.sql" />
</ItemGroup> </ItemGroup>
</Project> </Project>

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

View File

@@ -39,7 +39,7 @@
}, },
processResults: function (data) { processResults: function (data) {
var arr = []; var arr = [];
arr.push({ arr.push({
id: 0, id: 0,
text: 'Any' text: 'Any'
@@ -74,11 +74,11 @@
ajaxCall( ajaxCall(
'/api/v1.1/Library?Name=' + encodeURIComponent(libName) + '&DefaultPlatformId=' + libPlatform[0].id + '&Path=' + encodeURIComponent(libPath), '/api/v1.1/Library?Name=' + encodeURIComponent(libName) + '&DefaultPlatformId=' + libPlatform[0].id + '&Path=' + encodeURIComponent(libPath),
'POST', 'POST',
function(result) { function (result) {
drawLibrary(); drawLibrary();
closeSubDialog(); closeDialog();
}, },
function(error) { function (error) {
alert('An error occurred while creating the library:\n\n' + JSON.stringify(error.responseText)); alert('An error occurred while creating the library:\n\n' + JSON.stringify(error.responseText));
} }
); );

View File

@@ -1,7 +1,9 @@
<div id="properties_toc"> <div id="properties_toc">
<div id="properties_toc_general" name="properties_toc_item" onclick="SelectTab('general');">General</div> <div id="properties_toc_general" name="properties_toc_item" onclick="SelectTab('general');">General</div>
<div id="properties_toc_archive" name="properties_toc_item" onclick="SelectTab('archive');" style="display: none;">Archive Contents</div> <div id="properties_toc_archive" name="properties_toc_item" onclick="SelectTab('archive');" style="display: none;">
<div id="properties_toc_attributes" name="properties_toc_item" onclick="SelectTab('attributes');" style="display: none;">Attributes</div> Archive Contents</div>
<div id="properties_toc_attributes" name="properties_toc_item" onclick="SelectTab('attributes');"
style="display: none;">Attributes</div>
<div id="properties_toc_match" name="properties_toc_item" onclick="SelectTab('match');">Title Match</div> <div id="properties_toc_match" name="properties_toc_item" onclick="SelectTab('match');">Title Match</div>
<!--<div id="properties_toc_manage" name="properties_toc_item" onclick="SelectTab('manage');">Manage</div>--> <!--<div id="properties_toc_manage" name="properties_toc_item" onclick="SelectTab('manage');">Manage</div>-->
</div> </div>
@@ -59,7 +61,7 @@
</div> </div>
<div id="properties_bodypanel_attributes" name="properties_tab" style="display: none;"> <div id="properties_bodypanel_attributes" name="properties_tab" style="display: none;">
</div> </div>
<div id="properties_bodypanel_match" name="properties_tab" style="display: none;"> <div id="properties_bodypanel_match" name="properties_tab" style="display: none;">
@@ -78,7 +80,9 @@
<td style="width: 75%;"><select id="properties_fixgame" style="width: 100%;"></select></td> <td style="width: 75%;"><select id="properties_fixgame" style="width: 100%;"></select></td>
</tr> </tr>
<tr> <tr>
<td colspan="2" style="text-align: right;"><button id="properties_fixclear" value="Clear Match" onclick="ClearFixedGame();">Clear Match</button><button id="properties_fixsave" value="Save Match" onclick="SaveFixedGame();">Save Match</button></td> <td colspan="2" style="text-align: right;"><button id="properties_fixclear" value="Clear Match"
onclick="ClearFixedGame();">Clear Match</button><button id="properties_fixsave"
value="Save Match" onclick="SaveFixedGame();">Save Match</button></td>
</tr> </tr>
</table> </table>
</div> </div>
@@ -144,7 +148,7 @@
document.getElementById('romDelete').style.display = 'none'; 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_attributes').appendChild(BuildAttributesTable(result.attributes, result.source));
document.getElementById('properties_bodypanel_archive_content').appendChild(BuildArchiveTable(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'); var aTable = document.createElement('table');
aTable.style.width = '100%'; aTable.style.width = '100%';
for (var i = 0; i < attributes.length; i++) { for (const [key, value] of Object.entries(attributes)) {
if (attributes[i].key != "ZipContents") { if (key != "ZipContents") {
// show attributes button // show attributes button
document.getElementById('properties_toc_attributes').style.display = ''; document.getElementById('properties_toc_attributes').style.display = '';
var aRow = document.createElement('tr'); var aRow = document.createElement('tr');
@@ -285,15 +289,15 @@
var aTitleCell = document.createElement('th'); var aTitleCell = document.createElement('th');
aTitleCell.width = "25%"; aTitleCell.width = "25%";
if (sourceName == "TOSEC") { if (sourceName == "TOSEC") {
aTitleCell.innerHTML = ConvertTOSECAttributeName(attributes[i].key); aTitleCell.innerHTML = ConvertTOSECAttributeName(key);
} else { } else {
aTitleCell.innerHTML = attributes[i].key; aTitleCell.innerHTML = key;
} }
aRow.appendChild(aTitleCell); aRow.appendChild(aTitleCell);
var aValueCell = document.createElement('td'); var aValueCell = document.createElement('td');
aValueCell.width = "75%"; aValueCell.width = "75%";
aValueCell.innerHTML = attributes[i].value; aValueCell.innerHTML = value;
aRow.appendChild(aValueCell); aRow.appendChild(aValueCell);
aTable.appendChild(aRow); aTable.appendChild(aRow);
@@ -304,13 +308,13 @@
} }
function BuildArchiveTable(attributes, sourceName) { function BuildArchiveTable(attributes, sourceName) {
for (var i = 0; i < attributes.length; i++) { for (const [key, value] of Object.entries(attributes)) {
if (attributes[i].key == "ZipContents") { if (key == "ZipContents") {
var archiveContent = JSON.parse(attributes[i].value); var archiveContent = JSON.parse(value);
// show archive button // show archive button
document.getElementById('properties_toc_archive').style.display = ''; document.getElementById('properties_toc_archive').style.display = '';
var aTable = document.createElement('table'); var aTable = document.createElement('table');
aTable.className = 'romtable'; aTable.className = 'romtable';
aTable.setAttribute('cellspacing', 0); aTable.setAttribute('cellspacing', 0);
@@ -343,6 +347,17 @@
hRow.appendChild(aHashCell); hRow.appendChild(aHashCell);
aBody.appendChild(hRow); 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); aTable.appendChild(aBody);
} }
} }
@@ -354,18 +369,18 @@
function ConvertTOSECAttributeName(attributeName) { function ConvertTOSECAttributeName(attributeName) {
var tosecAttributeNames = { var tosecAttributeNames = {
"cr": "Cracked", "cr": "Cracked",
"f" : "Fixed", "f": "Fixed",
"h" : "Hacked", "h": "Hacked",
"m" : "Modified", "m": "Modified",
"p" : "Pirated", "p": "Pirated",
"t" : "Trained", "t": "Trained",
"tr": "Translated", "tr": "Translated",
"o" : "Over Dump", "o": "Over Dump",
"u" : "Under Dump", "u": "Under Dump",
"v" : "Virus", "v": "Virus",
"b" : "Bad Dump", "b": "Bad Dump",
"a" : "Alternate", "a": "Alternate",
"!" : "Known Verified Dump" "!": "Known Verified Dump"
}; };
if (attributeName in tosecAttributeNames) { if (attributeName in tosecAttributeNames) {
@@ -378,4 +393,4 @@
SelectTab('general'); SelectTab('general');
document.getElementById('romDelete').setAttribute("onclick", "showSubDialog('romdelete', " + modalVariables + ");"); document.getElementById('romDelete').setAttribute("onclick", "showSubDialog('romdelete', " + modalVariables + ");");
</script> </script>

View File

@@ -1,4 +1,5 @@
<div id="bgImage" style="background-image: url('/images/SettingsWallpaper.jpg'); background-position: center; background-repeat: no-repeat; background-size: cover; filter: blur(10px); -webkit-filter: blur(10px);"> <div id="bgImage"
style="background-image: url('/images/SettingsWallpaper.jpg'); background-position: center; background-repeat: no-repeat; background-size: cover; filter: blur(10px); -webkit-filter: blur(10px);">
<div id="bgImage_Opacity"></div> <div id="bgImage_Opacity"></div>
</div> </div>
@@ -6,27 +7,40 @@
<div id="properties_toc" class="settings_toc"> <div id="properties_toc" class="settings_toc">
<div class="filter_header">Settings</div> <div class="filter_header">Settings</div>
<div id="properties_toc_system" name="properties_toc_item" onclick="SelectTab('system');">System</div> <div id="properties_toc_system" name="properties_toc_item" onclick="SelectTab('system');">System</div>
<div id="properties_toc_settings" name="properties_toc_item" onclick="SelectTab('settings');" style="display: none;">Settings</div> <div id="properties_toc_settings" name="properties_toc_item" onclick="SelectTab('settings');"
<div id="properties_toc_users" name="properties_toc_item" onclick="SelectTab('users');" style="display: none;">Users</div> style="display: none;">Settings</div>
<div id="properties_toc_mapping" name="properties_toc_item" onclick="SelectTab('mapping');" style="display: none;">Platform Mapping</div> <div id="properties_toc_libraries" name="properties_toc_item" onclick="SelectTab('libraries');"
style="display: none;">
Libraries</div>
<div id="properties_toc_users" name="properties_toc_item" onclick="SelectTab('users');" style="display: none;">
Users</div>
<div id="properties_toc_services" name="properties_toc_item" onclick="SelectTab('services');"
style="display: none;">
Services</div>
<div id="properties_toc_mapping" name="properties_toc_item" onclick="SelectTab('mapping');"
style="display: none;">Platform Mapping</div>
<div id="properties_toc_bios" name="properties_toc_item" onclick="SelectTab('bios');">Firmware</div> <div id="properties_toc_bios" name="properties_toc_item" onclick="SelectTab('bios');">Firmware</div>
<div id="properties_toc_logs" name="properties_toc_item" onclick="SelectTab('logs');" style="display: none;">Logs</div> <div id="properties_toc_logs" name="properties_toc_item" onclick="SelectTab('logs');" style="display: none;">
Logs</div>
<div id="properties_toc_about" name="properties_toc_item" onclick="SelectTab('about');">About</div> <div id="properties_toc_about" name="properties_toc_item" onclick="SelectTab('about');">About</div>
</div> </div>
<div id="properties_bodypanel"> <div id="properties_bodypanel">
</div> </div>
</div> </div>
<div id="settings_photocredit"> <div id="settings_photocredit">
Wallpaper by <a href="https://unsplash.com/@lorenzoherrera" class="romlink">Lorenzo Herrera</a> / <a href="https://unsplash.com/photos/p0j-mE6mGo4" class="romlink">Unsplash</a> Wallpaper by <a href="https://unsplash.com/@lorenzoherrera" class="romlink">Lorenzo Herrera</a> / <a
href="https://unsplash.com/photos/p0j-mE6mGo4" class="romlink">Unsplash</a>
</div> </div>
<script type="text/javascript"> <script type="text/javascript">
if (userProfile.roles.includes("Admin")) { if (userProfile.roles.includes("Admin")) {
document.getElementById('properties_toc_settings').style.display = ''; document.getElementById('properties_toc_settings').style.display = '';
document.getElementById('properties_toc_libraries').style.display = '';
document.getElementById('properties_toc_users').style.display = ''; document.getElementById('properties_toc_users').style.display = '';
document.getElementById('properties_toc_services').style.display = '';
document.getElementById('properties_toc_mapping').style.display = ''; document.getElementById('properties_toc_mapping').style.display = '';
document.getElementById('properties_toc_logs').style.display = ''; document.getElementById('properties_toc_logs').style.display = '';
} }
@@ -62,4 +76,4 @@
$('#properties_bodypanel').load('/pages/settings/' + TabName + '.html?v=' + AppVersion); $('#properties_bodypanel').load('/pages/settings/' + TabName + '.html?v=' + AppVersion);
} }
</script> </script>

View File

@@ -0,0 +1,63 @@
<div id="gametitle">
<h1 id="gametitle_label">Libraries</h1>
</div>
<table id="settings_libraries" class="romtable" style="width: 100%;" cellspacing="0">
</table>
<div style="text-align: right;"><button id="settings_newlibrary" onclick="showDialog('librarynew');">New
Library</button></div>
<script type="text/javascript">
function drawLibrary() {
ajaxCall(
'/api/v1.1/Library',
'GET',
function (result) {
var newTable = document.getElementById('settings_libraries');
newTable.innerHTML = '';
newTable.appendChild(createTableRow(true, ['Name', 'Path', 'Default Platform', 'Default Library', '']));
for (var i = 0; i < result.length; i++) {
var platformName = '';
if (result[i].defaultPlatformId == 0) {
if (result[i].isDefaultLibrary == true) {
platformName = "n/a";
} else {
platformName = "";
}
} else {
platformName = result[i].defaultPlatformName;
}
var defaultLibrary = '';
if (result[i].isDefaultLibrary == true) {
defaultLibrary = "Yes";
} else {
defaultLibrary = "";
}
var deleteButton = '';
if (result[i].isDefaultLibrary == false) {
var deleteButton = '<a href="#" onclick="showSubDialog(\'librarydelete\', ' + result[i].id + ');" class="romlink"><img src="/images/delete.svg" class="banner_button_image" alt="Delete" title="Delete" /></a>';
}
newTable.appendChild(createTableRow(
false,
[
result[i].name,
result[i].path,
platformName,
defaultLibrary,
'<div style="text-align: right;">' + deleteButton + '</div>'
],
'romrow',
'romcell'
));
}
}
);
}
drawLibrary();
</script>

View File

@@ -0,0 +1,290 @@
<div id="gametitle">
<h1 id="gametitle_label">Services</h1>
</div>
<table id="settings_tasktimers" class="romtable" style="width: 100%;" cellspacing="0">
</table>
<div style="text-align: right;"><button id="settings_tasktimers_default" onclick="defaultTaskTimers();">Reset to
Default</button><button id="settings_tasktimers_new" onclick="saveTaskTimers();">Save</button></div>
<script type="text/javascript">
function getBackgroundTaskTimers() {
ajaxCall(
'/api/v1/System/Settings/BackgroundTasks/Configuration',
'GET',
function (result) {
var targetTable = document.getElementById('settings_tasktimers');
targetTable.innerHTML = '';
for (const [key, value] of Object.entries(result)) {
var newTableRowBody = document.createElement('tbody');
newTableRowBody.className = 'romrow';
var enabledString = "";
if (value.enabled == true) {
enabledString = 'checked="checked"';
}
var newTableIntervalRow = createTableRow(
false,
[
GetTaskFriendlyName(value.task),
'Enabled',
'<input id="settings_enabled_' + value.task + '" name="settings_tasktimers_enabled" type="checkbox" ' + enabledString + '/>',
],
'',
'romcell'
);
newTableRowBody.appendChild(newTableIntervalRow);
var newTableRow = createTableRow(
false,
[
'',
'Minimum Interval (Minutes):',
'<input id="settings_tasktimers_' + value.task + '" name="settings_tasktimers_values" data-name="' + value.task + '" data-default="' + value.defaultInterval + '" type="number" placeholder="' + value.defaultInterval + '" min="' + value.minimumAllowedInterval + '" value="' + value.interval + '" />'
],
'',
'romcell'
);
newTableRowBody.appendChild(newTableRow);
// allowed time periods row
var newTableRowTime = document.createElement('tr');
var rowTimeSpace = document.createElement('td');
newTableRowTime.appendChild(rowTimeSpace);
var rowTimeContentTitle = document.createElement('td');
rowTimeContentTitle.className = 'romcell';
rowTimeContentTitle.innerHTML = "Allowed Days:";
newTableRowTime.appendChild(rowTimeContentTitle);
var rowTimeContent = document.createElement('td');
// rowTimeContent.setAttribute('colspan', 2);
rowTimeContent.className = 'romcell';
var daySelector = document.createElement('select');
daySelector.id = 'settings_alloweddays_' + value.task;
daySelector.name = 'settings_alloweddays';
daySelector.multiple = 'multiple';
daySelector.setAttribute('data-default', value.defaultAllowedDays.join(","));
daySelector.style.width = '95%';
var days = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"];
for (var d = 0; d < days.length; d++) {
var dayOpt = document.createElement('option');
dayOpt.value = days[d];
dayOpt.innerHTML = days[d];
if (value.allowedDays.includes(days[d])) {
dayOpt.selected = 'selected';
}
daySelector.appendChild(dayOpt);
}
rowTimeContent.appendChild(daySelector);
$(daySelector).select2({
tags: false
});
newTableRowTime.appendChild(rowTimeContent);
newTableRowBody.appendChild(newTableRowTime);
// add start and end times
var newTableRowClock = document.createElement('tr');
var rowClockSpace = document.createElement('td');
newTableRowClock.appendChild(rowClockSpace);
var rowClockContentTitle = document.createElement('td');
rowClockContentTitle.className = 'romcell';
rowClockContentTitle.innerHTML = "Time Range:";
newTableRowClock.appendChild(rowClockContentTitle);
var rowClockContent = document.createElement('td');
rowClockContent.className = 'romcell';
// rowClockContent.setAttribute('colspan', 2);
rowClockContent.appendChild(generateTimeDropDowns(value.task, 'Start', value.defaultAllowedStartHours, value.defaultAllowedStartMinutes, value.allowedStartHours, value.allowedStartMinutes));
rowClockContentSeparator = document.createElement('span');
rowClockContentSeparator.innerHTML = '&nbsp;-&nbsp;';
rowClockContent.appendChild(rowClockContentSeparator);
rowClockContent.appendChild(generateTimeDropDowns(value.task, 'End', value.defaultAllowedEndHours, value.defaultAllowedEndMinutes, value.allowedEndHours, value.allowedEndMinutes));
newTableRowClock.appendChild(rowClockContent);
newTableRowBody.appendChild(newTableRowClock);
// blocks tasks
var newTableRowBlocks = document.createElement('tr');
var rowBlocksSpace = document.createElement('td');
newTableRowBlocks.appendChild(rowBlocksSpace);
var rowBlocksContentTitle = document.createElement('td');
rowBlocksContentTitle.className = 'romcell';
rowBlocksContentTitle.innerHTML = "Blocks:";
newTableRowBlocks.appendChild(rowBlocksContentTitle);
var rowBlocksContent = document.createElement('td');
rowBlocksContent.className = 'romcell';
// rowBlocksContent.setAttribute('colspan', 2);
var blocksString = "";
for (var i = 0; i < value.blocks.length; i++) {
if (blocksString.length > 0) { blocksString += ", "; }
blocksString += GetTaskFriendlyName(value.blocks[i]);
}
if (blocksString.length == 0) { blocksString = 'None'; }
rowBlocksContent.innerHTML = blocksString;
newTableRowBlocks.appendChild(rowBlocksContent);
newTableRowBody.appendChild(newTableRowBlocks);
// blocked by tasks
var newTableRowBlockedBy = document.createElement('tr');
var rowBlockedBySpace = document.createElement('td');
newTableRowBlockedBy.appendChild(rowBlockedBySpace);
var rowBlockedByContentTitle = document.createElement('td');
rowBlockedByContentTitle.className = 'romcell';
rowBlockedByContentTitle.innerHTML = "Blocked By:";
newTableRowBlockedBy.appendChild(rowBlockedByContentTitle);
var rowBlockedByContent = document.createElement('td');
rowBlockedByContent.className = 'romcell';
// rowBlockedByContent.setAttribute('colspan', 2);
var BlockedByString = "";
for (var i = 0; i < value.blockedBy.length; i++) {
if (BlockedByString.length > 0) { BlockedByString += ", "; }
BlockedByString += GetTaskFriendlyName(value.blockedBy[i]);
}
if (BlockedByString.length == 0) { BlockedByString = 'None'; }
rowBlockedByContent.innerHTML = BlockedByString;
newTableRowBlockedBy.appendChild(rowBlockedByContent);
newTableRowBody.appendChild(newTableRowBlockedBy);
// complete row
targetTable.appendChild(newTableRowBody);
}
}
);
}
function generateTimeDropDowns(taskName, rangeName, defaultHour, defaultMinute, valueHour, valueMinute) {
var container = document.createElement('div');
container.style.display = 'inline';
var elementName = 'settings_tasktimers_time';
var hourSelector = document.createElement('input');
hourSelector.id = 'settings_tasktimers_' + taskName + '_' + rangeName + '_Hour';
hourSelector.name = elementName;
hourSelector.setAttribute('data-name', taskName);
hourSelector.setAttribute('type', 'number');
hourSelector.setAttribute('min', '0');
hourSelector.setAttribute('max', '23');
hourSelector.setAttribute('placeholder', defaultHour);
hourSelector.value = valueHour;
container.appendChild(hourSelector);
var separator = document.createElement('span');
separator.innerHTML = " : ";
container.appendChild(separator);
var minSelector = document.createElement('input');
minSelector.id = 'settings_tasktimers_' + taskName + '_' + rangeName + '_Minute';
minSelector.name = elementName;
minSelector.setAttribute('type', 'number');
minSelector.setAttribute('min', '0');
minSelector.setAttribute('max', '59');
minSelector.setAttribute('placeholder', defaultMinute);
minSelector.value = valueMinute;
container.appendChild(minSelector);
return container;
}
function saveTaskTimers() {
var timerValues = document.getElementsByName('settings_tasktimers_values');
var model = [];
for (var i = 0; i < timerValues.length; i++) {
var taskName = timerValues[i].getAttribute('data-name');
var taskEnabled = document.getElementById('settings_enabled_' + taskName).checked;
var taskIntervalObj = document.getElementById('settings_tasktimers_' + taskName);
var taskInterval = function () { if (taskIntervalObj.value) { return taskIntervalObj.value; } else { return taskIntervalObj.getAttribute('placeholder'); } };
var taskDaysRaw = $('#settings_alloweddays_' + taskName).select2('data');
var taskDays = [];
if (taskDaysRaw.length > 0) {
for (var d = 0; d < taskDaysRaw.length; d++) {
taskDays.push(taskDaysRaw[d].id);
}
} else {
taskDays.push("Monday");
}
var taskStartHourObj = document.getElementById('settings_tasktimers_' + taskName + '_Start_Hour');
var taskStartMinuteObj = document.getElementById('settings_tasktimers_' + taskName + '_Start_Minute');
var taskEndHourObj = document.getElementById('settings_tasktimers_' + taskName + '_End_Hour');
var taskEndMinuteObj = document.getElementById('settings_tasktimers_' + taskName + '_End_Minute');
var taskStartHour = function () { if (taskStartHourObj.value) { return taskStartHourObj.value; } else { return taskStartHourObj.getAttribute('placeholder'); } };
var taskStartMinute = function () { if (taskStartMinuteObj.value) { return taskStartMinuteObj.value; } else { return taskStartMinuteObj.getAttribute('placeholder'); } };
var taskEndHour = function () { if (taskEndHourObj.value) { return taskEndHourObj.value; } else { return taskEndHourObj.getAttribute('placeholder'); } };
var taskEndMinute = function () { if (taskEndMinuteObj.value) { return taskEndMinuteObj.value; } else { return taskEndMinuteObj.getAttribute('placeholder'); } };
model.push(
{
"task": taskName,
"enabled": taskEnabled,
"interval": taskInterval(),
"allowedDays": taskDays,
"allowedStartHours": taskStartHour(),
"allowedStartMinutes": taskStartMinute(),
"allowedEndHours": taskEndHour(),
"allowedEndMinutes": taskEndMinute()
}
);
}
ajaxCall(
'/api/v1/System/Settings/BackgroundTasks/Configuration',
'POST',
function (result) {
getBackgroundTaskTimers();
},
function (error) {
getBackgroundTaskTimers();
},
JSON.stringify(model)
);
}
function defaultTaskTimers() {
var timerValues = document.getElementsByName('settings_tasktimers_enabled');
for (var i = 0; i < timerValues.length; i++) {
timerValues[i].checked = true;
}
var timerValues = document.getElementsByName('settings_tasktimers_values');
for (var i = 0; i < timerValues.length; i++) {
timerValues[i].value = timerValues[i].getAttribute('data-default');
}
var timerValues = document.getElementsByName('settings_alloweddays');
for (var i = 0; i < timerValues.length; i++) {
var defaultSelections = timerValues[i].getAttribute('data-default').split(',');
$(timerValues[i]).val(defaultSelections);
$(timerValues[i]).trigger('change');
}
var timerValues = document.getElementsByName('settings_tasktimers_time');
for (var i = 0; i < timerValues.length; i++) {
timerValues[i].value = timerValues[i].getAttribute('placeholder');
}
saveTaskTimers();
}
getBackgroundTaskTimers();
</script>

View File

@@ -2,37 +2,74 @@
<h1 id="gametitle_label">Settings</h1> <h1 id="gametitle_label">Settings</h1>
</div> </div>
<h3>Libraries</h3> <table cellspacing="0" style="width: 100%; vertical-align: top;">
<table id="settings_libraries" class="romtable" style="width: 100%;" cellspacing="0">
</table>
<div style="text-align: right;"><button id="settings_newlibrary" onclick="showDialog('librarynew');">New Library</button></div>
<h2>Advanced Settings</h2>
<p><strong>Warning</strong> Do not modify the below settings unless you know what you're doing.</p>
<h3>Background Task Timers</h3>
<table id="settings_tasktimers" class="romtable" style="width: 100%;" cellspacing="0">
</table>
<div style="text-align: right;"><button id="settings_tasktimers_default" onclick="defaultTaskTimers();">Reset to Default</button><button id="settings_tasktimers_new" onclick="saveTaskTimers();">Save</button></div>
<h3>System Settings</h3>
<table cellspacing="0" style="width: 100%;">
<tr> <tr>
<th colspan="2">Logging</th> <th colspan="2">
<h3>Metadata Sources</h3>
</th>
</tr>
<tr>
<th style="width: 25%;">
Signature Source
</th>
<td>
<input type="radio" name="settings_signaturesource" id="settings_signaturesource_local" value="LocalOnly"
onclick="document.getElementById('settings_hasheoushost_row').style.display = 'none';">
<label for="settings_signaturesource_local">Local Only</label>
</td>
</tr>
<tr>
<td></td>
<td>
<input type="radio" name="settings_signaturesource" id="settings_signaturesource_hasheous" value="Hasheous"
onclick="document.getElementById('settings_hasheoushost_row').style.display = '';">
<label for="settings_signaturesource_hasheous">Hasheous</label>
</td>
</tr>
<tr id="settings_hasheoushost_row" style="display: none;">
<th>
Hasheous Host
</th>
<td>
<input type="url" id="settings_signaturesource_hasheoushost" style="width: 90%;">
</td>
</tr>
<tr>
<th>
<label for="settings_hasheoussubmit">Submit updates to Hasheous when fixing ROM matches</label>
</th>
<td>
<input type="checkbox" id="settings_hasheoussubmit" onchange="toggleHasheousAPIKey(this);">
</td>
</tr>
<tr id="settings_hasheousapikey_row" style="display: none;">
<th>
Hasheous API key
</th>
<td>
<textarea id="settings_hasheousapikey" rows="2" style="width: 90%;"></textarea>
</td>
</tr>
<tr>
<th colspan="2">
<h3>Logging</h3>
</th>
</tr> </tr>
<tr> <tr>
<th> <th>
Write logs Write logs
</th> </th>
<td> <td>
<input type="radio" name="settings_logs_write" id="settings_logs_write_db" value="false" checked="checked"><label for="settings_logs_write_db"> To database only (default)</label> <input type="radio" name="settings_logs_write" id="settings_logs_write_db" value="false"
checked="checked"><label for="settings_logs_write_db"> To database only
(default)</label>
</td> </td>
</tr> </tr>
<tr> <tr>
<td></td> <td></td>
<td> <td>
<input type="radio" name="settings_logs_write" id="settings_logs_write_fs" value="true"><label for="settings_logs_write_fs"> To database and disk</label> <input type="radio" name="settings_logs_write" id="settings_logs_write_fs" value="true"><label
for="settings_logs_write_fs"> To database and disk</label>
</td> </td>
</tr> </tr>
<tr> <tr>
@@ -47,10 +84,12 @@
</td> </td>
</tr> </tr>
<tr> <tr>
<th colspan="2">Emulator</th> <th colspan="2">
<h3>Emulator</h3>
</th>
</tr> </tr>
<tr> <tr>
<th>Enable debug mode</th> <th><label for="settings_emulator_debug">Enable debug mode</label></th>
<td><input type="checkbox" name="settings_emulator" id="settings_emulator_debug" checked="checked" /></td> <td><input type="checkbox" name="settings_emulator" id="settings_emulator_debug" checked="checked" /></td>
</tr> </tr>
<tr> <tr>
@@ -61,339 +100,11 @@
</table> </table>
<script type="text/javascript"> <script type="text/javascript">
function drawLibrary() {
ajaxCall(
'/api/v1.1/Library',
'GET',
function (result) {
var newTable = document.getElementById('settings_libraries');
newTable.innerHTML = '';
newTable.appendChild(createTableRow(true, ['Name', 'Path', 'Default Platform', 'Default Library', '']));
for (var i = 0; i < result.length; i++) {
var platformName = '';
if (result[i].defaultPlatformId == 0) {
if (result[i].isDefaultLibrary == true) {
platformName = "n/a";
} else {
platformName = "";
}
} else {
platformName = result[i].defaultPlatformName;
}
var defaultLibrary = '';
if (result[i].isDefaultLibrary == true) {
defaultLibrary = "Yes";
} else {
defaultLibrary = "";
}
var deleteButton = '';
if (result[i].isDefaultLibrary == false) {
var deleteButton = '<a href="#" onclick="showSubDialog(\'librarydelete\', ' + result[i].id + ');" class="romlink"><img src="/images/delete.svg" class="banner_button_image" alt="Delete" title="Delete" /></a>';
}
newTable.appendChild(createTableRow(
false,
[
result[i].name,
result[i].path,
platformName,
defaultLibrary,
'<div style="text-align: right;">' + deleteButton + '</div>'
],
'romrow',
'romcell'
));
}
}
);
}
function getBackgroundTaskTimers() {
ajaxCall(
'/api/v1/System/Settings/BackgroundTasks/Configuration',
'GET',
function(result) {
var targetTable = document.getElementById('settings_tasktimers');
targetTable.innerHTML = '';
for (const [key, value] of Object.entries(result)) {
var newTableRowBody = document.createElement('tbody');
newTableRowBody.className = 'romrow';
var enabledString = "";
if (value.enabled == true) {
enabledString = 'checked="checked"';
}
var newTableIntervalRow = createTableRow(
false,
[
GetTaskFriendlyName(value.task),
'Enabled',
'<input id="settings_enabled_' + value.task + '" name="settings_tasktimers_enabled" type="checkbox" ' + enabledString + '/>',
],
'',
'romcell'
);
newTableRowBody.appendChild(newTableIntervalRow);
var newTableRow = createTableRow(
false,
[
'',
'Minimum Interval (Minutes):',
'<input id="settings_tasktimers_' + value.task + '" name="settings_tasktimers_values" data-name="' + value.task + '" data-default="' + value.defaultInterval + '" type="number" placeholder="' + value.defaultInterval + '" min="' + value.minimumAllowedInterval + '" value="' + value.interval + '" />'
],
'',
'romcell'
);
newTableRowBody.appendChild(newTableRow);
// allowed time periods row
var newTableRowTime = document.createElement('tr');
var rowTimeSpace = document.createElement('td');
newTableRowTime.appendChild(rowTimeSpace);
var rowTimeContentTitle = document.createElement('td');
rowTimeContentTitle.className = 'romcell';
rowTimeContentTitle.innerHTML = "Allowed Days:";
newTableRowTime.appendChild(rowTimeContentTitle);
var rowTimeContent = document.createElement('td');
// rowTimeContent.setAttribute('colspan', 2);
rowTimeContent.className = 'romcell';
var daySelector = document.createElement('select');
daySelector.id = 'settings_alloweddays_' + value.task;
daySelector.name = 'settings_alloweddays';
daySelector.multiple = 'multiple';
daySelector.setAttribute('data-default', value.defaultAllowedDays.join(","));
daySelector.style.width = '95%';
var days = [ "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday" ];
for (var d = 0; d < days.length; d++) {
var dayOpt = document.createElement('option');
dayOpt.value = days[d];
dayOpt.innerHTML = days[d];
if (value.allowedDays.includes(days[d])) {
dayOpt.selected = 'selected';
}
daySelector.appendChild(dayOpt);
}
rowTimeContent.appendChild(daySelector);
$(daySelector).select2({
tags: false
});
newTableRowTime.appendChild(rowTimeContent);
newTableRowBody.appendChild(newTableRowTime);
// add start and end times
var newTableRowClock = document.createElement('tr');
var rowClockSpace = document.createElement('td');
newTableRowClock.appendChild(rowClockSpace);
var rowClockContentTitle = document.createElement('td');
rowClockContentTitle.className = 'romcell';
rowClockContentTitle.innerHTML = "Time Range:";
newTableRowClock.appendChild(rowClockContentTitle);
var rowClockContent = document.createElement('td');
rowClockContent.className = 'romcell';
// rowClockContent.setAttribute('colspan', 2);
rowClockContent.appendChild(generateTimeDropDowns(value.task, 'Start', value.defaultAllowedStartHours, value.defaultAllowedStartMinutes, value.allowedStartHours, value.allowedStartMinutes));
rowClockContentSeparator = document.createElement('span');
rowClockContentSeparator.innerHTML = '&nbsp;-&nbsp;';
rowClockContent.appendChild(rowClockContentSeparator);
rowClockContent.appendChild(generateTimeDropDowns(value.task, 'End', value.defaultAllowedEndHours, value.defaultAllowedEndMinutes, value.allowedEndHours, value.allowedEndMinutes));
newTableRowClock.appendChild(rowClockContent);
newTableRowBody.appendChild(newTableRowClock);
// blocks tasks
var newTableRowBlocks = document.createElement('tr');
var rowBlocksSpace = document.createElement('td');
newTableRowBlocks.appendChild(rowBlocksSpace);
var rowBlocksContentTitle = document.createElement('td');
rowBlocksContentTitle.className = 'romcell';
rowBlocksContentTitle.innerHTML = "Blocks:";
newTableRowBlocks.appendChild(rowBlocksContentTitle);
var rowBlocksContent = document.createElement('td');
rowBlocksContent.className = 'romcell';
// rowBlocksContent.setAttribute('colspan', 2);
var blocksString = "";
for (var i = 0; i < value.blocks.length; i++) {
if (blocksString.length > 0) { blocksString += ", "; }
blocksString += GetTaskFriendlyName(value.blocks[i]);
}
if (blocksString.length == 0) { blocksString = 'None'; }
rowBlocksContent.innerHTML = blocksString;
newTableRowBlocks.appendChild(rowBlocksContent);
newTableRowBody.appendChild(newTableRowBlocks);
// blocked by tasks
var newTableRowBlockedBy = document.createElement('tr');
var rowBlockedBySpace = document.createElement('td');
newTableRowBlockedBy.appendChild(rowBlockedBySpace);
var rowBlockedByContentTitle = document.createElement('td');
rowBlockedByContentTitle.className = 'romcell';
rowBlockedByContentTitle.innerHTML = "Blocked By:";
newTableRowBlockedBy.appendChild(rowBlockedByContentTitle);
var rowBlockedByContent = document.createElement('td');
rowBlockedByContent.className = 'romcell';
// rowBlockedByContent.setAttribute('colspan', 2);
var BlockedByString = "";
for (var i = 0; i < value.blockedBy.length; i++) {
if (BlockedByString.length > 0) { BlockedByString += ", "; }
BlockedByString += GetTaskFriendlyName(value.blockedBy[i]);
}
if (BlockedByString.length == 0) { BlockedByString = 'None'; }
rowBlockedByContent.innerHTML = BlockedByString;
newTableRowBlockedBy.appendChild(rowBlockedByContent);
newTableRowBody.appendChild(newTableRowBlockedBy);
// complete row
targetTable.appendChild(newTableRowBody);
}
}
);
}
function generateTimeDropDowns(taskName, rangeName, defaultHour, defaultMinute, valueHour, valueMinute) {
var container = document.createElement('div');
container.style.display = 'inline';
var elementName = 'settings_tasktimers_time';
var hourSelector = document.createElement('input');
hourSelector.id = 'settings_tasktimers_' + taskName + '_' + rangeName + '_Hour';
hourSelector.name = elementName;
hourSelector.setAttribute('data-name', taskName);
hourSelector.setAttribute('type', 'number');
hourSelector.setAttribute('min', '0');
hourSelector.setAttribute('max', '23');
hourSelector.setAttribute('placeholder', defaultHour);
hourSelector.value = valueHour;
container.appendChild(hourSelector);
var separator = document.createElement('span');
separator.innerHTML = " : ";
container.appendChild(separator);
var minSelector = document.createElement('input');
minSelector.id = 'settings_tasktimers_' + taskName + '_' + rangeName + '_Minute';
minSelector.name = elementName;
minSelector.setAttribute('type', 'number');
minSelector.setAttribute('min', '0');
minSelector.setAttribute('max', '59');
minSelector.setAttribute('placeholder', defaultMinute);
minSelector.value = valueMinute;
container.appendChild(minSelector);
return container;
}
function saveTaskTimers() {
var timerValues = document.getElementsByName('settings_tasktimers_values');
var model = [];
for (var i = 0; i < timerValues.length; i++) {
var taskName = timerValues[i].getAttribute('data-name');
var taskEnabled = document.getElementById('settings_enabled_' + taskName).checked;
var taskIntervalObj = document.getElementById('settings_tasktimers_' + taskName);
var taskInterval = function() { if (taskIntervalObj.value) { return taskIntervalObj.value; } else { return taskIntervalObj.getAttribute('placeholder'); } };
var taskDaysRaw = $('#settings_alloweddays_' + taskName).select2('data');
var taskDays = [];
if (taskDaysRaw.length > 0) {
for (var d = 0; d < taskDaysRaw.length; d++) {
taskDays.push(taskDaysRaw[d].id);
}
} else {
taskDays.push("Monday");
}
var taskStartHourObj = document.getElementById('settings_tasktimers_' + taskName + '_Start_Hour');
var taskStartMinuteObj = document.getElementById('settings_tasktimers_' + taskName + '_Start_Minute');
var taskEndHourObj = document.getElementById('settings_tasktimers_' + taskName + '_End_Hour');
var taskEndMinuteObj = document.getElementById('settings_tasktimers_' + taskName + '_End_Minute');
var taskStartHour = function() { if (taskStartHourObj.value) { return taskStartHourObj.value; } else { return taskStartHourObj.getAttribute('placeholder'); } };
var taskStartMinute = function() { if (taskStartMinuteObj.value) { return taskStartMinuteObj.value; } else { return taskStartMinuteObj.getAttribute('placeholder'); } };
var taskEndHour = function() { if (taskEndHourObj.value) { return taskEndHourObj.value; } else { return taskEndHourObj.getAttribute('placeholder'); } };
var taskEndMinute = function() { if (taskEndMinuteObj.value) { return taskEndMinuteObj.value; } else { return taskEndMinuteObj.getAttribute('placeholder'); } };
model.push(
{
"task": taskName,
"enabled": taskEnabled,
"interval": taskInterval(),
"allowedDays": taskDays,
"allowedStartHours": taskStartHour(),
"allowedStartMinutes": taskStartMinute(),
"allowedEndHours": taskEndHour(),
"allowedEndMinutes": taskEndMinute()
}
);
}
ajaxCall(
'/api/v1/System/Settings/BackgroundTasks/Configuration',
'POST',
function(result) {
getBackgroundTaskTimers();
},
function(error) {
getBackgroundTaskTimers();
},
JSON.stringify(model)
);
}
function defaultTaskTimers() {
var timerValues = document.getElementsByName('settings_tasktimers_enabled');
for (var i = 0; i < timerValues.length; i++) {
timerValues[i].checked = true;
}
var timerValues = document.getElementsByName('settings_tasktimers_values');
for (var i = 0; i < timerValues.length; i++) {
timerValues[i].value = timerValues[i].getAttribute('data-default');
}
var timerValues = document.getElementsByName('settings_alloweddays');
for (var i = 0; i < timerValues.length; i++) {
var defaultSelections = timerValues[i].getAttribute('data-default').split(',');
$(timerValues[i]).val(defaultSelections);
$(timerValues[i]).trigger('change');
}
var timerValues = document.getElementsByName('settings_tasktimers_time');
for (var i = 0; i < timerValues.length; i++) {
timerValues[i].value = timerValues[i].getAttribute('placeholder');
}
saveTaskTimers();
}
function getSystemSettings() { function getSystemSettings() {
ajaxCall( ajaxCall(
'/api/v1/System/Settings/System', '/api/v1/System/Settings/System',
'GET', 'GET',
function(result) { function (result) {
var optionToSelect = 'settings_logs_write_db'; var optionToSelect = 'settings_logs_write_db';
if (result.alwaysLogToDisk == true) { if (result.alwaysLogToDisk == true) {
optionToSelect = 'settings_logs_write_fs'; optionToSelect = 'settings_logs_write_fs';
@@ -403,6 +114,27 @@
document.getElementById('settings_logs_retention').value = result.minimumLogRetentionPeriod; document.getElementById('settings_logs_retention').value = result.minimumLogRetentionPeriod;
document.getElementById('settings_emulator_debug').checked = result.emulatorDebugMode; document.getElementById('settings_emulator_debug').checked = result.emulatorDebugMode;
switch (result.signatureSource.source) {
case "LocalOnly":
document.getElementById('settings_signaturesource_local').checked = true;
break;
case "Hasheous":
document.getElementById('settings_signaturesource_hasheous').checked = true;
document.getElementById('settings_hasheoushost_row').style.display = '';
break;
}
document.getElementById('settings_signaturesource_hasheoushost').value = result.signatureSource.hasheousHost;
let hasheousSubmitCheck = document.getElementById('settings_hasheoussubmit');
if (result.signatureSource.hasheousSubmitFixes == true) {
hasheousSubmitCheck.checked = true;
}
document.getElementById('settings_hasheousapikey').innerHTML = result.signatureSource.hasheousAPIKey;
toggleHasheousAPIKey(hasheousSubmitCheck);
} }
); );
} }
@@ -423,24 +155,37 @@
var model = { var model = {
"alwaysLogToDisk": alwaysLogToDisk, "alwaysLogToDisk": alwaysLogToDisk,
"minimumLogRetentionPeriod": retentionValue, "minimumLogRetentionPeriod": Number(retentionValue),
"emulatorDebugMode": document.getElementById('settings_emulator_debug').checked "emulatorDebugMode": document.getElementById('settings_emulator_debug').checked,
"signatureSource": {
"Source": $("input[type='radio'][name='settings_signaturesource']:checked").val(),
"HasheousHost": document.getElementById('settings_signaturesource_hasheoushost').value,
"HasheousAPIKey": document.getElementById('settings_hasheousapikey').innerHTML,
"HasheousSubmitFixes": document.getElementById('settings_hasheoussubmit').checked
}
}; };
ajaxCall( ajaxCall(
'/api/v1/System/Settings/System', '/api/v1/System/Settings/System',
'POST', 'POST',
function(result) { function (result) {
getSystemSettings(); getSystemSettings();
}, },
function(error) { function (error) {
getSystemSettings(); getSystemSettings();
}, },
JSON.stringify(model) JSON.stringify(model)
); );
} }
drawLibrary(); function toggleHasheousAPIKey(checkbox) {
getBackgroundTaskTimers(); let settings_hasheousapikey_row = document.getElementById('settings_hasheousapikey_row');
if (checkbox.checked == true) {
settings_hasheousapikey_row.style.display = '';
} else {
settings_hasheousapikey_row.style.display = 'none';
}
}
getSystemSettings(); getSystemSettings();
</script> </script>

View File

@@ -22,7 +22,7 @@
<p><strong>Database</strong></p> <p><strong>Database</strong></p>
<div id="system_database"></div> <div id="system_database"></div>
<h3>Signatures</h3> <h3>Local Database Signatures</h3>
<div id="system_signatures"></div> <div id="system_signatures"></div>
<script type="text/javascript"> <script type="text/javascript">
@@ -37,10 +37,10 @@
for (var i = 0; i < result.length; i++) { for (var i = 0; i < result.length; i++) {
if (result[i].itemState != "Disabled") { if (result[i].itemState != "Disabled") {
var itemTypeName = GetTaskFriendlyName(result[i].itemType, result[i].options); var itemTypeName = GetTaskFriendlyName(result[i].itemType, result[i].options);
var itemStateName; var itemStateName;
var itemLastStart; var itemLastStart;
var hasError = ""; var hasError = "";
if (result[i].hasErrors) { if (result[i].hasErrors) {
if (result[i].hasErrors.errorType != null) { if (result[i].hasErrors.errorType != null) {
@@ -82,7 +82,7 @@
var nextRunTime = moment(result[i].nextRunTime).format("YYYY-MM-DD h:mm:ss a"); var nextRunTime = moment(result[i].nextRunTime).format("YYYY-MM-DD h:mm:ss a");
var startButton = ''; var startButton = '';
if (userProfile.roles.includes("Admin")) { if (userProfile.roles.includes("Admin")) {
if (result[i].allowManualStart == true && ![ "Running"].includes(result[i].itemState) && result[i].isBlocked == false) { if (result[i].allowManualStart == true && !["Running"].includes(result[i].itemState) && result[i].isBlocked == false) {
startButton = "<span id='startProcess' class='romstart' onclick='StartProcess(\"" + result[i].itemType + "\");'>Start</span>"; startButton = "<span id='startProcess' class='romstart' onclick='StartProcess(\"" + result[i].itemType + "\");'>Start</span>";
} }
} }
@@ -271,4 +271,4 @@
setInterval(SystemLoadSystemStatus, 60000); setInterval(SystemLoadSystemStatus, 60000);
SystemSignaturesStatus(); SystemSignaturesStatus();
setInterval(SystemSignaturesStatus, 300000); setInterval(SystemSignaturesStatus, 300000);
</script> </script>

View File

@@ -340,6 +340,11 @@ input[type="datetime-local"]:hover {
border-color: #939393; border-color: #939393;
} }
textarea {
height: unset;
font-family: 'Courier New', Courier, monospace;
}
input[id='filter_panel_search'] { input[id='filter_panel_search'] {
width: 160px; width: 160px;
} }
@@ -607,22 +612,22 @@ input[name='filter_panel_range_max'] {
.game_tile:hover { .game_tile:hover {
cursor: pointer; cursor: pointer;
text-decoration: underline; /* text-decoration: underline;
background-color: #2b2b2b; background-color: #2b2b2b;
border-radius: 10px 10px 10px 10px; border-radius: 10px 10px 10px 10px;
-webkit-border-radius: 10px 10px 10px 10px; -webkit-border-radius: 10px 10px 10px 10px;
-moz-border-radius: 10px 10px 10px 10px; -moz-border-radius: 10px 10px 10px 10px;
border: 1px solid #2b2b2b; border: 1px solid #2b2b2b; */
} }
.game_tile_small:hover { .game_tile_small:hover {
cursor: pointer; cursor: pointer;
text-decoration: underline; /* text-decoration: underline;
background-color: #2b2b2b; background-color: #2b2b2b;
border-radius: 10px 10px 10px 10px; border-radius: 10px 10px 10px 10px;
-webkit-border-radius: 10px 10px 10px 10px; -webkit-border-radius: 10px 10px 10px 10px;
-moz-border-radius: 10px 10px 10px 10px; -moz-border-radius: 10px 10px 10px 10px;
border: 1px solid #2b2b2b; border: 1px solid #2b2b2b; */
} }
.game_tile_small_search { .game_tile_small_search {
@@ -652,6 +657,19 @@ input[name='filter_panel_range_max'] {
display: inline-block; display: inline-block;
max-width: 200px; max-width: 200px;
max-height: 200px; max-height: 200px;
border-radius: 7px;
border-width: 2px;
border-style: solid;
border-color: transparent;
overflow: hidden;
}
.game_tile:hover .game_tile_box {
border-color: yellow;
outline-width: 2px;
outline-style: solid;
outline-offset: -3px;
outline-color: black;
} }
.game_tile_box_row { .game_tile_box_row {