diff --git a/gaseous-server/Classes/Collections.cs b/gaseous-server/Classes/Collections.cs index c6d7747..c7a3c9e 100644 --- a/gaseous-server/Classes/Collections.cs +++ b/gaseous-server/Classes/Collections.cs @@ -437,7 +437,7 @@ namespace gaseous_server.Classes case CollectionItem.FolderStructures.RetroPie: try { - PlatformMapping.PlatformMapItem platformMapItem = PlatformMapping.GetPlatformMappingByIGDBid(collectionPlatformItem.Id); + PlatformMapping.PlatformMapItem platformMapItem = PlatformMapping.GetPlatformMap(collectionPlatformItem.Id); ZipPlatformPath = Path.Combine(ZipFileTempPath, "roms", platformMapItem.RetroPieDirectoryName); } catch diff --git a/gaseous-server/Classes/Metadata/Platforms.cs b/gaseous-server/Classes/Metadata/Platforms.cs index b61969b..aa3d4c4 100644 --- a/gaseous-server/Classes/Metadata/Platforms.cs +++ b/gaseous-server/Classes/Metadata/Platforms.cs @@ -22,7 +22,7 @@ namespace gaseous_server.Classes.Metadata Config.IGDB.Secret ); - public static Platform? GetPlatform(long Id) + public static Platform? GetPlatform(long Id, bool forceRefresh = false) { if (Id == 0) { @@ -46,18 +46,18 @@ namespace gaseous_server.Classes.Metadata } else { - Task RetVal = _GetPlatform(SearchUsing.id, Id); + Task RetVal = _GetPlatform(SearchUsing.id, Id, forceRefresh); return RetVal.Result; } } - public static Platform GetPlatform(string Slug) + public static Platform GetPlatform(string Slug, bool forceRefresh = false) { - Task RetVal = _GetPlatform(SearchUsing.slug, Slug); + Task RetVal = _GetPlatform(SearchUsing.slug, Slug, forceRefresh); return RetVal.Result; } - private static async Task _GetPlatform(SearchUsing searchUsing, object searchValue) + private static async Task _GetPlatform(SearchUsing searchUsing, object searchValue, bool forceRefresh) { // check database first Storage.CacheStatus? cacheStatus = new Storage.CacheStatus(); @@ -70,6 +70,11 @@ namespace gaseous_server.Classes.Metadata cacheStatus = Storage.GetCacheStatus("Platform", (string)searchValue); } + if (forceRefresh == true) + { + if (cacheStatus == Storage.CacheStatus.Current) { cacheStatus = Storage.CacheStatus.Expired; } + } + // set up where clause string WhereClause = ""; switch (searchUsing) @@ -91,11 +96,13 @@ namespace gaseous_server.Classes.Metadata returnValue = await GetObjectFromServer(WhereClause); Storage.NewCacheValue(returnValue); UpdateSubClasses(returnValue); + AddPlatformMapping(returnValue); return returnValue; case Storage.CacheStatus.Expired: returnValue = await GetObjectFromServer(WhereClause); Storage.NewCacheValue(returnValue, true); UpdateSubClasses(returnValue); + AddPlatformMapping(returnValue); return returnValue; case Storage.CacheStatus.Current: return Storage.GetCacheValue(returnValue, "id", (long)searchValue); @@ -120,6 +127,31 @@ namespace gaseous_server.Classes.Metadata } } + private static void AddPlatformMapping(Platform platform) + { + // ensure a mapping item exists for this platform + Models.PlatformMapping.PlatformMapItem item = new Models.PlatformMapping.PlatformMapItem(); + try + { + Logging.Log(Logging.LogType.Information, "Platform Map", "Checking if " + platform.Name + " is in database."); + item = Models.PlatformMapping.GetPlatformMap((long)platform.Id); + // exists - skip + Logging.Log(Logging.LogType.Information, "Platform Map", "Skipping import of " + platform.Name + " - already in database."); + } + catch + { + Logging.Log(Logging.LogType.Information, "Platform Map", "Importing " + platform.Name + " from predefined data."); + // doesn't exist - add it + item = new Models.PlatformMapping.PlatformMapItem{ + IGDBId = (long)platform.Id, + IGDBName = platform.Name, + IGDBSlug = platform.Slug, + AlternateNames = new List{ platform.AlternativeName } + }; + Models.PlatformMapping.WritePlatformMap(item, false); + } + } + private enum SearchUsing { id, diff --git a/gaseous-server/Classes/MetadataManagement.cs b/gaseous-server/Classes/MetadataManagement.cs index 7f6e151..c788e64 100644 --- a/gaseous-server/Classes/MetadataManagement.cs +++ b/gaseous-server/Classes/MetadataManagement.cs @@ -1,5 +1,6 @@ using System; using System.Data; +using gaseous_server.Models; using gaseous_tools; namespace gaseous_server.Classes @@ -9,8 +10,29 @@ namespace gaseous_server.Classes public static void RefreshMetadata(bool forceRefresh = false) { Database db = new gaseous_tools.Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); - string sql = "SELECT Id, `Name` FROM Game;"; - DataTable dt = db.ExecuteCMD(sql); + string sql = ""; + DataTable dt = new DataTable(); + + // update platforms + sql = "SELECT Id, `Name` FROM Platform;"; + dt = db.ExecuteCMD(sql); + + foreach (DataRow dr in dt.Rows) + { + try + { + Logging.Log(Logging.LogType.Information, "Metadata Refresh", "Refreshing metadata for platform " + dr["name"] + " (" + dr["id"] + ")"); + Metadata.Platforms.GetPlatform((long)dr["id"], true); + } + catch (Exception ex) + { + Logging.Log(Logging.LogType.Critical, "Metadata Refresh", "An error occurred while refreshing metadata for " + dr["name"], ex); + } + } + + // update games + sql = "SELECT Id, `Name` FROM Game;"; + dt = db.ExecuteCMD(sql); foreach (DataRow dr in dt.Rows) { diff --git a/gaseous-server/Controllers/PlatformMapsController.cs b/gaseous-server/Controllers/PlatformMapsController.cs new file mode 100644 index 0000000..f14b82d --- /dev/null +++ b/gaseous-server/Controllers/PlatformMapsController.cs @@ -0,0 +1,166 @@ +using System; +using System.Collections.Generic; +using System.Data; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Threading.Tasks; +using gaseous_server.Classes.Metadata; +using gaseous_server.Models; +using gaseous_tools; +using IGDB.Models; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.CodeAnalysis.Scripting; + +namespace gaseous_server.Controllers +{ + [Route("api/v1/[controller]")] + [ApiController] + public class PlatformMapsController : Controller + { + [HttpGet] + [ProducesResponseType(typeof(List), StatusCodes.Status200OK)] + public ActionResult GetPlatformMap(bool ResetToDefault = false) + { + if (ResetToDefault == true) + { + PlatformMapping.ExtractPlatformMap(true); + } + + return Ok(PlatformMapping.PlatformMap); + } + + [HttpGet] + [Route("{PlatformId}")] + [ProducesResponseType(typeof(PlatformMapping.PlatformMapItem), StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public ActionResult PlatformMap(long PlatformId) + { + try + { + PlatformMapping.PlatformMapItem platformMapItem = PlatformMapping.GetPlatformMap(PlatformId); + + if (platformMapItem != null) + { + return Ok(platformMapItem); + } + else + { + return NotFound(); + } + } + catch + { + return NotFound(); + } + } + + [HttpPost] + [ProducesResponseType(typeof(List), StatusCodes.Status200OK)] + [RequestSizeLimit(long.MaxValue)] + [DisableRequestSizeLimit, RequestFormLimits(MultipartBodyLengthLimit = long.MaxValue, ValueLengthLimit = int.MaxValue)] + public async Task UploadPlatformMap(List files) + { + Guid sessionid = Guid.NewGuid(); + + string workPath = Path.Combine(Config.LibraryConfiguration.LibraryUploadDirectory, sessionid.ToString()); + + long size = files.Sum(f => f.Length); + + List> UploadedFiles = new List>(); + + foreach (IFormFile formFile in files) + { + if (formFile.Length > 0) + { + Guid FileId = Guid.NewGuid(); + + string filePath = Path.Combine(workPath, Path.GetFileName(formFile.FileName)); + + if (!Directory.Exists(workPath)) + { + Directory.CreateDirectory(workPath); + } + + using (var stream = System.IO.File.Create(filePath)) + { + await formFile.CopyToAsync(stream); + + Dictionary UploadedFile = new Dictionary(); + UploadedFile.Add("id", FileId.ToString()); + UploadedFile.Add("originalname", Path.GetFileName(formFile.FileName)); + UploadedFile.Add("fullpath", filePath); + UploadedFiles.Add(UploadedFile); + } + } + } + + // Process uploaded files + foreach (Dictionary UploadedFile in UploadedFiles) + { + Models.PlatformMapping.ExtractPlatformMap((string)UploadedFile["fullpath"]); + } + + if (Directory.Exists(workPath)) + { + Directory.Delete(workPath, true); + } + + return Ok(new { count = files.Count, size }); + } + + [HttpPost] + [Route("{PlatformId}")] + [ProducesResponseType(typeof(PlatformMapping.PlatformMapItem), StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + [ProducesResponseType(StatusCodes.Status409Conflict)] + public ActionResult NewPlatformMap(long PlatformId, PlatformMapping.PlatformMapItem Map) + { + try + { + PlatformMapping.PlatformMapItem platformMapItem = PlatformMapping.GetPlatformMap(PlatformId); + + if (platformMapItem != null) + { + return Conflict(); + } + else + { + PlatformMapping.WritePlatformMap(Map, false); + return Ok(PlatformMapping.GetPlatformMap(PlatformId)); + } + } + catch + { + return NotFound(); + } + } + + [HttpPatch] + [Route("{PlatformId}")] + [ProducesResponseType(typeof(PlatformMapping.PlatformMapItem), StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public ActionResult EditPlatformMap(long PlatformId, PlatformMapping.PlatformMapItem Map) + { + try + { + PlatformMapping.PlatformMapItem platformMapItem = PlatformMapping.GetPlatformMap(PlatformId); + + if (platformMapItem != null) + { + PlatformMapping.WritePlatformMap(Map, true); + return Ok(PlatformMapping.GetPlatformMap(PlatformId)); + } + else + { + return NotFound(); + } + } + catch + { + return NotFound(); + } + } + } +} \ No newline at end of file diff --git a/gaseous-server/Controllers/PlatformsController.cs b/gaseous-server/Controllers/PlatformsController.cs index 7b5fb0c..774f303 100644 --- a/gaseous-server/Controllers/PlatformsController.cs +++ b/gaseous-server/Controllers/PlatformsController.cs @@ -6,6 +6,7 @@ using System.Linq; using System.Reflection; using System.Threading.Tasks; using gaseous_server.Classes.Metadata; +using gaseous_server.Models; using gaseous_tools; using IGDB.Models; using Microsoft.AspNetCore.Http; @@ -21,6 +22,11 @@ namespace gaseous_server.Controllers [HttpGet] [ProducesResponseType(typeof(List), StatusCodes.Status200OK)] public ActionResult Platform() + { + return Ok(PlatformsController.GetPlatforms()); + } + + public static List GetPlatforms() { Database db = new gaseous_tools.Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); @@ -34,7 +40,7 @@ namespace gaseous_server.Controllers RetVal.Add(Classes.Metadata.Platforms.GetPlatform((long)dr["id"])); } - return Ok(RetVal); + return RetVal; } [HttpGet] diff --git a/gaseous-server/Models/PlatformMapping.cs b/gaseous-server/Models/PlatformMapping.cs index c2928c4..0b724fc 100644 --- a/gaseous-server/Models/PlatformMapping.cs +++ b/gaseous-server/Models/PlatformMapping.cs @@ -1,49 +1,309 @@ using System; +using System.Collections; +using System.Data; +using System.Linq; using System.Reflection; using System.Text.Json.Serialization; using gaseous_server.Classes; +using gaseous_server.Classes.Metadata; +using gaseous_server.Controllers; +using gaseous_tools; +using IGDB.Models; +using Newtonsoft.Json; namespace gaseous_server.Models { public class PlatformMapping { - public PlatformMapping() + /// + /// Updates the platform map from the embedded platform map resource + /// + public static void ExtractPlatformMap(bool ResetToDefault = false) { + using (Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("gaseous_server.Support.PlatformMap.json")) + using (StreamReader reader = new StreamReader(stream)) + { + string rawJson = reader.ReadToEnd(); + List platforms = new List(); + platforms = Newtonsoft.Json.JsonConvert.DeserializeObject>(rawJson); + foreach (PlatformMapItem mapItem in platforms) + { + // check if it exists first - only add if it doesn't exist + try + { + Logging.Log(Logging.LogType.Information, "Platform Map", "Checking if " + mapItem.IGDBName + " is in database."); + PlatformMapItem item = GetPlatformMap(mapItem.IGDBId); + // exists + if (ResetToDefault == false) + { + Logging.Log(Logging.LogType.Information, "Platform Map", "Skipping import of " + mapItem.IGDBName + " - already in database."); + } + else + { + WritePlatformMap(mapItem, true); + Logging.Log(Logging.LogType.Information, "Platform Map", "Overwriting " + mapItem.IGDBName + " with default values."); + } + } + catch + { + Logging.Log(Logging.LogType.Information, "Platform Map", "Importing " + mapItem.IGDBName + " from predefined data."); + // doesn't exist - add it + WritePlatformMap(mapItem, false); + } + } + } } - //private static List _PlatformMaps = new List(); + /// + /// Updates the platform map from the provided file - existing items are overwritten + /// + /// + public static void ExtractPlatformMap(string ImportFile) + { + string rawJson = File.ReadAllText(ImportFile); + List platforms = new List(); + platforms = Newtonsoft.Json.JsonConvert.DeserializeObject>(rawJson); + + foreach (PlatformMapItem mapItem in platforms) + { + try + { + PlatformMapItem item = GetPlatformMap(mapItem.IGDBId); + + // still here? we must have found the item we're looking for! overwrite it + Logging.Log(Logging.LogType.Information, "Platform Map", "Replacing " + mapItem.IGDBName + " from external JSON file."); + WritePlatformMap(mapItem, true); + } + catch + { + // we caught a not found error, insert a new record + Logging.Log(Logging.LogType.Information, "Platform Map", "Importing " + mapItem.IGDBName + " from external JSON file."); + WritePlatformMap(mapItem, false); + } + } + } + public static List PlatformMap { get { - // load platform maps from: gaseous_server.Support.PlatformMap.json - List _PlatformMaps = new List(); - var assembly = Assembly.GetExecutingAssembly(); - var resourceName = "gaseous_server.Support.PlatformMap.json"; - using (Stream stream = assembly.GetManifestResourceStream(resourceName)) - using (StreamReader reader = new StreamReader(stream)) + Database db = new gaseous_tools.Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); + string sql = "SELECT * FROM PlatformMap"; + DataTable data = db.ExecuteCMD(sql); + + List platformMaps = new List(); + foreach (DataRow row in data.Rows) { - string rawJson = reader.ReadToEnd(); - _PlatformMaps.Clear(); - _PlatformMaps = Newtonsoft.Json.JsonConvert.DeserializeObject>(rawJson); + platformMaps.Add(BuildPlatformMapItem(row)); } - return _PlatformMaps; + platformMaps.Sort((x, y) => x.IGDBName.CompareTo(y.IGDBName)); + + return platformMaps; } } - public static PlatformMapItem GetPlatformMappingByIGDBid(long Id) + public static PlatformMapItem GetPlatformMap(long Id) { - foreach (Models.PlatformMapping.PlatformMapItem platformMapping in PlatformMap) + Database db = new gaseous_tools.Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); + string sql = "SELECT * FROM PlatformMap WHERE Id = @Id"; + Dictionary dbDict = new Dictionary(); + dbDict.Add("Id", Id); + DataTable data = db.ExecuteCMD(sql, dbDict); + + if (data.Rows.Count > 0) { - if (platformMapping.IGDBId == Id) + PlatformMapItem platformMap = BuildPlatformMapItem(data.Rows[0]); + + return platformMap; + } + else + { + throw new Exception(""); + } + } + + public static void WritePlatformMap(PlatformMapItem item, bool Update) + { + Database db = new gaseous_tools.Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); + string sql = ""; + Dictionary dbDict = new Dictionary(); + if (Update == false) + { + // insert + sql = "INSERT INTO PlatformMap (Id, RetroPieDirectoryName, WebEmulator_Type, WebEmulator_Core) VALUES (@Id, @RetroPieDirectoryName, @WebEmulator_Type, @WebEmulator_Core)"; + } + else + { + // update + sql = "UPDATE PlatformMap SET RetroPieDirectoryName=@RetroPieDirectoryName, WebEmulator_Type=@WebEmulator_Type, WebEmulator_Core=@WebEmulator_Core WHERE Id = @Id"; + } + dbDict.Add("Id", item.IGDBId); + dbDict.Add("RetroPieDirectoryName", item.RetroPieDirectoryName); + if (item.WebEmulator != null) + { + dbDict.Add("WebEmulator_Type", item.WebEmulator.Type); + dbDict.Add("WebEmulator_Core", item.WebEmulator.Core); + } + else + { + dbDict.Add("WebEmulator_Type", ""); + dbDict.Add("WebEmulator_Core", ""); + } + db.ExecuteCMD(sql, dbDict); + + // remove existing items so they can be re-inserted + sql = "DELETE FROM PlatformMap_AlternateNames WHERE Id = @Id; DELETE FROM PlatformMap_Extensions WHERE Id = @Id; DELETE FROM PlatformMap_UniqueExtensions WHERE Id = @Id; DELETE FROM PlatformMap_Bios WHERE Id = @Id;"; + db.ExecuteCMD(sql, dbDict); + + // insert alternate names + if (item.AlternateNames != null) + { + foreach (string alternateName in item.AlternateNames) { - return platformMapping; + sql = "INSERT INTO PlatformMap_AlternateNames (Id, Name) VALUES (@Id, @Name);"; + dbDict.Clear(); + dbDict.Add("Id", item.IGDBId); + dbDict.Add("Name", alternateName); + db.ExecuteCMD(sql, dbDict); } } - throw new Exception("Platform id not found"); + // insert extensions + if (item.Extensions != null) + { + foreach (string extension in item.Extensions.SupportedFileExtensions) + { + sql = "INSERT INTO PlatformMap_Extensions (Id, Extension) VALUES (@Id, @Extension);"; + dbDict.Clear(); + dbDict.Add("Id", item.IGDBId); + dbDict.Add("Extension", extension); + db.ExecuteCMD(sql, dbDict); + } + + // delete duplicates + sql = "DELETE FROM PlatformMap_UniqueExtensions; INSERT INTO PlatformMap_UniqueExtensions SELECT * FROM PlatformMap_Extensions WHERE Extension IN (SELECT Extension FROM PlatformMap_Extensions GROUP BY Extension HAVING COUNT(Extension) = 1);"; + db.ExecuteCMD(sql); + } + + // insert bios + if (item.Bios != null) + { + foreach (PlatformMapItem.EmulatorBiosItem biosItem in item.Bios) + { + sql = "INSERT INTO PlatformMap_Bios (Id, Filename, Description, Hash) VALUES (@Id, @Filename, @Description, @Hash);"; + dbDict.Clear(); + dbDict.Add("Id", item.IGDBId); + dbDict.Add("Filename", biosItem.filename); + dbDict.Add("Description", biosItem.description); + dbDict.Add("Hash", biosItem.hash); + db.ExecuteCMD(sql, dbDict); + } + } + } + + static PlatformMapItem BuildPlatformMapItem(DataRow row) + { + long IGDBId = (long)row["Id"]; + Database db = new gaseous_tools.Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); + Dictionary dbDict = new Dictionary(); + string sql = ""; + + // get platform data + IGDB.Models.Platform platform = Platforms.GetPlatform(IGDBId); + + // get platform alternate names + sql = "SELECT * FROM PlatformMap_AlternateNames WHERE Id = @Id ORDER BY Name"; + dbDict.Clear(); + dbDict.Add("Id", IGDBId); + DataTable altTable = db.ExecuteCMD(sql, dbDict); + + List alternateNames = new List(); + foreach (DataRow altRow in altTable.Rows) + { + string altVal = (string)altRow["Name"]; + if (!alternateNames.Contains(altVal, StringComparer.OrdinalIgnoreCase)) + { + alternateNames.Add(altVal); + } + } + if (platform.AlternativeName != null) + { + if (!alternateNames.Contains(platform.AlternativeName, StringComparer.OrdinalIgnoreCase)) + { + alternateNames.Add(platform.AlternativeName); + } + } + + // get platform known extensions + sql = "SELECT * FROM PlatformMap_Extensions WHERE Id = @Id ORDER BY Extension"; + dbDict.Clear(); + dbDict.Add("Id", IGDBId); + DataTable extTable = db.ExecuteCMD(sql, dbDict); + + List knownExtensions = new List(); + foreach (DataRow extRow in extTable.Rows) + { + string extVal = (string)extRow["Extension"]; + if (!knownExtensions.Contains(extVal, StringComparer.OrdinalIgnoreCase)) + { + knownExtensions.Add(extVal); + } + } + + // get platform unique extensions + sql = "SELECT * FROM PlatformMap_UniqueExtensions WHERE Id = @Id ORDER BY Extension"; + dbDict.Clear(); + dbDict.Add("Id", IGDBId); + DataTable uextTable = db.ExecuteCMD(sql, dbDict); + + List uniqueExtensions = new List(); + foreach (DataRow uextRow in uextTable.Rows) + { + string uextVal = (string)uextRow["Extension"]; + if (!uniqueExtensions.Contains(uextVal, StringComparer.OrdinalIgnoreCase)) + { + uniqueExtensions.Add(uextVal); + } + } + + // get platform bios + sql = "SELECT * FROM PlatformMap_Bios WHERE Id = @Id ORDER BY Filename"; + dbDict.Clear(); + dbDict.Add("Id", IGDBId); + DataTable biosTable = db.ExecuteCMD(sql, dbDict); + + List bioss = new List(); + foreach (DataRow biosRow in biosTable.Rows) + { + PlatformMapItem.EmulatorBiosItem bios = new PlatformMapItem.EmulatorBiosItem + { + filename = (string)Common.ReturnValueIfNull(biosRow["Filename"], ""), + description = (string)Common.ReturnValueIfNull(biosRow["Description"], ""), + hash = (string)Common.ReturnValueIfNull(biosRow["Hash"], "") + }; + bioss.Add(bios); + } + + // build item + PlatformMapItem mapItem = new PlatformMapItem(); + mapItem.IGDBId = IGDBId; + mapItem.IGDBName = platform.Name; + mapItem.IGDBSlug = platform.Slug; + mapItem.AlternateNames = alternateNames; + mapItem.Extensions = new PlatformMapItem.FileExtensions{ + SupportedFileExtensions = knownExtensions, + UniqueFileExtensions = uniqueExtensions + }; + mapItem.RetroPieDirectoryName = (string)Common.ReturnValueIfNull(row["RetroPieDirectoryName"], ""); + mapItem.WebEmulator = new PlatformMapItem.WebEmulatorItem{ + Type = (string)Common.ReturnValueIfNull(row["WebEmulator_Type"], ""), + Core = (string)Common.ReturnValueIfNull(row["WebEmulator_Core"], "") + }; + mapItem.Bios = bioss; + + return mapItem; } public static void GetIGDBPlatformMapping(ref Models.Signatures_Games Signature, FileInfo RomFileInfo, bool SetSystemName) @@ -51,17 +311,20 @@ namespace gaseous_server.Models bool PlatformFound = false; foreach (Models.PlatformMapping.PlatformMapItem PlatformMapping in Models.PlatformMapping.PlatformMap) { - if (PlatformMapping.KnownFileExtensions.Contains(RomFileInfo.Extension, StringComparer.OrdinalIgnoreCase)) + if (PlatformMapping.Extensions != null) { - if (SetSystemName == true) + if (PlatformMapping.Extensions.UniqueFileExtensions.Contains(RomFileInfo.Extension, StringComparer.OrdinalIgnoreCase)) { - if (Signature.Game != null) { Signature.Game.System = PlatformMapping.IGDBName; } - } - Signature.Flags.IGDBPlatformId = PlatformMapping.IGDBId; - Signature.Flags.IGDBPlatformName = PlatformMapping.IGDBName; + if (SetSystemName == true) + { + if (Signature.Game != null) { Signature.Game.System = PlatformMapping.IGDBName; } + } + Signature.Flags.IGDBPlatformId = PlatformMapping.IGDBId; + Signature.Flags.IGDBPlatformName = PlatformMapping.IGDBName; - PlatformFound = true; - break; + PlatformFound = true; + break; + } } } @@ -90,10 +353,19 @@ namespace gaseous_server.Models public class PlatformMapItem { - public int IGDBId { get; set; } + public long IGDBId { get; set; } public string IGDBName { get; set; } + public string IGDBSlug { get; set; } public List AlternateNames { get; set; } = new List(); - public List KnownFileExtensions { get; set; } = new List(); + + public FileExtensions Extensions { get; set; } + public class FileExtensions + { + public List SupportedFileExtensions { get; set; } = new List(); + + public List UniqueFileExtensions { get; set; } = new List(); + } + public string RetroPieDirectoryName { get; set; } public WebEmulatorItem? WebEmulator { get; set; } diff --git a/gaseous-server/Program.cs b/gaseous-server/Program.cs index f496f15..8e240a0 100644 --- a/gaseous-server/Program.cs +++ b/gaseous-server/Program.cs @@ -1,6 +1,7 @@ using System.Reflection; using System.Text.Json.Serialization; using gaseous_server; +using gaseous_server.Models; using gaseous_server.SignatureIngestors.XML; using gaseous_tools; using Microsoft.AspNetCore.Http.Features; @@ -141,8 +142,8 @@ Config.LibraryConfiguration.InitLibrary(); gaseous_server.Classes.Metadata.Games.GetGame(0, false, false, false); gaseous_server.Classes.Metadata.Platforms.GetPlatform(0); -// organise library -//gaseous_server.Classes.ImportGame.OrganiseLibrary(); +// extract platform map if not present +PlatformMapping.ExtractPlatformMap(); // add background tasks ProcessQueue.QueueItems.Add(new ProcessQueue.QueueItem(ProcessQueue.QueueItemType.SignatureIngestor, 60)); diff --git a/gaseous-server/Support/PlatformMap.json b/gaseous-server/Support/PlatformMap.json index 1556681..f621b51 100644 --- a/gaseous-server/Support/PlatformMap.json +++ b/gaseous-server/Support/PlatformMap.json @@ -1,733 +1,859 @@ [ { - "IGDBId": 50, - "IGDBName": "3DO Interactive Multiplayer", - "RetroPieDirectoryName": "3do", - "AlternateNames": [ - "3DO", - "3DO Interactive Multiplayer" - ], - "KnownFileExtensions": [ - - ], - "WebEmulator": { - "Type": "EmulatorJS", - "Core": "3do" + "IGDBId": 50, + "IGDBName": "3DO Interactive Multiplayer", + "IGDBSlug": "3do", + "AlternateNames": [ + "3DO", + "3DO Interactive Multiplayer" + ], + "Extensions": { + "SupportedFileExtensions": [], + "UniqueFileExtensions": [] + }, + "RetroPieDirectoryName": "3do", + "WebEmulator": { + "Type": "EmulatorJS", + "Core": "3do" + }, + "Bios": [ + { + "hash": "f47264dd47fe30f73ab3c010015c155b", + "description": "Panasonic FZ-1", + "filename": "panafz1.bin" }, - "Bios": [ - { - "hash": "f47264dd47fe30f73ab3c010015c155b", - "description": "Panasonic FZ-1", - "filename": "panafz1.bin" - }, - { - "hash": "51f2f43ae2f3508a14d9f56597e2d3ce", - "description": "Panasonic FZ-10", - "filename": "panafz10.bin" - }, - { - "hash": "1477bda80dc33731a65468c1f5bcbee9", - "description": "Panasonic FZ-10 [RSA Patch]", - "filename": "panafz10-norsa.bin" - }, - { - "hash": "a48e6746bd7edec0f40cff078f0bb19f", - "description": "Panasonic FZ-10-E [Anvil]", - "filename": "panafz10e-anvil.bin" - }, - { - "hash": "cf11bbb5a16d7af9875cca9de9a15e09", - "description": "Panasonic FZ-10-E [Anvil RSA Patch]", - "filename": "panafz10e-anvil-norsa.bin" - }, - { - "hash": "a496cfdded3da562759be3561317b605", - "description": "Panasonic FZ-1J", - "filename": "panafz1j.bin" - }, - { - "hash": "f6c71de7470d16abe4f71b1444883dc8", - "description": "Panasonic FZ-1J [RSA Patch]", - "filename": "panafz1j-norsa.bin" - }, - { - "hash": "8639fd5e549bd6238cfee79e3e749114", - "description": "Goldstar GDO-101M", - "filename": "goldstar.bin" - }, - { - "hash": "35fa1a1ebaaeea286dc5cd15487c13ea", - "description": "Sanyo IMP-21J TRY", - "filename": "sanyotry.bin" - }, - { - "hash": "8970fc987ab89a7f64da9f8a8c4333ff", - "description": "Shootout At Old Tucson", - "filename": "3do_arcade_saot.bin" - } - ] - }, - { - "IGDBId": 16, - "IGDBName": "Amiga", - "RetroPieDirectoryName": "amiga", - "AlternateNames": [ - "Amiga", - "Commodore Amiga" - ], - "KnownFileExtensions": [ - ".ADF", - ".ADZ", - ".DMS", - ".UAE", - ".HDF", - ".HDZ" - ], - "Bios": [ - { - "hash": "85ad74194e87c08904327de1a9443b7a", - "description": "Kickstart v1.2 rev 33.180", - "filename": "kick33180.A500" - }, - { - "hash": "82a21c1890cae844b3df741f2762d48d", - "description": "Kickstart v1.3 rev 34.005", - "filename": "kick34005.A500" - }, - { - "hash": "dc10d7bdd1b6f450773dfb558477c230", - "description": "Kickstart v2.04 rev 37.175", - "filename": "kick37175.A500" - }, - { - "hash": "e40a5dfb3d017ba8779faba30cbd1c8e", - "description": "Kickstart v3.1 rev 40.063", - "filename": "kick40063.A600" - }, - { - "hash": "646773759326fbac3b2311fd8c8793ee", - "description": "Kickstart v3.1 rev 40.068", - "filename": "kick40068.A1200" - }, - { - "hash": "9bdedde6a4f33555b4a270c8ca53297d", - "description": "Kickstart v3.1 rev 40.068", - "filename": "kick40068.A4000" - }, - { - "hash": "89da1838a24460e4b93f4f0c5d92d48d", - "description": "CDTV extended ROM v1.00", - "filename": "kick34005.CDTV" - }, - { - "hash": "5f8924d013dd57a89cf349f4cdedc6b1", - "description": "CD32 Kickstart v3.1 rev 40.060", - "filename": "kick40060.CD32" - }, - { - "hash": "bb72565701b1b6faece07d68ea5da639", - "description": "CD32 extended ROM rev 40.060", - "filename": "kick40060.CD32.ext" - }, - { - "hash": "f2f241bf094168cfb9e7805dc2856433", - "description": "CD32 KS + extended v3.1 rev 40.060", - "filename": "kick40060.CD32" - } - ] - }, - { - "IGDBId": 25, - "IGDBName": "Amstrad CPC", - "RetroPieDirectoryName": "amstradcpc", - "AlternateNames": [ - "Amstrad CPC" - ], - "KnownFileExtensions": [ - ".CPC" - ], - "Bios": [ - ] - }, - { - "IGDBId": 75, - "IGDBName": "Apple II", - "RetroPieDirectoryName": "apple2", - "AlternateNames": [ - "Apple II", - "Apple ][", - "Apple 2" - ], - "KnownFileExtensions": [ - ], - "Bios": [ - ] - }, - { - "IGDBId": 59, - "IGDBName": "Atari 2600", - "RetroPieDirectoryName": "atari2600", - "AlternateNames": [ - "Atari 2600", - "Atari VCS" - ], - "KnownFileExtensions": [ - ".A26" - ], - "WebEmulator": { - "Type": "EmulatorJS", - "Core": "atari2600" + { + "hash": "51f2f43ae2f3508a14d9f56597e2d3ce", + "description": "Panasonic FZ-10", + "filename": "panafz10.bin" }, - "Bios": [ - ] - }, - { - "IGDBId": 60, - "IGDBName": "Atari 7800", - "RetroPieDirectoryName": "atari7800", - "AlternateNames": [ - "Atari 7800 ProSystem", - "Atari VCS" - ], - "KnownFileExtensions": [ - ".A78" - ], - "WebEmulator": { - "Type": "EmulatorJS", - "Core": "atari7800" + { + "hash": "1477bda80dc33731a65468c1f5bcbee9", + "description": "Panasonic FZ-10 [RSA Patch]", + "filename": "panafz10-norsa.bin" }, - "Bios": [ - { - "hash": "0763f1ffb006ddbe32e52d497ee848ae", - "description": "7800 BIOS - Optional", - "filename": "7800 BIOS (U).rom" - } - ] - }, - { - "IGDBId": 66, - "IGDBName": "Atari 5200", - "RetroPieDirectoryName": "atari5200", - "AlternateNames": [ - "Atari 5200", - "Atari 5200 SuperSystem" - ], - "KnownFileExtensions": [ - ".A52" - ], - "WebEmulator": { - "Type": "EmulatorJS", - "Core": "a5200" + { + "hash": "a48e6746bd7edec0f40cff078f0bb19f", + "description": "Panasonic FZ-10-E [Anvil]", + "filename": "panafz10e-anvil.bin" }, - "Bios": [ - { - "hash": "281f20ea4320404ec820fb7ec0693b38", - "description": "BIOS for the Atari 5200", - "filename": "5200.rom" - } - ] - }, - { - "IGDBId": 62, - "IGDBName": "Atari Jaguar", - "RetroPieDirectoryName": "atarijaguar", - "AlternateNames": [ - "Atari Jaguar", - "Jaguar" - ], - "KnownFileExtensions": [ - ".J64", - ".JAG" - ], - "WebEmulator": { - "Type": "EmulatorJS", - "Core": "jaguar" + { + "hash": "cf11bbb5a16d7af9875cca9de9a15e09", + "description": "Panasonic FZ-10-E [Anvil RSA Patch]", + "filename": "panafz10e-anvil-norsa.bin" }, - "Bios": [ - ] - }, - { - "IGDBId": 61, - "IGDBName": "Atari Lynx", - "RetroPieDirectoryName": "atarilynx", - "AlternateNames": [ - "Atari Lynx", - "Lynx" - ], - "KnownFileExtensions": [ - ".LNX" - ], - "WebEmulator": { - "Type": "EmulatorJS", - "Core": "lynx" + { + "hash": "a496cfdded3da562759be3561317b605", + "description": "Panasonic FZ-1J", + "filename": "panafz1j.bin" }, - "Bios": [ - { - "hash": "fcd403db69f54290b51035d82f835e7b", - "description": "BIOS for the Atari Lynx", - "filename": "lynxboot.img" - } - ] - }, - { - "IGDBId": 63, - "IGDBName": "Atari ST/STE", - "RetroPieDirectoryName": "atarist", - "AlternateNames": [ - "Atari ST/STE", - "Atari ST", - "Atari STE" - ], - "KnownFileExtensions": [ - ".ST", - ".STX", - ".MSA" - ], - "Bios": [ - { - "hash": "C1C57CE48E8EE4135885CEE9E63A68A2", - "description": "TOS", - "filename": "tos.img" - } - ] - }, - { - "IGDBId": 15, - "IGDBName": "Commodore C64/128/MAX", - "RetroPieDirectoryName": "c64", - "AlternateNames": [ - "C64", - "Commodore C64", - "Commodore C128", - "Commodore C64DTV", - "Commodore C64/128/MAX" - ], - "KnownFileExtensions": [ - ".C64", - ".CRT", - ".D64", - ".D71", - ".D81", - ".G64", - ".PRG", - ".T64", - ".TAP" - ], - "Bios": [ - { - "hash": "be09394f0576cf81fa8bacf634daf9a2", - "description": "JiffyDOS C64 Kernal", - "filename": "JiffyDOS_C64.bin" - }, - { - "hash": "1b1e985ea5325a1f46eb7fd9681707bf", - "description": "JiffyDOS 1541 drive BIOS", - "filename": "JiffyDOS_1541-II.bin" - }, - { - "hash": "41c6cc528e9515ffd0ed9b180f8467c0", - "description": "JiffyDOS 1571 drive BIOS", - "filename": "JiffyDOS_1571_repl310654.bin" - }, - { - "hash": "20b6885c6dc2d42c38754a365b043d71", - "description": "JiffyDOS 1581 drive BIOS", - "filename": "JiffyDOS_1581.bin" - } - ] - }, - { - "IGDBId": 24, - "IGDBName": "Game Boy Advance", - "RetroPieDirectoryName": "gba", - "AlternateNames": [ - "GBA", - "Game Boy Advance" - ], - "KnownFileExtensions": [ - ".GBA" - ], - "Bios": [ - { - "hash": "a860e8c0b6d573d191e4ec7db1b1e4f6", - "description": "[BIOS] Game Boy Advance (World).gba", - "filename": "gba_bios.bin" - }, - { - "hash": "32FBBD84168D3482956EB3C5051637F5", - "description": "[BIOS] Nintendo Game Boy Boot ROM (World) (Rev 1).gb", - "filename": "gb_bios.bin" - }, - { - "hash": "DBFCE9DB9DEAA2567F6A84FDE55F9680", - "description": "[BIOS] Nintendo Game Boy Color Boot ROM (World).gbc", - "filename": "gbc_bios.bin" - }, - { - "hash": "D574D4F9C12F305074798F54C091A8B4", - "description": "SGB-CPU (World) (Enhancement Chip).bin", - "filename": "sgb_bios.bin" - } - ] - }, - { - "IGDBId": 22, - "IGDBName": "Game Boy Color", - "RetroPieDirectoryName": "gbc", - "AlternateNames": [ - "GBC", - "Game Boy Color" - ], - "KnownFileExtensions": [ - ".GBC" - ], - "Bios": [ - { - "hash": "dbfce9db9deaa2567f6a84fde55f9680", - "description": "BIOS for Game Boy Color", - "filename": "gbc_bios.bin" - }, - { - "hash": "d574d4f9c12f305074798f54c091a8b4", - "description": "Super Game Boy", - "filename": "sgb_bios.bin" - } - ] - }, - { - "IGDBId": 33, - "IGDBName": "Game Boy", - "RetroPieDirectoryName": "gb", - "AlternateNames": [ - "GB", - "Game Boy" - ], - "KnownFileExtensions": [ - ".GB" - ], - "Bios": [ - { - "hash": "32fbbd84168d3482956eb3c5051637f5and", - "description": "BIOS for Game Boy", - "filename": "gb_bios.bin" - }, - { - "hash": "d574d4f9c12f305074798f54c091a8b4", - "description": "Super Game Boy", - "filename": "sgb_bios.bin" - } - ] - }, - { - "IGDBId": 21, - "IGDBName": "Nintendo GameCube", - "RetroPieDirectoryName": "gc", - "AlternateNames": [ - "GC", - "GameCube", - "Nintendo GameCube" - ], - "KnownFileExtensions": [ - ".GC" - ], - "Bios": [ - ] - }, - { - "IGDBId": 35, - "IGDBName": "Sega Game Gear", - "RetroPieDirectoryName": "gamegear", - "AlternateNames": [ - "GG", - "Game Gear", - "Sega Game Gear" - ], - "KnownFileExtensions": [ - ".GG" - ], - "Bios": [ - { - "hash": "672e104c3be3a238301aceffc3b23fd6", - "description": "GameGear BIOS (bootrom) - Optional", - "filename": "bios.gg" - } - ] - }, - { - "IGDBId": 29, - "IGDBName": "Sega Mega Drive/Genesis", - "RetroPieDirectoryName": "megadrive", - "AlternateNames": [ - "Sega Mega Drive/Genesis", - "Sega Mega Drive", - "Sega Genesis", - "Sega Mega Drive & Genesis" - ], - "KnownFileExtensions": [ - ".GEN", - ".MD", - ".SG", - ".SMD" - ], - "WebEmulator": { - "Type": "EmulatorJS", - "Core": "segaMD" + { + "hash": "f6c71de7470d16abe4f71b1444883dc8", + "description": "Panasonic FZ-1J [RSA Patch]", + "filename": "panafz1j-norsa.bin" }, - "Bios": [ - { - "hash": "45e298905a08f9cfb38fd504cd6dbc84", - "description": "MegaDrive TMSS startup ROM", - "filename": "bios_MD.bin" - } - ] - }, - { - "IGDBId": 64, - "IGDBName": "Sega Master System/Mark III", - "RetroPieDirectoryName": "mastersystem", - "AlternateNames": [ - "Sega Master System/Mark III", - "Sega Master System", - "Sega Mark III & Master System" - ], - "KnownFileExtensions": [ - ".SMS" - ], - "WebEmulator": { - "Type": "EmulatorJS", - "Core": "segaMS" + { + "hash": "8639fd5e549bd6238cfee79e3e749114", + "description": "Goldstar GDO-101M", + "filename": "goldstar.bin" }, - "Bios": [ - { - "hash": "840481177270d5642a14ca71ee72844c", - "description": "MasterSystem EU BIOS", - "filename": "bios_E.sms", - "region": "EU" - }, - { - "hash": "840481177270d5642a14ca71ee72844c", - "description": "MasterSystem US BIOS", - "filename": "bios_U.sms", - "region": "US" - }, - { - "hash": "24a519c53f67b00640d0048ef7089105", - "description": "MasterSystem JP BIOS", - "filename": "bios_J.sms", - "region": "JP" - } - ] - }, - { - "IGDBId": 4, - "IGDBName": "Nintendo 64", - "RetroPieDirectoryName": "n64", - "AlternateNames": [ - "Nintendo 64", - "N64" - ], - "KnownFileExtensions": [ - ".Z64", - ".V64", - ".N64" - ], - "WebEmulator": { - "Type": "EmulatorJS", - "Core": "n64" + { + "hash": "35fa1a1ebaaeea286dc5cd15487c13ea", + "description": "Sanyo IMP-21J TRY", + "filename": "sanyotry.bin" + }, + { + "hash": "8970fc987ab89a7f64da9f8a8c4333ff", + "description": "Shootout At Old Tucson", + "filename": "3do_arcade_saot.bin" } + ] }, { - "IGDBId": 18, - "IGDBName": "Nintendo Entertainment System", - "RetroPieDirectoryName": "nes", - "AlternateNames": [ - "Nintendo Entertainment System", - "NES", - "Nintendo Famicom & Entertainment System" + "IGDBId": 16, + "IGDBName": "Amiga", + "IGDBSlug": "amiga", + "AlternateNames": [ + "Amiga", + "Commodore Amiga" + ], + "Extensions": { + "SupportedFileExtensions": [ + ".ADF", + ".ADZ", + ".DMS", + ".UAE", + ".HDF", + ".HDZ" ], - "KnownFileExtensions": [ - ".NES", - ".NEZ", - ".UNF", - ".UNIF" - ], - "WebEmulator": { - "Type": "EmulatorJS", - "Core": "nes" + "UniqueFileExtensions": [] + }, + "RetroPieDirectoryName": "amiga", + "WebEmulator": null, + "Bios": [ + { + "hash": "85ad74194e87c08904327de1a9443b7a", + "description": "Kickstart v1.2 rev 33.180", + "filename": "kick33180.A500" }, - "Bios": [ - { - "hash": "ca30b50f880eb660a320674ed365ef7a", - "description": "Family Computer Disk System BIOS - Required for Famicom Disk System emulation", - "filename": "disksys.rom" - }, - { - "hash": "7f98d77d7a094ad7d069b74bd553ec98", - "description": "Game Genie add-on cartridge - Required for Game Genei Add-on emulation (Only supported on the fceumm core)", - "filename": "gamegenie.nes" - } - ] - }, - { - "IGDBId": 7, - "IGDBName": "PlayStation", - "RetroPieDirectoryName": "psx", - "AlternateNames": [ - "Sony PlayStation", - "PS1", - "PSX", - "PSOne", - "PS" - ], - "KnownFileExtensions": [], - "WebEmulator": { - "Type": "EmulatorJS", - "Core": "psx" + { + "hash": "82a21c1890cae844b3df741f2762d48d", + "description": "Kickstart v1.3 rev 34.005", + "filename": "kick34005.A500" }, - "Bios": [ - { - "hash": "8dd7d5296a650fac7319bce665a6a53c", - "description": "PS1 JP BIOS - Required for JP games", - "filename": "scph5500.bin", - "region": "JP" - }, - { - "hash": "490f666e1afb15b7362b406ed1cea246", - "description": "PS1 US BIOS - Required for US games", - "filename": "scph5501.bin", - "region": "US" - }, - { - "hash": "32736f17079d0b2b7024407c39bd3050", - "description": "PS1 EU BIOS - Required for EU games", - "filename": "scph5502.bin", - "region": "EU" - } - ] - }, - { - "IGDBId": 32, - "IGDBName": "Sega Saturn", - "RetroPieDirectoryName": "saturn", - "AlternateNames": [ - "Sega Saturn", - "JVC Saturn", - "Hi-Saturn", - "Samsung Saturn", - "V-Saturn", - "Saturn" - ], - "KnownFileExtensions": [], - "WebEmulator": { - "Type": "EmulatorJS", - "Core": "segaSaturn" + { + "hash": "dc10d7bdd1b6f450773dfb558477c230", + "description": "Kickstart v2.04 rev 37.175", + "filename": "kick37175.A500" }, - "Bios": [ - { - "hash": "af5828fdff51384f99b3c4926be27762", - "description": "Saturn BIOS", - "filename": "saturn_bios.bin" - } - ] - }, - { - "IGDBId": 30, - "IGDBName": "Sega 32X", - "RetroPieDirectoryName": "sega32x", - "AlternateNames": [ - "Sega 32X", - "Sega32", - "Sega32X" - ], - "KnownFileExtensions": [], - "WebEmulator": { - "Type": "EmulatorJS", - "Core": "sega32x" + { + "hash": "e40a5dfb3d017ba8779faba30cbd1c8e", + "description": "Kickstart v3.1 rev 40.063", + "filename": "kick40063.A600" }, - "Bios": [] - }, - { - "IGDBId": 78, - "IGDBName": "Sega CD", - "RetroPieDirectoryName": "segacd", - "AlternateNames": [ - "Sega CD", - "Mega CD", - "segacd", - "Sega Mega-CD & Sega CD" - ], - "KnownFileExtensions": [], - "WebEmulator": { - "Type": "EmulatorJS", - "Core": "segaCD" + { + "hash": "646773759326fbac3b2311fd8c8793ee", + "description": "Kickstart v3.1 rev 40.068", + "filename": "kick40068.A1200" }, - "Bios": [ - { - "hash": "e66fa1dc5820d254611fdcdba0662372", - "description": "MegaCD EU BIOS - Required", - "filename": "bios_CD_E.bin", - "region": "EU" - }, - { - "hash": "2efd74e3232ff260e371b99f84024f7f", - "description": "SegaCD US BIOS - Required", - "filename": "bios_CD_U.bin", - "region": "US" - }, - { - "hash": "278a9397d192149e84e820ac621a8edd", - "description": "MegaCD JP BIOS - Required", - "filename": "bios_CD_J.bin", - "region": "JP" - } - ] - }, - { - "IGDBId": 19, - "IGDBName": "Super Nintendo Entertainment System", - "RetroPieDirectoryName": "snes", - "AlternateNames": [ - "Nintendo Super Famicom & Super Entertainment System", - "Super Nintendo Entertainment System", - "Super Nintendo", - "SNES" - ], - "KnownFileExtensions": [ - ".SFC", - ".SMC" - ], - "WebEmulator": { - "Type": "EmulatorJS", - "Core": "snes" + { + "hash": "9bdedde6a4f33555b4a270c8ca53297d", + "description": "Kickstart v3.1 rev 40.068", + "filename": "kick40068.A4000" + }, + { + "hash": "89da1838a24460e4b93f4f0c5d92d48d", + "description": "CDTV extended ROM v1.00", + "filename": "kick34005.CDTV" + }, + { + "hash": "5f8924d013dd57a89cf349f4cdedc6b1", + "description": "CD32 Kickstart v3.1 rev 40.060", + "filename": "kick40060.CD32" + }, + { + "hash": "bb72565701b1b6faece07d68ea5da639", + "description": "CD32 extended ROM rev 40.060", + "filename": "kick40060.CD32.ext" + }, + { + "hash": "f2f241bf094168cfb9e7805dc2856433", + "description": "CD32 KS + extended v3.1 rev 40.060", + "filename": "kick40060.CD32" } + ] }, { - "IGDBId": 26, - "IGDBName": "ZX Spectrum", - "RetroPieDirectoryName": "zxspectrum", - "AlternateNames": [ - "ZX Spectrum", - "Sinclair ZX", - "Sinclair ZX Spectrum" + "IGDBId": 25, + "IGDBName": "Amstrad CPC", + "IGDBSlug": "acpc", + "AlternateNames": [ + "Amstrad CPC", + "Colour Personal Computer", + "acpc" + ], + "Extensions": { + "SupportedFileExtensions": [ + ".CPC" ], - "KnownFileExtensions": [ - ".SNA", - ".SZX", - ".Z80", - ".SCL" - ] + "UniqueFileExtensions": [] + }, + "RetroPieDirectoryName": "amstradcpc", + "WebEmulator": null, + "Bios": [] }, { - "IGDBId": 52, - "IGDBName": "Arcade", - "AlternateNames": [ - "Arcade" + "IGDBId": 75, + "IGDBName": "Apple II", + "IGDBSlug": "appleii", + "AlternateNames": [ + "Apple II", + "Apple ][", + "Apple 2", + "appleii" + ], + "Extensions": { + "SupportedFileExtensions": [], + "UniqueFileExtensions": [] + }, + "RetroPieDirectoryName": "apple2", + "WebEmulator": null, + "Bios": [] + }, + { + "IGDBId": 52, + "IGDBName": "Arcade", + "IGDBSlug": "arcade", + "AlternateNames": [ + "Arcade" + ], + "Extensions": { + "SupportedFileExtensions": [], + "UniqueFileExtensions": [] + }, + "RetroPieDirectoryName": null, + "WebEmulator": { + "Type": "EmulatorJS", + "Core": "arcade" + }, + "Bios": [] + }, + { + "IGDBId": 59, + "IGDBName": "Atari 2600", + "IGDBSlug": "atari2600", + "AlternateNames": [ + "Atari 2600", + "Atari VCS", + "atari2600" + ], + "Extensions": { + "SupportedFileExtensions": [ + ".A26" ], - "KnownFileExtensions": [], - "WebEmulator": { - "Type": "EmulatorJS", - "Core": "arcade" + "UniqueFileExtensions": [] + }, + "RetroPieDirectoryName": "atari2600", + "WebEmulator": { + "Type": "EmulatorJS", + "Core": "atari2600" + }, + "Bios": [] + }, + { + "IGDBId": 66, + "IGDBName": "Atari 5200", + "IGDBSlug": "atari5200", + "AlternateNames": [ + "Atari 5200", + "Atari 5200 SuperSystem", + "atari5200" + ], + "Extensions": { + "SupportedFileExtensions": [ + ".A52" + ], + "UniqueFileExtensions": [] + }, + "RetroPieDirectoryName": "atari5200", + "WebEmulator": { + "Type": "EmulatorJS", + "Core": "a5200" + }, + "Bios": [ + { + "hash": "281f20ea4320404ec820fb7ec0693b38", + "description": "BIOS for the Atari 5200", + "filename": "5200.rom" + } + ] + }, + { + "IGDBId": 60, + "IGDBName": "Atari 7800", + "IGDBSlug": "atari7800", + "AlternateNames": [ + "Atari 7800 ProSystem", + "Atari VCS", + "Atari 7800", + "atari7800" + ], + "Extensions": { + "SupportedFileExtensions": [ + ".A78" + ], + "UniqueFileExtensions": [] + }, + "RetroPieDirectoryName": "atari7800", + "WebEmulator": { + "Type": "EmulatorJS", + "Core": "atari7800" + }, + "Bios": [ + { + "hash": "0763f1ffb006ddbe32e52d497ee848ae", + "description": "7800 BIOS - Optional", + "filename": "7800 BIOS (U).rom" + } + ] + }, + { + "IGDBId": 62, + "IGDBName": "Atari Jaguar", + "IGDBSlug": "jaguar", + "AlternateNames": [ + "Atari Jaguar", + "Jaguar" + ], + "Extensions": { + "SupportedFileExtensions": [ + ".J64", + ".JAG" + ], + "UniqueFileExtensions": [] + }, + "RetroPieDirectoryName": "atarijaguar", + "WebEmulator": { + "Type": "EmulatorJS", + "Core": "jaguar" + }, + "Bios": [] + }, + { + "IGDBId": 61, + "IGDBName": "Atari Lynx", + "IGDBSlug": "lynx", + "AlternateNames": [ + "Atari Lynx", + "Lynx" + ], + "Extensions": { + "SupportedFileExtensions": [ + ".LNX" + ], + "UniqueFileExtensions": [] + }, + "RetroPieDirectoryName": "atarilynx", + "WebEmulator": { + "Type": "EmulatorJS", + "Core": "lynx" + }, + "Bios": [ + { + "hash": "fcd403db69f54290b51035d82f835e7b", + "description": "BIOS for the Atari Lynx", + "filename": "lynxboot.img" + } + ] + }, + { + "IGDBId": 63, + "IGDBName": "Atari ST/STE", + "IGDBSlug": "atari-st", + "AlternateNames": [ + "Atari ST/STE", + "Atari ST", + "Atari STE", + "atari-st" + ], + "Extensions": { + "SupportedFileExtensions": [ + ".ST", + ".STX", + ".MSA" + ], + "UniqueFileExtensions": [] + }, + "RetroPieDirectoryName": "atarist", + "WebEmulator": null, + "Bios": [ + { + "hash": "C1C57CE48E8EE4135885CEE9E63A68A2", + "description": "TOS", + "filename": "tos.img" + } + ] + }, + { + "IGDBId": 15, + "IGDBName": "Commodore C64/128/MAX", + "IGDBSlug": "c64", + "AlternateNames": [ + "C64", + "Commodore C64", + "Commodore C128", + "Commodore C64DTV", + "Commodore C64/128/MAX", + "C64/C128/MAX" + ], + "Extensions": { + "SupportedFileExtensions": [ + ".C64", + ".CRT", + ".D64", + ".D71", + ".D81", + ".G64", + ".PRG", + ".T64", + ".TAP" + ], + "UniqueFileExtensions": [] + }, + "RetroPieDirectoryName": "c64", + "WebEmulator": null, + "Bios": [ + { + "hash": "be09394f0576cf81fa8bacf634daf9a2", + "description": "JiffyDOS C64 Kernal", + "filename": "JiffyDOS_C64.bin" }, - "Bios": [] + { + "hash": "1b1e985ea5325a1f46eb7fd9681707bf", + "description": "JiffyDOS 1541 drive BIOS", + "filename": "JiffyDOS_1541-II.bin" + }, + { + "hash": "41c6cc528e9515ffd0ed9b180f8467c0", + "description": "JiffyDOS 1571 drive BIOS", + "filename": "JiffyDOS_1571_repl310654.bin" + }, + { + "hash": "20b6885c6dc2d42c38754a365b043d71", + "description": "JiffyDOS 1581 drive BIOS", + "filename": "JiffyDOS_1581.bin" + } + ] + }, + { + "IGDBId": 33, + "IGDBName": "Game Boy", + "IGDBSlug": "gb", + "AlternateNames": [ + "GB", + "Game Boy" + ], + "Extensions": { + "SupportedFileExtensions": [ + ".GB" + ], + "UniqueFileExtensions": [] + }, + "RetroPieDirectoryName": "gb", + "WebEmulator": null, + "Bios": [ + { + "hash": "32fbbd84168d3482956eb3c5051637f5and", + "description": "BIOS for Game Boy", + "filename": "gb_bios.bin" + }, + { + "hash": "d574d4f9c12f305074798f54c091a8b4", + "description": "Super Game Boy", + "filename": "sgb_bios.bin" + } + ] + }, + { + "IGDBId": 24, + "IGDBName": "Game Boy Advance", + "IGDBSlug": "gba", + "AlternateNames": [ + "GBA", + "Game Boy Advance" + ], + "Extensions": { + "SupportedFileExtensions": [ + ".GBA" + ], + "UniqueFileExtensions": [] + }, + "RetroPieDirectoryName": "gba", + "WebEmulator": null, + "Bios": [ + { + "hash": "a860e8c0b6d573d191e4ec7db1b1e4f6", + "description": "[BIOS] Game Boy Advance (World).gba", + "filename": "gba_bios.bin" + }, + { + "hash": "32FBBD84168D3482956EB3C5051637F5", + "description": "[BIOS] Nintendo Game Boy Boot ROM (World) (Rev 1).gb", + "filename": "gb_bios.bin" + }, + { + "hash": "DBFCE9DB9DEAA2567F6A84FDE55F9680", + "description": "[BIOS] Nintendo Game Boy Color Boot ROM (World).gbc", + "filename": "gbc_bios.bin" + }, + { + "hash": "D574D4F9C12F305074798F54C091A8B4", + "description": "SGB-CPU (World) (Enhancement Chip).bin", + "filename": "sgb_bios.bin" + } + ] + }, + { + "IGDBId": 22, + "IGDBName": "Game Boy Color", + "IGDBSlug": "gbc", + "AlternateNames": [ + "GBC", + "Game Boy Color" + ], + "Extensions": { + "SupportedFileExtensions": [ + ".GBC" + ], + "UniqueFileExtensions": [] + }, + "RetroPieDirectoryName": "gbc", + "WebEmulator": null, + "Bios": [ + { + "hash": "dbfce9db9deaa2567f6a84fde55f9680", + "description": "BIOS for Game Boy Color", + "filename": "gbc_bios.bin" + }, + { + "hash": "d574d4f9c12f305074798f54c091a8b4", + "description": "Super Game Boy", + "filename": "sgb_bios.bin" + } + ] + }, + { + "IGDBId": 4, + "IGDBName": "Nintendo 64", + "IGDBSlug": "n64", + "AlternateNames": [ + "Nintendo 64", + "N64" + ], + "Extensions": { + "SupportedFileExtensions": [ + ".Z64", + ".V64", + ".N64" + ], + "UniqueFileExtensions": [] + }, + "RetroPieDirectoryName": "n64", + "WebEmulator": { + "Type": "EmulatorJS", + "Core": "n64" + }, + "Bios": null + }, + { + "IGDBId": 18, + "IGDBName": "Nintendo Entertainment System", + "IGDBSlug": "nes", + "AlternateNames": [ + "Nintendo Entertainment System", + "NES", + "Nintendo Famicom & Entertainment System" + ], + "Extensions": { + "SupportedFileExtensions": [ + ".NES", + ".NEZ", + ".UNF", + ".UNIF" + ], + "UniqueFileExtensions": [] + }, + "RetroPieDirectoryName": "nes", + "WebEmulator": { + "Type": "EmulatorJS", + "Core": "nes" + }, + "Bios": [ + { + "hash": "ca30b50f880eb660a320674ed365ef7a", + "description": "Family Computer Disk System BIOS - Required for Famicom Disk System emulation", + "filename": "disksys.rom" + }, + { + "hash": "7f98d77d7a094ad7d069b74bd553ec98", + "description": "Game Genie add-on cartridge - Required for Game Genei Add-on emulation (Only supported on the fceumm core)", + "filename": "gamegenie.nes" + } + ] + }, + { + "IGDBId": 21, + "IGDBName": "Nintendo GameCube", + "IGDBSlug": "ngc", + "AlternateNames": [ + "GC", + "GameCube", + "Nintendo GameCube", + "GCN", + "ngc" + ], + "Extensions": { + "SupportedFileExtensions": [ + ".GC" + ], + "UniqueFileExtensions": [] + }, + "RetroPieDirectoryName": "gc", + "WebEmulator": null, + "Bios": [] + }, + { + "IGDBId": 7, + "IGDBName": "PlayStation", + "IGDBSlug": "ps", + "AlternateNames": [ + "Sony PlayStation", + "PS1", + "PSX", + "PSOne", + "PS", + "PlayStation", + "PSX, PSOne, PS" + ], + "Extensions": { + "SupportedFileExtensions": [], + "UniqueFileExtensions": [] + }, + "RetroPieDirectoryName": "psx", + "WebEmulator": { + "Type": "EmulatorJS", + "Core": "psx" + }, + "Bios": [ + { + "hash": "8dd7d5296a650fac7319bce665a6a53c", + "description": "PS1 JP BIOS - Required for JP games", + "filename": "scph5500.bin" + }, + { + "hash": "490f666e1afb15b7362b406ed1cea246", + "description": "PS1 US BIOS - Required for US games", + "filename": "scph5501.bin" + }, + { + "hash": "32736f17079d0b2b7024407c39bd3050", + "description": "PS1 EU BIOS - Required for EU games", + "filename": "scph5502.bin" + } + ] + }, + { + "IGDBId": 30, + "IGDBName": "Sega 32X", + "IGDBSlug": "sega32", + "AlternateNames": [ + "Sega 32X", + "Sega32", + "Sega32X" + ], + "Extensions": { + "SupportedFileExtensions": [], + "UniqueFileExtensions": [] + }, + "RetroPieDirectoryName": "sega32x", + "WebEmulator": { + "Type": "EmulatorJS", + "Core": "sega32x" + }, + "Bios": [] + }, + { + "IGDBId": 78, + "IGDBName": "Sega CD", + "IGDBSlug": "segacd", + "AlternateNames": [ + "Sega CD", + "Mega CD", + "segacd", + "Sega Mega-CD & Sega CD" + ], + "Extensions": { + "SupportedFileExtensions": [], + "UniqueFileExtensions": [] + }, + "RetroPieDirectoryName": "segacd", + "WebEmulator": { + "Type": "EmulatorJS", + "Core": "segaCD" + }, + "Bios": [ + { + "hash": "e66fa1dc5820d254611fdcdba0662372", + "description": "MegaCD EU BIOS - Required", + "filename": "bios_CD_E.bin" + }, + { + "hash": "2efd74e3232ff260e371b99f84024f7f", + "description": "SegaCD US BIOS - Required", + "filename": "bios_CD_U.bin" + }, + { + "hash": "278a9397d192149e84e820ac621a8edd", + "description": "MegaCD JP BIOS - Required", + "filename": "bios_CD_J.bin" + } + ] + }, + { + "IGDBId": 35, + "IGDBName": "Sega Game Gear", + "IGDBSlug": "gamegear", + "AlternateNames": [ + "GG", + "Game Gear", + "Sega Game Gear", + "gamegear" + ], + "Extensions": { + "SupportedFileExtensions": [ + ".GG" + ], + "UniqueFileExtensions": [] + }, + "RetroPieDirectoryName": "gamegear", + "WebEmulator": null, + "Bios": [ + { + "hash": "672e104c3be3a238301aceffc3b23fd6", + "description": "GameGear BIOS (bootrom) - Optional", + "filename": "bios.gg" + } + ] + }, + { + "IGDBId": 64, + "IGDBName": "Sega Master System/Mark III", + "IGDBSlug": "sms", + "AlternateNames": [ + "Sega Master System/Mark III", + "Sega Master System", + "Sega Mark III & Master System", + "SMS, Mark III", + "sms" + ], + "Extensions": { + "SupportedFileExtensions": [ + ".SMS" + ], + "UniqueFileExtensions": [] + }, + "RetroPieDirectoryName": "mastersystem", + "WebEmulator": { + "Type": "EmulatorJS", + "Core": "segaMS" + }, + "Bios": [ + { + "hash": "840481177270d5642a14ca71ee72844c", + "description": "MasterSystem EU BIOS", + "filename": "bios_E.sms" + }, + { + "hash": "840481177270d5642a14ca71ee72844c", + "description": "MasterSystem US BIOS", + "filename": "bios_U.sms" + }, + { + "hash": "24a519c53f67b00640d0048ef7089105", + "description": "MasterSystem JP BIOS", + "filename": "bios_J.sms" + } + ] + }, + { + "IGDBId": 29, + "IGDBName": "Sega Mega Drive/Genesis", + "IGDBSlug": "genesis-slash-megadrive", + "AlternateNames": [ + "Sega Mega Drive/Genesis", + "Sega Mega Drive", + "Sega Genesis", + "Sega Mega Drive & Genesis", + "genesis-slash-megadrive" + ], + "Extensions": { + "SupportedFileExtensions": [ + ".GEN", + ".MD", + ".SG", + ".SMD" + ], + "UniqueFileExtensions": [] + }, + "RetroPieDirectoryName": "megadrive", + "WebEmulator": { + "Type": "EmulatorJS", + "Core": "segaMD" + }, + "Bios": [ + { + "hash": "45e298905a08f9cfb38fd504cd6dbc84", + "description": "MegaDrive TMSS startup ROM", + "filename": "bios_MD.bin" + } + ] + }, + { + "IGDBId": 32, + "IGDBName": "Sega Saturn", + "IGDBSlug": "saturn", + "AlternateNames": [ + "Sega Saturn", + "JVC Saturn", + "Hi-Saturn", + "Samsung Saturn", + "V-Saturn", + "Saturn", + "JVC Saturn, Hi-Saturn, Samsung Saturn, V-Saturn" + ], + "Extensions": { + "SupportedFileExtensions": [], + "UniqueFileExtensions": [] + }, + "RetroPieDirectoryName": "saturn", + "WebEmulator": { + "Type": "EmulatorJS", + "Core": "segaSaturn" + }, + "Bios": [ + { + "hash": "af5828fdff51384f99b3c4926be27762", + "description": "Saturn BIOS", + "filename": "saturn_bios.bin" + } + ] + }, + { + "IGDBId": 19, + "IGDBName": "Super Nintendo Entertainment System", + "IGDBSlug": "snes", + "AlternateNames": [ + "Nintendo Super Famicom & Super Entertainment System", + "Super Nintendo Entertainment System", + "Super Nintendo", + "SNES", + "SNES, Super Nintendo" + ], + "Extensions": { + "SupportedFileExtensions": [ + ".SFC", + ".SMC" + ], + "UniqueFileExtensions": [] + }, + "RetroPieDirectoryName": "snes", + "WebEmulator": { + "Type": "EmulatorJS", + "Core": "snes" + }, + "Bios": null + }, + { + "IGDBId": 26, + "IGDBName": "ZX Spectrum", + "IGDBSlug": "zxs", + "AlternateNames": [ + "ZX Spectrum", + "Sinclair ZX", + "Sinclair ZX Spectrum", + "zxs" + ], + "Extensions": { + "SupportedFileExtensions": [ + ".SNA", + ".SZX", + ".Z80", + ".SCL" + ], + "UniqueFileExtensions": [] + }, + "RetroPieDirectoryName": "zxspectrum", + "WebEmulator": null, + "Bios": null } -] + ] \ No newline at end of file diff --git a/gaseous-server/wwwroot/images/map.svg b/gaseous-server/wwwroot/images/map.svg new file mode 100644 index 0000000..8f18a74 --- /dev/null +++ b/gaseous-server/wwwroot/images/map.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/gaseous-server/wwwroot/index.html b/gaseous-server/wwwroot/index.html index af8f68f..5ea9dd0 100644 --- a/gaseous-server/wwwroot/index.html +++ b/gaseous-server/wwwroot/index.html @@ -12,6 +12,7 @@ + diff --git a/gaseous-server/wwwroot/pages/dialogs/platformmapedit.html b/gaseous-server/wwwroot/pages/dialogs/platformmapedit.html new file mode 100644 index 0000000..47470b4 --- /dev/null +++ b/gaseous-server/wwwroot/pages/dialogs/platformmapedit.html @@ -0,0 +1,257 @@ +
+
+

Title Matching

+ + + + + + + + + +
+

Alternative Names

+
+ +
+

Supported File Extensions

+
+ +
+ +

Collections

+ + + + + + + + + + + + +
+

Standard Directory Naming

+
+ +
Note: Standard directory naming uses the IGDB slug for the platform is not editable.
+

RetroPie Directory Naming

+
+ +
+ +

Web Emulator

+ + + + + + + + + + + + + +
+

Web Emulator

+
+ +
+

Engine

+
+ +
+

Core

+
+ +
+ +

BIOS/Firmware

+
+
+
+ +
+ +
+ + + + \ No newline at end of file diff --git a/gaseous-server/wwwroot/pages/game.html b/gaseous-server/wwwroot/pages/game.html index 9a684b6..9edcbda 100644 --- a/gaseous-server/wwwroot/pages/game.html +++ b/gaseous-server/wwwroot/pages/game.html @@ -368,7 +368,7 @@ var platformRow = document.createElement('tr'); var platformHeader = document.createElement('th'); platformHeader.setAttribute('colspan', 6); - platformHeader.innerHTML = '' + result[i].platform.name; + platformHeader.innerHTML = '' + result[i].platform.name; platformRow.appendChild(platformHeader); newTable.appendChild(platformRow); } diff --git a/gaseous-server/wwwroot/pages/settings.html b/gaseous-server/wwwroot/pages/settings.html index 060c7db..08446fb 100644 --- a/gaseous-server/wwwroot/pages/settings.html +++ b/gaseous-server/wwwroot/pages/settings.html @@ -6,6 +6,7 @@
Settings
System
+
Platform Mapping
Firmware
Logs
About
diff --git a/gaseous-server/wwwroot/pages/settings/logs.html b/gaseous-server/wwwroot/pages/settings/logs.html index 20d479e..42b37ba 100644 --- a/gaseous-server/wwwroot/pages/settings/logs.html +++ b/gaseous-server/wwwroot/pages/settings/logs.html @@ -31,7 +31,7 @@ for (var i = 0; i < result.length; i++) { var exceptionString = ''; if (result[i].exceptionValue) { - exceptionString = "

Exception

" + syntaxHighlight(JSON.stringify(result[i].exceptionValue, null, 2)) + "
"; + exceptionString = "

Exception

" + syntaxHighlight(JSON.stringify(result[i].exceptionValue, null, 2)).replace(/\\n/g, "
") + "
"; } var newRow = [ diff --git a/gaseous-server/wwwroot/pages/settings/mapping.html b/gaseous-server/wwwroot/pages/settings/mapping.html new file mode 100644 index 0000000..5e97ae2 --- /dev/null +++ b/gaseous-server/wwwroot/pages/settings/mapping.html @@ -0,0 +1,87 @@ +
+

Platform Mapping

+
+ +

When determining the platform of a ROM or image (which is later used when determining the game title), only the "Unique File Extensions" are used. All other extensions are ignored as they will limit the ability of Gaseous to determine the game title (see https://github.com/gaseous-project/gaseous-server#game-image-title-matching for more information on how matching works).

+ +

This list is pre-populated with some of the more common platforms. New platforms will appear in this list as titles are added.

+ +

+ + + + + +
+ + \ No newline at end of file diff --git a/gaseous-server/wwwroot/scripts/main.js b/gaseous-server/wwwroot/scripts/main.js index d47fda4..9d84989 100644 --- a/gaseous-server/wwwroot/scripts/main.js +++ b/gaseous-server/wwwroot/scripts/main.js @@ -282,4 +282,95 @@ function syntaxHighlight(json) { } return '' + match + ''; }); +} + +function ShowPlatformMappingDialog(platformId) { + showDialog('platformmapedit', platformId); +} + +function CreateEditableTable(TableName, Headers) { + var eDiv = document.createElement('div'); + + var eTable = document.createElement('table'); + eTable.id = 'EditableTable_' + TableName; + eTable.style.width = '100%'; + + var headRow = document.createElement('tr'); + for (var i = 0; i < Headers.length; i++) { + var headCell = document.createElement('th'); + headCell.id = 'EditableTable_' + TableName + '_' + Headers[i].name; + headCell.innerHTML = Headers[i].label; + headRow.appendChild(headCell); + } + eTable.appendChild(headRow); + + eDiv.appendChild(eTable); + + // add more button + var addButton = document.createElement('button'); + addButton.value = 'Add Row'; + addButton.innerHTML = 'Add Row'; + + $(addButton).click(function() { + eTable.appendChild(AddEditableTableRow(Headers)); + }); + + eDiv.appendChild(addButton); + + return eDiv; +} + +function AddEditableTableRow(Headers) { + var uniqueId = Math.floor(Math.random() * Date.now()); + + var row = document.createElement('tr'); + row.setAttribute('id', uniqueId); + for (var i = 0; i < Headers.length; i++) { + var cell = document.createElement('td'); + + var input = document.createElement('input'); + input.type = 'text'; + input.setAttribute('data-cell', Headers[i].name); + input.style.width = '95%'; + + cell.appendChild(input); + row.appendChild(cell); + } + + // delete button + var delButtonCell = document.createElement('td'); + delButtonCell.style.textAlign = 'right'; + var delButton = document.createElement('button'); + delButton.value = 'Delete'; + delButton.innerHTML = 'Delete'; + delButton.setAttribute('onclick', 'document.getElementById("' + uniqueId + '").remove();'); + + delButtonCell.appendChild(delButton); + row.appendChild(delButtonCell); + + return row; +} + +function LoadEditableTableData(TableName, Headers, Values) { + var eTable = document.getElementById('EditableTable_' + TableName); + + for (var i = 0; i < Values.length; i++) { + // get new row + var row = AddEditableTableRow(Headers); + for (var v = 0; v < row.childNodes.length; v++) { + // looking at the cells here + var cell = row.childNodes[v]; + for (var c = 0; c < cell.childNodes.length; c++) { + if (cell.childNodes[c].getAttribute('data-cell')) { + var nodeName = cell.childNodes[c].getAttribute('data-cell'); + if (Values[i][nodeName]) { + row.childNodes[v].childNodes[c].value = Values[i][nodeName]; + } + break; + } + } + } + + eTable.appendChild(row); + } } \ No newline at end of file diff --git a/gaseous-server/wwwroot/scripts/simpleUpload.min.js b/gaseous-server/wwwroot/scripts/simpleUpload.min.js new file mode 100644 index 0000000..322bc86 --- /dev/null +++ b/gaseous-server/wwwroot/scripts/simpleUpload.min.js @@ -0,0 +1,13 @@ +/* + * simpleUpload.js v.1.1 + * + * Copyright 2018, Michael Brook, All rights reserved. + * http://simpleupload.michaelcbrook.com/ + * + * simpleUpload.js is an extremely simple yet powerful jQuery file upload plugin. + * It is free to use under the MIT License (http://opensource.org/licenses/MIT) + * + * https://github.com/michaelcbrook/simpleUpload.js + * @michaelcbrook + */ +function simpleUpload(e,l,n){var t=!1,a=null,o=0,r=0,i=[],s=[],p="auto",u=null,f=null,d="file",c={},m={},h=function(e){},v=function(e){},y=function(e){},U=function(e){},g=function(e){},b=function(){},w=function(e){},x=function(){},j=function(e,l){},k=[],E=[],S={files:k},z=0,F=null,T=function(e,l){M(e,l),0==--z&&D(),simpleUpload.activeUploads--,simpleUpload.uploadNext()},C=function(e){return h.call(S,e)},I=function(e,l){return!(R(e)>0)&&(!1===v.call(k[e],l)?(O(e,4),!1):!(R(e)>0)&&void O(e,1))},L=function(e,l){1==R(e)&&y.call(k[e],l)},q=function(e,l){1==R(e)&&(O(e,2),U.call(k[e],l),T(e,"success"))},W=function(e,l){1==R(e)&&(O(e,3),g.call(k[e],l),T(e,"error"))},_=function(e){b.call(k[e]),T(e,"cancel")},M=function(e,l){w.call(k[e],l)},D=function(){x.call(S),null!=F&&F.remove()},N=function(e,l,n){j.call(k[e],l,n)};function A(n){if(1==R(n)){if(null!=a){if(null==a[n]||null==a[n])return void W(n,{name:"InternalError",message:"There was an error uploading the file"});if(window.FormData){var t=$.ajaxSettings.xhr();if(t.upload){var o=a[n],r=new FormData;!function e(l,n,t){null!=t&&""!==t||(t=null);for(var a in n)void 0===n[a]||null===n[a]?l.append(null==t?a+"":t+"["+a+"]",""):"object"==typeof n[a]?e(l,n[a],null==t?a+"":t+"["+a+"]"):"boolean"==typeof n[a]?l.append(null==t?a+"":t+"["+a+"]",n[a]?"true":"false"):"number"==typeof n[a]?l.append(null==t?a+"":t+"["+a+"]",n[a]+""):"string"==typeof n[a]&&l.append(null==t?a+"":t+"["+a+"]",n[a])}(r,c),r.append(d,o);var i={url:e,data:r,type:"post",cache:!1,xhrFields:m,beforeSend:function(e,l){N(n,e,l),E[n].xhr=e},xhr:function(){return t.upload.addEventListener("progress",function(e){e.lengthComputable&&L(n,e.loaded/e.total*100)},!1),t},error:function(e){E[n].xhr=null,W(n,{name:"RequestError",message:"Upload failed",xhr:e})},success:function(e){E[n].xhr=null,L(n,100),q(n,e)},contentType:!1,processData:!1};return"auto"!=p&&(i.dataType=p),void $.ajax(i)}}}"object"==typeof l&&null!==l?function(l){if(0==l){var n=simpleUpload.queueIframe({origin:function(e){var l=document.createElement("a");l.href=e;var n=l.host,t=l.protocol;""==n&&(n=window.location.host);""!=t&&":"!=t||(t=window.location.protocol);return t.replace(/\:$/,"")+"://"+n}(e),expect:p,complete:function(e){1==R(l)&&(E[l].iframe=null,simpleUpload.dequeueIframe(n),L(l,100),q(l,e))},error:function(e){1==R(l)&&(E[l].iframe=null,simpleUpload.dequeueIframe(n),W(l,{name:"RequestError",message:e}))}});E[l].iframe=n;var t=function e(l,n){null!=n&&""!==n||(n=null);var t="";for(var a in l)void 0===l[a]||null===l[a]?t+=$("
").append($('').attr("name",null==n?a+"":n+"["+a+"]").val("")).html():"object"==typeof l[a]?t+=e(l[a],null==n?a+"":n+"["+a+"]"):"boolean"==typeof l[a]?t+=$("
").append($('').attr("name",null==n?a+"":n+"["+a+"]").val(l[a]?"true":"false")).html():"number"==typeof l[a]?t+=$("
").append($('').attr("name",null==n?a+"":n+"["+a+"]").val(l[a]+"")).html():"string"==typeof l[a]&&(t+=$("
").append($('').attr("name",null==n?a+"":n+"["+a+"]").val(l[a])).html());return t}(c);F.attr("action",e+(-1==e.lastIndexOf("?")?"?":"&")+"_iframeUpload="+n+"&_="+(new Date).getTime()).attr("target","simpleUpload_iframe_"+n).prepend(t).submit()}else W(l,{name:"UnsupportedError",message:"Multiple file uploads not supported"})}(n):W(n,{name:"UnsupportedError",message:"Your browser does not support this upload method"})}}function R(e){return E[e].state}function O(e,l){var n="";if(0==l)n="init";else if(1==l)n="uploading";else if(2==l)n="success";else if(3==l)n="error";else{if(4!=l)return!1;n="cancel"}E[e].state=l,k[e].upload.state=n}function B(e){var l=e.lastIndexOf(".");return-1!=l?e.substr(l+1):""}function J(e){return!isNaN(e)&&parseInt(e)+""==e}!function(){if("object"==typeof n&&null!==n){if("boolean"==typeof n.forceIframe&&(t=n.forceIframe),"function"==typeof n.init&&(h=n.init),"function"==typeof n.start&&(v=n.start),"function"==typeof n.progress&&(y=n.progress),"function"==typeof n.success&&(U=n.success),"function"==typeof n.error&&(g=n.error),"function"==typeof n.cancel&&(b=n.cancel),"function"==typeof n.complete&&(w=n.complete),"function"==typeof n.finish&&(x=n.finish),"function"==typeof n.beforeSend&&(j=n.beforeSend),"string"==typeof n.hashWorker&&""!=n.hashWorker&&(u=n.hashWorker),"function"==typeof n.hashComplete&&(f=n.hashComplete),"object"==typeof n.data&&null!==n.data)for(var e in n.data)c[e]=n.data[e];if("number"==typeof n.limit&&J(n.limit)&&n.limit>0&&(o=n.limit),"number"==typeof n.maxFileSize&&J(n.maxFileSize)&&n.maxFileSize>0&&(r=n.maxFileSize),"object"==typeof n.allowedExts&&null!==n.allowedExts)for(var e in n.allowedExts)i.push(n.allowedExts[e]);if("object"==typeof n.allowedTypes&&null!==n.allowedTypes)for(var e in n.allowedTypes)s.push(n.allowedTypes[e]);if("string"==typeof n.expect&&""!=n.expect){var S=n.expect.toLowerCase(),T=["auto","json","xml","html","script","text"];for(var e in T)if(T[e]==S){p=S;break}}if("object"==typeof n.xhrFields&&null!==n.xhrFields)for(var e in n.xhrFields)m[e]=n.xhrFields[e]}if("object"==typeof l&&null!==l&&l instanceof jQuery){if(!(l.length>0))return!1;l=l.get(0)}if(!t&&window.File&&window.FileReader&&window.FileList&&window.Blob&&("object"==typeof n&&null!==n&&"object"==typeof n.files&&null!==n.files?a=n.files:"object"==typeof l&&null!==l&&"object"==typeof l.files&&null!==l.files&&(a=l.files)),("object"!=typeof l||null===l)&&null==a)return!1;"object"==typeof n&&null!==n&&"string"==typeof n.name&&""!=n.name?d=n.name.replace(/\[\s*\]/g,"[0]"):"object"==typeof l&&null!==l&&"string"==typeof l.name&&""!=l.name&&(d=l.name.replace(/\[\s*\]/g,"[0]"));var M=0;if(null!=a?a.length>0&&(M=a.length>1&&window.FormData&&$.ajaxSettings.xhr().upload?o>0&&a.length>o?o:a.length:1):""!=l.value&&(M=1),M>0){if("object"==typeof l&&null!==l){var N=$(l);F=$("
").hide().attr("enctype","multipart/form-data").attr("method","post").appendTo("body"),N.after(N.clone(!0).val("")).removeAttr("onchange").off().removeAttr("id").attr("name",d).appendTo(F)}for(var Q=0;Q=0&&H0?(z=G.length,simpleUpload.queueUpload(G,function(e){!function(e){if(1==R(e)){var n=null;if(null!=a){if(null==a[e]||null==a[e])return void W(e,{name:"InternalError",message:"There was an error uploading the file"});n=a[e]}else if(""==l.value)return void W(e,{name:"InternalError",message:"There was an error uploading the file"});i.length>0&&!function(e,n){if(null!=n&&null!=n){var t=n.name;if(null!=t&&null!=t&&""!=t){var a=B(t).toLowerCase();if(""!=a){var o=!1;for(var r in e)if(e[r].toLowerCase()==a){o=!0;break}return!!o}return!1}}if("object"!=typeof l||null===l)return!0;var i=l.value;if(""!=i){var a=B(i).toLowerCase();if(""!=a){var o=!1;for(var r in e)if(e[r].toLowerCase()==a){o=!0;break}if(o)return!0}}return!1}(i,n)?W(e,{name:"InvalidFileExtensionError",message:"That file format is not allowed"}):s.length>0&&!function(e,l){if(null!=l&&null!=l){var n=l.type;if(null!=n&&null!=n&&""!=n){n=n.toLowerCase();var t=!1;for(var a in e)if(e[a].toLowerCase()==n){t=!0;break}return!!t}}return!0}(s,n)?W(e,{name:"InvalidFileTypeError",message:"That file format is not allowed"}):r>0&&!function(e,l){if(null!=l&&null!=l){var n=l.size;if(null!=n&&null!=n&&""!=n&&J(n))return n<=e}return!0}(r,n)?W(e,{name:"MaxFileSizeError",message:"That file is too big"}):null!=u&&null!=f?function(e){if(null!=a&&null!=a[e]&&null!=a[e]&&window.Worker){var l=a[e];if(null!=l.size&&null!=l.size&&""!=l.size&&J(l.size)&&(l.slice||l.webkitSlice||l.mozSlice))try{var n,t,o,r,i,s,p=new Worker(u);return p.addEventListener("error",function(l){p.terminate(),E[e].hashWorker=null,A(e)},!1),p.addEventListener("message",function(l){if(l.data.result){var n=l.data.result;p.terminate(),E[e].hashWorker=null,function(e,l){if(1==R(e)){var n=!1;f.call(k[e],l,{success:function(l){return 1==R(e)&&!n&&(n=!0,L(e,100),q(e,l),!0)},proceed:function(){return 1==R(e)&&!n&&(n=!0,A(e),!0)},error:function(l){return 1==R(e)&&!n&&(n=!0,W(e,{name:"HashError",message:l}),!0)}})}}(e,n)}},!1),s=function(e){p.postMessage({message:e.target.result,block:t})},i=function(e){t.end!==l.size&&(t.start+=n,t.end+=n,t.end>l.size&&(t.end=l.size),(o=new FileReader).onload=s,l.slice?r=l.slice(t.start,t.end):l.webkitSlice?r=l.webkitSlice(t.start,t.end):l.mozSlice&&(r=l.mozSlice(t.start,t.end)),o.readAsArrayBuffer(r))},n=1048576,(t={file_size:l.size,start:0}).end=n>l.size?l.size:n,p.addEventListener("message",i,!1),(o=new FileReader).onload=s,l.slice?r=l.slice(t.start,t.end):l.webkitSlice?r=l.webkitSlice(t.start,t.end):l.mozSlice&&(r=l.mozSlice(t.start,t.end)),o.readAsArrayBuffer(r),void(E[e].hashWorker=p)}catch(e){}}A(e)}(e):A(e)}}(e)}),simpleUpload.uploadNext()):D()}else{for(var Y in k)O(Y,4);D()}}}()}simpleUpload.maxUploads=10,simpleUpload.activeUploads=0,simpleUpload.uploads=[],simpleUpload.iframes={},simpleUpload.iframeCount=0,simpleUpload.queueUpload=function(e,l){simpleUpload.uploads[simpleUpload.uploads.length]={uploads:e,callback:l}},simpleUpload.uploadNext=function(){if(simpleUpload.uploads.length>0&&simpleUpload.activeUploads'),l},simpleUpload.dequeueIframe=function(e){e in simpleUpload.iframes&&($("iframe[name=simpleUpload_iframe_"+e+"]").remove(),delete simpleUpload.iframes[e],simpleUpload.iframeCount--)},simpleUpload.convertDataType=function(e,l,n){var t="auto";if("auto"==e){if("string"==typeof l&&""!=l){var a=l.toLowerCase(),o=["json","xml","html","script","text"];for(var r in o)if(o[r]==a){t=a;break}}}else t=e;if("auto"==t)return void 0===n?"":"object"==typeof n?n:String(n);if("json"==t){if(null==n)return null;if("object"==typeof n)return n;if("string"==typeof n)try{return $.parseJSON(n)}catch(e){return!1}return!1}if("xml"==t){if(null==n)return null;if("string"==typeof n)try{return $.parseXML(n)}catch(e){return!1}return!1}if("script"==t){if(void 0===n)return"";if("string"==typeof n)try{return $.globalEval(n),n}catch(e){return!1}return!1}return void 0===n?"":String(n)},simpleUpload.iframeCallback=function(e){if("object"==typeof e&&null!==e){var l=e.id;if(l in simpleUpload.iframes){var n=simpleUpload.convertDataType(simpleUpload.iframes[l].expect,e.type,e.data);!1!==n?simpleUpload.iframes[l].complete(n):simpleUpload.iframes[l].error("Upload failed")}}},simpleUpload.postMessageCallback=function(e){try{var l=e[e.message?"message":"data"];if("string"==typeof l&&""!=l&&"object"==typeof(l=$.parseJSON(l))&&null!==l&&"string"==typeof l.namespace&&"simpleUpload"==l.namespace){var n=l.id;if(n in simpleUpload.iframes&&e.origin===simpleUpload.iframes[n].origin){var t=simpleUpload.convertDataType(simpleUpload.iframes[n].expect,l.type,l.data);!1!==t?simpleUpload.iframes[n].complete(t):simpleUpload.iframes[n].error("Upload failed")}}}catch(e){}},window.addEventListener?window.addEventListener("message",simpleUpload.postMessageCallback,!1):window.attachEvent("onmessage",simpleUpload.postMessageCallback),function(e){"function"==typeof define&&define.amd?define(["jquery"],e):"object"==typeof exports?module.exports=e(require("jquery")):e(jQuery)}(function(e){e.fn.simpleUpload=function(l,n){return 0==e(this).length&&"object"==typeof n&&null!==n&&"object"==typeof n.files&&null!==n.files?(new simpleUpload(l,null,n),this):this.each(function(){new simpleUpload(l,this,n)})},e.fn.simpleUpload.maxSimultaneousUploads=function(e){return void 0===e?simpleUpload.maxUploads:"number"==typeof e&&e>0?(simpleUpload.maxUploads=e,this):void 0}}); \ No newline at end of file diff --git a/gaseous-server/wwwroot/styles/style.css b/gaseous-server/wwwroot/styles/style.css index 465f38c..84865dd 100644 --- a/gaseous-server/wwwroot/styles/style.css +++ b/gaseous-server/wwwroot/styles/style.css @@ -146,7 +146,7 @@ h3 { .banner_button_image_smaller { height: 16px; width: 16px; - margin: unset; + margin-left: 5px; } #banner_header { diff --git a/gaseous-tools/Config.cs b/gaseous-tools/Config.cs index c19f3aa..870b576 100644 --- a/gaseous-tools/Config.cs +++ b/gaseous-tools/Config.cs @@ -34,6 +34,14 @@ namespace gaseous_tools } } + public static string PlatformMappingFile + { + get + { + return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".gaseous-server", "platformmap.json"); + } + } + public static ConfigFile.Database DatabaseConfiguration { get diff --git a/gaseous-tools/Database/MySQL/gaseous-1002.sql b/gaseous-tools/Database/MySQL/gaseous-1002.sql index c9cfcb6..747d286 100644 --- a/gaseous-tools/Database/MySQL/gaseous-1002.sql +++ b/gaseous-tools/Database/MySQL/gaseous-1002.sql @@ -14,3 +14,33 @@ ALTER TABLE `RomCollections` ADD COLUMN `FolderStructure` INT NULL DEFAULT 0 AFTER `MaximumCollectionSizeInBytes`, ADD COLUMN `IncludeBIOSFiles` BOOLEAN NULL DEFAULT 0 AFTER `FolderStructure`, ADD COLUMN `AlwaysInclude` JSON NULL AFTER `IncludeBIOSFiles`; + +CREATE TABLE `PlatformMap` ( + `Id` BIGINT NOT NULL, + `RetroPieDirectoryName` VARCHAR(45) NULL, + `WebEmulator_Type` VARCHAR(45) NULL, + `WebEmulator_Core` VARCHAR(45) NULL, + PRIMARY KEY (`Id`), + UNIQUE INDEX `Id_UNIQUE` (`Id` ASC) VISIBLE); + +CREATE TABLE `PlatformMap_AlternateNames` ( + `Id` BIGINT NOT NULL, + `Name` VARCHAR(255) NOT NULL, + PRIMARY KEY (`Id`, `Name`)); + +CREATE TABLE `PlatformMap_Extensions` ( + `Id` BIGINT NOT NULL, + `Extension` VARCHAR(45) NOT NULL, + PRIMARY KEY (`Id`, `Extension`)); + +CREATE TABLE `PlatformMap_UniqueExtensions` ( + `Id` BIGINT NOT NULL, + `Extension` VARCHAR(45) NOT NULL, + PRIMARY KEY (`Id`, `Extension`)); + +CREATE TABLE `PlatformMap_Bios` ( + `Id` BIGINT NOT NULL, + `Filename` VARCHAR(45) NOT NULL, + `Description` LONGTEXT NOT NULL, + `Hash` VARCHAR(45) NOT NULL, + PRIMARY KEY (`Id`, `Filename`, `Hash`)); diff --git a/gaseous-tools/Logging.cs b/gaseous-tools/Logging.cs index 6ab97cc..ca95e11 100644 --- a/gaseous-tools/Logging.cs +++ b/gaseous-tools/Logging.cs @@ -74,9 +74,7 @@ namespace gaseous_tools // write log file string JsonOutput = Newtonsoft.Json.JsonConvert.SerializeObject(logItem, serializerSettings); - StreamWriter jsonLogFile = File.AppendText(Config.LogFilePath); - jsonLogFile.WriteLine(JsonOutput); - jsonLogFile.Close(); + File.AppendAllText(Config.LogFilePath, JsonOutput); } // quick clean before we go @@ -116,7 +114,14 @@ namespace gaseous_tools FileInfo fi = new FileInfo(file); if (fi.LastAccessTime.AddDays(Config.LoggingConfiguration.LogRetention) < DateTime.Now) { - fi.Delete(); + try + { + fi.Delete(); + } + catch + { + Log(LogType.Warning, "Log Cleanup", "Failed purging log " + fi.FullName); + } } } }