Integrate new Hasheous client (#354)

The new Hasheous client has been integrated into Gaseous to take
advantage of the new updates to Hasheous that bring improved matching
and community contributions.
This commit is contained in:
Michael Green
2024-05-18 15:05:25 +10:00
committed by GitHub
parent f881459c0b
commit eda171efa1
29 changed files with 2169 additions and 1017 deletions

2
.gitignore vendored
View File

@@ -405,3 +405,5 @@ ASALocalRun/
.localhistory/
gaseous-server/.DS_Store
gaseous-server/wwwroot/emulators/EmulatorJS
.devcontainer/.env
.mono/

3
.vscode/launch.json vendored
View File

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

View File

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

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 25.0.1704.4
@@ -27,30 +27,18 @@ Global
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{08699C93-15CD-4E39-9053-D3C8056CE938}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{08699C93-15CD-4E39-9053-D3C8056CE938}.Debug|Any CPU.Build.0 = Debug|Any CPU
{08699C93-15CD-4E39-9053-D3C8056CE938}.Release|Any CPU.ActiveCfg = Release|Any CPU
{08699C93-15CD-4E39-9053-D3C8056CE938}.Release|Any CPU.Build.0 = Release|Any CPU
{FFCEC386-033F-4772-A45B-D33579F2E5EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FFCEC386-033F-4772-A45B-D33579F2E5EE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FFCEC386-033F-4772-A45B-D33579F2E5EE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FFCEC386-033F-4772-A45B-D33579F2E5EE}.Release|Any CPU.Build.0 = Release|Any CPU
{A01D2EFF-C82E-473B-84D7-7C25E736F5D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A01D2EFF-C82E-473B-84D7-7C25E736F5D2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A01D2EFF-C82E-473B-84D7-7C25E736F5D2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A01D2EFF-C82E-473B-84D7-7C25E736F5D2}.Release|Any CPU.Build.0 = Release|Any CPU
{B07A4655-A003-416B-A790-ADAA5B548E1A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B07A4655-A003-416B-A790-ADAA5B548E1A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B07A4655-A003-416B-A790-ADAA5B548E1A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B07A4655-A003-416B-A790-ADAA5B548E1A}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {979BF092-AFBC-4F19-B55F-32E73959BD1A}
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{F1A847C7-57BC-4DA9-8F83-CD060A7F5122} = {17FA6F12-8532-420C-9489-CB8FDE42137C}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {979BF092-AFBC-4F19-B55F-32E73959BD1A}
EndGlobalSection
EndGlobal

View File

@@ -1,5 +1,6 @@
using System.Collections.Concurrent;
using System.ComponentModel;
using System.Data;
using System.IO.Compression;
using System.Reflection;
using System.Security.Cryptography;
@@ -19,7 +20,8 @@ namespace gaseous_server.Classes
if (ObjectToCheck == null || ObjectToCheck == System.DBNull.Value)
{
return IfNullValue;
} else
}
else
{
return ObjectToCheck;
}
@@ -27,10 +29,10 @@ namespace gaseous_server.Classes
static public DateTime ConvertUnixToDateTime(double UnixTimeStamp)
{
DateTime dateTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
dateTime = dateTime.AddSeconds(UnixTimeStamp).ToLocalTime();
return dateTime;
}
DateTime dateTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
dateTime = dateTime.AddSeconds(UnixTimeStamp).ToLocalTime();
return dateTime;
}
public class hashObject
{
@@ -41,21 +43,21 @@ namespace gaseous_server.Classes
public hashObject(string FileName)
{
var xmlStream = File.OpenRead(FileName);
var xmlStream = File.OpenRead(FileName);
var md5 = MD5.Create();
byte[] md5HashByte = md5.ComputeHash(xmlStream);
string md5Hash = BitConverter.ToString(md5HashByte).Replace("-", "").ToLowerInvariant();
var md5 = MD5.Create();
byte[] md5HashByte = md5.ComputeHash(xmlStream);
string md5Hash = BitConverter.ToString(md5HashByte).Replace("-", "").ToLowerInvariant();
_md5hash = md5Hash;
var sha1 = SHA1.Create();
var sha1 = SHA1.Create();
xmlStream.Position = 0;
byte[] sha1HashByte = sha1.ComputeHash(xmlStream);
string sha1Hash = BitConverter.ToString(sha1HashByte).Replace("-", "").ToLowerInvariant();
byte[] sha1HashByte = sha1.ComputeHash(xmlStream);
string sha1Hash = BitConverter.ToString(sha1HashByte).Replace("-", "").ToLowerInvariant();
_sha1hash = sha1Hash;
xmlStream.Close();
}
}
string _md5hash = "";
string _sha1hash = "";
@@ -85,29 +87,29 @@ namespace gaseous_server.Classes
}
}
public static long DirSize(DirectoryInfo d)
{
long size = 0;
// Add file sizes.
FileInfo[] fis = d.GetFiles();
foreach (FileInfo fi in fis)
{
size += fi.Length;
}
// Add subdirectory sizes.
DirectoryInfo[] dis = d.GetDirectories();
foreach (DirectoryInfo di in dis)
{
size += DirSize(di);
}
return size;
}
public static long DirSize(DirectoryInfo d)
{
long size = 0;
// Add file sizes.
FileInfo[] fis = d.GetFiles();
foreach (FileInfo fi in fis)
{
size += fi.Length;
}
// Add subdirectory sizes.
DirectoryInfo[] dis = d.GetDirectories();
foreach (DirectoryInfo di in dis)
{
size += DirSize(di);
}
return size;
}
public static string[] SkippableFiles = {
".DS_STORE",
"desktop.ini"
};
public static string NormalizePath(string path)
{
return Path.GetFullPath(new Uri(path).LocalPath)
@@ -155,30 +157,74 @@ namespace gaseous_server.Classes
return defaultValue;
}
}
}
public static int GetLookupByCode(LookupTypes LookupType, string Code)
{
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "SELECT Id FROM " + LookupType.ToString() + " WHERE Code = @code";
Dictionary<string, object> dbDict = new Dictionary<string, object>{
{ "code", Code }
};
DataTable data = db.ExecuteCMD(sql, dbDict);
if (data.Rows.Count == 0)
{
return -1;
}
else
{
return (int)data.Rows[0]["Id"];
}
}
public static int GetLookupByValue(LookupTypes LookupType, string Value)
{
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "SELECT Id FROM " + LookupType.ToString() + " WHERE Value = @value";
Dictionary<string, object> dbDict = new Dictionary<string, object>{
{ "value", Value }
};
DataTable data = db.ExecuteCMD(sql, dbDict);
if (data.Rows.Count == 0)
{
return -1;
}
else
{
return (int)data.Rows[0]["Id"];
}
}
public enum LookupTypes
{
Country,
Language
}
}
/// <summary>
/// Provides a way to set contextual data that flows with the call and
/// async context of a test or invocation.
/// </summary>
public static class CallContext
{
static ConcurrentDictionary<string, AsyncLocal<object>> state = new ConcurrentDictionary<string, AsyncLocal<object>>();
/// Provides a way to set contextual data that flows with the call and
/// async context of a test or invocation.
/// </summary>
public static class CallContext
{
static ConcurrentDictionary<string, AsyncLocal<object>> state = new ConcurrentDictionary<string, AsyncLocal<object>>();
/// <summary>
/// Stores a given object and associates it with the specified name.
/// </summary>
/// <param name="name">The name with which to associate the new item in the call context.</param>
/// <param name="data">The object to store in the call context.</param>
public static void SetData(string name, object data) =>
state.GetOrAdd(name, _ => new AsyncLocal<object>()).Value = data;
/// <summary>
/// Stores a given object and associates it with the specified name.
/// </summary>
/// <param name="name">The name with which to associate the new item in the call context.</param>
/// <param name="data">The object to store in the call context.</param>
public static void SetData(string name, object data) =>
state.GetOrAdd(name, _ => new AsyncLocal<object>()).Value = data;
/// <summary>
/// Retrieves an object with the specified name from the <see cref="CallContext"/>.
/// </summary>
/// <param name="name">The name of the item in the call context.</param>
/// <returns>The object in the call context associated with the specified name, or <see langword="null"/> if not found.</returns>
public static object GetData(string name) =>
state.TryGetValue(name, out AsyncLocal<object> data) ? data.Value : null;
}
/// <summary>
/// Retrieves an object with the specified name from the <see cref="CallContext"/>.
/// </summary>
/// <param name="name">The name of the item in the call context.</param>
/// <returns>The object in the call context associated with the specified name, or <see langword="null"/> if not found.</returns>
public static object GetData(string name) =>
state.TryGetValue(name, out AsyncLocal<object> data) ? data.Value : null;
}
}

View File

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

View File

@@ -1,14 +1,17 @@
using System;
using System.Data;
using System.Reflection;
using gaseous_server.Classes.Metadata;
using gaseous_server.Models;
using IGDB.Models;
namespace gaseous_server.Classes
{
public static class DatabaseMigration
{
public static class DatabaseMigration
{
public static List<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
var assembly = Assembly.GetExecutingAssembly();
@@ -20,14 +23,14 @@ namespace gaseous_server.Classes
Logging.Log(Logging.LogType.Information, "Database", "Checking for pre-upgrade for schema version " + TargetSchemaVersion);
switch(DatabaseType)
switch (DatabaseType)
{
case Database.databaseType.MySql:
switch (TargetSchemaVersion)
{
case 1005:
Logging.Log(Logging.LogType.Information, "Database", "Running pre-upgrade for schema version " + TargetSchemaVersion);
// there was a mistake at dbschema version 1004-1005
// the first preview release of v1.7 reused dbschema version 1004
// if table "Relation_Game_AgeRatings" exists - then we need to apply the gaseous-fix-1005.sql script before applying the standard 1005 script
@@ -62,14 +65,16 @@ namespace gaseous_server.Classes
}
}
public static void PostUpgradeScript(int TargetSchemaVersion, Database.databaseType? DatabaseType)
public static void PostUpgradeScript(int TargetSchemaVersion, Database.databaseType? DatabaseType)
{
var assembly = Assembly.GetExecutingAssembly();
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "";
Dictionary<string, object> dbDict = new Dictionary<string, object>();
DataTable data;
switch(DatabaseType)
switch (DatabaseType)
{
case Database.databaseType.MySql:
switch (TargetSchemaVersion)
@@ -78,7 +83,7 @@ namespace gaseous_server.Classes
// this is a safe background task
BackgroundUpgradeTargetSchemaVersions.Add(1002);
break;
case 1004:
// needs to run on start up
@@ -103,6 +108,51 @@ namespace gaseous_server.Classes
sql = "DELETE FROM Settings WHERE Setting LIKE 'LastRun_%';";
db.ExecuteNonQuery(sql);
break;
case 1022:
// load country list
Logging.Log(Logging.LogType.Information, "Database Upgrade", "Adding country look up table contents");
string countryResourceName = "gaseous_server.Support.Country.txt";
using (Stream stream = assembly.GetManifestResourceStream(countryResourceName))
using (StreamReader reader = new StreamReader(stream))
{
do
{
string[] line = reader.ReadLine().Split("|");
sql = "INSERT INTO Country (Code, Value) VALUES (@code, @value);";
dbDict = new Dictionary<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;
}
@@ -117,11 +167,16 @@ namespace gaseous_server.Classes
case 1002:
MySql_1002_MigrateMetadataVersion();
break;
case 1022:
MySql_1022_MigrateMetadataVersion();
break;
}
}
}
public static void MySql_1002_MigrateMetadataVersion() {
public static void MySql_1002_MigrateMetadataVersion()
{
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "";
Dictionary<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");
int Counter = 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<KeyValuePair<string, object>> Attributes = new List<KeyValuePair<string, object>>();
@@ -207,7 +262,7 @@ namespace gaseous_server.Classes
dbDict.Add("id", (int)row["Id"]);
db.ExecuteCMD(updateSQL, dbDict);
if ((Counter - LastCounterCheck) > 10)
if ((Counter - LastCounterCheck) > 10)
{
LastCounterCheck = Counter;
Logging.Log(Logging.LogType.Information, "Signature Ingestor - Database Update", "Updating " + Counter + " / " + data.Rows.Count + " database entries");
@@ -216,5 +271,36 @@ namespace gaseous_server.Classes
}
}
}
public static void MySql_1022_MigrateMetadataVersion()
{
FileSignature fileSignature = new FileSignature();
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "SELECT * FROM Games_Roms WHERE RomDataVersion = 1;";
DataTable data = db.ExecuteCMD(sql);
foreach (DataRow row in data.Rows)
{
Logging.Log(Logging.LogType.Information, "Database Migration", "Updating ROM table for ROM: " + (string)row["Name"]);
GameLibrary.LibraryItem library = GameLibrary.GetLibrary((int)row["LibraryId"]);
Common.hashObject hash = new Common.hashObject()
{
md5hash = (string)row["MD5"],
sha1hash = (string)row["SHA1"]
};
Signatures_Games signature = fileSignature.GetFileSignature(
library,
hash,
new FileInfo((string)row["Path"]),
(string)row["Path"]
);
Platform platform = Platforms.GetPlatform((long)row["PlatformId"], false);
Game game = Games.GetGame((long)row["GameId"], false, false, false);
ImportGame.StoreROM(library, hash, game, platform, signature, (string)row["Path"], (long)row["Id"]);
}
}
}
}

View File

@@ -1,6 +1,8 @@
using System.Collections.Concurrent;
using System.IO.Compression;
using gaseous_server.Classes.Metadata;
using HasheousClient.Models;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using NuGet.Common;
using SevenZip;
using SharpCompress.Archives;
@@ -31,7 +33,7 @@ namespace gaseous_server.Classes
if (!Directory.Exists(ExtractPath)) { Directory.CreateDirectory(ExtractPath); }
try
{
switch(ImportedFileExtension)
switch (ImportedFileExtension)
{
case ".zip":
Logging.Log(Logging.LogType.Information, "Get Signature", "Decompressing using zip");
@@ -105,31 +107,24 @@ namespace gaseous_server.Classes
}
break;
}
Logging.Log(Logging.LogType.Information, "Get Signature", "Processing decompressed files for signature matches");
// loop through contents until we find the first signature match
List<ArchiveData> archiveFiles = new List<ArchiveData>();
bool signatureFound = false;
bool signatureSelectorAlreadyApplied = false;
foreach (string file in Directory.GetFiles(ExtractPath, "*.*", SearchOption.AllDirectories))
{
bool signatureSelector = false;
if (File.Exists(file))
{
FileInfo zfi = new FileInfo(file);
Common.hashObject zhash = new Common.hashObject(file);
Logging.Log(Logging.LogType.Information, "Get Signature", "Checking signature of decompressed file " + file);
if (zfi != null)
{
ArchiveData archiveData = new ArchiveData{
FileName = Path.GetFileName(file),
FilePath = zfi.Directory.FullName.Replace(ExtractPath, ""),
Size = zfi.Length,
MD5 = hash.md5hash,
SHA1 = hash.sha1hash
};
archiveFiles.Add(archiveData);
if (signatureFound == false)
{
gaseous_server.Models.Signatures_Games zDiscoveredSignature = _GetFileSignature(zhash, zfi.Name, zfi.Extension, zfi.Length, file, true);
@@ -138,7 +133,7 @@ namespace gaseous_server.Classes
if (zDiscoveredSignature.Score > discoveredSignature.Score)
{
if (
zDiscoveredSignature.Rom.SignatureSource == gaseous_server.Models.Signatures_Games.RomItem.SignatureSourceType.MAMEArcade ||
zDiscoveredSignature.Rom.SignatureSource == gaseous_server.Models.Signatures_Games.RomItem.SignatureSourceType.MAMEArcade ||
zDiscoveredSignature.Rom.SignatureSource == gaseous_server.Models.Signatures_Games.RomItem.SignatureSourceType.MAMEMess
)
{
@@ -151,15 +146,37 @@ namespace gaseous_server.Classes
discoveredSignature = zDiscoveredSignature;
signatureFound = true;
if (signatureSelectorAlreadyApplied == false)
{
signatureSelector = true;
signatureSelectorAlreadyApplied = true;
}
}
}
ArchiveData archiveData = new ArchiveData
{
FileName = Path.GetFileName(file),
FilePath = zfi.Directory.FullName.Replace(ExtractPath, ""),
Size = zfi.Length,
MD5 = zhash.md5hash,
SHA1 = zhash.sha1hash,
isSignatureSelector = signatureSelector
};
archiveFiles.Add(archiveData);
}
}
}
discoveredSignature.Rom.Attributes.Add(new KeyValuePair<string, object>(
if (discoveredSignature.Rom.Attributes == null)
{
discoveredSignature.Rom.Attributes = new Dictionary<string, object>();
}
discoveredSignature.Rom.Attributes.Add(
"ZipContents", Newtonsoft.Json.JsonConvert.SerializeObject(archiveFiles)
));
);
}
catch (Exception ex)
{
@@ -195,7 +212,7 @@ namespace gaseous_server.Classes
{
// signature retrieved from Hasheous
Logging.Log(Logging.LogType.Information, "Import Game", "Signature retrieved from Hasheous for game: " + dbSignature.Game.Name);
discoveredSignature = dbSignature;
}
else
@@ -203,7 +220,7 @@ namespace gaseous_server.Classes
// construct a signature from file data
dbSignature = _GetFileSignatureFromFileData(hash, ImageName, ImageExtension, ImageSize, GameFileImportPath);
Logging.Log(Logging.LogType.Information, "Import Game", "Signature generated from provided file for game: " + dbSignature.Game.Name);
discoveredSignature = dbSignature;
}
}
@@ -216,8 +233,8 @@ namespace gaseous_server.Classes
return discoveredSignature;
}
private gaseous_server.Models.Signatures_Games? _GetFileSignatureFromDatabase(Common.hashObject hash, string ImageName, string ImageExtension, long ImageSize, string GameFileImportPath)
{
private gaseous_server.Models.Signatures_Games? _GetFileSignatureFromDatabase(Common.hashObject hash, string ImageName, string ImageExtension, long ImageSize, string GameFileImportPath)
{
Logging.Log(Logging.LogType.Information, "Get Signature", "Checking local database for MD5: " + hash.md5hash);
// check 1: do we have a signature for it?
@@ -264,29 +281,67 @@ namespace gaseous_server.Classes
if (Config.MetadataConfiguration.SignatureSource == HasheousClient.Models.MetadataModel.SignatureSources.Hasheous)
{
HasheousClient.Hasheous hasheous = new HasheousClient.Hasheous();
SignatureLookupItem? HasheousResult = hasheous.RetrieveFromHasheousAsync(new HashLookupModel{
MD5 = hash.md5hash,
SHA1 = hash.sha1hash
});
Console.WriteLine(HasheousClient.WebApp.HttpHelper.BaseUri);
LookupItemModel? HasheousResult = null;
try
{
HasheousResult = hasheous.RetrieveFromHasheous(new HashLookupModel
{
MD5 = hash.md5hash,
SHA1 = hash.sha1hash
});
}
catch (Exception ex)
{
Logging.Log(Logging.LogType.Warning, "Import Game", "An error occurred while importing " + ImageName, ex);
return null;
}
if (HasheousResult != null)
{
if (HasheousResult.Signature != null)
{
gaseous_server.Models.Signatures_Games signature = new Models.Signatures_Games();
signature.Game = HasheousResult.Signature.Game;
signature.Rom = HasheousResult.Signature.Rom;
if (HasheousResult.MetadataResults != null)
string gameJson = Newtonsoft.Json.JsonConvert.SerializeObject(HasheousResult.Signature.Game);
string romJson = Newtonsoft.Json.JsonConvert.SerializeObject(HasheousResult.Signature.Rom);
signature.Game = Newtonsoft.Json.JsonConvert.DeserializeObject<Models.Signatures_Games.GameItem>(gameJson);
signature.Rom = Newtonsoft.Json.JsonConvert.DeserializeObject<Models.Signatures_Games.RomItem>(romJson);
// get platform metadata
if (HasheousResult.Platform != null)
{
if (HasheousResult.MetadataResults.Count > 0)
if (HasheousResult.Platform.metadata.Count > 0)
{
foreach (SignatureLookupItem.MetadataResult metadataResult in HasheousResult.MetadataResults)
foreach (HasheousClient.Models.MetadataItem metadataResult in HasheousResult.Platform.metadata)
{
if (metadataResult.Source == MetadataModel.MetadataSources.IGDB)
if (metadataResult.Id.Length > 0)
{
signature.Flags.IGDBPlatformId = (long)metadataResult.PlatformId;
signature.Flags.IGDBGameId = (long)metadataResult.GameId;
switch (metadataResult.Source)
{
case HasheousClient.Models.MetadataSources.IGDB:
signature.Flags.IGDBPlatformId = (long)Platforms.GetPlatform(metadataResult.Id, false).Id;
break;
}
}
}
}
}
// get game metadata
if (HasheousResult.Metadata != null)
{
if (HasheousResult.Metadata.Count > 0)
{
foreach (HasheousClient.Models.MetadataItem metadataResult in HasheousResult.Metadata)
{
if (metadataResult.Id.Length > 0)
{
switch (metadataResult.Source)
{
case HasheousClient.Models.MetadataSources.IGDB:
signature.Flags.IGDBGameId = (long)Games.GetGame(metadataResult.Id, false, false, false).Id;
break;
}
}
}
}
@@ -368,6 +423,7 @@ namespace gaseous_server.Classes
public long Size { get; set; }
public string MD5 { get; set; }
public string SHA1 { get; set; }
public bool isSignatureSelector { get; set; } = false;
}
}
}

View File

@@ -16,48 +16,49 @@ using HasheousClient.Models;
namespace gaseous_server.Classes
{
public class ImportGame : QueueItemStatus
{
public class ImportGame : QueueItemStatus
{
public void ProcessDirectory(string ImportPath)
{
if (Directory.Exists(ImportPath))
{
string[] importContents = Directory.GetFiles(ImportPath, "*.*", SearchOption.AllDirectories);
{
string[] importContents = Directory.GetFiles(ImportPath, "*.*", SearchOption.AllDirectories);
Logging.Log(Logging.LogType.Information, "Import Games", "Found " + importContents.Length + " files to process in import directory: " + ImportPath);
// import files first
// import files first
int importCount = 1;
foreach (string importContent in importContents) {
foreach (string importContent in importContents)
{
SetStatus(importCount, importContents.Length, "Importing file: " + importContent);
ImportGameFile(importContent, null);
ImportGameFile(importContent, null);
importCount += 1;
}
}
ClearStatus();
DeleteOrphanedDirectories(ImportPath);
}
else
{
Logging.Log(Logging.LogType.Critical, "Import Games", "The import directory " + ImportPath + " does not exist.");
throw new DirectoryNotFoundException("Invalid path: " + ImportPath);
}
else
{
Logging.Log(Logging.LogType.Critical, "Import Games", "The import directory " + ImportPath + " does not exist.");
throw new DirectoryNotFoundException("Invalid path: " + ImportPath);
}
}
public void ImportGameFile(string GameFileImportPath, IGDB.Models.Platform? OverridePlatform)
{
{
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "";
Dictionary<string, object> dbDict = new Dictionary<string, object>();
string sql = "";
Dictionary<string, object> dbDict = new Dictionary<string, object>();
if (Common.SkippableFiles.Contains<string>(Path.GetFileName(GameFileImportPath), StringComparer.OrdinalIgnoreCase))
{
{
Logging.Log(Logging.LogType.Debug, "Import Game", "Skipping item " + GameFileImportPath);
}
else
{
else
{
FileInfo fi = new FileInfo(GameFileImportPath);
Common.hashObject hash = new Common.hashObject(GameFileImportPath);
@@ -87,7 +88,7 @@ namespace gaseous_server.Classes
}
File.Move(GameFileImportPath, targetPathWithFileName, true);
}
// import source was the upload directory
if (GameFileImportPath.StartsWith(Config.LibraryConfiguration.LibraryUploadDirectory))
{
@@ -146,8 +147,8 @@ namespace gaseous_server.Classes
}
}
}
}
}
}
}
public static IGDB.Models.Game SearchForGame(gaseous_server.Models.Signatures_Games Signature, long PlatformId, bool FullDownload)
{
@@ -173,7 +174,7 @@ namespace gaseous_server.Classes
string GameName = Signature.Game.Name;
List<string> SearchCandidates = GetSearchCandidates(GameName);
foreach (string SearchCandidate in SearchCandidates)
{
bool GameFound = false;
@@ -197,8 +198,10 @@ namespace gaseous_server.Classes
Logging.Log(Logging.LogType.Information, "Import Game", " " + games.Length + " search results found");
// quite likely we've found sequels and alternate types
foreach (Game game in games) {
if (game.Name == SearchCandidate) {
foreach (Game game in games)
{
if (game.Name == SearchCandidate)
{
// found game title matches the search candidate
determinedGame = Metadata.Games.GetGame((long)games[0].Id, false, false, false);
Logging.Log(Logging.LogType.Information, "Import Game", "Found exact match!");
@@ -273,7 +276,8 @@ namespace gaseous_server.Classes
// assumption: no games have () in their titles so we'll remove them
int idx = GameName.IndexOf('(');
if (idx >= 0) {
if (idx >= 0)
{
GameName = GameName.Substring(0, idx);
}
@@ -304,10 +308,11 @@ namespace gaseous_server.Classes
if (UpdateId == 0)
{
sql = "INSERT INTO Games_Roms (PlatformId, GameId, Name, Size, CRC, MD5, SHA1, DevelopmentStatus, Attributes, RomType, RomTypeMedia, MediaLabel, Path, MetadataSource, MetadataGameName, MetadataVersion, LibraryId) VALUES (@platformid, @gameid, @name, @size, @crc, @md5, @sha1, @developmentstatus, @Attributes, @romtype, @romtypemedia, @medialabel, @path, @metadatasource, @metadatagamename, @metadataversion, @libraryid); SELECT CAST(LAST_INSERT_ID() AS SIGNED);";
} else
sql = "INSERT INTO Games_Roms (PlatformId, GameId, Name, Size, CRC, MD5, SHA1, DevelopmentStatus, Attributes, RomType, RomTypeMedia, MediaLabel, Path, MetadataSource, MetadataGameName, MetadataVersion, LibraryId, RomDataVersion) VALUES (@platformid, @gameid, @name, @size, @crc, @md5, @sha1, @developmentstatus, @Attributes, @romtype, @romtypemedia, @medialabel, @path, @metadatasource, @metadatagamename, @metadataversion, @libraryid, @romdataversion); SELECT CAST(LAST_INSERT_ID() AS SIGNED);";
}
else
{
sql = "UPDATE Games_Roms SET PlatformId=@platformid, GameId=@gameid, Name=@name, Size=@size, DevelopmentStatus=@developmentstatus, Attributes=@Attributes, RomType=@romtype, RomTypeMedia=@romtypemedia, MediaLabel=@medialabel, MetadataSource=@metadatasource, MetadataGameName=@metadatagamename, MetadataVersion=@metadataversion WHERE Id=@id;";
sql = "UPDATE Games_Roms SET PlatformId=@platformid, GameId=@gameid, Name=@name, Size=@size, DevelopmentStatus=@developmentstatus, Attributes=@Attributes, RomType=@romtype, RomTypeMedia=@romtypemedia, MediaLabel=@medialabel, MetadataSource=@metadatasource, MetadataGameName=@metadatagamename, MetadataVersion=@metadataversion, RomDataVersion=@romdataversion WHERE Id=@id;";
dbDict.Add("id", UpdateId);
}
dbDict.Add("platformid", Common.ReturnValueIfNull(determinedPlatform.Id, 0));
@@ -322,6 +327,7 @@ namespace gaseous_server.Classes
dbDict.Add("metadatagamename", discoveredSignature.Game.Name);
dbDict.Add("metadataversion", 2);
dbDict.Add("libraryid", library.Id);
dbDict.Add("romdataversion", 2);
if (discoveredSignature.Rom.Attributes != null)
{
@@ -348,7 +354,8 @@ namespace gaseous_server.Classes
if (UpdateId == 0)
{
romId = (long)romInsert.Rows[0][0];
} else
}
else
{
romId = UpdateId;
}
@@ -362,73 +369,73 @@ namespace gaseous_server.Classes
return romId;
}
public static string ComputeROMPath(long RomId)
{
Classes.Roms.GameRomItem rom = Classes.Roms.GetRom(RomId);
// get metadata
IGDB.Models.Platform platform = gaseous_server.Classes.Metadata.Platforms.GetPlatform(rom.PlatformId);
IGDB.Models.Game game = gaseous_server.Classes.Metadata.Games.GetGame(rom.GameId, false, false, false);
// build path
string platformSlug = "Unknown Platform";
if (platform != null)
{
platformSlug = platform.Slug;
}
string gameSlug = "Unknown Title";
if (game != null)
{
gameSlug = game.Slug;
}
string DestinationPath = Path.Combine(GameLibrary.GetDefaultLibrary.Path, gameSlug, platformSlug);
if (!Directory.Exists(DestinationPath))
{
Directory.CreateDirectory(DestinationPath);
}
string DestinationPathName = Path.Combine(DestinationPath, rom.Name);
return DestinationPathName;
}
public static bool MoveGameFile(long RomId)
{
public static string ComputeROMPath(long RomId)
{
Classes.Roms.GameRomItem rom = Classes.Roms.GetRom(RomId);
string romPath = rom.Path;
// get metadata
IGDB.Models.Platform platform = gaseous_server.Classes.Metadata.Platforms.GetPlatform(rom.PlatformId);
IGDB.Models.Game game = gaseous_server.Classes.Metadata.Games.GetGame(rom.GameId, false, false, false);
// build path
string platformSlug = "Unknown Platform";
if (platform != null)
{
platformSlug = platform.Slug;
}
string gameSlug = "Unknown Title";
if (game != null)
{
gameSlug = game.Slug;
}
string DestinationPath = Path.Combine(GameLibrary.GetDefaultLibrary.Path, gameSlug, platformSlug);
if (!Directory.Exists(DestinationPath))
{
Directory.CreateDirectory(DestinationPath);
}
string DestinationPathName = Path.Combine(DestinationPath, rom.Name);
return DestinationPathName;
}
public static bool MoveGameFile(long RomId)
{
Classes.Roms.GameRomItem rom = Classes.Roms.GetRom(RomId);
string romPath = rom.Path;
if (File.Exists(romPath))
{
string DestinationPath = ComputeROMPath(RomId);
if (romPath == DestinationPath)
{
Logging.Log(Logging.LogType.Debug, "Move Game ROM", "Destination path is the same as the current path - aborting");
if (romPath == DestinationPath)
{
Logging.Log(Logging.LogType.Debug, "Move Game ROM", "Destination path is the same as the current path - aborting");
return true;
}
else
{
Logging.Log(Logging.LogType.Information, "Move Game ROM", "Moving " + romPath + " to " + DestinationPath);
if (File.Exists(DestinationPath))
{
Logging.Log(Logging.LogType.Information, "Move Game ROM", "A file with the same name exists at the destination - aborting");
}
else
{
Logging.Log(Logging.LogType.Information, "Move Game ROM", "Moving " + romPath + " to " + DestinationPath);
if (File.Exists(DestinationPath))
{
Logging.Log(Logging.LogType.Information, "Move Game ROM", "A file with the same name exists at the destination - aborting");
return false;
}
else
{
File.Move(romPath, DestinationPath);
}
else
{
File.Move(romPath, DestinationPath);
// update the db
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "UPDATE Games_Roms SET Path=@path WHERE Id=@id";
Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("id", RomId);
dbDict.Add("path", DestinationPath);
db.ExecuteCMD(sql, dbDict);
// update the db
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "UPDATE Games_Roms SET Path=@path WHERE Id=@id";
Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("id", RomId);
dbDict.Add("path", DestinationPath);
db.ExecuteCMD(sql, dbDict);
return true;
}
}
}
}
}
else
{
@@ -437,8 +444,8 @@ namespace gaseous_server.Classes
}
}
public void OrganiseLibrary()
{
public void OrganiseLibrary()
{
Logging.Log(Logging.LogType.Information, "Organise Library", "Starting default library organisation");
GameLibrary.LibraryItem library = GameLibrary.GetDefaultLibrary;
@@ -451,19 +458,19 @@ namespace gaseous_server.Classes
DataTable romDT = db.ExecuteCMD(sql, dbDict);
if (romDT.Rows.Count > 0)
{
for (int i = 0; i < romDT.Rows.Count; i++)
{
{
for (int i = 0; i < romDT.Rows.Count; i++)
{
SetStatus(i, romDT.Rows.Count, "Processing file " + romDT.Rows[i]["name"]);
Logging.Log(Logging.LogType.Information, "Organise Library", "(" + i + "/" + romDT.Rows.Count + ") Processing ROM " + romDT.Rows[i]["name"]);
Logging.Log(Logging.LogType.Information, "Organise Library", "(" + i + "/" + romDT.Rows.Count + ") Processing ROM " + romDT.Rows[i]["name"]);
long RomId = (long)romDT.Rows[i]["id"];
MoveGameFile(RomId);
}
}
MoveGameFile(RomId);
}
}
ClearStatus();
// clean up empty directories
DeleteOrphanedDirectories(GameLibrary.GetDefaultLibrary.Path);
// clean up empty directories
DeleteOrphanedDirectories(GameLibrary.GetDefaultLibrary.Path);
Logging.Log(Logging.LogType.Information, "Organise Library", "Finsihed default library organisation");
}
@@ -476,7 +483,7 @@ namespace gaseous_server.Classes
string[] files = Directory.GetFiles(directory);
string[] directories = Directory.GetDirectories(directory);
if (files.Length == 0 &&
directories.Length == 0)
{
@@ -563,7 +570,7 @@ namespace gaseous_server.Classes
public void LibrarySpecificScan(GameLibrary.LibraryItem library)
{
Logging.Log(Logging.LogType.Information, "Library Scan", "Starting scan of library: " + library.Name);
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
@@ -632,7 +639,7 @@ namespace gaseous_server.Classes
{
// file is not in database - process it
Logging.Log(Logging.LogType.Information, "Library Scan", "Orphaned file found in library: " + LibraryFile);
Common.hashObject hash = new Common.hashObject(LibraryFile);
FileInfo fi = new FileInfo(LibraryFile);
@@ -644,8 +651,8 @@ namespace gaseous_server.Classes
// get discovered platform
long PlatformId;
IGDB.Models.Platform determinedPlatform;
if (sig.Flags.IGDBPlatformId == null || sig.Flags.IGDBPlatformId == 0 )
if (sig.Flags.IGDBPlatformId == null || sig.Flags.IGDBPlatformId == 0)
{
// no platform discovered in the signature
PlatformId = library.DefaultPlatformId;
@@ -770,8 +777,8 @@ namespace gaseous_server.Classes
// get discovered platform
long PlatformId;
IGDB.Models.Platform determinedPlatform;
if (sig.Flags.IGDBPlatformId == null || sig.Flags.IGDBPlatformId == 0 )
if (sig.Flags.IGDBPlatformId == null || sig.Flags.IGDBPlatformId == 0)
{
// no platform discovered in the signature
PlatformId = library.DefaultPlatformId;

View File

@@ -5,8 +5,8 @@ using IGDB.Models;
namespace gaseous_server.Classes.Metadata
{
public class Games
{
public class Games
{
const string fieldList = "fields age_ratings,aggregated_rating,aggregated_rating_count,alternative_names,artworks,bundles,category,checksum,collection,collections,cover,created_at,dlcs,expanded_games,expansions,external_games,first_release_date,follows,forks,franchise,franchises,game_engines,game_localizations,game_modes,genres,hypes,involved_companies,keywords,language_supports,multiplayer_modes,name,parent_game,platforms,player_perspectives,ports,rating,rating_count,release_dates,remakes,remasters,screenshots,similar_games,slug,standalone_expansions,status,storyline,summary,tags,themes,total_rating,total_rating_count,updated_at,url,version_parent,version_title,videos,websites;";
public Games()
@@ -15,9 +15,9 @@ namespace gaseous_server.Classes.Metadata
}
public class InvalidGameId : Exception
{
{
public InvalidGameId(long Id) : base("Unable to find Game by id " + Id)
{}
{ }
}
public static Game? GetGame(long Id, bool getAllMetadata, bool followSubGames, bool forceRefresh)
@@ -78,15 +78,17 @@ namespace gaseous_server.Classes.Metadata
if (cacheStatus == Storage.CacheStatus.Current) { cacheStatus = Storage.CacheStatus.Expired; }
}
// set up where clause
string WhereClause = "";
string searchField = "";
switch (searchUsing)
{
case SearchUsing.id:
WhereClause = "where id = " + searchValue;
searchField = "id";
break;
case SearchUsing.slug:
WhereClause = "where slug = " + searchValue;
WhereClause = "where slug = \"" + searchValue + "\"";
searchField = "slug";
break;
default:
throw new Exception("Invalid search type");
@@ -110,11 +112,11 @@ namespace gaseous_server.Classes.Metadata
catch (Exception ex)
{
Logging.Log(Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
returnValue = Storage.GetCacheValue<Game>(returnValue, "id", (long)searchValue);
returnValue = Storage.GetCacheValue<Game>(returnValue, searchField, searchValue);
}
return returnValue;
case Storage.CacheStatus.Current:
returnValue = Storage.GetCacheValue<Game>(returnValue, "id", (long)searchValue);
returnValue = Storage.GetCacheValue<Game>(returnValue, searchField, searchValue);
UpdateSubClasses(returnValue, false, false, false);
return returnValue;
default:
@@ -285,7 +287,7 @@ namespace gaseous_server.Classes.Metadata
{
try
{
Screenshot GameScreenshot = Screenshots.GetScreenshot(ScreenshotId, Config.LibraryConfiguration.LibraryMetadataDirectory_Game(Game), forceRefresh);
Screenshot GameScreenshot = Screenshots.GetScreenshot(ScreenshotId, Config.LibraryConfiguration.LibraryMetadataDirectory_Game(Game), forceRefresh);
}
catch (Exception ex)
{
@@ -347,7 +349,7 @@ namespace gaseous_server.Classes.Metadata
// get missing metadata from parent if this is a port
if (result.Category == Category.Port)
{
{
if (result.Summary == null)
{
if (result.ParentGame != null)
@@ -364,7 +366,7 @@ namespace gaseous_server.Classes.Metadata
return result;
}
public static void AssignAllGamesToPlatformIdZero()
{
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
@@ -428,7 +430,7 @@ namespace gaseous_server.Classes.Metadata
}
string sql = "SELECT Game.Id, Game.`Name`, Game.Slug, Relation_Game_Platforms.PlatformsId AS PlatformsId, Game.Summary FROM gaseous.Game JOIN Relation_Game_Platforms ON Game.Id = Relation_Game_Platforms.GameId WHERE " + whereClause + ";";
// get Game metadata
Game[]? results = new Game[0];
@@ -439,7 +441,8 @@ namespace gaseous_server.Classes.Metadata
DataTable data = db.ExecuteCMD(sql, dbDict);
foreach (DataRow row in data.Rows)
{
Game game = new Game{
Game game = new Game
{
Id = (long)row["Id"],
Name = (string)Common.ReturnValueIfNull(row["Name"], ""),
Slug = (string)Common.ReturnValueIfNull(row["Slug"], ""),
@@ -476,12 +479,12 @@ namespace gaseous_server.Classes.Metadata
searchBody = "where platforms = (" + PlatformId + ") & name ~ \"" + SearchString + "\";";
break;
}
// check search cache
Game[]? games = Communications.GetSearchCache<Game[]?>(searchFields, searchBody);
if (games == null)
{
{
// cache miss
// get Game metadata
Communications comms = new Communications();
@@ -513,7 +516,7 @@ namespace gaseous_server.Classes.Metadata
foreach (DataRow row in data.Rows)
{
KeyValuePair<long, string> valuePair = new KeyValuePair<long, string>((long)row["PlatformId"], (string)row["Name"]);
platforms.Add(valuePair);
platforms.Add(valuePair);
}
return platforms;
@@ -533,7 +536,7 @@ namespace gaseous_server.Classes.Metadata
{
}
public MinimalGameItem(Game gameObject)
{
this.Id = gameObject.Id;

View File

@@ -7,16 +7,16 @@ using IGDB.Models;
namespace gaseous_server.Classes.Metadata
{
public class Platforms
{
{
const string fieldList = "fields abbreviation,alternative_name,category,checksum,created_at,generation,name,platform_family,platform_logo,slug,summary,updated_at,url,versions,websites;";
public Platforms()
{
{
}
}
public static Platform? GetPlatform(long Id, bool forceRefresh = false)
{
{
if (Id == 0)
{
Platform returnValue = new Platform();
@@ -44,7 +44,7 @@ namespace gaseous_server.Classes.Metadata
Task<Platform> RetVal = _GetPlatform(SearchUsing.id, Id, forceRefresh);
return RetVal.Result;
}
catch(Exception ex)
catch (Exception ex)
{
Logging.Log(Logging.LogType.Warning, "Metadata", "An error occurred fetching Platform Id " + Id, ex);
return null;
@@ -52,14 +52,14 @@ namespace gaseous_server.Classes.Metadata
}
}
public static Platform GetPlatform(string Slug, bool forceRefresh = false)
{
Task<Platform> RetVal = _GetPlatform(SearchUsing.slug, Slug, forceRefresh);
return RetVal.Result;
}
public static Platform GetPlatform(string Slug, bool forceRefresh = false)
{
Task<Platform> RetVal = _GetPlatform(SearchUsing.slug, Slug, forceRefresh);
return RetVal.Result;
}
private static async Task<Platform> _GetPlatform(SearchUsing searchUsing, object searchValue, bool forceRefresh)
{
private static async Task<Platform> _GetPlatform(SearchUsing searchUsing, object searchValue, bool forceRefresh)
{
// check database first
Storage.CacheStatus? cacheStatus = new Storage.CacheStatus();
if (searchUsing == SearchUsing.id)
@@ -78,13 +78,16 @@ namespace gaseous_server.Classes.Metadata
// set up where clause
string WhereClause = "";
string searchField = "";
switch (searchUsing)
{
case SearchUsing.id:
WhereClause = "where id = " + searchValue;
searchField = "id";
break;
case SearchUsing.slug:
WhereClause = "where slug = " + searchValue;
WhereClause = "where slug = \"" + searchValue + "\"";
searchField = "slug";
break;
default:
throw new Exception("Invalid search type");
@@ -111,10 +114,10 @@ namespace gaseous_server.Classes.Metadata
catch (Exception ex)
{
Logging.Log(Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
return Storage.GetCacheValue<Platform>(returnValue, "id", (long)searchValue);
return Storage.GetCacheValue<Platform>(returnValue, searchField, searchValue);
}
case Storage.CacheStatus.Current:
return Storage.GetCacheValue<Platform>(returnValue, "id", (long)searchValue);
return Storage.GetCacheValue<Platform>(returnValue, searchField, searchValue);
default:
throw new Exception("How did you get here?");
}
@@ -158,11 +161,12 @@ namespace gaseous_server.Classes.Metadata
{
Logging.Log(Logging.LogType.Information, "Platform Map", "Importing " + platform.Name + " from predefined data.");
// doesn't exist - add it
item = new Models.PlatformMapping.PlatformMapItem{
item = new Models.PlatformMapping.PlatformMapItem
{
IGDBId = (long)platform.Id,
IGDBName = platform.Name,
IGDBSlug = platform.Slug,
AlternateNames = new List<string>{ platform.AlternativeName }
AlternateNames = new List<string> { platform.AlternativeName }
};
Models.PlatformMapping.WritePlatformMap(item, false, true);
}

View File

@@ -4,29 +4,32 @@ using gaseous_signature_parser.models.RomSignatureObject;
using static gaseous_server.Classes.RomMediaGroup;
using gaseous_server.Classes.Metadata;
using IGDB.Models;
using static HasheousClient.Models.FixMatchModel;
using NuGet.Protocol.Core.Types;
using static gaseous_server.Classes.FileSignature;
namespace gaseous_server.Classes
{
public class Roms
{
public class InvalidRomId : Exception
{
public InvalidRomId(long Id) : base("Unable to find ROM by id " + Id)
{}
}
{
public InvalidRomId(long Id) : base("Unable to find ROM by id " + Id)
{ }
}
public static GameRomObject GetRoms(long GameId, long PlatformId = -1, string NameSearch = "", int pageNumber = 0, int pageSize = 0, string userid = "")
{
GameRomObject GameRoms = new GameRomObject();
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "";
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "";
string sqlCount = "";
string sqlPlatform = "";
Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("id", GameId);
dbDict.Add("id", GameId);
dbDict.Add("userid", userid);
string NameSearchWhere = "";
if (NameSearch.Length > 0)
{
@@ -37,13 +40,16 @@ namespace gaseous_server.Classes
// platform query
sqlPlatform = "SELECT DISTINCT Games_Roms.PlatformId, Platform.`Name` FROM Games_Roms LEFT JOIN Platform ON Games_Roms.PlatformId = Platform.Id WHERE GameId = @id ORDER BY Platform.`Name`;";
if (PlatformId == -1) {
if (PlatformId == -1)
{
// data query
sql = "SELECT DISTINCT Games_Roms.*, Platform.`Name` AS platformname, GameState.RomId AS SavedStateRomId FROM Games_Roms LEFT JOIN Platform ON Games_Roms.PlatformId = Platform.Id LEFT JOIN GameState ON (Games_Roms.Id = GameState.RomId AND GameState.UserId = @userid AND GameState.IsMediaGroup = 0) WHERE Games_Roms.GameId = @id" + NameSearchWhere + " ORDER BY Platform.`Name`, Games_Roms.`Name` LIMIT 1000;";
// count query
sqlCount = "SELECT COUNT(Games_Roms.Id) AS RomCount FROM Games_Roms WHERE Games_Roms.GameId = @id" + NameSearchWhere + ";";
} else {
}
else
{
// data query
sql = "SELECT DISTINCT Games_Roms.*, Platform.`Name` AS platformname, GameState.RomId AS SavedStateRomId FROM Games_Roms LEFT JOIN Platform ON Games_Roms.PlatformId = Platform.Id LEFT JOIN GameState ON (Games_Roms.Id = GameState.RomId AND GameState.UserId = @userid AND GameState.IsMediaGroup = 0) WHERE Games_Roms.GameId = @id AND Games_Roms.PlatformId = @platformid" + NameSearchWhere + " ORDER BY Platform.`Name`, Games_Roms.`Name` LIMIT 1000;";
@@ -52,12 +58,12 @@ namespace gaseous_server.Classes
dbDict.Add("platformid", PlatformId);
}
DataTable romDT = db.ExecuteCMD(sql, dbDict);
DataTable romDT = db.ExecuteCMD(sql, dbDict);
Dictionary<string, object> rowCount = db.ExecuteCMDDict(sqlCount, dbDict)[0];
DataTable platformDT = db.ExecuteCMD(sqlPlatform, dbDict);
if (romDT.Rows.Count > 0)
{
if (romDT.Rows.Count > 0)
{
// set count of roms
GameRoms.Count = int.Parse((string)rowCount["RomCount"]);
@@ -73,12 +79,12 @@ namespace gaseous_server.Classes
}
return GameRoms;
}
else
{
throw new Games.InvalidGameId(GameId);
}
}
}
else
{
throw new Games.InvalidGameId(GameId);
}
}
public static GameRomItem GetRom(long RomId)
{
@@ -108,18 +114,65 @@ namespace gaseous_server.Classes
// ensure metadata for gameid is present
IGDB.Models.Game game = Classes.Metadata.Games.GetGame(GameId, false, false, false);
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "UPDATE Games_Roms SET PlatformId=@platformid, GameId=@gameid WHERE Id = @id";
Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("id", RomId);
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "UPDATE Games_Roms SET PlatformId=@platformid, GameId=@gameid WHERE Id = @id";
Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("id", RomId);
dbDict.Add("platformid", PlatformId);
dbDict.Add("gameid", GameId);
db.ExecuteCMD(sql, dbDict);
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;
}
}
public static void DeleteRom(long RomId)
{
@@ -137,7 +190,7 @@ namespace gaseous_server.Classes
dbDict.Add("id", RomId);
db.ExecuteCMD(sql, dbDict);
}
}
}
private static GameRomItem BuildRom(DataRow romDR)
{
@@ -150,28 +203,44 @@ 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
{
Id = (long)romDR["id"],
PlatformId = (long)romDR["platformid"],
{
Id = (long)romDR["id"],
PlatformId = (long)romDR["platformid"],
Platform = (string)romDR["platformname"],
GameId = (long)romDR["gameid"],
Name = (string)romDR["name"],
Size = (long)romDR["size"],
Crc = ((string)romDR["crc"]).ToLower(),
Md5 = ((string)romDR["md5"]).ToLower(),
Sha1 = ((string)romDR["sha1"]).ToLower(),
DevelopmentStatus = (string)romDR["developmentstatus"],
Attributes = Newtonsoft.Json.JsonConvert.DeserializeObject<List<KeyValuePair<string, object>>>((string)Common.ReturnValueIfNull(romDR["attributes"], "[ ]")),
RomType = (HasheousClient.Models.LookupResponseModel.RomItem.RomTypes)(int)romDR["romtype"],
RomTypeMedia = (string)romDR["romtypemedia"],
MediaLabel = (string)romDR["medialabel"],
Path = (string)romDR["path"],
GameId = (long)romDR["gameid"],
Name = (string)romDR["name"],
Size = (long)romDR["size"],
Crc = ((string)romDR["crc"]).ToLower(),
Md5 = ((string)romDR["md5"]).ToLower(),
Sha1 = ((string)romDR["sha1"]).ToLower(),
DevelopmentStatus = (string)romDR["developmentstatus"],
Attributes = romAttributes,
RomType = (HasheousClient.Models.SignatureModel.RomItem.RomTypes)(int)romDR["romtype"],
RomTypeMedia = (string)romDR["romtypemedia"],
MediaLabel = (string)romDR["medialabel"],
Path = (string)romDR["path"],
SignatureSource = (gaseous_server.Models.Signatures_Games.RomItem.SignatureSourceType)(Int32)romDR["metadatasource"],
SignatureSourceGameTitle = (string)Common.ReturnValueIfNull(romDR["MetadataGameName"], ""),
HasSaveStates = hasSaveStates,
Library = GameLibrary.GetLibrary((int)romDR["LibraryId"])
};
};
// check for a web emulator and update the romItem
foreach (Models.PlatformMapping.PlatformMapItem platformMapping in Models.PlatformMapping.PlatformMap)
@@ -185,8 +254,8 @@ namespace gaseous_server.Classes
}
}
return romItem;
}
return romItem;
}
public class GameRomObject
{
@@ -194,17 +263,17 @@ namespace gaseous_server.Classes
public int Count { get; set; }
}
public class GameRomItem : HasheousClient.Models.LookupResponseModel.RomItem
public class GameRomItem : HasheousClient.Models.SignatureModel.RomItem
{
public long PlatformId { get; set; }
public string Platform { get; set; }
public Models.PlatformMapping.PlatformMapItem.WebEmulatorItem? Emulator { get; set; }
public long GameId { get; set; }
public Models.PlatformMapping.PlatformMapItem.WebEmulatorItem? Emulator { get; set; }
public long GameId { get; set; }
public string? Path { get; set; }
public string? SignatureSourceGameTitle { get; set;}
public string? SignatureSourceGameTitle { get; set; }
public bool HasSaveStates { get; set; } = false;
public GameLibrary.LibraryItem Library { get; set; }
}
}
}
}
}

View File

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

View File

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

@@ -70,7 +70,8 @@ namespace gaseous_server.Controllers
[HttpGet]
[Route("Version")]
[ProducesResponseType(StatusCodes.Status200OK)]
public Version GetSystemVersion() {
public Version GetSystemVersion()
{
return Assembly.GetExecutingAssembly().GetName().Version;
}
@@ -80,18 +81,19 @@ namespace gaseous_server.Controllers
[Route("VersionFile")]
[AllowAnonymous]
[ProducesResponseType(StatusCodes.Status200OK)]
public FileContentResult GetSystemVersionAsFile() {
public FileContentResult GetSystemVersionAsFile()
{
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
// get age ratings dictionary
Dictionary<int, string> ClassificationBoardsStrings = new Dictionary<int, string>();
foreach(IGDB.Models.AgeRatingCategory ageRatingCategory in Enum.GetValues(typeof(IGDB.Models.AgeRatingCategory)) )
foreach (IGDB.Models.AgeRatingCategory ageRatingCategory in Enum.GetValues(typeof(IGDB.Models.AgeRatingCategory)))
{
ClassificationBoardsStrings.Add((int)ageRatingCategory, ageRatingCategory.ToString());
}
Dictionary<int, string> AgeRatingsStrings = new Dictionary<int, string>();
foreach(IGDB.Models.AgeRatingTitle ageRatingTitle in Enum.GetValues(typeof(IGDB.Models.AgeRatingTitle)) )
foreach (IGDB.Models.AgeRatingTitle ageRatingTitle in Enum.GetValues(typeof(IGDB.Models.AgeRatingTitle)))
{
AgeRatingsStrings.Add((int)ageRatingTitle, ageRatingTitle.ToString());
}
@@ -99,13 +101,16 @@ namespace gaseous_server.Controllers
string ver = "var AppVersion = \"" + Assembly.GetExecutingAssembly().GetName().Version.ToString() + "\";" + Environment.NewLine +
"var DBSchemaVersion = \"" + db.GetDatabaseSchemaVersion() + "\";" + Environment.NewLine +
"var FirstRunStatus = " + Config.ReadSetting<string>("FirstRunStatus", "0") + ";" + Environment.NewLine +
"var AgeRatingBoardsStrings = " + JsonSerializer.Serialize(ClassificationBoardsStrings, new JsonSerializerOptions{
"var AgeRatingBoardsStrings = " + JsonSerializer.Serialize(ClassificationBoardsStrings, new JsonSerializerOptions
{
WriteIndented = true
}) + ";" + Environment.NewLine +
"var AgeRatingStrings = " + JsonSerializer.Serialize(AgeRatingsStrings, new JsonSerializerOptions{
"var AgeRatingStrings = " + JsonSerializer.Serialize(AgeRatingsStrings, new JsonSerializerOptions
{
WriteIndented = true
}) + ";" + Environment.NewLine +
"var AgeRatingGroups = " + JsonSerializer.Serialize(AgeGroups.AgeGroupingsFlat, new JsonSerializerOptions{
"var AgeRatingGroups = " + JsonSerializer.Serialize(AgeGroups.AgeGroupingsFlat, new JsonSerializerOptions
{
WriteIndented = true
}) + ";" + Environment.NewLine +
"var emulatorDebugMode = " + Config.ReadSetting<string>("emulatorDebugMode", false.ToString()).ToLower() + ";";
@@ -159,7 +164,7 @@ namespace gaseous_server.Controllers
{
// update task enabled
Logging.Log(Logging.LogType.Information, "Update Background Task", "Updating task " + TaskConfiguration.Task + " with enabled value " + TaskConfiguration.Enabled.ToString());
Config.SetSetting<string>("Enabled_" + TaskConfiguration.Task, TaskConfiguration.Enabled.ToString());
// update existing process
@@ -170,12 +175,12 @@ namespace gaseous_server.Controllers
item.Enabled(Boolean.Parse(TaskConfiguration.Enabled.ToString()));
}
}
// update task interval
if (TaskConfiguration.Interval >= taskItem.MinimumAllowedInterval)
{
Logging.Log(Logging.LogType.Information, "Update Background Task", "Updating task " + TaskConfiguration.Task + " with new interval " + TaskConfiguration.Interval);
Config.SetSetting<string>("Interval_" + TaskConfiguration.Task, TaskConfiguration.Interval.ToString());
// update existing process
@@ -194,7 +199,7 @@ namespace gaseous_server.Controllers
// update task weekdays
Logging.Log(Logging.LogType.Information, "Update Background Task", "Updating task " + TaskConfiguration.Task + " with new weekdays " + String.Join(", ", TaskConfiguration.AllowedDays));
Config.SetSetting<string>("AllowedDays_" + TaskConfiguration.Task, Newtonsoft.Json.JsonConvert.SerializeObject(TaskConfiguration.AllowedDays));
// update existing process
@@ -208,7 +213,7 @@ namespace gaseous_server.Controllers
// update task hours
Logging.Log(Logging.LogType.Information, "Update Background Task", "Updating task " + TaskConfiguration.Task + " with new hours " + TaskConfiguration.AllowedStartHours + ":" + TaskConfiguration.AllowedStartMinutes.ToString("00") + " to " + TaskConfiguration.AllowedEndHours + ":" + TaskConfiguration.AllowedEndMinutes.ToString("00"));
Config.SetSetting<string>("AllowedStartHours_" + TaskConfiguration.Task, TaskConfiguration.AllowedStartHours.ToString());
Config.SetSetting<string>("AllowedStartMinutes_" + TaskConfiguration.Task, TaskConfiguration.AllowedStartMinutes.ToString());
Config.SetSetting<string>("AllowedEndHours_" + TaskConfiguration.Task, TaskConfiguration.AllowedEndHours.ToString());
@@ -225,7 +230,7 @@ namespace gaseous_server.Controllers
item.AllowedEndMinutes = TaskConfiguration.AllowedEndMinutes;
}
}
}
else
{
@@ -251,10 +256,18 @@ namespace gaseous_server.Controllers
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult GetSystemSettings()
{
SystemSettingsModel systemSettingsModel = new SystemSettingsModel{
SystemSettingsModel systemSettingsModel = new SystemSettingsModel
{
AlwaysLogToDisk = Config.LoggingConfiguration.AlwaysLogToDisk,
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);
@@ -273,6 +286,10 @@ namespace gaseous_server.Controllers
Config.LoggingConfiguration.AlwaysLogToDisk = model.AlwaysLogToDisk;
Config.LoggingConfiguration.LogRetention = model.MinimumLogRetentionPeriod;
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();
}
@@ -281,7 +298,8 @@ namespace gaseous_server.Controllers
private SystemInfo.PathItem GetDisk(string Path)
{
SystemInfo.PathItem pathItem = new SystemInfo.PathItem {
SystemInfo.PathItem pathItem = new SystemInfo.PathItem
{
LibraryPath = Path,
SpaceUsed = Common.DirSize(new DirectoryInfo(Path)),
SpaceAvailable = new DriveInfo(Path).AvailableFreeSpace,
@@ -293,11 +311,12 @@ namespace gaseous_server.Controllers
public class SystemInfo
{
public Version ApplicationVersion {
public Version ApplicationVersion
{
get
{
return Assembly.GetExecutingAssembly().GetName().Version;
}
{
return Assembly.GetExecutingAssembly().GetName().Version;
}
}
public List<PathItem>? Paths { get; set; }
public long DatabaseSize { get; set; }
@@ -352,7 +371,7 @@ namespace gaseous_server.Controllers
this.DefaultAllowedEndHours = 23;
this.DefaultAllowedEndMinutes = 59;
break;
case ProcessQueue.QueueItemType.TitleIngestor:
this._UserManageable = true;
this.DefaultInterval = 1;
@@ -589,7 +608,8 @@ namespace gaseous_server.Controllers
}
private bool _UserManageable;
public bool UserManageable => _UserManageable;
public int Interval {
public int Interval
{
get
{
return int.Parse(Config.ReadSetting<string>("Interval_" + Task, DefaultInterval.ToString()));
@@ -642,7 +662,7 @@ namespace gaseous_server.Controllers
public List<ProcessQueue.QueueItemType> Blocks
{
get
{
{
if (_Blocks.Contains(ProcessQueue.QueueItemType.All))
{
List<ProcessQueue.QueueItemType> blockList = new List<ProcessQueue.QueueItemType>();
@@ -710,5 +730,14 @@ namespace gaseous_server.Controllers
public bool AlwaysLogToDisk { get; set; }
public int MinimumLogRetentionPeriod { get; set; }
public bool EmulatorDebugMode { get; set; }
public SignatureSourceItem SignatureSource { get; set; }
public class SignatureSourceItem
{
public HasheousClient.Models.MetadataModel.SignatureSources Source { get; set; }
public string HasheousHost { get; set; }
public string HasheousAPIKey { get; set; }
public bool HasheousSubmitFixes { get; set; }
}
}
}

View File

@@ -4,12 +4,36 @@ using gaseous_signature_parser.models.RomSignatureObject;
namespace gaseous_server.Models
{
public class Signatures_Games : HasheousClient.Models.LookupResponseModel
public class Signatures_Games : HasheousClient.Models.SignatureModel
{
public Signatures_Games()
{
}
[JsonIgnore]
public int Score
{
get
{
int _score = 0;
if (Game != null)
{
_score = _score + Game.Score;
}
if (Rom != null)
{
_score = _score + Rom.Score;
}
return _score;
}
}
public GameItem Game = new GameItem();
public RomItem Rom = new RomItem();
public SignatureFlags Flags = new SignatureFlags();
public class SignatureFlags
@@ -18,6 +42,213 @@ namespace gaseous_server.Models
public string IGDBPlatformName { get; set; }
public long IGDBGameId { get; set; }
}
public class GameItem : HasheousClient.Models.SignatureModel.GameItem
{
public GameItem()
{
}
[JsonIgnore]
public int Score
{
get
{
// calculate a score based on the availablility of data
int _score = 0;
var properties = this.GetType().GetProperties();
foreach (var prop in properties)
{
if (prop.GetGetMethod() != null)
{
switch (prop.Name.ToLower())
{
case "id":
case "score":
break;
case "name":
case "year":
case "publisher":
case "system":
if (prop.PropertyType == typeof(string))
{
if (prop.GetValue(this) != null)
{
string propVal = prop.GetValue(this).ToString();
if (propVal.Length > 0)
{
_score = _score + 10;
}
}
}
break;
default:
if (prop.PropertyType == typeof(string))
{
if (prop.GetValue(this) != null)
{
string propVal = prop.GetValue(this).ToString();
if (propVal.Length > 0)
{
_score = _score + 1;
}
}
}
break;
}
}
}
return _score;
}
}
}
public class RomItem : HasheousClient.Models.SignatureModel.RomItem
{
[JsonIgnore]
public int Score
{
get
{
// calculate a score based on the availablility of data
int _score = 0;
var properties = this.GetType().GetProperties();
foreach (var prop in properties)
{
if (prop.GetGetMethod() != null)
{
switch (prop.Name.ToLower())
{
case "name":
case "size":
case "crc":
case "developmentstatus":
case "flags":
case "attributes":
case "romtypemedia":
case "medialabel":
if (prop.PropertyType == typeof(string) || prop.PropertyType == typeof(Int64) || prop.PropertyType == typeof(List<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
};
Logging.Log(Logging.LogType.Debug, "Signature Import", "Processing TOSEC files");
tIngest.Import(Path.Combine(Config.LibraryConfiguration.LibrarySignatureImportDirectory, "TOSEC"), gaseous_signature_parser.parser.SignatureParser.TOSEC);
Logging.Log(Logging.LogType.Debug, "Signature Import", "Processing MAME Arcade files");
tIngest.Import(Path.Combine(Config.LibraryConfiguration.LibrarySignatureImportDirectory, "MAME Arcade"), gaseous_signature_parser.parser.SignatureParser.MAMEArcade);
foreach (int i in Enum.GetValues(typeof(gaseous_signature_parser.parser.SignatureParser)))
{
gaseous_signature_parser.parser.SignatureParser parserType = (gaseous_signature_parser.parser.SignatureParser)i;
if (
parserType != gaseous_signature_parser.parser.SignatureParser.Auto &&
parserType != gaseous_signature_parser.parser.SignatureParser.Unknown
)
{
Logging.Log(Logging.LogType.Debug, "Signature Import", "Processing " + parserType + " files");
string SignaturePath = Path.Combine(Config.LibraryConfiguration.LibrarySignaturesDirectory, parserType.ToString());
string SignatureProcessedPath = Path.Combine(Config.LibraryConfiguration.LibrarySignaturesProcessedDirectory, parserType.ToString());
if (!Directory.Exists(SignaturePath))
{
Directory.CreateDirectory(SignaturePath);
}
if (!Directory.Exists(SignatureProcessedPath))
{
Directory.CreateDirectory(SignatureProcessedPath);
}
tIngest.Import(SignaturePath, SignatureProcessedPath, parserType);
}
}
Logging.Log(Logging.LogType.Debug, "Signature Import", "Processing MAME MESS files");
tIngest.Import(Path.Combine(Config.LibraryConfiguration.LibrarySignatureImportDirectory, "MAME MESS"), gaseous_signature_parser.parser.SignatureParser.MAMEMess);
_SaveLastRunTime = true;
break;

View File

@@ -64,6 +64,7 @@ if (Directory.Exists(Config.LibraryConfiguration.LibraryUploadDirectory))
// kick off any delayed upgrade tasks
// run 1002 background updates in the background on every start
DatabaseMigration.BackgroundUpgradeTargetSchemaVersions.Add(1002);
DatabaseMigration.BackgroundUpgradeTargetSchemaVersions.Add(1022);
// start the task
ProcessQueue.QueueItem queueItem = new ProcessQueue.QueueItem(
ProcessQueue.QueueItemType.BackgroundDatabaseUpgrade,
@@ -273,7 +274,7 @@ using (var scope = app.Services.CreateScope())
{
var roleManager = scope.ServiceProvider.GetRequiredService<RoleStore>();
var roles = new[] { "Admin", "Gamer", "Player" };
foreach (var role in roles)
{
if (await roleManager.FindByNameAsync(role, CancellationToken.None) == null)
@@ -303,11 +304,11 @@ app.Use(async (context, next) =>
string correlationId = Guid.NewGuid().ToString();
CallContext.SetData("CorrelationId", correlationId);
CallContext.SetData("CallingProcess", context.Request.Method + ": " + context.Request.Path);
string userIdentity;
try
{
userIdentity = context.User.Claims.Where(x=>x.Type==System.Security.Claims.ClaimTypes.NameIdentifier).FirstOrDefault().Value;
userIdentity = context.User.Claims.Where(x => x.Type == System.Security.Claims.ClaimTypes.NameIdentifier).FirstOrDefault().Value;
}
catch
{
@@ -329,7 +330,7 @@ app.Use(async (context, next) =>
// - the server will not start while the RecoverAccount.txt file exists
string PasswordRecoveryFile = Path.Combine(Config.LibraryConfiguration.LibraryRootDirectory, "RecoverAccount.txt");
if (!String.IsNullOrEmpty(Environment.GetEnvironmentVariable("recoveraccount")))
{
{
if (File.Exists(PasswordRecoveryFile))
{
// password has already been set - do nothing and just exit
@@ -345,7 +346,7 @@ if (!String.IsNullOrEmpty(Environment.GetEnvironmentVariable("recoveraccount")))
string password = new string(Enumerable.Repeat(chars, length).Select(s => s[random.Next(s.Length)]).ToArray());
File.WriteAllText(PasswordRecoveryFile, password);
// reset the password
using (var scope = app.Services.CreateScope())
{

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,20 +16,20 @@
<DocumentationFile>bin\Release\net8.0\gaseous-server.xml</DocumentationFile>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Asp.Versioning.Mvc" Version="8.0.0" />
<PackageReference Include="Asp.Versioning.Mvc.ApiExplorer" Version="8.0.0" />
<PackageReference Include="gaseous-signature-parser" Version="2.1.0" />
<PackageReference Include="Asp.Versioning.Mvc" Version="8.1.0" />
<PackageReference Include="Asp.Versioning.Mvc.ApiExplorer" Version="8.1.0" />
<PackageReference Include="gaseous-signature-parser" Version="2.2.1" />
<PackageReference Include="gaseous.IGDB" Version="1.0.2" />
<PackageReference Include="hasheous-client" Version="0.2.0" />
<PackageReference Include="Magick.NET-Q8-AnyCPU" Version="13.5.0" />
<PackageReference Include="sharpcompress" Version="0.36.0" />
<PackageReference Include="Squid-Box.SevenZipSharp" Version="1.6.1.23" />
<PackageReference Include="hasheous-client" Version="1.0.2" />
<PackageReference Include="Magick.NET-Q8-AnyCPU" Version="13.7.0" />
<PackageReference Include="sharpcompress" Version="0.37.2" />
<PackageReference Include="Squid-Box.SevenZipSharp" Version="1.6.2.24" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
<PackageReference Include="MySqlConnector" Version="2.3.5" />
<PackageReference Include="Microsoft.AspNetCore.Identity.UI" Version="8.0.1" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.1" />
<PackageReference Include="MySqlConnector" Version="2.3.7" />
<PackageReference Include="Microsoft.AspNetCore.Identity.UI" Version="8.0.4" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.4" />
<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" />
</ItemGroup>
<ItemGroup>
@@ -39,6 +39,8 @@
<None Remove="Classes\" />
<None Remove="Classes\SignatureIngestors\" />
<None Remove="Support\" />
<None Remove="Support\Country.txt" />
<None Remove="Support\Language.txt" />
<None Remove="Support\Database\" />
<None Remove="Support\Database\MySQL\" />
<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-1020.sql" />
<None Remove="Support\Database\MySQL\gaseous-1021.sql" />
<None Remove="Support\Database\MySQL\gaseous-1022.sql" />
<None Remove="Classes\Metadata\" />
</ItemGroup>
<ItemGroup>
@@ -85,6 +88,8 @@
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</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-1001.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-1020.sql" />
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1021.sql" />
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1022.sql" />
</ItemGroup>
</Project>

View File

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

View File

@@ -1,7 +1,9 @@
<div id="properties_toc">
<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_attributes" name="properties_toc_item" onclick="SelectTab('attributes');" style="display: none;">Attributes</div>
<div id="properties_toc_archive" name="properties_toc_item" onclick="SelectTab('archive');" style="display: none;">
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_manage" name="properties_toc_item" onclick="SelectTab('manage');">Manage</div>-->
</div>
@@ -59,7 +61,7 @@
</div>
<div id="properties_bodypanel_attributes" name="properties_tab" style="display: none;">
</div>
<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>
</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>
</table>
</div>
@@ -144,7 +148,7 @@
document.getElementById('romDelete').style.display = 'none';
}
if (result.attributes.length > 0) {
if (result.attributes) {
document.getElementById('properties_bodypanel_attributes').appendChild(BuildAttributesTable(result.attributes, result.source));
document.getElementById('properties_bodypanel_archive_content').appendChild(BuildArchiveTable(result.attributes, result.source));
}
@@ -276,8 +280,8 @@
var aTable = document.createElement('table');
aTable.style.width = '100%';
for (var i = 0; i < attributes.length; i++) {
if (attributes[i].key != "ZipContents") {
for (const [key, value] of Object.entries(attributes)) {
if (key != "ZipContents") {
// show attributes button
document.getElementById('properties_toc_attributes').style.display = '';
var aRow = document.createElement('tr');
@@ -285,15 +289,15 @@
var aTitleCell = document.createElement('th');
aTitleCell.width = "25%";
if (sourceName == "TOSEC") {
aTitleCell.innerHTML = ConvertTOSECAttributeName(attributes[i].key);
aTitleCell.innerHTML = ConvertTOSECAttributeName(key);
} else {
aTitleCell.innerHTML = attributes[i].key;
aTitleCell.innerHTML = key;
}
aRow.appendChild(aTitleCell);
var aValueCell = document.createElement('td');
aValueCell.width = "75%";
aValueCell.innerHTML = attributes[i].value;
aValueCell.innerHTML = value;
aRow.appendChild(aValueCell);
aTable.appendChild(aRow);
@@ -304,13 +308,13 @@
}
function BuildArchiveTable(attributes, sourceName) {
for (var i = 0; i < attributes.length; i++) {
if (attributes[i].key == "ZipContents") {
var archiveContent = JSON.parse(attributes[i].value);
for (const [key, value] of Object.entries(attributes)) {
if (key == "ZipContents") {
var archiveContent = JSON.parse(value);
// show archive button
document.getElementById('properties_toc_archive').style.display = '';
var aTable = document.createElement('table');
aTable.className = 'romtable';
aTable.setAttribute('cellspacing', 0);
@@ -343,6 +347,17 @@
hRow.appendChild(aHashCell);
aBody.appendChild(hRow);
if (archiveContent[r].isSignatureSelector == true) {
var sigRow = document.createElement('tr');
var sigCell = document.createElement('td');
sigCell.setAttribute('colspan', 2);
sigCell.style.paddingLeft = '20px';
sigCell.innerHTML = "Hash used to identify this archive";
sigRow.appendChild(sigCell);
aBody.appendChild(sigRow);
}
aTable.appendChild(aBody);
}
}
@@ -354,18 +369,18 @@
function ConvertTOSECAttributeName(attributeName) {
var tosecAttributeNames = {
"cr": "Cracked",
"f" : "Fixed",
"h" : "Hacked",
"m" : "Modified",
"p" : "Pirated",
"t" : "Trained",
"f": "Fixed",
"h": "Hacked",
"m": "Modified",
"p": "Pirated",
"t": "Trained",
"tr": "Translated",
"o" : "Over Dump",
"u" : "Under Dump",
"v" : "Virus",
"b" : "Bad Dump",
"a" : "Alternate",
"!" : "Known Verified Dump"
"o": "Over Dump",
"u": "Under Dump",
"v": "Virus",
"b": "Bad Dump",
"a": "Alternate",
"!": "Known Verified Dump"
};
if (attributeName in tosecAttributeNames) {
@@ -378,4 +393,4 @@
SelectTab('general');
document.getElementById('romDelete').setAttribute("onclick", "showSubDialog('romdelete', " + modalVariables + ");");
</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>
@@ -6,27 +7,40 @@
<div id="properties_toc" class="settings_toc">
<div class="filter_header">Settings</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_users" name="properties_toc_item" onclick="SelectTab('users');" style="display: none;">Users</div>
<div id="properties_toc_mapping" name="properties_toc_item" onclick="SelectTab('mapping');" style="display: none;">Platform Mapping</div>
<div id="properties_toc_settings" name="properties_toc_item" onclick="SelectTab('settings');"
style="display: none;">Settings</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_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>
<div id="properties_bodypanel">
</div>
</div>
<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>
<script type="text/javascript">
if (userProfile.roles.includes("Admin")) {
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_services').style.display = '';
document.getElementById('properties_toc_mapping').style.display = '';
document.getElementById('properties_toc_logs').style.display = '';
}
@@ -62,4 +76,4 @@
$('#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>
</div>
<h3>Libraries</h3>
<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%;">
<table cellspacing="0" style="width: 100%; vertical-align: top;">
<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>
<th>
Write logs
</th>
<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>
</tr>
<tr>
<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>
</tr>
<tr>
@@ -47,10 +84,12 @@
</td>
</tr>
<tr>
<th colspan="2">Emulator</th>
<th colspan="2">
<h3>Emulator</h3>
</th>
</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>
</tr>
<tr>
@@ -61,339 +100,11 @@
</table>
<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() {
ajaxCall(
'/api/v1/System/Settings/System',
'GET',
function(result) {
function (result) {
var optionToSelect = 'settings_logs_write_db';
if (result.alwaysLogToDisk == true) {
optionToSelect = 'settings_logs_write_fs';
@@ -403,6 +114,27 @@
document.getElementById('settings_logs_retention').value = result.minimumLogRetentionPeriod;
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 = {
"alwaysLogToDisk": alwaysLogToDisk,
"minimumLogRetentionPeriod": retentionValue,
"emulatorDebugMode": document.getElementById('settings_emulator_debug').checked
"minimumLogRetentionPeriod": Number(retentionValue),
"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(
'/api/v1/System/Settings/System',
'POST',
function(result) {
function (result) {
getSystemSettings();
},
function(error) {
function (error) {
getSystemSettings();
},
JSON.stringify(model)
);
}
drawLibrary();
getBackgroundTaskTimers();
function toggleHasheousAPIKey(checkbox) {
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();
</script>

View File

@@ -22,7 +22,7 @@
<p><strong>Database</strong></p>
<div id="system_database"></div>
<h3>Signatures</h3>
<h3>Local Database Signatures</h3>
<div id="system_signatures"></div>
<script type="text/javascript">
@@ -37,10 +37,10 @@
for (var i = 0; i < result.length; i++) {
if (result[i].itemState != "Disabled") {
var itemTypeName = GetTaskFriendlyName(result[i].itemType, result[i].options);
var itemStateName;
var itemLastStart;
var hasError = "";
if (result[i].hasErrors) {
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 startButton = '';
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>";
}
}
@@ -271,4 +271,4 @@
setInterval(SystemLoadSystemStatus, 60000);
SystemSignaturesStatus();
setInterval(SystemSignaturesStatus, 300000);
</script>
</script>

View File

@@ -23,21 +23,29 @@ h3 {
border-bottom-width: 1px;
/*border-image: linear-gradient(to right, blue 25%, yellow 25%, yellow 50%,red 50%, red 75%, teal 75%) 5;*/
border-image: linear-gradient(to right, rgba(255,0,0,1) 0%, rgba(251,255,0,1) 16%, rgba(0,255,250,1) 30%, rgba(0,16,255,1) 46%, rgba(250,0,255,1) 62%, rgba(255,0,0,1) 78%, rgba(255,237,0,1) 90%, rgba(20,255,0,1) 100%) 5;
border-image: linear-gradient(to right, rgba(255, 0, 0, 1) 0%, rgba(251, 255, 0, 1) 16%, rgba(0, 255, 250, 1) 30%, rgba(0, 16, 255, 1) 46%, rgba(250, 0, 255, 1) 62%, rgba(255, 0, 0, 1) 78%, rgba(255, 237, 0, 1) 90%, rgba(20, 255, 0, 1) 100%) 5;
}
/* The Modal (background) */
.modal {
display: none; /* Hidden by default */
position: fixed; /* Stay in place */
z-index: 100; /* Sit on top */
display: none;
/* Hidden by default */
position: fixed;
/* Stay in place */
z-index: 100;
/* Sit on top */
left: 0;
top: 0;
width: 100%; /* Full width */
height: 100%; /* Full height */
overflow: none; /* Enable scroll if needed */
background-color: rgb(0,0,0); /* Fallback color */
background-color: rgba(0,0,0,0.4); /* Black w/ opacity */
width: 100%;
/* Full width */
height: 100%;
/* Full height */
overflow: none;
/* Enable scroll if needed */
background-color: rgb(0, 0, 0);
/* Fallback color */
background-color: rgba(0, 0, 0, 0.4);
/* Black w/ opacity */
backdrop-filter: blur(8px);
-webkit-backdrop-filter: blur(8px);
filter: drop-shadow(5px 5px 10px #000);
@@ -47,22 +55,28 @@ h3 {
/* Modal Content/Box */
.modal-content {
background-color: #383838;
margin: 10% auto; /* 15% from the top and centered */
margin: 10% auto;
/* 15% from the top and centered */
padding: 10px;
border: 1px solid #888;
border-radius: 10px;
width: 700px; /* Could be more or less, depending on screen size */
width: 700px;
/* Could be more or less, depending on screen size */
min-height: 358px;
}
.modal-content-sub {
background-color: #383838;
margin: 20% auto; /* 20% from the top and centered */
margin: 20% auto;
/* 20% from the top and centered */
padding: 10px;
border: 1px solid #888;
border-radius: 10px;
width: 300px; /* Could be more or less, depending on screen size */
width: 300px;
/* Could be more or less, depending on screen size */
min-height: 110px;
}
#modal-heading {
margin-block: 5px;
border-bottom-style: solid;
@@ -70,8 +84,9 @@ h3 {
border-bottom-width: 3px;
/*border-image: linear-gradient(to right, blue 25%, yellow 25%, yellow 50%,red 50%, red 75%, teal 75%) 5;*/
border-image: linear-gradient(to right, rgba(255,0,0,1) 0%, rgba(251,255,0,1) 16%, rgba(0,255,250,1) 30%, rgba(0,16,255,1) 46%, rgba(250,0,255,1) 62%, rgba(255,0,0,1) 78%, rgba(255,237,0,1) 90%, rgba(20,255,0,1) 100%) 5;
border-image: linear-gradient(to right, rgba(255, 0, 0, 1) 0%, rgba(251, 255, 0, 1) 16%, rgba(0, 255, 250, 1) 30%, rgba(0, 16, 255, 1) 46%, rgba(250, 0, 255, 1) 62%, rgba(255, 0, 0, 1) 78%, rgba(255, 237, 0, 1) 90%, rgba(20, 255, 0, 1) 100%) 5;
}
#modal-content {
height: 100%;
}
@@ -268,7 +283,7 @@ h3 {
}
#games_filter {
width: 200px;
/* border-style: solid;
border-width: 1px;
@@ -296,7 +311,13 @@ h3 {
z-index: 1;
}
input[type='text'], input[type='number'], input[type="email"], input[type="password"], input[type="datetime-local"] {
input[type='text'],
input[type='number'],
input[type="email"],
input[type="password"],
input[type="datetime-local"],
input[type="url"],
textarea {
background-color: #2b2b2b;
color: white;
padding: 4px;
@@ -313,10 +334,21 @@ input[type='text'], input[type='number'], input[type="email"], input[type="passw
height: 21px;
}
input[type='text']:hover, input[type='number']:hover, input[type="email"]:hover, input[type="password"]:hover, input[type="datetime-local"]:hover {
input[type='text']:hover,
input[type='number']:hover,
input[type="email"]:hover,
input[type="password"]:hover,
input[type="datetime-local"]:hover,
input[type="url"]:hover,
textarea:hover {
border-color: #939393;
}
textarea {
height: unset;
font-family: 'Courier New', Courier, monospace;
}
input[id='filter_panel_search'] {
width: 160px;
}
@@ -351,9 +383,7 @@ input[name='filter_panel_range_max'] {
background: #555;
}
.text_link {
}
.text_link {}
.text_link:hover {
cursor: pointer;
@@ -390,9 +420,9 @@ input[name='filter_panel_range_max'] {
background-color: rgba(0, 22, 56, 0.8);
backdrop-filter: blur(8px);
-webkit-backdrop-filter: blur(8px);
box-shadow: 5px 5px 19px 0px rgba(0,0,0,0.44);
-webkit-box-shadow: 5px 5px 19px 0px rgba(0,0,0,0.44);
-moz-box-shadow: 5px 5px 19px 0px rgba(0,0,0,0.44);
box-shadow: 5px 5px 19px 0px rgba(0, 0, 0, 0.44);
-webkit-box-shadow: 5px 5px 19px 0px rgba(0, 0, 0, 0.44);
-moz-box-shadow: 5px 5px 19px 0px rgba(0, 0, 0, 0.44);
}
.games_pager_number {
@@ -525,13 +555,13 @@ input[name='filter_panel_range_max'] {
overflow-y: auto;
/* display: flex; */
justify-content: center;
align-items: center;
align-items: center;
}
#games_library_alpha_pager {
width: 50px;
justify-content: center;
align-items: center;
align-items: center;
}
.games_library_alpha_pager_letter {
@@ -586,22 +616,22 @@ input[name='filter_panel_range_max'] {
.game_tile:hover {
cursor: pointer;
text-decoration: underline;
/* text-decoration: underline;
background-color: #2b2b2b;
border-radius: 10px 10px 10px 10px;
-webkit-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 {
cursor: pointer;
text-decoration: underline;
/* text-decoration: underline;
background-color: #2b2b2b;
border-radius: 10px 10px 10px 10px;
-webkit-border-radius: 10px 10px 10px 10px;
-moz-border-radius: 10px 10px 10px 10px;
border: 1px solid #2b2b2b;
border: 1px solid #2b2b2b; */
}
.game_tile_row {
@@ -625,6 +655,19 @@ input[name='filter_panel_range_max'] {
display: inline-block;
max-width: 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 {
@@ -706,9 +749,9 @@ input[name='filter_panel_range_max'] {
}
.game_tile_image_shadow {
box-shadow: 5px 5px 19px 0px rgba(0,0,0,0.44);
-webkit-box-shadow: 5px 5px 19px 0px rgba(0,0,0,0.44);
-moz-box-shadow: 5px 5px 19px 0px rgba(0,0,0,0.44);
box-shadow: 5px 5px 19px 0px rgba(0, 0, 0, 0.44);
-webkit-box-shadow: 5px 5px 19px 0px rgba(0, 0, 0, 0.44);
-moz-box-shadow: 5px 5px 19px 0px rgba(0, 0, 0, 0.44);
}
.game_tile_image_row {
@@ -720,11 +763,13 @@ input[name='filter_panel_range_max'] {
background-color: transparent;
}
.game_tile_image, .unknown {
.game_tile_image,
.unknown {
background-color: transparent;
}
.game_tile_image_row, .unknown {
.game_tile_image_row,
.unknown {
background-color: transparent;
}
@@ -790,9 +835,9 @@ input[name='filter_panel_range_max'] {
max-width: 250px;
max-height: 350px;
width: 100%;
box-shadow: 5px 5px 19px 0px rgba(0,0,0,0.44);
-webkit-box-shadow: 5px 5px 19px 0px rgba(0,0,0,0.44);
-moz-box-shadow: 5px 5px 19px 0px rgba(0,0,0,0.44);
box-shadow: 5px 5px 19px 0px rgba(0, 0, 0, 0.44);
-webkit-box-shadow: 5px 5px 19px 0px rgba(0, 0, 0, 0.44);
-moz-box-shadow: 5px 5px 19px 0px rgba(0, 0, 0, 0.44);
}
.gamegenrelabel {
@@ -842,9 +887,9 @@ input[name='filter_panel_range_max'] {
padding: 10px;
/*height: 350px;*/
margin-bottom: 20px;
box-shadow: 5px 5px 19px 0px rgba(0,0,0,0.44);
-webkit-box-shadow: 5px 5px 19px 0px rgba(0,0,0,0.44);
-moz-box-shadow: 5px 5px 19px 0px rgba(0,0,0,0.44);
box-shadow: 5px 5px 19px 0px rgba(0, 0, 0, 0.44);
-webkit-box-shadow: 5px 5px 19px 0px rgba(0, 0, 0, 0.44);
-moz-box-shadow: 5px 5px 19px 0px rgba(0, 0, 0, 0.44);
}
#gamescreenshots_main {
@@ -903,11 +948,16 @@ iframe {
background-color: #383838;
color: black;
text-align: center;
user-select: none; /* standard syntax */
-webkit-user-select: none; /* webkit (safari, chrome) browsers */
-moz-user-select: none; /* mozilla browsers */
-khtml-user-select: none; /* webkit (konqueror) browsers */
-ms-user-select: none; /* IE10+ */
user-select: none;
/* standard syntax */
-webkit-user-select: none;
/* webkit (safari, chrome) browsers */
-moz-user-select: none;
/* mozilla browsers */
-khtml-user-select: none;
/* webkit (konqueror) browsers */
-ms-user-select: none;
/* IE10+ */
}
.gamescreenshots_arrows:hover {
@@ -981,9 +1031,7 @@ iframe {
background-color: rgba(56, 56, 56, 0.9);
}
#gamesummarytext_label {
}
#gamesummarytext_label {}
.line-clamp-4 {
overflow: hidden;
@@ -1057,9 +1105,9 @@ th {
-webkit-border-radius: 5px 5px 5px 5px;
-moz-border-radius: 5px 5px 5px 5px;
border: 1px solid #19d348;
box-shadow: 5px 5px 19px 0px rgba(0,0,0,0.44);
-webkit-box-shadow: 5px 5px 19px 0px rgba(0,0,0,0.44);
-moz-box-shadow: 5px 5px 19px 0px rgba(0,0,0,0.44);
box-shadow: 5px 5px 19px 0px rgba(0, 0, 0, 0.44);
-webkit-box-shadow: 5px 5px 19px 0px rgba(0, 0, 0, 0.44);
-moz-box-shadow: 5px 5px 19px 0px rgba(0, 0, 0, 0.44);
}
.romstart:hover {
@@ -1084,9 +1132,9 @@ th {
border-color: white;
background-color: blue;
outline-color: blue;
box-shadow: 5px 5px 19px 0px rgba(0,0,0,0.44);
-webkit-box-shadow: 5px 5px 19px 0px rgba(0,0,0,0.44);
-moz-box-shadow: 5px 5px 19px 0px rgba(0,0,0,0.44);
box-shadow: 5px 5px 19px 0px rgba(0, 0, 0, 0.44);
-webkit-box-shadow: 5px 5px 19px 0px rgba(0, 0, 0, 0.44);
-moz-box-shadow: 5px 5px 19px 0px rgba(0, 0, 0, 0.44);
}
.properties_button:hover {
@@ -1115,14 +1163,18 @@ th {
height: 100%;
}
div[name="properties_toc_item"],div[name="properties_user_toc_item"],div[name="properties_profile_toc_item"] {
div[name="properties_toc_item"],
div[name="properties_user_toc_item"],
div[name="properties_profile_toc_item"] {
padding: 10px;
border-bottom-width: 1px;
border-bottom-style: solid;
border-bottom-color: #2b2b2b;
}
div[name="properties_toc_item"]:hover,div[name="properties_user_toc_item"]:hover,div[name="properties_profile_toc_item"]:hover {
div[name="properties_toc_item"]:hover,
div[name="properties_user_toc_item"]:hover,
div[name="properties_profile_toc_item"]:hover {
background-color: #2b2b2b;
cursor: pointer;
}
@@ -1150,7 +1202,8 @@ div[name="properties_toc_item"]:hover,div[name="properties_user_toc_item"]:hover
border-radius: 5px;
}
.select2-container--default:hover, .select2-selection--multiple:hover {
.select2-container--default:hover,
.select2-selection--multiple:hover {
border-color: #939393;
}
@@ -1197,7 +1250,8 @@ div[name="properties_toc_item"]:hover,div[name="properties_user_toc_item"]:hover
border-radius: 5px;
}
.select2-selection--single:hover, .select2-selection__rendered:hover {
.select2-selection--single:hover,
.select2-selection__rendered:hover {
border-color: #939393;
}
@@ -1302,9 +1356,7 @@ button:not(.select2-selection__choice__remove):not(.ejs_menu_button):disabled {
background-color: #555;
}
#emulator {
}
#emulator {}
.emulator_partscreen {
margin: 0 auto;
@@ -1377,15 +1429,14 @@ button:not(.select2-selection__choice__remove):not(.ejs_menu_button):disabled {
margin-left: 15px;
}
.rom_checkbox_box {
}
.rom_checkbox_box {}
.rom_checkbox_box_hidden {
display: none;
}
#rom_edit, #rom_edit_delete {
#rom_edit,
#rom_edit_delete {
float: right;
}
@@ -1398,9 +1449,9 @@ button:not(.select2-selection__choice__remove):not(.ejs_menu_button):disabled {
}
#game {
box-shadow: 5px 5px 19px 0px rgba(0,0,0,0.44);
-webkit-box-shadow: 5px 5px 19px 0px rgba(0,0,0,0.44);
-moz-box-shadow: 5px 5px 19px 0px rgba(0,0,0,0.44);
box-shadow: 5px 5px 19px 0px rgba(0, 0, 0, 0.44);
-webkit-box-shadow: 5px 5px 19px 0px rgba(0, 0, 0, 0.44);
-moz-box-shadow: 5px 5px 19px 0px rgba(0, 0, 0, 0.44);
}
#gametitle_criticrating {
@@ -1440,7 +1491,7 @@ button:not(.select2-selection__choice__remove):not(.ejs_menu_button):disabled {
width: 1000px;
height: 90%;
margin: 25px auto;
/* overflow-x: scroll;*/
/* overflow-x: scroll;*/
position: relative;
}
@@ -1468,7 +1519,8 @@ button:not(.select2-selection__choice__remove):not(.ejs_menu_button):disabled {
}
.bgalt1 {
background-color: transparent;;
background-color: transparent;
;
}
.logs_table_cell_150px {
@@ -1520,13 +1572,33 @@ button:not(.select2-selection__choice__remove):not(.ejs_menu_button):disabled {
background-color: #383838;
}
.string { color: lightblue; }
.number { color: lightblue; }
.boolean { color: lightblue; }
.null { color: magenta; }
.key { color: greenyellow; }
.brace { color: #888; }
.square { color: #fff000; }
.string {
color: lightblue;
}
.number {
color: lightblue;
}
.boolean {
color: lightblue;
}
.null {
color: magenta;
}
.key {
color: greenyellow;
}
.brace {
color: #888;
}
.square {
color: #fff000;
}
.tagBox {
position: absolute;
@@ -1557,12 +1629,16 @@ button:not(.select2-selection__choice__remove):not(.ejs_menu_button):disabled {
}
.loginwindow {
position: fixed; /* Stay in place */
position: fixed;
/* Stay in place */
left: 0;
top: 0;
width: 100%; /* Full width */
height: 100%; /* Full height */
overflow: auto; /* Enable scroll if needed */
width: 100%;
/* Full width */
height: 100%;
/* Full height */
overflow: auto;
/* Enable scroll if needed */
/*background-color: rgb(0,0,0); /* Fallback color */
/*background-color: rgba(0,0,0,0.4); /* Black w/ opacity */
/*backdrop-filter: blur(8px);
@@ -1575,11 +1651,13 @@ button:not(.select2-selection__choice__remove):not(.ejs_menu_button):disabled {
.loginwindow-content {
position: relative;
background-color: #383838;
margin: 15% auto; /* 15% from the top and centered */
margin: 15% auto;
/* 15% from the top and centered */
padding: 10px;
border: 1px solid #888;
border-radius: 10px;
width: 350px; /* Could be more or less, depending on screen size */
width: 350px;
/* Could be more or less, depending on screen size */
min-height: 250px;
}
@@ -1615,7 +1693,8 @@ button:not(.select2-selection__choice__remove):not(.ejs_menu_button):disabled {
}
/* Links inside the dropdown */
.dropdown-content a, .dropdown-content span {
.dropdown-content a,
.dropdown-content span {
color: black;
padding: 12px 16px;
text-decoration: none;
@@ -1625,12 +1704,16 @@ button:not(.select2-selection__choice__remove):not(.ejs_menu_button):disabled {
.dropdown-content span {
cursor: auto;
}
/* Change color of dropdown links on hover */
.dropdown-content a:hover {background-color: #ddd;}
.dropdown-content a:hover {
background-color: #ddd;
}
/* Show the dropdown menu (use JS to add this class to the .dropdown-content container when the user clicks on the dropdown button) */
.show {display:block;}
.show {
display: block;
}
.dropdownroleitem {
text-transform: capitalize;