Compare commits

...

21 Commits

Author SHA1 Message Date
Michael Green
c396a81c1b Implement Bulk Change Function (#44)
* feat: added Sega 32X and Sega CD mappings

* feat: added lazy loading to the main game library

* fix: using full file name when loading roms into the emulator #43

* feat: introduced bulk rom matching #25

* fix: xss fix
2023-08-04 10:30:22 +10:00
Michael Green
59df041cfd fix: typo in zip signature builder would cause a loop (#41) 2023-07-27 16:49:30 +10:00
Michael Green
2355c5ac97 fix: zip inspection now works during library scans as well as during import (#40) 2023-07-27 15:41:58 +10:00
Michael Green
7891acd218 feat: extract zip files smaller than 1GiB to attempt to get a signature from the files inside. The first hit is considered to be the signature for the archive. (#39) 2023-07-27 00:15:15 +10:00
Michael Green
4ad51e98e2 feat: minor styling updates (#38) 2023-07-26 22:36:50 +10:00
Michael Green
8a001f9fa4 feat: added platform disk usage breakdown on the settings page (#37) 2023-07-25 21:26:59 +10:00
Michael Green
98fb360483 feat: updated EmulatorJS to v4.0.5 (#36) 2023-07-24 21:39:11 +10:00
dependabot[bot]
56cbc441f5 chore(deps): bump MySql.Data from 8.0.33 to 8.1.0 (#33)
Bumps MySql.Data from 8.0.33 to 8.1.0.

---
updated-dependencies:
- dependency-name: MySql.Data
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-07-22 23:37:41 +10:00
Michael Green
113de6a009 ci: add dependabot support for EmulatorJS submodule (#32) 2023-07-22 22:52:04 +10:00
Michael Green
dc2a6b4638 feat: add an upload button to ease adding game files (#29)
* feat: API support for uploading ROM’s

* fix: downloads of files larger than approx 300MB would cause an out of memory error

* fix: resolved broken bios path

* feat: added an upload button
2023-07-22 00:46:26 +10:00
Michael Green
9081b0bed9 Merge pull request #26 from gaseous-project/dependabot/nuget/Microsoft.VisualStudio.Web.CodeGeneration.Design-7.0.8
chore(deps): bump Microsoft.VisualStudio.Web.CodeGeneration.Design from 7.0.7 to 7.0.8
2023-07-19 13:27:40 +10:00
Michael Green
d64877543a Merge branch 'main' into dependabot/nuget/Microsoft.VisualStudio.Web.CodeGeneration.Design-7.0.8 2023-07-19 13:27:29 +10:00
Michael Green
649fba1bfa Merge pull request #27 from gaseous-project/dependabot/nuget/Microsoft.AspNetCore.OpenApi-7.0.9
chore(deps): bump Microsoft.AspNetCore.OpenApi from 7.0.8 to 7.0.9
2023-07-19 13:27:09 +10:00
Michael Green
7dfb97608f Merge branch 'main' into dependabot/nuget/Microsoft.AspNetCore.OpenApi-7.0.9 2023-07-19 13:26:46 +10:00
Michael Green
35bb2f18d9 Add support for adding EmulatorJS firmware (#28)
* feat: EmulatorJS support - importing of BIOS files #15

* feat: added Bios controller to make Bios files available to the emulator, also resolved SNES identification issues (see: #25)

* feat: added firmware selector to emulator screen

* refactor: moved EmulatorJS to a subfolder

* feat: added firmware image availability page
2023-07-19 13:18:39 +10:00
dependabot[bot]
ad84f5ae58 chore(deps): bump Microsoft.AspNetCore.OpenApi from 7.0.8 to 7.0.9
Bumps [Microsoft.AspNetCore.OpenApi](https://github.com/dotnet/aspnetcore) from 7.0.8 to 7.0.9.
- [Release notes](https://github.com/dotnet/aspnetcore/releases)
- [Changelog](https://github.com/dotnet/aspnetcore/blob/main/docs/ReleasePlanning.md)
- [Commits](https://github.com/dotnet/aspnetcore/compare/v7.0.8...v7.0.9)

---
updated-dependencies:
- dependency-name: Microsoft.AspNetCore.OpenApi
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-17 04:33:32 +00:00
dependabot[bot]
922c429716 chore(deps): bump Microsoft.VisualStudio.Web.CodeGeneration.Design
Bumps [Microsoft.VisualStudio.Web.CodeGeneration.Design](https://github.com/dotnet/Scaffolding) from 7.0.7 to 7.0.8.
- [Release notes](https://github.com/dotnet/Scaffolding/releases)
- [Commits](https://github.com/dotnet/Scaffolding/commits)

---
updated-dependencies:
- dependency-name: Microsoft.VisualStudio.Web.CodeGeneration.Design
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-17 04:33:26 +00:00
Michael Green
a2d634d96f fix: included accidentally excluded scripts (#24) 2023-07-13 14:52:43 +10:00
Michael Green
7a8e445471 EmulatorJS - First Version (#23)
* feat: added EmulatorJS support for Mega Drive, NES, and N64 (see: #15)

* doc: updated attribution in README.MD to include EmulatorJS

* ci: updated action to include submodules
2023-07-13 13:31:38 +10:00
Michael Green
b010f9742b fix: copy external js to local wwwroot (#22) 2023-07-12 11:00:27 +10:00
Michael Green
afd70e6b02 Update README.MD (#14)
* doc: updated documentation to include instructions on starting with docker-compose
2023-07-11 23:00:24 +10:00
50 changed files with 7397 additions and 210 deletions

BIN
.DS_Store vendored

Binary file not shown.

View File

@@ -9,3 +9,9 @@ updates:
directory: "/" # Location of package manifests
schedule:
interval: "weekly"
- package-ecosystem: "gitsubmodule"
directory: "/"
allow:
- dependency-name: "gaseous-server/wwwroot/emulators/EmulatorJS"
schedule:
interval: "weekly"

View File

@@ -12,6 +12,8 @@ jobs:
-
name: Checkout
uses: actions/checkout@v3
with:
submodules: 'true'
-
name: Set up QEMU
uses: docker/setup-qemu-action@v2

3
.gitmodules vendored
View File

@@ -0,0 +1,3 @@
[submodule "gaseous-server/wwwroot/emulators/EmulatorJS"]
path = gaseous-server/wwwroot/emulators/EmulatorJS
url = https://github.com/EmulatorJS/EmulatorJS.git

View File

@@ -21,6 +21,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
Dockerfile = Dockerfile
README.MD = README.MD
LICENSE = LICENSE
.gitignore = .gitignore
.github\dependabot.yml = .github\dependabot.yml
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "screenshots", "screenshots", "{F1A847C7-57BC-4DA9-8F83-CD060A7F5122}"

View File

@@ -16,6 +16,7 @@ The following projects are used by Gaseous
* https://github.com/JamesNK/Newtonsoft.Json
* https://www.nuget.org/packages/MySql.Data/8.0.32.1
* https://github.com/kamranayub/igdb-dotnet
* https://github.com/EmulatorJS/EmulatorJS
## Configuration File
When Gaseous-Server is started for the first time, it creates a configuration file at ~/.gaseous-server/config.json if it doesn't exist. Some values can be filled in using environment variables (such as in the case of using docker).
@@ -57,10 +58,17 @@ When Gaseous-Server is started for the first time, it creates a configuration fi
## Deploy with Docker
Dockerfile and docker-compose.yml files have been provided to make deployment of the server as easy as possible.
1. Download the docker-compose.yml file
2. Open the docker-compose.yml file and edit the igdbclientid and igdbclientsecret to the values retrieved from your IGDB account
3. Run the command "docker-compose up -d"
4. Connect to the host on port 5198
## Build and Deploy with Docker
Dockerfile and docker-compose-build.yml files have been provided to make deployment of the server as easy as possible.
1. Clone the repo with "git clone https://github.com/gaseous-project/gaseous-server.git"
2. Change into the gaseous-server directory
3. Open the docker-compose.yml file and edit the igdbclientid and igdbclientsecret to the values retrieved from your IGDB account
4. Run the command "docker-compose up -d"
3. Open the docker-compose-build.yml file and edit the igdbclientid and igdbclientsecret to the values retrieved from your IGDB account
4. Run the command "docker-compose up --file docker-compose-build.yml -d"
5. Connect to the host on port 5198
## Adding Content
@@ -98,4 +106,4 @@ Loop through each of the search candidates searching using:
2. "wherefuzzy" - partial match using wildcards
3. "search" - uses a more flexible search method
Note: that if more than one result is found, the image will be set as "Unknown" as there is no way for Gaseous to know which title is the correct one.
Note: that if more than one result is found, the image will be set as "Unknown" as there is no way for Gaseous to know which title is the correct one.

Binary file not shown.

View File

@@ -0,0 +1,120 @@
using System;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using gaseous_tools;
namespace gaseous_server.Classes
{
public class Bios
{
public Bios()
{
}
public static Models.PlatformMapping.PlatformMapItem? BiosHashSignatureLookup(string MD5)
{
foreach (Models.PlatformMapping.PlatformMapItem platformMapping in Models.PlatformMapping.PlatformMap)
{
if (platformMapping.WebEmulator != null)
{
if (platformMapping.WebEmulator.Bios != null)
{
foreach (Models.PlatformMapping.PlatformMapItem.WebEmulatorItem.EmulatorBiosItem emulatorBiosItem in platformMapping.WebEmulator.Bios)
{
if (emulatorBiosItem.hash.ToLower() == MD5.ToLower())
{
return platformMapping;
}
}
}
}
}
return null;
}
public static List<BiosItem> GetBios()
{
return BuildBiosList();
}
public static List<BiosItem> GetBios(long PlatformId, bool HideUnavailable)
{
List<BiosItem> biosItems = new List<BiosItem>();
foreach (BiosItem biosItem in BuildBiosList())
{
if (biosItem.platformid == PlatformId)
{
if (HideUnavailable == true)
{
if (biosItem.Available == true)
{
biosItems.Add(biosItem);
}
}
else
{
biosItems.Add(biosItem);
}
}
}
return biosItems;
}
private static List<BiosItem> BuildBiosList()
{
List<BiosItem> biosItems = new List<BiosItem>();
foreach (Models.PlatformMapping.PlatformMapItem platformMapping in Models.PlatformMapping.PlatformMap)
{
if (platformMapping.WebEmulator != null)
{
if (platformMapping.WebEmulator.Bios != null)
{
IGDB.Models.Platform platform = Metadata.Platforms.GetPlatform(platformMapping.IGDBId);
foreach (Models.PlatformMapping.PlatformMapItem.WebEmulatorItem.EmulatorBiosItem emulatorBios in platformMapping.WebEmulator.Bios)
{
BiosItem biosItem = new BiosItem
{
platformid = platformMapping.IGDBId,
platformslug = platform.Slug,
platformname = platform.Name,
description = emulatorBios.description,
filename = emulatorBios.filename,
region = emulatorBios.region,
hash = emulatorBios.hash
};
biosItems.Add(biosItem);
}
}
}
}
return biosItems;
}
public class BiosItem : Models.PlatformMapping.PlatformMapItem.WebEmulatorItem.EmulatorBiosItem
{
public long platformid { get; set; }
public string platformslug { get; set; }
public string platformname { get; set; }
public string biosPath
{
get
{
return Path.Combine(Config.LibraryConfiguration.LibraryBIOSDirectory, platformslug, base.filename);
}
}
public bool Available {
get
{
bool fileExists = File.Exists(biosPath);
return fileExists;
}
}
}
}
}

View File

@@ -1,5 +1,6 @@
using System;
using System.Data;
using System.IO.Compression;
using System.Security.Policy;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
@@ -58,42 +59,104 @@ namespace gaseous_server.Classes
FileInfo fi = new FileInfo(GameFileImportPath);
Common.hashObject hash = new Common.hashObject(GameFileImportPath);
// check to make sure we don't already have this file imported
sql = "SELECT COUNT(Id) AS count FROM Games_Roms WHERE MD5=@md5 AND SHA1=@sha1";
dbDict.Add("md5", hash.md5hash);
dbDict.Add("sha1", hash.sha1hash);
DataTable importDB = db.ExecuteCMD(sql, dbDict);
if ((Int64)importDB.Rows[0]["count"] > 0)
{
if (!GameFileImportPath.StartsWith(Config.LibraryConfiguration.LibraryImportDirectory))
{
Logging.Log(Logging.LogType.Warning, "Import Game", " " + GameFileImportPath + " already in database - skipping");
}
}
else
{
Logging.Log(Logging.LogType.Information, "Import Game", " " + GameFileImportPath + " not in database - processing");
Models.PlatformMapping.PlatformMapItem? IsBios = Classes.Bios.BiosHashSignatureLookup(hash.md5hash);
// process as a single file
Models.Signatures_Games discoveredSignature = GetFileSignature(hash, fi, GameFileImportPath);
if (IsBios == null)
{
// file is a rom
// check to make sure we don't already have this file imported
sql = "SELECT COUNT(Id) AS count FROM Games_Roms WHERE MD5=@md5 AND SHA1=@sha1";
dbDict.Add("md5", hash.md5hash);
dbDict.Add("sha1", hash.sha1hash);
DataTable importDB = db.ExecuteCMD(sql, dbDict);
if ((Int64)importDB.Rows[0]["count"] > 0)
{
if (!GameFileImportPath.StartsWith(Config.LibraryConfiguration.LibraryImportDirectory))
{
Logging.Log(Logging.LogType.Warning, "Import Game", " " + GameFileImportPath + " already in database - skipping");
}
}
else
{
Logging.Log(Logging.LogType.Information, "Import Game", " " + GameFileImportPath + " not in database - processing");
// get discovered platform
IGDB.Models.Platform determinedPlatform = Metadata.Platforms.GetPlatform(discoveredSignature.Flags.IGDBPlatformId);
if (determinedPlatform == null)
{
determinedPlatform = new IGDB.Models.Platform();
}
Models.Signatures_Games discoveredSignature = GetFileSignature(hash, fi, GameFileImportPath);
IGDB.Models.Game determinedGame = SearchForGame(discoveredSignature.Game.Name, discoveredSignature.Flags.IGDBPlatformId);
// get discovered platform
IGDB.Models.Platform determinedPlatform = Metadata.Platforms.GetPlatform(discoveredSignature.Flags.IGDBPlatformId);
if (determinedPlatform == null)
{
determinedPlatform = new IGDB.Models.Platform();
}
// add to database
StoreROM(hash, determinedGame, determinedPlatform, discoveredSignature, GameFileImportPath);
IGDB.Models.Game determinedGame = SearchForGame(discoveredSignature.Game.Name, discoveredSignature.Flags.IGDBPlatformId);
// add to database
StoreROM(hash, determinedGame, determinedPlatform, discoveredSignature, GameFileImportPath);
}
}
else
{
// file is a bios
if (IsBios.WebEmulator != null)
{
foreach (Classes.Bios.BiosItem biosItem in Classes.Bios.GetBios())
{
if (biosItem.Available == false && biosItem.hash == hash.md5hash)
{
string biosPath = biosItem.biosPath.Replace(biosItem.filename, "");
if (!Directory.Exists(biosPath))
{
Directory.CreateDirectory(biosPath);
}
File.Move(GameFileImportPath, biosItem.biosPath, true);
break;
}
}
}
}
}
}
}
public static Models.Signatures_Games GetFileSignature(Common.hashObject hash, FileInfo fi, string GameFileImportPath)
public static Models.Signatures_Games GetFileSignature(Common.hashObject hash, FileInfo fi, string GameFileImportPath)
{
Models.Signatures_Games discoveredSignature = _GetFileSignature(hash, fi, GameFileImportPath);
if ((Path.GetExtension(GameFileImportPath) == ".zip") && (fi.Length < 1073741824))
{
// file is a zip and less than 1 GiB
// extract the zip file and search the contents
string ExtractPath = Path.Combine(Config.LibraryConfiguration.LibraryRootDirectory, "Temp", Path.GetRandomFileName());
if (!Directory.Exists(ExtractPath)) { Directory.CreateDirectory(ExtractPath); }
ZipFile.ExtractToDirectory(GameFileImportPath, ExtractPath);
// loop through contents until we find the first signature match
foreach (string file in Directory.GetFiles(ExtractPath))
{
FileInfo zfi = new FileInfo(file);
Common.hashObject zhash = new Common.hashObject(file);
Models.Signatures_Games zDiscoveredSignature = _GetFileSignature(zhash, zfi, file);
zDiscoveredSignature.Rom.Name = Path.ChangeExtension(zDiscoveredSignature.Rom.Name, ".zip");
if (zDiscoveredSignature.Score > discoveredSignature.Score)
{
discoveredSignature = zDiscoveredSignature;
break;
}
}
if (Directory.Exists(ExtractPath)) { Directory.Delete(ExtractPath, true); }
}
return discoveredSignature;
}
private static Models.Signatures_Games _GetFileSignature(Common.hashObject hash, FileInfo fi, string GameFileImportPath)
{
// check 1: do we have a signature for it?
gaseous_server.Controllers.SignaturesController sc = new Controllers.SignaturesController();
@@ -312,7 +375,7 @@ namespace gaseous_server.Classes
}
dbDict.Add("platformid", Common.ReturnValueIfNull(determinedPlatform.Id, 0));
dbDict.Add("gameid", Common.ReturnValueIfNull(determinedGame.Id, 0));
dbDict.Add("name", Common.ReturnValueIfNull(discoveredSignature.Rom.Name, ""));
dbDict.Add("name", Common.ReturnValueIfNull(discoveredSignature.Rom.Name, 0));
dbDict.Add("size", Common.ReturnValueIfNull(discoveredSignature.Rom.Size, 0));
dbDict.Add("md5", hash.md5hash);
dbDict.Add("sha1", hash.sha1hash);

View File

@@ -107,6 +107,19 @@ namespace gaseous_server.Classes
Path = (string)romDR["path"],
Source = (GameRomItem.SourceType)(Int32)romDR["metadatasource"]
};
// check for a web emulator and update the romItem
foreach (Models.PlatformMapping.PlatformMapItem platformMapping in Models.PlatformMapping.PlatformMap)
{
if (platformMapping.IGDBId == romItem.PlatformId)
{
if (platformMapping.WebEmulator != null)
{
romItem.Emulator = platformMapping.WebEmulator;
}
}
}
return romItem;
}
@@ -115,7 +128,9 @@ namespace gaseous_server.Classes
public long Id { get; set; }
public long PlatformId { get; set; }
public IGDB.Models.Platform Platform { get; set; }
public long GameId { get; set; }
//public Dictionary<string, object>? Emulator { get; set; }
public Models.PlatformMapping.PlatformMapItem.WebEmulatorItem? Emulator { get; set; }
public long GameId { get; set; }
public string? Name { get; set; }
public long Size { get; set; }
public string? CRC { get; set; }

View File

@@ -0,0 +1,109 @@
using System;
using System.Collections.Generic;
using System.IO.Compression;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
namespace gaseous_server.Controllers
{
[ApiController]
[Route("api/v1/[controller]")]
public class BiosController : Controller
{
[HttpGet]
[ProducesResponseType(StatusCodes.Status200OK)]
public List<Classes.Bios.BiosItem> GetBios()
{
return Classes.Bios.GetBios();
}
[HttpGet]
[Route("{PlatformId}")]
[ProducesResponseType(StatusCodes.Status200OK)]
public List<Classes.Bios.BiosItem> GetBios(long PlatformId, bool AvailableOnly = true)
{
return Classes.Bios.GetBios(PlatformId, AvailableOnly);
}
[HttpGet]
[HttpHead]
[Route("zip/{PlatformId}")]
[ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public ActionResult GetBiosCompressed(long PlatformId)
{
try
{
IGDB.Models.Platform platform = Classes.Metadata.Platforms.GetPlatform(PlatformId);
string biosPath = Path.Combine(gaseous_tools.Config.LibraryConfiguration.LibraryBIOSDirectory, platform.Slug);
string tempFile = Path.GetTempFileName();
using (FileStream zipFile = System.IO.File.Create(tempFile))
using (var zipArchive = new ZipArchive(zipFile, ZipArchiveMode.Create))
{
foreach (string file in Directory.GetFiles(biosPath))
{
zipArchive.CreateEntryFromFile(file, Path.GetFileName(file));
}
}
var stream = new FileStream(tempFile, FileMode.Open);
return File(stream, "application/zip", platform.Slug + ".zip");
}
catch
{
return NotFound();
}
}
[HttpGet]
[HttpHead]
[Route("{PlatformId}/{BiosName}")]
[ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public ActionResult BiosFile(long PlatformId, string BiosName)
{
try
{
foreach (Classes.Bios.BiosItem biosItem in Classes.Bios.GetBios(PlatformId, true))
{
if (biosItem.filename == BiosName)
{
if (System.IO.File.Exists(biosItem.biosPath))
{
string filename = Path.GetFileName(biosItem.biosPath);
string filepath = biosItem.biosPath;
byte[] filedata = System.IO.File.ReadAllBytes(filepath);
string contentType = "application/octet-stream";
var cd = new System.Net.Mime.ContentDisposition
{
FileName = filename,
Inline = false,
};
Response.Headers.Add("Content-Disposition", cd.ToString());
Response.Headers.Add("Cache-Control", "public, max-age=604800");
return File(filedata, contentType);
}
else
{
return NotFound();
}
}
}
return NotFound();
}
catch
{
return NotFound();
}
}
}
}

View File

@@ -749,6 +749,7 @@ namespace gaseous_server.Controllers
}
[HttpGet]
[HttpHead]
[Route("{GameId}/roms/{RomId}/file")]
[ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
@@ -767,21 +768,44 @@ namespace gaseous_server.Controllers
string romFilePath = rom.Path;
if (System.IO.File.Exists(romFilePath))
{
string filename = Path.GetFileName(romFilePath);
string filepath = romFilePath;
byte[] filedata = System.IO.File.ReadAllBytes(filepath);
string contentType = "application/octet-stream";
FileStream content = new FileStream(romFilePath, FileMode.Open, FileAccess.Read, FileShare.Read);
FileStreamResult response = File(content, "application/octet-stream", rom.Name);
return response;
}
else
{
return NotFound();
}
}
catch
{
return NotFound();
}
}
[HttpGet]
[HttpHead]
[Route("{GameId}/roms/{RomId}/{FileName}")]
[ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public ActionResult GameRomFile(long GameId, long RomId, string FileName)
{
try
{
IGDB.Models.Game gameObject = Classes.Metadata.Games.GetGame(GameId, false, false);
var cd = new System.Net.Mime.ContentDisposition
{
FileName = filename,
Inline = false,
};
Classes.Roms.GameRomItem rom = Classes.Roms.GetRom(RomId);
if (rom.GameId != GameId || rom.Name != FileName)
{
return NotFound();
}
Response.Headers.Add("Content-Disposition", cd.ToString());
Response.Headers.Add("Cache-Control", "public, max-age=604800");
return File(filedata, contentType);
string romFilePath = rom.Path;
if (System.IO.File.Exists(romFilePath))
{
FileStream content = new FileStream(romFilePath, FileMode.Open, FileAccess.Read, FileShare.Read);
FileStreamResult response = File(content, "application/octet-stream", rom.Name);
return response;
}
else
{

View File

@@ -0,0 +1,80 @@
using System;
using System.Collections;
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_tools;
using IGDB.Models;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.CodeAnalysis.Scripting;
using Org.BouncyCastle.Asn1.X509;
using static gaseous_server.Classes.Metadata.AgeRatings;
namespace gaseous_server.Controllers
{
[Route("api/v1/[controller]")]
[ApiController]
public class RomsController : ControllerBase
{
[HttpPost]
[ProducesResponseType(typeof(List<IFormFile>), StatusCodes.Status200OK)]
[RequestSizeLimit(long.MaxValue)]
[DisableRequestSizeLimit, RequestFormLimits(MultipartBodyLengthLimit = long.MaxValue, ValueLengthLimit = int.MaxValue)]
public async Task<IActionResult> UploadRom(List<IFormFile> files)
{
Guid sessionid = Guid.NewGuid();
string workPath = Path.Combine(Config.LibraryConfiguration.LibraryUploadDirectory, sessionid.ToString());
long size = files.Sum(f => f.Length);
List<Dictionary<string, object>> UploadedFiles = new List<Dictionary<string, object>>();
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<string, object> UploadedFile = new Dictionary<string, object>();
UploadedFile.Add("id", FileId.ToString());
UploadedFile.Add("originalname", Path.GetFileName(formFile.FileName));
UploadedFile.Add("fullpath", filePath);
UploadedFiles.Add(UploadedFile);
}
}
}
// Process uploaded files
// Don't rely on or trust the FileName property without validation.
foreach (Dictionary<string, object> UploadedFile in UploadedFiles)
{
Classes.ImportGame.ImportGameFile((string)UploadedFile["fullpath"]);
}
if (Directory.Exists(workPath))
{
Directory.Delete(workPath, true);
}
return Ok(new { count = files.Count, size });
}
}
}

View File

@@ -14,35 +14,73 @@ namespace gaseous_server.Controllers
{
[HttpGet]
[ProducesResponseType(StatusCodes.Status200OK)]
public Dictionary<string, object> GetSystemStatus()
public SystemInfo GetSystemStatus()
{
Database db = new gaseous_tools.Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
Dictionary<string, object> ReturnValue = new Dictionary<string, object>();
SystemInfo ReturnValue = new SystemInfo();
// disk size
List<Dictionary<string, object>> Disks = new List<Dictionary<string, object>>();
List<SystemInfo.PathItem> Disks = new List<SystemInfo.PathItem>();
//Disks.Add(GetDisk(gaseous_tools.Config.ConfigurationPath));
Disks.Add(GetDisk(gaseous_tools.Config.LibraryConfiguration.LibraryRootDirectory));
ReturnValue.Add("Paths", Disks);
ReturnValue.Paths = Disks;
// database size
string sql = "SELECT table_schema, SUM(data_length + index_length) FROM information_schema.tables WHERE table_schema = '" + Config.DatabaseConfiguration.DatabaseName + "'";
DataTable dbResponse = db.ExecuteCMD(sql);
ReturnValue.Add("DatabaseSize", dbResponse.Rows[0][1]);
ReturnValue.DatabaseSize = (long)(System.Decimal)dbResponse.Rows[0][1];
// platform statistics
sql = "SELECT Platform.`name`, grc.Count, grs.Size FROM Platform INNER JOIN (SELECT Platform.`name` AS `Name`, SUM(grs.Size) AS Size FROM Platform JOIN Games_Roms AS grs ON (grs.PlatformId = Platform.Id) GROUP BY Platform.`name`) grs ON (grs.`Name` = Platform.`name`) INNER JOIN (SELECT Platform.`name` AS `Name`, COUNT(grc.Size) AS Count FROM Platform JOIN Games_Roms AS grc ON (grc.PlatformId = Platform.Id) GROUP BY Platform.`name`) grc ON (grc.`Name` = Platform.`name`) ORDER BY Platform.`name`;";
dbResponse = db.ExecuteCMD(sql);
ReturnValue.PlatformStatistics = new List<SystemInfo.PlatformStatisticsItem>();
foreach (DataRow dr in dbResponse.Rows)
{
SystemInfo.PlatformStatisticsItem platformStatisticsItem = new SystemInfo.PlatformStatisticsItem
{
Platform = (string)dr["name"],
RomCount = (long)dr["Count"],
TotalSize = (long)(System.Decimal)dr["Size"]
};
ReturnValue.PlatformStatistics.Add(platformStatisticsItem);
}
return ReturnValue;
}
private Dictionary<string, object> GetDisk(string Path)
private SystemInfo.PathItem GetDisk(string Path)
{
Dictionary<string, object> DiskValues = new Dictionary<string, object>();
DiskValues.Add("LibraryPath", Path);
DiskValues.Add("SpaceUsed", gaseous_tools.Common.DirSize(new DirectoryInfo(Path)));
DiskValues.Add("SpaceAvailable", new DriveInfo(Path).AvailableFreeSpace);
DiskValues.Add("TotalSpace", new DriveInfo(Path).TotalSize);
SystemInfo.PathItem pathItem = new SystemInfo.PathItem {
LibraryPath = Path,
SpaceUsed = gaseous_tools.Common.DirSize(new DirectoryInfo(Path)),
SpaceAvailable = new DriveInfo(Path).AvailableFreeSpace,
TotalSpace = new DriveInfo(Path).TotalSize
};
return DiskValues;
return pathItem;
}
public class SystemInfo
{
public List<PathItem>? Paths { get; set; }
public long DatabaseSize { get; set; }
public List<PlatformStatisticsItem>? PlatformStatistics { get; set; }
public class PathItem
{
public string LibraryPath { get; set; }
public long SpaceUsed { get; set; }
public long SpaceAvailable { get; set; }
public long TotalSpace { get; set; }
}
public class PlatformStatisticsItem
{
public string Platform { get; set; }
public long RomCount { get; set; }
public long TotalSize { get; set; }
}
}
}
}

View File

@@ -1,5 +1,7 @@
using System;
using System.Reflection;
using System.Text.Json.Serialization;
using gaseous_server.Classes;
namespace gaseous_server.Models
{
@@ -10,23 +12,21 @@ namespace gaseous_server.Models
}
private static List<PlatformMapItem> _PlatformMaps = new List<PlatformMapItem>();
//private static List<PlatformMapItem> _PlatformMaps = new List<PlatformMapItem>();
public static List<PlatformMapItem> PlatformMap
{
get
{
if (_PlatformMaps.Count == 0)
// load platform maps from: gaseous_server.Support.PlatformMap.json
List<PlatformMapItem> _PlatformMaps = new List<PlatformMapItem>();
var assembly = Assembly.GetExecutingAssembly();
var resourceName = "gaseous_server.Support.PlatformMap.json";
using (Stream stream = assembly.GetManifestResourceStream(resourceName))
using (StreamReader reader = new StreamReader(stream))
{
// load platform maps from: gaseous_server.Support.PlatformMap.json
var assembly = Assembly.GetExecutingAssembly();
var resourceName = "gaseous_server.Support.PlatformMap.json";
using (Stream stream = assembly.GetManifestResourceStream(resourceName))
using (StreamReader reader = new StreamReader(stream))
{
string rawJson = reader.ReadToEnd();
_PlatformMaps.Clear();
_PlatformMaps = Newtonsoft.Json.JsonConvert.DeserializeObject<List<PlatformMapItem>>(rawJson);
}
string rawJson = reader.ReadToEnd();
_PlatformMaps.Clear();
_PlatformMaps = Newtonsoft.Json.JsonConvert.DeserializeObject<List<PlatformMapItem>>(rawJson);
}
return _PlatformMaps;
@@ -73,11 +73,28 @@ namespace gaseous_server.Models
}
public class PlatformMapItem
{
{
public int IGDBId { get; set; }
public string IGDBName { get; set; }
public List<string> AlternateNames { get; set; } = new List<string>();
public List<string> KnownFileExtensions { get; set; } = new List<string>();
//public Dictionary<string, object>? WebEmulator { get; set; }
public WebEmulatorItem? WebEmulator { get; set; }
public class WebEmulatorItem
{
public string Type { get; set; }
public string Core { get; set; }
public List<EmulatorBiosItem> Bios { get; set; }
public class EmulatorBiosItem
{
public string hash { get; set; }
public string description { get; set; }
public string filename { get; set; }
public string region { get; set; }
}
}
}
}
}

View File

@@ -236,7 +236,7 @@ namespace gaseous_server.Models
public class SignatureFlags
{
public int IGDBPlatformId { get; set; }
public long IGDBPlatformId { get; set; }
public string IGDBPlatformName { get; set; }
}
}

View File

@@ -1,7 +1,9 @@
using System.Text.Json.Serialization;
using gaseous_server;
using gaseous_tools;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Server.Kestrel.Core;
Logging.Log(Logging.LogType.Information, "Startup", "Starting Gaseous Server");
@@ -20,6 +22,11 @@ if (Config.ReadSetting("API Key", "Test API Key") == "Test API Key")
Logging.Log(Logging.LogType.Information, "Startup", "Setting initial API key");
Config.SetSetting("API Key", APIKey.ToString());
}
if (Config.ReadSetting("Emulator: Default BIOS Region", "Default Value") == "Default Value")
{
Logging.Log(Logging.LogType.Information, "Startup", "Setting default BIOS region to US");
Config.SetSetting("Emulator: Default BIOS Region", "US");
}
// set up server
var builder = WebApplication.CreateBuilder(args);
@@ -32,6 +39,9 @@ builder.Services.AddControllers().AddJsonOptions(x =>
// suppress nulls
x.JsonSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull;
// set max depth
x.JsonSerializerOptions.MaxDepth = 64;
});
builder.Services.AddResponseCaching();
builder.Services.AddControllers(options =>
@@ -55,6 +65,22 @@ builder.Services.AddControllers(options =>
});
});
// set max upload size
builder.Services.Configure<IISServerOptions>(options =>
{
options.MaxRequestBodySize = int.MaxValue;
});
builder.Services.Configure<KestrelServerOptions>(options =>
{
options.Limits.MaxRequestBodySize = int.MaxValue;
});
builder.Services.Configure<FormOptions>(options =>
{
options.ValueLengthLimit = int.MaxValue;
options.MultipartBodyLengthLimit = int.MaxValue;
options.MultipartHeadersLengthLimit = int.MaxValue;
});
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
@@ -76,7 +102,11 @@ app.UseResponseCaching();
app.UseAuthorization();
app.UseDefaultFiles();
app.UseStaticFiles();
app.UseStaticFiles(new StaticFileOptions
{
ServeUnknownFileTypes = true, //allow unkown file types also to be served
DefaultContentType = "plain/text" //content type to returned if fileType is not known.
});
app.MapControllers();

View File

@@ -44,7 +44,31 @@
],
"KnownFileExtensions": [
".SMS"
]
],
"WebEmulator": {
"Type": "EmulatorJS",
"Core": "segaMS",
"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": 29,
@@ -60,7 +84,11 @@
".MD",
".SG",
".SMD"
]
],
"WebEmulator": {
"Type": "EmulatorJS",
"Core": "segaMD"
}
},
{
"IGDBId": 4,
@@ -71,7 +99,11 @@
],
"KnownFileExtensions": [
".Z64"
]
],
"WebEmulator": {
"Type": "EmulatorJS",
"Core": "n64"
}
},
{
"IGDBId": 18,
@@ -88,6 +120,122 @@
".SFC",
".SMC",
".SWC"
]
],
"WebEmulator": {
"Type": "EmulatorJS",
"Core": "nes"
}
},
{
"IGDBId": 19,
"IGDBName": "Super Nintendo Entertainment System",
"AlternateNames": [
"Nintendo Super Famicom & Super Entertainment System",
"Super Nintendo Entertainment System",
"Super Nintendo",
"SNES"
],
"KnownFileExtensions": [],
"WebEmulator": {
"Type": "EmulatorJS",
"Core": "snes"
}
},
{
"IGDBId": 7,
"IGDBName": "PlayStation",
"AlternateNames": [
"Sony PlayStation",
"PS1",
"PSX",
"PSOne",
"PS"
],
"KnownFileExtensions": [],
"WebEmulator": {
"Type": "EmulatorJS",
"Core": "psx",
"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": 52,
"IGDBName": "Arcade",
"AlternateNames": [],
"KnownFileExtensions": [],
"WebEmulator": {
"Type": "EmulatorJS",
"Core": "arcade",
"Bios": []
}
},
{
"IGDBId": 30,
"IGDBName": "Sega 32X",
"AlternateNames": [
"Sega 32X",
"Sega32",
"Sega32X"
],
"KnownFileExtensions": [],
"WebEmulator": {
"Type": "EmulatorJS",
"Core": "sega32x",
"Bios": []
}
},
{
"IGDBId": 78,
"IGDBName": "Sega CD",
"AlternateNames": [
"Sega CD",
"Mega CD",
"segacd",
"Sega Mega-CD & Sega CD"
],
"KnownFileExtensions": [],
"WebEmulator": {
"Type": "EmulatorJS",
"Core": "segaCD",
"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"
}
]
}
}
]

View File

@@ -14,9 +14,9 @@
<DocumentationFile>bin\Debug\net7.0\gaseous-server.xml</DocumentationFile>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="7.0.8" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="7.0.9" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="7.0.7" />
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="7.0.8" />
<PackageReference Include="IGDB" Version="2.3.2" />
</ItemGroup>
@@ -105,6 +105,7 @@
<Folder Include="Assets\Ratings\CLASS_IND\" />
<Folder Include="wwwroot\fonts\" />
<Folder Include="wwwroot\pages\dialogs\" />
<Folder Include="wwwroot\pages\settings\" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\gaseous-tools\gaseous-tools.csproj">
@@ -119,6 +120,7 @@
</ItemGroup>
<ItemGroup>
<Content Remove="Support\PlatformMap.json" />
<Content Remove="wwwroot\pages\settings\" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Support\PlatformMap.json" Condition="'$(ExcludeConfigFilesFromBuildOutput)'!='true'">

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 607 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 KiB

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 24.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
<g>
<path d="M230.4,358.4V76.8c0-14.13,11.48-25.6,25.6-25.6c14.15,0,25.6,11.47,25.6,25.6v281.6h25.6c14.15,0,25.6,11.48,25.6,25.6
c0,14.15-11.45,25.6-25.6,25.6h-25.6v25.6c0,14.15-11.45,25.6-25.6,25.6c-14.13,0-25.6-11.45-25.6-25.6v-25.6h-25.6
c-14.13,0-25.6-11.45-25.6-25.6c0-14.13,11.48-25.6,25.6-25.6H230.4z M409.6,102.4V76.8c0-14.13,11.48-25.6,25.6-25.6
c14.15,0,25.6,11.47,25.6,25.6v25.6h25.6c14.15,0,25.6,11.47,25.6,25.6c0,14.15-11.45,25.6-25.6,25.6h-25.6v281.6
c0,14.15-11.45,25.6-25.6,25.6c-14.13,0-25.6-11.45-25.6-25.6V153.6H384c-14.13,0-25.6-11.45-25.6-25.6
c0-14.13,11.48-25.6,25.6-25.6H409.6z M102.4,179.2H128c14.15,0,25.6,11.48,25.6,25.6c0,14.15-11.45,25.6-25.6,25.6h-25.6v204.8
c0,14.15-11.45,25.6-25.6,25.6c-14.13,0-25.6-11.45-25.6-25.6V230.4H25.6C11.48,230.4,0,218.95,0,204.8
c0-14.13,11.48-25.6,25.6-25.6h25.6V76.8c0-14.13,11.47-25.6,25.6-25.6c14.15,0,25.6,11.47,25.6,25.6V179.2z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 21.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
<g>
<path d="M509,261.1c-3.3-12.7-9.3-24-16.9-33.4c-5.7-7.1-12.4-13.2-19.6-18.4c-10.8-7.8-22.8-13.6-35-17.7
c-7.8-2.5-15.7-4.4-23.5-5.6c-3-18-8.4-34.6-15.7-49.6c-13.6-27.9-34-50.1-58.7-65.3C315,55.8,286.2,47.8,256,47.8
c-19,0-36.6,3.2-52.4,8.8c-11.9,4.2-22.7,9.8-32.5,16.4c-14.7,9.9-26.9,22-36.7,35.2c-7.5,10.2-13.6,21.1-18.1,32.2
c-12.9,2-25.3,5.3-37.1,10c-11.1,4.5-21.6,10.2-31.1,17.1c-14.3,10.4-26.4,23.7-34.8,39.5c-4.3,8-7.6,16.5-9.9,25.6
C1.2,241.7,0,251.4,0,261.4c0,17.9,3.9,34.8,11,49.8c5.3,11.3,12.4,21.5,20.9,30.4c12.7,13.4,28.5,23.9,46.2,31.1
c17.7,7.2,37.4,11,57.9,11h56c8.8,0,16-7.2,16-16s-7.2-16-16-16h-56c-14.7,0-28.7-2.4-41.2-6.9c-9.4-3.3-18.1-7.8-25.7-13.2
c-11.5-8.1-20.7-18.3-27.1-30.1c-6.4-11.8-9.9-25.2-10-40.2c0-13.4,2.8-25.1,7.6-35.4c3.6-7.8,8.4-14.8,14.3-21
c8.8-9.4,20-17.2,32.9-23c12.9-5.8,27.5-9.6,42.7-11c6.4-0.6,11.8-4.9,13.7-11c3.2-9.8,8-19.9,14.4-29.3
c4.8-7.1,10.5-13.8,17.1-19.9c9.8-9.1,21.5-16.8,35-22.3c13.5-5.4,28.9-8.7,46.3-8.7c16.4,0,32,2.9,46.4,8.4
c21.6,8.2,40.3,22.2,54.6,41.3s24.1,43.5,27.1,72.8c0.8,7.8,7.3,13.9,15.1,14.3c9.2,0.5,19.3,2.3,28.9,5.5
c7.2,2.4,14.2,5.6,20.5,9.5c4.7,2.9,9.1,6.2,12.9,9.8c5.8,5.5,10.3,11.6,13.5,18.7c3.2,7,5,14.9,5,24.2c0,5.7-0.6,10.9-1.7,15.7
c-1.9,8.4-5.2,15.6-9.8,21.8c-3.4,4.7-7.6,8.9-12.4,12.6c-7.3,5.5-16.2,9.9-26.4,12.9s-21.6,4.6-33.7,4.6h-76c-8.8,0-16,7.2-16,16
s7.2,16,16,16h76c15.2,0,29.8-2,43.4-6.1c10.2-3.1,19.8-7.3,28.6-12.8c6.6-4.1,12.6-8.9,18-14.4c8.1-8.3,14.7-18.1,19.1-29.3
c4.5-11.2,6.8-23.6,6.8-37C511.9,276.1,510.9,268.4,509,261.1z"/>
<path d="M308.7,267.1c6.2,6.2,16.4,6.2,22.6,0c6.3-6.2,6.3-16.4,0-22.6l-64-64c-6.2-6.2-16.4-6.2-22.6,0l-64,64
c-6.2,6.2-6.2,16.4,0,22.6c6.2,6.2,16.4,6.2,22.6,0l36.7-36.7v217.8c0,8.8,7.2,16,16,16s16-7.2,16-16V230.4L308.7,267.1z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@@ -3,10 +3,14 @@
<head>
<meta charset="utf-8" />
<link type="text/css" rel="stylesheet" href="/styles/style.css" />
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script src="https://momentjs.com/downloads/moment.js"></script>
<link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet" />
<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>
<script src="/scripts/jquery-3.6.0.min.js"></script>
<script src="/scripts/moment.js"></script>
<link href="/styles/select2.min.css" rel="stylesheet" />
<link rel="stylesheet" href="/styles/dropzone.min.css" type="text/css" />
<script src="/scripts/jquery.lazy.min.js"></script>
<script src="/scripts/jquery.lazy.plugins.min.js"></script>
<script src="/scripts/select2.min.js"></script>
<script src="/scripts/dropzone.min.js"></script>
<script src="/scripts/main.js" type="text/javascript"></script>
<script src="/scripts/filterformating.js" type="text/javascript"></script>
<script src="/scripts/gamesformating.js" type="text/javascript"></script>
@@ -23,8 +27,12 @@
<div id="banner_header">
<div id="banner_header_label">Gaseous Games</div>
</div>
<div id="banner_cog" onclick="window.location.href = '/index.html?page=system';">
<img src="/images/cog.jpg" alt="System" id="banner_system_image" />
<div id="banner_upload" onclick="showDialog('upload');">
<img src="/images/upload.svg" alt="Upload" title="Upload" id="banner_upload_image" />
<span id="banner_upload_label">Upload Games</span>
</div>
<div id="banner_cog" onclick="window.location.href = '/index.html?page=settings';">
<img src="/images/settings.svg" alt="Settings" title="Settings" id="banner_system_image" />
</div>
<div id="content">
@@ -43,11 +51,21 @@
</div>
<!-- The Modal -->
<div id="myModalSub" class="modal">
<!-- Modal content -->
<div class="modal-content-sub">
<span id="modal-close-sub" class="close">&times;</span>
<div id="modal-content-sub">Some text in the Modal..</div>
</div>
</div>
<script type="text/javascript">var modalVariables = null;
$(document).ready(function () {
const urlParams = new URLSearchParams(window.location.search);
var myParam = urlParams.get('page');
var myParam = getQueryString('page', 'string');
if (!myParam) {
myParam = 'home';

View File

@@ -0,0 +1,27 @@
<div style='width:640px;height:480px;max-width:100%'>
<div id='game'></div>
</div>
<script type='text/javascript'>
EJS_player = '#game';
// Can also be fceumm or nestopia
EJS_core = getQueryString('core', 'string');
// Lightgun
EJS_lightgun = false; // can be true or false
// URL to BIOS file
EJS_biosUrl = emuBios;
// URL to Game rom
EJS_gameUrl = decodeURIComponent(getQueryString('rompath', 'string'));
// Path to the data directory
EJS_pathtodata = '/emulators/EmulatorJS/data/';
EJS_DEBUG_XX = false;
EJS_startOnLoaded = false;
</script>
<script src='/emulators/EmulatorJS/data/loader.js'></script>

View File

@@ -1,15 +1,4 @@
<!-- The Modal -->
<div id="myModalSub" class="modal">
<!-- Modal content -->
<div class="modal-content-sub">
<span id="modal-close-sub" class="close">&times;</span>
<div id="modal-content-sub">Some text in the Modal..</div>
</div>
</div>
<div id="properties_toc">
<div id="properties_toc">
<div id="properties_toc_general" name="properties_toc_item" onclick="SelectTab('general');">General</div>
<div id="properties_toc_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>-->
@@ -85,9 +74,10 @@
</div>
</div>
<script type="text/javascript">document.getElementById('modal-heading').innerHTML = "Properties";
<script type="text/javascript">
document.getElementById('modal-heading').innerHTML = "Properties";
var gameId = urlParams.get('id');
var gameId = getQueryString('id', 'int');
var romData;
@@ -221,27 +211,6 @@
}
});
function DropDownRenderGameOption(state) {
console.log(JSON.stringify(state));
if (state.loading) {
return state;
}
var response;
if (state.cover) {
response = $(
'<table class="dropdown-div"><tr><td class="dropdown-cover"><img src="https://images.igdb.com/igdb/image/upload/t_cover_small/' + state.cover.value.imageId + '.jpg" /></td><td class="dropdown-label"><span>' + state.text + '</span></td></tr></table>'
);
} else {
response = $(
'<table class="dropdown-div"><tr><td class="dropdown-cover"><img src="/images/unknowngame.png" /></td><td class="dropdown-label"><span>' + state.text + '</span></td></tr></table>'
);
}
return response;
}
function SaveFixedGame() {
var fixplatform = $('#properties_fixplatform').select2('data');
var fixgame = $('#properties_fixgame').select2('data');
@@ -253,48 +222,6 @@
});
}
var subModalVariables;
function showSubDialog(dialogPage, variables) {
// Get the modal
var submodal = document.getElementById("myModalSub");
// Get the modal content
var subModalContent = document.getElementById("modal-content-sub");
// Get the button that opens the modal
var subbtn = document.getElementById("romDelete");
// Get the <span> element that closes the modal
var subspan = document.getElementById("modal-close-sub");
// When the user clicks on the button, open the modal
submodal.style.display = "block";
// When the user clicks on <span> (x), close the modal
subspan.onclick = function () {
submodal.style.display = "none";
subModalContent.innerHTML = "";
subModalVariables = null;
}
subModalVariables = modalVariables;
$('#modal-content-sub').load('/pages/dialogs/' + dialogPage + '.html');
}
function closeSubDialog() {
// Get the modal
var submodal = document.getElementById("myModalSub");
// Get the modal content
var subModalContent = document.getElementById("modal-content-sub");
submodal.style.display = "none";
subModalContent.innerHTML = "";
subModalVariables = null;
}
SelectTab('general');
document.getElementById('romDelete').setAttribute("onclick", "showSubDialog('romdelete', " + modalVariables + ");");

View File

@@ -0,0 +1,10 @@
<p>Are you sure you want to delete the selected ROMs?</p>
<p><strong>Warning:</strong> This cannot be undone!</p>
<div style="width: 100%; text-align: center;">
<div style="display: inline-block; margin-right: 20px;">
<button class="redbutton" value="Delete" onclick="deleteGameRomsCallback(); closeSubDialog();">Delete</button>
</div>
<div style="display: inline-block; margin-left: 20px;">
<button value="Cancel" onclick="closeSubDialog();">Cancel</button>
</div>
</div>

View File

@@ -0,0 +1,72 @@
<!-- The Modal -->
<div id="myModalSub" class="modal">
<!-- Modal content -->
<div class="modal-content-sub">
<span id="modal-close-sub" class="close">&times;</span>
<div id="modal-content-sub">Some text in the Modal..</div>
</div>
</div>
<div>
<div id="upload_target" class="dropzone"></div>
</div>
<script type="text/javascript">
document.getElementById('modal-heading').innerHTML = "Upload";
var myDropzone = new Dropzone("div#upload_target", {
url: "/api/v1/Roms",
autoProcessQueue: true,
uploadMultiple: true,
paramName: myParamName,
maxFilesize: 60000,
createImageThumbnails: false,
disablePreviews: false
});
function myParamName() {
return "files";
}
function showSubDialog(dialogPage, variables) {
// Get the modal
var submodal = document.getElementById("myModalSub");
// Get the modal content
var subModalContent = document.getElementById("modal-content-sub");
// Get the button that opens the modal
var subbtn = document.getElementById("romDelete");
// Get the <span> element that closes the modal
var subspan = document.getElementById("modal-close-sub");
// When the user clicks on the button, open the modal
submodal.style.display = "block";
// When the user clicks on <span> (x), close the modal
subspan.onclick = function () {
submodal.style.display = "none";
subModalContent.innerHTML = "";
subModalVariables = null;
}
subModalVariables = modalVariables;
$('#modal-content-sub').load('/pages/dialogs/' + dialogPage + '.html');
}
function closeSubDialog() {
// Get the modal
var submodal = document.getElementById("myModalSub");
// Get the modal content
var subModalContent = document.getElementById("modal-content-sub");
submodal.style.display = "none";
subModalContent.innerHTML = "";
subModalVariables = null;
}
</script>

View File

@@ -0,0 +1,57 @@
<div id="bgImage">
<div id="bgImage_Opacity"></div>
</div>
<div id="emulator"></div>
<script type="text/javascript">
var gameId = getQueryString('gameid', 'int');
var platformId = getQueryString('platformid', 'int');
var gameData;
var artworks = null;
var artworksPosition = 0;
var emuBios = '';
ajaxCall('/api/v1/Games/' + gameId, 'GET', function (result) {
gameData = result;
// load artwork
if (result.artworks) {
artworks = result.artworks.ids;
var startPos = randomIntFromInterval(0, result.artworks.ids.length);
artworksPosition = startPos;
rotateBackground();
} else {
if (result.cover) {
var bg = document.getElementById('bgImage');
bg.setAttribute('style', 'background-image: url("/api/v1/Games/' + gameId + '/cover/image"); background-position: center; background-repeat: no-repeat; background-size: cover; filter: blur(10px); -webkit-filter: blur(10px);');
}
}
});
ajaxCall('/api/v1/Bios/' + platformId, 'GET', function (result) {
if (result.length == 0) {
emuBios = '';
} else {
emuBios = '/api/v1/Bios/zip/' + platformId;
}
switch (getQueryString('engine', 'string')) {
case 'EmulatorJS':
$('#emulator').load('/pages/EmulatorJS.html');
break;
}
});
function rotateBackground() {
if (artworks) {
artworksPosition += 1;
if (artworks[artworksPosition] == null) {
artworksPosition = 0;
}
var bg = document.getElementById('bgImage');
bg.setAttribute('style', 'background-image: url("/api/v1/Games/' + gameId + '/artwork/' + artworks[artworksPosition] + '/image"); background-position: center; background-repeat: no-repeat; background-size: cover; filter: blur(10px); -webkit-filter: blur(10px);');
}
}
</script>

View File

@@ -46,15 +46,24 @@
<p id="gamesummarytext_label_button_contract" class="text_link" style="display: none;" onclick="document.querySelector('#gamesummarytext_label').classList.add('line-clamp-4'); document.querySelector('#gamesummarytext_label_button_expand').setAttribute('style', ''); document.querySelector('#gamesummarytext_label_button_contract').setAttribute('style', 'display: none;');">Read less...</p>
</div>
<div id="gamesummaryroms">
<span id="rom_edit" class="romlink" onclick="DisplayROMCheckboxes(true);">Edit</span>
<h3>ROM's/Images</h3>
<div id="rom_edit_panel" style="display: none;">
<div id="rom_edit_panel_center">
<button id="rom_edit_delete" class="redbutton" onclick="deleteGameRoms();">Delete</button>
<select id="rom_edit_fixplatform" style="width: 150px;"></select>
<select id="rom_edit_fixgame" style="width: 300px;"></select>
<button id="rom_edit_update" onclick="remapTitles();">Update</button>
</div>
</div>
</div>
</div>
</div>
<script type="text/javascript">
const urlParams = new URLSearchParams(window.location.search);
var gameId = urlParams.get('id');
var gameId = getQueryString('id', 'int');
var gameData;
var artworks = null;
var artworksPosition = 0;
@@ -296,15 +305,25 @@
}
// load roms
loadRoms();
});
function loadRoms(displayCheckboxes) {
var existingTable = document.getElementById('romtable');
if (existingTable) {
existingTable.remove();
}
var gameRoms = document.getElementById('gamesummaryroms');
ajaxCall('/api/v1/Games/' + gameId + '/roms', 'GET', function (result) {
if (result) {
result.sort((a, b) => a.platform.name.charCodeAt(0) - b.platform.name.charCodeAt(0));
var newTable = document.createElement('table');
newTable.id = 'romtable';
newTable.className = 'romtable';
newTable.setAttribute('cellspacing', 0);
newTable.appendChild(createTableRow(true, ['Name', 'Size', 'Media', '', '']));
newTable.appendChild(createTableRow(true, [['<input id="rom_mastercheck" type="checkbox" onclick="selectAllChecks();"/>', 'rom_checkbox_box_hidden', 'rom_edit_checkbox'], 'Name', 'Size', 'Media', '', '', '']));
var lastPlatform = '';
for (var i = 0; i < result.length; i++) {
@@ -312,28 +331,39 @@
lastPlatform = result[i].platform.name;
var platformRow = document.createElement('tr');
var platformHeader = document.createElement('th');
platformHeader.setAttribute('colspan', 4);
platformHeader.setAttribute('colspan', 6);
platformHeader.innerHTML = result[i].platform.name;
platformRow.appendChild(platformHeader);
newTable.appendChild(platformRow);
}
var launchButton = '';
if (result[i].emulator) {
launchButton = '<a href="/index.html?page=emulator&engine=' + result[i].emulator.type + '&core=' + result[i].emulator.core + '&platformid=' + result[i].platform.id + '&gameid=' + gameId + '&rompath=' + encodeURIComponent('/api/v1/Games/' + gameId + '/roms/' + result[i].id + '/' + encodeURIComponent(result[i].name)) + '" class="romstart">Launch</a>';
}
var newRow = [
'<a href="/api/v1/Games/' + gameId + '/roms/' + result[i].id + '/file" class="romlink">' + result[i].name + '</a>',
['<input type="checkbox" name="rom_checkbox" data-romid="' + result[i].id + '" />', 'rom_checkbox_box_hidden', 'rom_edit_checkbox'],
'<a href="/api/v1/Games/' + gameId + '/roms/' + result[i].id + '/' + encodeURIComponent(result[i].name) +'" class="romlink">' + result[i].name + '</a>',
formatBytes(result[i].size, 2),
result[i].romTypeMedia,
result[i].mediaLabel,
'<span class="romlink" onclick="showDialog(\'rominfo\', ' + result[i].id + ');">...</span>'
launchButton,
'<div class="properties_button" onclick="showDialog(\'rominfo\', ' + result[i].id + ');">i</div>'
];
newTable.appendChild(createTableRow(false, newRow, 'romrow', 'romcell'));
}
gameRoms.appendChild(newTable);
if (displayCheckboxes == true) {
DisplayROMCheckboxes(true);
}
} else {
gameRoms.setAttribute('style', 'display: none;');
}
});
});
}
function rotateBackground() {
if (artworks) {
@@ -410,4 +440,161 @@
selectScreenshot(selectedScreenshot);
}
function DisplayROMCheckboxes(visible) {
var checkbox_boxes = document.getElementsByName('rom_edit_checkbox');
for (var i = 0; i < checkbox_boxes.length; i++) {
if (visible == true) {
checkbox_boxes[i].className = 'rom_checkbox_box';
} else {
checkbox_boxes[i].className = 'rom_checkbox_box_hidden';
}
}
var editButton = document.getElementById('rom_edit');
var deleteButton = document.getElementById('rom_edit_panel');
if (visible == true) {
editButton.innerHTML = 'Cancel';
deleteButton.style.display = '';
} else {
editButton.innerHTML = 'Edit';
document.getElementById('rom_mastercheck').checked = false;
deleteButton.style.display = 'none';
selectAllChecks(false);
}
editButton.setAttribute('onclick', 'DisplayROMCheckboxes(' + !visible + ');');
}
function selectAllChecks(value) {
var mastercheckbox = document.getElementById('rom_mastercheck');
var checkboxes = document.getElementsByName('rom_checkbox');
for (var i = 0; i < checkboxes.length; i++) {
if (value) {
checkboxes[i].checked = value;
} else {
checkboxes[i].checked = mastercheckbox.checked;
}
}
}
$('#rom_edit_fixplatform').select2({
minimumInputLength: 3,
placeholder: "Platform",
ajax: {
url: '/api/v1/Search/Platform',
data: function (params) {
var query = {
SearchString: params.term
}
// Query parameters will be ?SearchString=[term]
return query;
},
processResults: function (data) {
var arr = [];
for (var i = 0; i < data.length; i++) {
arr.push({
id: data[i].id,
text: data[i].name
});
}
return {
results: arr
};
}
}
});
$('#rom_edit_fixgame').select2({
minimumInputLength: 3,
templateResult: DropDownRenderGameOption,
placeholder: "Game",
ajax: {
url: '/api/v1/Search/Game',
data: function (params) {
fixplatform = $('#rom_edit_fixplatform').select2('data');
var query = {
PlatformId: fixplatform[0].id,
SearchString: params.term
}
// Query parameters will be ?SearchString=[term]
return query;
},
processResults: function (data) {
var arr = [];
for (var i = 0; i < data.length; i++) {
arr.push({
id: data[i].id,
text: data[i].name,
cover: data[i].cover
});
}
return {
results: arr
};
}
}
});
var remapCallCounter = 0;
function remapTitles() {
var fixplatform = $('#rom_edit_fixplatform').select2('data');
var fixgame = $('#rom_edit_fixgame').select2('data');
if (fixplatform[0] && fixgame[0]) {
var rom_checks = document.getElementsByName('rom_checkbox');
for (var i = 0; i < rom_checks.length; i++) {
if (rom_checks[i].checked == true) {
var romId = rom_checks[i].getAttribute('data-romid');
remapCallCounter += 1;
ajaxCall('/api/v1/Games/' + gameId + '/roms/' + romId + '?NewPlatformId=' + fixplatform[0].id + '&NewGameId=' + fixgame[0].id, 'PATCH', function (result) {
remapTitlesCallback();
});
}
}
}
}
function remapTitlesCallback() {
remapCallCounter = remapCallCounter - 1;
if (remapCallCounter <= 0) {
loadRoms(true);
remapCallCounter = 0;
}
}
function deleteGameRoms() {
var rom_checks = document.getElementsByName('rom_checkbox');
var itemsChecked = false;
for (var i = 0; i < rom_checks.length; i++) {
if (rom_checks[i].checked == true) {
itemsChecked = true;
break;
}
}
if (itemsChecked == true) {
showSubDialog('romsdelete');
}
}
function deleteGameRomsCallback() {
var rom_checks = document.getElementsByName('rom_checkbox');
for (var i = 0; i < rom_checks.length; i++) {
if (rom_checks[i].checked == true) {
var romId = rom_checks[i].getAttribute('data-romid');
remapCallCounter += 1;
ajaxCall('/api/v1/Games/' + gameId + '/roms/' + romId, 'DELETE', function (result) {
remapTitlesCallback();
});
}
}
}
</script>

View File

@@ -0,0 +1,47 @@
<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>
<div id="gamepage">
<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_bios" name="properties_toc_item" onclick="SelectTab('bios');">Firmware</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>
</div>
<script type="text/javascript">
var myParam = getQueryString('sub', 'string');
var selectedTab = '';
if (myParam) {
selectedTab = myParam;
} else {
selectedTab = 'system';
}
SelectTab(selectedTab);
function SelectTab(TabName) {
var tocs = document.getElementsByName('properties_toc_item');
for (var i = 0; i < tocs.length; i++) {
if ((tocs[i].id) == ("properties_toc_" + TabName)) {
tocs[i].className = "properties_toc_item_selected";
} else {
tocs[i].className = '';
}
}
$('#properties_bodypanel').load('/pages/settings/' + TabName + '.html');
}
</script>

View File

@@ -0,0 +1,14 @@
<div id="gametitle">
<h1 id="gametitle_label">About Gaseous</h1>
</div>
<table>
<tr>
<th>Home Page</th>
<td><a href="https://github.com/gaseous-project/gaseous-server" class="romlink">https://github.com/gaseous-project/gaseous-server</a></td>
</tr>
<tr>
<th>Bugs and Feature Requests</th>
<td><a href="https://github.com/gaseous-project/gaseous-server/issues" class="romlink">https://github.com/gaseous-project/gaseous-server/issues</a></td>
</tr>
</table>

View File

@@ -0,0 +1,53 @@
<div id="gametitle">
<h1 id="gametitle_label">Firmware</h1>
</div>
<h3>Firmware Availablility</h3>
<table id="table_firmware" class="romtable" cellspacing="0">
</table>
<script type="text/javascript">
ajaxCall('/api/v1/Bios', 'GET', function (result) {
result.sort((a, b) => a.platformname.charCodeAt(0) - b.platformname.charCodeAt(0));
var lastPlatform = '';
var newTable = document.getElementById('table_firmware');
newTable.appendChild(createTableRow(true, ['Description', 'File name', 'MD5 Hash', 'Available']));
for (var i = 0; i < result.length; i++) {
if (result[i].platformname != lastPlatform) {
lastPlatform = result[i].platformname;
var platformRow = document.createElement('tr');
var platformHeader = document.createElement('th');
platformHeader.setAttribute('colspan', 4);
platformHeader.innerHTML = result[i].platformname;
platformRow.appendChild(platformHeader);
newTable.appendChild(platformRow);
}
var biosFilename = document.createElement('a');
biosFilename.href = '/api/v1/Bios/' + result[i].platformid + '/' + result[i].filename;
biosFilename.innerHTML = result[i].filename;
biosFilename.className = 'romlink';
var availableText = document.createElement('span');
if (result[i].available == true) {
availableText.innerHTML = 'Available';
availableText.className = 'greentext';
} else {
availableText.innerHTML = 'Unavailable';
availableText.className = 'redtext';
}
var newRow = [
result[i].description,
biosFilename,
result[i].hash,
availableText
];
newTable.appendChild(createTableRow(false, newRow, 'romrow', 'romcell'));
}
});
</script>

View File

@@ -1,23 +1,31 @@
<div id="gamepage">
<div id="gametitle">
<h1 id="gametitle_label">System</h1>
</div>
<h3>Background Tasks</h3>
<div id="system_tasks"></div>
<h3>Usage</h3>
<p><strong>Library</strong></p>
<div id="system_disks"></div>
<p><strong>Database</strong></p>
<div id="system_database"></div>
<h3>Signatures</h3>
<div id="system_signatures"></div>
<div id="gametitle">
<h1 id="gametitle_label">System</h1>
</div>
<script type="text/javascript">
function SystemLoadStatus() {
<h3>Background Tasks</h3>
<div id="system_tasks"></div>
<h3>Usage</h3>
<p><strong>Disk</strong></p>
<div id="system_disks"></div>
<p><strong>Library</strong></p>
<div>
<table cellspacing="0" style="width: 100%;">
<tr>
<td id="system_platforms"></td>
</tr>
<tr>
<td id="system_platforms_legend"></td>
</tr>
</table>
</div>
<p><strong>Database</strong></p>
<div id="system_database"></div>
<h3>Signatures</h3>
<div id="system_signatures"></div>
<script type="text/javascript">function SystemLoadStatus() {
ajaxCall('/api/v1/BackgroundTasks', 'GET', function (result) {
var newTable = document.createElement('table');
newTable.className = 'romtable';
@@ -71,7 +79,7 @@
var startButton = '';
if (result[i].itemState != "Running") {
startButton = "<span id='startProcess' class='romlink' onclick='StartProcess(\"" + result[i].itemType + "\");'>Start</span>";
startButton = "<span id='startProcess' class='romstart' onclick='StartProcess(\"" + result[i].itemType + "\");'>Start</span>";
}
var newRow = [
@@ -95,29 +103,32 @@
function SystemLoadSystemStatus() {
ajaxCall('/api/v1/System', 'GET', function (result) {
if (result) {
var totalLibrarySpace = 0;
// disks
var newTable = document.createElement('table');
newTable.className = 'romtable';
newTable.setAttribute('cellspacing', 0);
newTable.appendChild(createTableRow(true, ['Path', 'Library Size <div id="disk_LibSize" style="width: 10px; height: 10px; background-color: green;"></div>', 'Other <div id="disk_OtherSize" style="width: 10px; height: 10px; background-color: lightgreen;"></div>', 'Total Size <div id="disk_FreeSize" style="width: 10px; height: 10px; background-color: lightgray;"></div>']));
for (var i = 0; i < result.Paths.length; i++) {
var spaceUsedByLibrary = result.Paths[i].SpaceUsed;
var spaceUsedByOthers = result.Paths[i].TotalSpace - result.Paths[i].SpaceAvailable;
for (var i = 0; i < result.paths.length; i++) {
var spaceUsedByLibrary = result.paths[i].spaceUsed;
totalLibrarySpace += spaceUsedByLibrary;
var spaceUsedByOthers = result.paths[i].totalSpace - result.paths[i].spaceAvailable;
var newRow = [
result.Paths[i].LibraryPath,
result.paths[i].libraryPath,
formatBytes(spaceUsedByLibrary),
formatBytes(spaceUsedByOthers),
formatBytes(result.Paths[i].TotalSpace)
formatBytes(result.paths[i].totalSpace)
];
newTable.appendChild(createTableRow(false, newRow, 'romrow', 'romcell'));
var spaceRow = document.createElement('tr');
var spaceCell = document.createElement('td');
spaceCell.setAttribute('colspan', 4);
spaceCell.appendChild(BuildSpaceBar(spaceUsedByLibrary, spaceUsedByOthers, result.Paths[i].TotalSpace));
spaceCell.appendChild(BuildSpaceBar(spaceUsedByLibrary, spaceUsedByOthers, result.paths[i].totalSpace));
spaceRow.appendChild(spaceCell);
newTable.appendChild(spaceRow);
}
@@ -126,11 +137,13 @@
targetDiv.innerHTML = '';
targetDiv.appendChild(newTable);
BuildLibraryStatisticsBar(document.getElementById('system_platforms'), document.getElementById('system_platforms_legend'), result.platformStatistics, totalLibrarySpace);
// database
var newDbTable = document.createElement('table');
newDbTable.className = 'romtable';
newDbTable.setAttribute('cellspacing', 0);
newDbTable.appendChild(createTableRow(false, ['Database Size', formatBytes(result.DatabaseSize)]));
newDbTable.appendChild(createTableRow(false, ['Database Size', formatBytes(result.databaseSize)]));
var targetDbDiv = document.getElementById('system_database');
targetDbDiv.innerHTML = '';
@@ -168,6 +181,40 @@
return newTable;
}
function BuildLibraryStatisticsBar(TargetObject, TargetObjectLegend, LibraryStatistics, LibrarySize) {
var newTable = document.createElement('table');
newTable.setAttribute('cellspacing', 0);
newTable.setAttribute('style', 'width: 100%; height: 10px;');
var newRow = document.createElement('tr');
for (var i = 0; i < LibraryStatistics.length; i++) {
var platformSizePercent = LibraryStatistics[i].totalSize / LibrarySize * 100;
var platformSizeColour = intToRGB(hashCode(LibraryStatistics[i].platform));
var newCell = document.createElement('td');
newCell.setAttribute('style', 'min-width: 2px; width: ' + platformSizePercent + '%; background-color: #' + platformSizeColour);
newRow.appendChild(newCell);
var legend = document.createElement('div');
legend.className = 'legend_box';
var legendColour = document.createElement('div');
legendColour.className = 'legend_colour';
legendColour.setAttribute('style', 'background-color: #' + platformSizeColour + ';');
var legendLabel = document.createElement('div');
legendLabel.className = 'legend_label';
legendLabel.innerHTML = LibraryStatistics[i].platform + '<br />' + formatBytes(LibraryStatistics[i].totalSize);
legend.appendChild(legendColour);
legend.appendChild(legendLabel);
TargetObjectLegend.appendChild(legend);
}
newTable.appendChild(newRow);
TargetObject.appendChild(newTable);
}
function SystemSignaturesStatus() {
ajaxCall('/api/v1/Signatures/Status', 'GET', function (result) {
var newTable = document.createElement('table');
@@ -202,5 +249,4 @@
SystemLoadSystemStatus();
setInterval(SystemLoadStatus, 60000);
SystemSignaturesStatus();
setInterval(SystemSignaturesStatus, 300000);
</script>
setInterval(SystemSignaturesStatus, 300000);</script>

File diff suppressed because one or more lines are too long

View File

@@ -4,6 +4,12 @@
var game = renderGameIcon(result[i], true, false);
targetElement.appendChild(game);
}
$('.lazy').Lazy({
scrollDirection: 'vertical',
effect: 'fadeIn',
visibleOnly: true
});
}
function renderGameIcon(gameObject, showTitle, showRatings) {
@@ -12,9 +18,9 @@ function renderGameIcon(gameObject, showTitle, showRatings) {
gameBox.setAttribute('onclick', 'window.location.href = "/index.html?page=game&id=' + gameObject.id + '";');
var gameImage = document.createElement('img');
gameImage.className = 'game_tile_image';
gameImage.className = 'game_tile_image lazy';
if (gameObject.cover) {
gameImage.src = '/api/v1/Games/' + gameObject.id + '/cover/image';
gameImage.setAttribute('data-src', '/api/v1/Games/' + gameObject.id + '/cover/image');
} else {
gameImage.src = '/images/unknowngame.png';
gameImage.className = 'game_tile_image unknown';

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,2 @@
/*! jQuery & Zepto Lazy v1.7.10 - http://jquery.eisbehr.de/lazy - MIT&GPL-2.0 license - Copyright 2012-2018 Daniel 'Eisbehr' Kern */
!function(t,e){"use strict";function r(r,a,i,u,l){function f(){L=t.devicePixelRatio>1,i=c(i),a.delay>=0&&setTimeout(function(){s(!0)},a.delay),(a.delay<0||a.combined)&&(u.e=v(a.throttle,function(t){"resize"===t.type&&(w=B=-1),s(t.all)}),u.a=function(t){t=c(t),i.push.apply(i,t)},u.g=function(){return i=n(i).filter(function(){return!n(this).data(a.loadedName)})},u.f=function(t){for(var e=0;e<t.length;e++){var r=i.filter(function(){return this===t[e]});r.length&&s(!1,r)}},s(),n(a.appendScroll).on("scroll."+l+" resize."+l,u.e))}function c(t){var i=a.defaultImage,o=a.placeholder,u=a.imageBase,l=a.srcsetAttribute,f=a.loaderAttribute,c=a._f||{};t=n(t).filter(function(){var t=n(this),r=m(this);return!t.data(a.handledName)&&(t.attr(a.attribute)||t.attr(l)||t.attr(f)||c[r]!==e)}).data("plugin_"+a.name,r);for(var s=0,d=t.length;s<d;s++){var A=n(t[s]),g=m(t[s]),h=A.attr(a.imageBaseAttribute)||u;g===N&&h&&A.attr(l)&&A.attr(l,b(A.attr(l),h)),c[g]===e||A.attr(f)||A.attr(f,c[g]),g===N&&i&&!A.attr(E)?A.attr(E,i):g===N||!o||A.css(O)&&"none"!==A.css(O)||A.css(O,"url('"+o+"')")}return t}function s(t,e){if(!i.length)return void(a.autoDestroy&&r.destroy());for(var o=e||i,u=!1,l=a.imageBase||"",f=a.srcsetAttribute,c=a.handledName,s=0;s<o.length;s++)if(t||e||A(o[s])){var g=n(o[s]),h=m(o[s]),b=g.attr(a.attribute),v=g.attr(a.imageBaseAttribute)||l,p=g.attr(a.loaderAttribute);g.data(c)||a.visibleOnly&&!g.is(":visible")||!((b||g.attr(f))&&(h===N&&(v+b!==g.attr(E)||g.attr(f)!==g.attr(F))||h!==N&&v+b!==g.css(O))||p)||(u=!0,g.data(c,!0),d(g,h,v,p))}u&&(i=n(i).filter(function(){return!n(this).data(c)}))}function d(t,e,r,i){++z;var o=function(){y("onError",t),p(),o=n.noop};y("beforeLoad",t);var u=a.attribute,l=a.srcsetAttribute,f=a.sizesAttribute,c=a.retinaAttribute,s=a.removeAttribute,d=a.loadedName,A=t.attr(c);if(i){var g=function(){s&&t.removeAttr(a.loaderAttribute),t.data(d,!0),y(T,t),setTimeout(p,1),g=n.noop};t.off(I).one(I,o).one(D,g),y(i,t,function(e){e?(t.off(D),g()):(t.off(I),o())})||t.trigger(I)}else{var h=n(new Image);h.one(I,o).one(D,function(){t.hide(),e===N?t.attr(C,h.attr(C)).attr(F,h.attr(F)).attr(E,h.attr(E)):t.css(O,"url('"+h.attr(E)+"')"),t[a.effect](a.effectTime),s&&(t.removeAttr(u+" "+l+" "+c+" "+a.imageBaseAttribute),f!==C&&t.removeAttr(f)),t.data(d,!0),y(T,t),h.remove(),p()});var m=(L&&A?A:t.attr(u))||"";h.attr(C,t.attr(f)).attr(F,t.attr(l)).attr(E,m?r+m:null),h.complete&&h.trigger(D)}}function A(t){var e=t.getBoundingClientRect(),r=a.scrollDirection,n=a.threshold,i=h()+n>e.top&&-n<e.bottom,o=g()+n>e.left&&-n<e.right;return"vertical"===r?i:"horizontal"===r?o:i&&o}function g(){return w>=0?w:w=n(t).width()}function h(){return B>=0?B:B=n(t).height()}function m(t){return t.tagName.toLowerCase()}function b(t,e){if(e){var r=t.split(",");t="";for(var a=0,n=r.length;a<n;a++)t+=e+r[a].trim()+(a!==n-1?",":"")}return t}function v(t,e){var n,i=0;return function(o,u){function l(){i=+new Date,e.call(r,o)}var f=+new Date-i;n&&clearTimeout(n),f>t||!a.enableThrottle||u?l():n=setTimeout(l,t-f)}}function p(){--z,i.length||z||y("onFinishedAll")}function y(t,e,n){return!!(t=a[t])&&(t.apply(r,[].slice.call(arguments,1)),!0)}var z=0,w=-1,B=-1,L=!1,T="afterLoad",D="load",I="error",N="img",E="src",F="srcset",C="sizes",O="background-image";"event"===a.bind||o?f():n(t).on(D+"."+l,f)}function a(a,o){var u=this,l=n.extend({},u.config,o),f={},c=l.name+"-"+ ++i;return u.config=function(t,r){return r===e?l[t]:(l[t]=r,u)},u.addItems=function(t){return f.a&&f.a("string"===n.type(t)?n(t):t),u},u.getItems=function(){return f.g?f.g():{}},u.update=function(t){return f.e&&f.e({},!t),u},u.force=function(t){return f.f&&f.f("string"===n.type(t)?n(t):t),u},u.loadAll=function(){return f.e&&f.e({all:!0},!0),u},u.destroy=function(){return n(l.appendScroll).off("."+c,f.e),n(t).off("."+c),f={},e},r(u,l,a,f,c),l.chainable?a:u}var n=t.jQuery||t.Zepto,i=0,o=!1;n.fn.Lazy=n.fn.lazy=function(t){return new a(this,t)},n.Lazy=n.lazy=function(t,r,i){if(n.isFunction(r)&&(i=r,r=[]),n.isFunction(i)){t=n.isArray(t)?t:[t],r=n.isArray(r)?r:[r];for(var o=a.prototype.config,u=o._f||(o._f={}),l=0,f=t.length;l<f;l++)(o[t[l]]===e||n.isFunction(o[t[l]]))&&(o[t[l]]=i);for(var c=0,s=r.length;c<s;c++)u[r[c]]=t[0]}},a.prototype.config={name:"lazy",chainable:!0,autoDestroy:!0,bind:"load",threshold:500,visibleOnly:!1,appendScroll:t,scrollDirection:"both",imageBase:null,defaultImage:"data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==",placeholder:null,delay:-1,combined:!1,attribute:"data-src",srcsetAttribute:"data-srcset",sizesAttribute:"data-sizes",retinaAttribute:"data-retina",loaderAttribute:"data-loader",imageBaseAttribute:"data-imagebase",removeAttribute:!0,handledName:"handled",loadedName:"loaded",effect:"show",effectTime:0,enableThrottle:!0,throttle:250,beforeLoad:e,afterLoad:e,onError:e,onFinishedAll:e},n(t).on("load",function(){o=!0})}(window);

View File

@@ -0,0 +1,2 @@
/*! jQuery & Zepto Lazy - All Plugins v1.7.10 - http://jquery.eisbehr.de/lazy - MIT&GPL-2.0 license - Copyright 2012-2018 Daniel 'Eisbehr' Kern */
!function(t){function a(a,e,r,o){o=o?o.toUpperCase():"GET";var i;"POST"!==o&&"PUT"!==o||!a.config("ajaxCreateData")||(i=a.config("ajaxCreateData").apply(a,[e])),t.ajax({url:e.attr("data-src"),type:"POST"===o||"PUT"===o?o:"GET",data:i,dataType:e.attr("data-type")||"html",success:function(t){e.html(t),r(!0),a.config("removeAttribute")&&e.removeAttr("data-src data-method data-type")},error:function(){r(!1)}})}t.lazy("ajax",function(t,e){a(this,t,e,t.attr("data-method"))}),t.lazy("get",function(t,e){a(this,t,e,"GET")}),t.lazy("post",function(t,e){a(this,t,e,"POST")}),t.lazy("put",function(t,e){a(this,t,e,"PUT")})}(window.jQuery||window.Zepto),function(t){t.lazy(["av","audio","video"],["audio","video"],function(a,e){var r=a[0].tagName.toLowerCase();if("audio"===r||"video"===r){var o=a.find("data-src"),i=a.find("data-track"),n=0,c=function(){++n===o.length&&e(!1)},s=function(){var a=t(this),e=a[0].tagName.toLowerCase(),r=a.prop("attributes"),o=t("data-src"===e?"<source>":"<track>");"data-src"===e&&o.one("error",c),t.each(r,function(t,a){o.attr(a.name,a.value)}),a.replaceWith(o)};a.one("loadedmetadata",function(){e(!0)}).off("load error").attr("poster",a.attr("data-poster")),o.length?o.each(s):a.attr("data-src")?(t.each(a.attr("data-src").split(","),function(e,r){var o=r.split("|");a.append(t("<source>").one("error",c).attr({src:o[0].trim(),type:o[1].trim()}))}),this.config("removeAttribute")&&a.removeAttr("data-src")):e(!1),i.length&&i.each(s)}else e(!1)})}(window.jQuery||window.Zepto),function(t){t.lazy(["frame","iframe"],"iframe",function(a,e){var r=this;if("iframe"===a[0].tagName.toLowerCase()){var o=a.attr("data-error-detect");"true"!==o&&"1"!==o?(a.attr("src",a.attr("data-src")),r.config("removeAttribute")&&a.removeAttr("data-src data-error-detect")):t.ajax({url:a.attr("data-src"),dataType:"html",crossDomain:!0,xhrFields:{withCredentials:!0},success:function(t){a.html(t).attr("src",a.attr("data-src")),r.config("removeAttribute")&&a.removeAttr("data-src data-error-detect")},error:function(){e(!1)}})}else e(!1)})}(window.jQuery||window.Zepto),function(t){t.lazy("noop",function(){}),t.lazy("noop-success",function(t,a){a(!0)}),t.lazy("noop-error",function(t,a){a(!1)})}(window.jQuery||window.Zepto),function(t){function a(a,e,i){var n=a.prop("attributes"),c=t("<"+e+">");return t.each(n,function(t,a){"srcset"!==a.name&&a.name!==o||(a.value=r(a.value,i)),c.attr(a.name,a.value)}),a.replaceWith(c),c}function e(a,e,r){var o=t("<img>").one("load",function(){r(!0)}).one("error",function(){r(!1)}).appendTo(a).attr("src",e);o.complete&&o.load()}function r(t,a){if(a){var e=t.split(",");t="";for(var r=0,o=e.length;r<o;r++)t+=a+e[r].trim()+(r!==o-1?",":"")}return t}var o="data-src";t.lazy(["pic","picture"],["picture"],function(i,n){if("picture"===i[0].tagName.toLowerCase()){var c=i.find(o),s=i.find("data-img"),d=this.config("imageBase")||"";c.length?(c.each(function(){a(t(this),"source",d)}),1===s.length?(s=a(s,"img",d),s.on("load",function(){n(!0)}).on("error",function(){n(!1)}),s.attr("src",s.attr(o)),this.config("removeAttribute")&&s.removeAttr(o)):i.attr(o)?(e(i,d+i.attr(o),n),this.config("removeAttribute")&&i.removeAttr(o)):n(!1)):i.attr("data-srcset")?(t("<source>").attr({media:i.attr("data-media"),sizes:i.attr("data-sizes"),type:i.attr("data-type"),srcset:r(i.attr("data-srcset"),d)}).appendTo(i),e(i,d+i.attr(o),n),this.config("removeAttribute")&&i.removeAttr(o+" data-srcset data-media data-sizes data-type")):n(!1)}else n(!1)})}(window.jQuery||window.Zepto),function(t){t.lazy(["js","javascript","script"],"script",function(t,a){"script"===t[0].tagName.toLowerCase()?(t.attr("src",t.attr("data-src")),this.config("removeAttribute")&&t.removeAttr("data-src")):a(!1)})}(window.jQuery||window.Zepto),function(t){t.lazy("vimeo",function(t,a){"iframe"===t[0].tagName.toLowerCase()?(t.attr("src","https://player.vimeo.com/video/"+t.attr("data-src")),this.config("removeAttribute")&&t.removeAttr("data-src")):a(!1)})}(window.jQuery||window.Zepto),function(t){t.lazy(["yt","youtube"],function(t,a){if("iframe"===t[0].tagName.toLowerCase()){var e=/1|true/.test(t.attr("data-nocookie"));t.attr("src","https://www.youtube"+(e?"-nocookie":"")+".com/embed/"+t.attr("data-src")+"?rel=0&amp;showinfo=0"),this.config("removeAttribute")&&t.removeAttr("data-src")}else a(!1)})}(window.jQuery||window.Zepto);

View File

@@ -23,6 +23,30 @@
});
}
function getQueryString(stringName, type) {
const urlParams = new URLSearchParams(window.location.search);
var myParam = urlParams.get(stringName);
switch (type) {
case "int":
if (typeof (Number(myParam)) == 'number') {
return Number(myParam);
} else {
return null;
}
break;
case "string":
if (typeof (myParam) == 'string') {
return encodeURIComponent(myParam);
} else {
return null;
}
default:
return null;
break;
}
}
function formatBytes(bytes, decimals = 2) {
if (!+bytes) return '0 Bytes'
@@ -72,6 +96,48 @@ function showDialog(dialogPage, variables) {
$('#modal-content').load('/pages/dialogs/' + dialogPage + '.html');
}
var subModalVariables;
function showSubDialog(dialogPage, variables) {
// Get the modal
var submodal = document.getElementById("myModalSub");
// Get the modal content
var subModalContent = document.getElementById("modal-content-sub");
// Get the button that opens the modal
var subbtn = document.getElementById("romDelete");
// Get the <span> element that closes the modal
var subspan = document.getElementById("modal-close-sub");
// When the user clicks on the button, open the modal
submodal.style.display = "block";
// When the user clicks on <span> (x), close the modal
subspan.onclick = function () {
submodal.style.display = "none";
subModalContent.innerHTML = "";
subModalVariables = null;
}
subModalVariables = modalVariables;
$('#modal-content-sub').load('/pages/dialogs/' + dialogPage + '.html');
}
function closeSubDialog() {
// Get the modal
var submodal = document.getElementById("myModalSub");
// Get the modal content
var subModalContent = document.getElementById("modal-content-sub");
submodal.style.display = "none";
subModalContent.innerHTML = "";
subModalVariables = null;
}
function randomIntFromInterval(min, max) { // min and max included
var rand = Math.floor(Math.random() * (max - min + 1) + min);
return rand;
@@ -90,13 +156,58 @@ function createTableRow(isHeader, row, rowClass, cellClass) {
var newCell = document.createElement(cellType);
if (typeof(row[i]) != "object") {
newCell.innerHTML = row[i];
newCell.className = cellClass;
} else {
newCell.appendChild(row[i]);
if (Array.isArray(row[i])) {
newCell.innerHTML = row[i][0];
if (row[i][1]) { newCell.className = row[i][1]; }
if (row[i][2]) { newCell.setAttribute('name', row[i][2]); }
} else {
newCell.appendChild(row[i]);
newCell.className = cellClass;
}
}
newCell.className = cellClass;
newRow.appendChild(newCell);
}
return newRow;
}
function hashCode(str) {
var hash = 0;
for (var i = 0; i < str.length; i++) {
hash = str.charCodeAt(i) + ((hash << 5) - hash);
}
return hash;
}
function intToRGB(i) {
var c = (i & 0x00FFFFFF)
.toString(16)
.toUpperCase();
return "00000".substring(0, 6 - c.length) + c;
}
function DropDownRenderGameOption(state) {
console.log(JSON.stringify(state));
if (state.loading) {
return state;
}
var response;
if (state.cover) {
response = $(
'<table class="dropdown-div"><tr><td class="dropdown-cover"><img src="https://images.igdb.com/igdb/image/upload/t_cover_small/' + state.cover.value.imageId + '.jpg" /></td><td class="dropdown-label"><span>' + state.text + '</span></td></tr></table>'
);
} else {
response = $(
'<table class="dropdown-div"><tr><td class="dropdown-cover"><img src="/images/unknowngame.png" /></td><td class="dropdown-label"><span>' + state.text + '</span></td></tr></table>'
);
}
return response;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -105,6 +105,28 @@ h3 {
height: 30px;
}
#banner_upload {
background-color: white;
position: fixed;
top: 0px;
right: 41px;
height: 40px;
align-items: center;
justify-content: center;
padding-left: 10px;
padding-right: 10px;
padding-top: 0px;
padding-bottom: 0px;
margin: 0px;
display: flex;
color: black;
}
#banner_upload:hover {
cursor: pointer;
background-color: lightgrey;
}
#banner_cog {
background-color: white;
position: fixed;
@@ -121,11 +143,18 @@ h3 {
#banner_cog:hover {
cursor: pointer;
background-color: lightgrey;
}
#banner_system_image {
height: 30px;
width: 30px;
height: 20px;
width: 20px;
}
#banner_upload_image {
height: 20px;
width: 20px;
margin-right: 5px;
}
#banner_header {
@@ -168,6 +197,7 @@ h3 {
}
.filter_header {
font-weight: bold;
padding: 10px;
background-color: #2b2b2b;
}
@@ -267,6 +297,9 @@ input[id='filter_panel_search'] {
.game_tile_image {
max-width: 200px;
max-height: 200px;
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, .unknown {
@@ -324,6 +357,9 @@ input[id='filter_panel_search'] {
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);
}
.gamegenrelabel {
@@ -337,6 +373,9 @@ input[id='filter_panel_search'] {
max-height: 64px;
margin-right: 20px;
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);
}
.rating_image_mini {
@@ -351,6 +390,9 @@ input[id='filter_panel_search'] {
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);
}
#gamescreenshots_main {
@@ -469,7 +511,7 @@ th {
.romlink {
color: white;
text-decoration: none;
/*text-decoration: none;*/
}
.romlink:hover {
@@ -478,6 +520,59 @@ th {
cursor: pointer;
}
.romstart {
padding-left: 7px;
padding-right: 7px;
padding-top: 3px;
padding-bottom: 3px;
background-color: #02B01B;
color: white;
text-shadow: 2px 2px 6px #003506;
text-decoration: none;
border-radius: 5px 5px 5px 5px;
-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);
}
.romstart:hover {
background-color: #003506;
border-color: #129834;
cursor: pointer;
}
.properties_button {
width: 15px;
height: 15px;
border-radius: 15px;
font-size: 13px;
font-weight: bold;
color: white;
line-height: 17px;
text-align: center;
border-width: 2px;
border-style: solid;
outline-width: 1px;
outline-style: solid;
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);
}
.properties_button:hover {
cursor: pointer;
color: blue;
border-color: blue;
background-color: white;
outline-color: white;
}
#gamedev_logo {
float: right;
max-height: 48px;
@@ -492,6 +587,8 @@ th {
display: block;
width: 150px;
min-width: 150px;
background-color: #383838;
height: 100%;
}
div[name="properties_toc_item"] {
@@ -518,6 +615,10 @@ div[name="properties_toc_item"]:hover {
padding-left: 10px;
}
.settings_toc {
margin-top: 20px;
}
.select2-container--open .select2-dropdown--below,
.select2-container--open .select2-dropdown--above {
background: #2b2b2b;
@@ -546,7 +647,7 @@ div[name="properties_toc_item"]:hover {
}
button {
background-color: #888;
background-color: #555;
color: white;
border-width: 1px;
border-color: #555;
@@ -555,10 +656,12 @@ button {
padding-bottom: 5px;
padding-left: 10px;
padding-right: 10px;
margin-right: 3px;
margin-bottom: 3px;
}
button:hover {
background-color: #555;
background-color: #888;
cursor: pointer;
}
@@ -578,4 +681,93 @@ button:disabled {
.redbutton:disabled {
background-color: #555;
}
#emulator {
margin: 0 auto;
width: 640px;
padding-top: 100px;
}
#emulatorbios {
margin: 0 auto;
width: 640px;
padding-top: 20px;
}
.greentext {
color: lightgreen;
}
.redtext {
color: red;
}
#settings_photocredit {
position: fixed;
bottom: 0px;
left: 0px;
right: 0px;
height: 20px;
background-color: rgba(0, 22, 56, 0.8);
backdrop-filter: blur(8px);
-webkit-backdrop-filter: blur(8px);
padding: 5px;
}
#upload_target {
background-color: #383838;
color: white;
border: none;
height: 310px;
margin-top: 10px;
overflow: auto;
}
.legend_box {
display: inline-block;
width: 145px;
height: 50px;
vertical-align: top;
margin-right: 5px;
padding-right: 5px;
margin-top: 10px;
}
.legend_colour {
float: left;
width: 10px;
height: 10px;
margin-top: 3px;
margin-bottom: 10px;
}
.legend_label {
margin-left: 15px;
}
.rom_checkbox_box {
}
.rom_checkbox_box_hidden {
display: none;
}
#rom_edit, #rom_edit_delete {
float: right;
}
#rom_edit_panel {
background-color: rgba(56, 56, 56, 0.3);
backdrop-filter: blur(8px);
-webkit-backdrop-filter: blur(8px);
padding: 15px;
margin-bottom: 10px;
}
#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);
}

View File

@@ -14,7 +14,7 @@
<ProjectReference Include="..\gaseous-tools\gaseous-tools.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="MySql.Data" Version="8.0.33" />
<PackageReference Include="MySql.Data" Version="8.1.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
</ItemGroup>
</Project>

View File

@@ -335,6 +335,22 @@ namespace gaseous_tools
}
}
public string LibraryBIOSDirectory
{
get
{
return Path.Combine(LibraryRootDirectory, "BIOS");
}
}
public string LibraryUploadDirectory
{
get
{
return Path.Combine(LibraryRootDirectory, "Upload");
}
}
public string LibraryMetadataDirectory
{
get
@@ -385,6 +401,8 @@ namespace gaseous_tools
if (!Directory.Exists(LibraryRootDirectory)) { Directory.CreateDirectory(LibraryRootDirectory); }
if (!Directory.Exists(LibraryImportDirectory)) { Directory.CreateDirectory(LibraryImportDirectory); }
if (!Directory.Exists(LibraryDataDirectory)) { Directory.CreateDirectory(LibraryDataDirectory); }
if (!Directory.Exists(LibraryBIOSDirectory)) { Directory.CreateDirectory(LibraryBIOSDirectory); }
if (!Directory.Exists(LibraryUploadDirectory)) { Directory.CreateDirectory(LibraryUploadDirectory); }
if (!Directory.Exists(LibraryMetadataDirectory)) { Directory.CreateDirectory(LibraryMetadataDirectory); }
if (!Directory.Exists(LibrarySignatureImportDirectory)) { Directory.CreateDirectory(LibrarySignatureImportDirectory); }
if (!Directory.Exists(LibrarySignatureImportDirectory_TOSEC)) { Directory.CreateDirectory(LibrarySignatureImportDirectory_TOSEC); }

View File

@@ -8,7 +8,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="MySql.Data" Version="8.0.33" />
<PackageReference Include="MySql.Data" Version="8.1.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="IGDB" Version="2.3.2" />
</ItemGroup>