Compare commits

...

40 Commits

Author SHA1 Message Date
Michael Green
590a7829b1 Another attempt at fixing the Embedded DB Docker image (#388) 2024-07-08 21:22:26 +10:00
Michael Green
ae75fc1490 Fix for Embedded DB Docker Image (#387) 2024-07-08 08:29:32 +10:00
Michael Green
0c645d04aa v1.7.0 git sync 2024-07-06 19:54:56 +10:00
Michael Green
13b3764fcf Add Famicom to PlatformMap (#384) 2024-07-06 15:52:57 +10:00
Michael Green
57d4fe7cf9 Merge branch 'main' into branch-v1.7.0 2024-06-30 00:31:06 +10:00
Michael Green
8519d4c745 Fixed EJS directory 2024-06-29 23:04:14 +10:00
Michael Green
822a40b61b Workflows updated to push Docker images to GitHub 2024-06-29 22:55:50 +10:00
Michael Green
b463bb6064 EJS Amiga fix 2024-06-29 22:49:34 +10:00
Michael Green
3e8696e6e8 Removed Hasheous POC - full Hasheous support will be available in 1.8.0 (#379) 2024-06-28 16:20:22 +10:00
Michael Green
d1f157ac08 Disable platform logo loading - these aren't used, added try/catch block around Hasheous to deal with failures. 2024-06-28 11:03:59 +10:00
Michael Green
30be179367 Improved maintenance tasks (#378)
During the daily maintenance task, server logs are now deleted in 1000
record chunks. It repeats this a maximum of 1000 times or until there
are no more records left to delete.

During the weekly maintenance task, the optimise task now has a longer
timeout.

Closes #352
2024-06-26 22:45:38 +10:00
Michael Green
787bb47bd3 Resolved many database errors (#377)
* Resolved missing table errors.
* These were due to some dynamically created tables being queried before
they were created.
  * These tables are now created at start up.
* Resolved many "INSERT" errors that were polluting the logs:
* These were due to a race condition where sometimes the database would
return the data as not being in the database causing Gaseous to try to
insert it - even though the data was already there.
2024-06-26 15:00:09 +10:00
Michael Green
ccf9afd561 Added extra logging when generating hashes (#376)
When hashing large files, it can appear that nothing is happening. This
change adds log entries before hashes are generated to indicate that
hashing is about to begin.
2024-06-26 06:25:05 +10:00
Michael Green
c7b6233ad6 Build a docker image that includes an embedded MariaDB server (#373)
This PR modifies the build process to generate two docker images; the
standard image, and one with MariaDB embedded into it for one click
installs.

This embedded MariaDB image is meant to support users on systems like
Unraid that don't easily support docker-compose.

Also; for users on Unraid, it will allow the creation of a single click
install template to be submitted to the Unraid marketplace (this will be
done at a later date after the release of 1.7.4).
2024-06-26 06:02:20 +10:00
Michael Green
afb72c3d74 Updated release actions 2024-06-25 23:48:08 +10:00
Michael Green
299e24793f Another change 2024-06-25 23:23:21 +10:00
Michael Green
de9b9bf706 More changes 2024-06-25 23:18:47 +10:00
Michael Green
28bc50a82e Path change 2024-06-25 23:03:28 +10:00
Michael Green
f9f65f3ffb Permission change 2024-06-25 22:57:39 +10:00
Michael Green
87f3a1aa89 More testing 2024-06-25 22:46:13 +10:00
Michael Green
b3e7696292 Added permission clause 2024-06-25 22:29:44 +10:00
Michael Green
29c685c791 Docker build test 2024-06-25 22:00:31 +10:00
Michael Green
f0020e5b1f Built a docker file that builds Gaseous with MariaDB built in 2024-06-25 22:00:03 +10:00
Michael Green
3897ed65ef Fix for first time start where new users weren't offered the opportunity to create a new account (#372) 2024-06-25 08:57:56 +10:00
Michael Green
cebab38dd6 Provide ability to upload save states (#371)
This change provides the ability to upload save states.

When a state is downloaded, the state (savestate.state), screenshot
(screenshot.jpg), and a json file (rominfo.json) describing the save
state are zipped and downloaded.

The json file description is defined as follows:
```json
{
  "Name": "Super Mario Bros. (1985-09-13)(Nintendo)(JP-US).zip",
  "StateDateTime": "2024-06-24T05:34:38",
  "StateName": "",
  "MD5": "7d158dcd242e77ba249ac8342474aa77",
  "SHA1": "3d4b04dc78f9d998f17d9fe9ad982a83b5ed72df",
  "Type": "ROM"
}
```

| Attribute | Value |
| -------- | ------|
| Name | The name of the ROM that the state belongs to. This is merely a
convenience attribute. |
| StateDateTime | The date and time (in UTC) when the state was
initially saved. |
| StateName | The name of the state |
| MD5 | The MD5 hash of the ROM that the state belongs to. |
| SHA1 | The SHA1 hash of the ROM that the state belongs to. |
| Type | Whether the state belongs to a ROM or ROM Group |

If the zip is re-uploaded, the above json file will be used to
automatically match the saved state to the ROM that created it.

If a zip is uploaded without the above three files, the upload will
fail.

If a file is uploaded that is not a zip, it will be stored against the
currently running ROM and a warning will be displayed that Gaseous was
unable to verify that the state belongs to the ROM, and may not function
as expected.

Closes #336
2024-06-24 22:38:54 +10:00
Michael Green
69da6ee346 Merge branch 'main' into branch-v1.7.0 2024-05-01 15:27:14 +10:00
Michael Green
59173d8ae5 Merge branch 'main' into branch-v1.7.0 2024-04-16 11:51:21 +10:00
Michael Green
178f70cb98 Performance improvements to infinite scrolling (#347) 2024-04-16 11:39:58 +10:00
Michael Green
60dbaf85a4 Merge branch 'main' into branch-v1.7.0 2024-04-15 14:35:32 +10:00
Michael Green
8a80274030 Integrate EJS 4.0.12 - Adds a new Master System core, and a new DS core (#344)
Closes #340
2024-04-15 14:23:11 +10:00
Michael Green
123239cf58 Merge branch 'main' into branch-v1.7.0 2024-03-12 00:48:56 +11:00
Michael Green
04419383aa When fixing matches, search doesn't return the correct values (#330)
Issue was due to the search result limit being too low. Increased the search result size to 100 returned objects.
2024-03-12 00:42:59 +11:00
Michael Green
0bef298abf SQL error when loading the library with the MySQL database server (#327)
Fixes #305
2024-03-10 13:58:42 +11:00
Michael Green
a4d581b369 Infinite Scrolling breaks clicking the Gaseous Games logo to return to library (#326)
Closes #316

Overhauled how infinite scrolling works. Should be more reliable now.
2024-03-10 01:47:51 +11:00
Michael Green
16cb0c89dc Hide unneeded buttons from the emulator (#324)
Closes #317

Removes the save and load file buttons - leaving the save and load state buttons
2024-03-06 22:38:32 +11:00
Michael Green
95b52c47dd New library dialog content overruns the edges of the dialog (#323)
Closes #303

This fix is a clean up fix - will review functionality for 1.8.0
2024-03-06 20:48:42 +11:00
Michael Green
b1056299b8 Ensure ROMs are decompressed before loading them into a media group (#321)
All compressed archives (zip, rar, and 7z) are decompressed when a media group is created.

Only files with an extension defined in the platform mapping are added to the M3U file.

Closes #307
2024-03-01 13:39:27 +11:00
Michael Green
14c5761959 Error on startup when reading value LastRun_BackgroundDatabaseUpgrade (#319)
Fixes #300
2024-02-26 16:17:26 +11:00
Michael Green
d7b3711be6 Update for PlatformMap.json to add missing core support (#318)
Adds cores to PlatformMap.json for platforms (closes #315):
* Family Computer = fceumm
* Super Famicom = snes9x
* TurboGrafx-16/PC Engine + Turbografx-16/PC Engine CD = mednafen_pce
* Neo Geo Pocket + Neo Geo Pocket Color = mednafen_ngp
* Wonderswan + Wonderswan Color = mednafen_wswan
* Nec PC-FX = mednafen_pcfx
2024-02-25 13:22:41 +11:00
Michael Green
f2a58d23f0 Fix for broken first run on fresh installs (#297)
* Fix for broken first run on fresh installs
2024-02-11 00:53:22 +11:00
51 changed files with 4635 additions and 3774 deletions

View File

@@ -9,9 +9,14 @@ on:
jobs:
docker:
runs-on: ubuntu-latest
permissions:
packages: write
contents: read
attestations: write
id-token: write
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
submodules: 'true'
- name: Install dotnet tool
@@ -21,18 +26,37 @@ jobs:
- name: Sign in to Nuget
run: dotnet nuget add source --username michael-j-green --password ${{ secrets.NUGETKEY }} --store-password-in-clear-text --name github "https://nuget.pkg.github.com/gaseous-project/index.json"
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v2
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v4
- name: Login to GitHub Package Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push standard image
uses: docker/build-push-action@v6
with:
context: .
file: ./build/Dockerfile
platforms: linux/amd64,linux/arm64
push: true
tags: gaseousgames/gaseousserver:${{ github.ref_name}}
tags: |
gaseousgames/gaseousserver:${{ github.ref_name}}
ghcr.io/gaseous-project/gaseousserver:${{ github.ref_name}}
- name: Build and push image with embedded mariadb
uses: docker/build-push-action@v6
with:
context: .
file: ./build/Dockerfile-EmbeddedDB
platforms: linux/amd64,linux/arm64
push: true
tags: |
gaseousgames/gaseousserver:${{ github.ref_name}}-embeddeddb
ghcr.io/gaseous-project/gaseousserver:${{ github.ref_name}}-embeddeddb

View File

@@ -8,9 +8,14 @@ on:
jobs:
docker:
runs-on: ubuntu-latest
permissions:
packages: write
contents: read
attestations: write
id-token: write
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
submodules: 'true'
- name: Install dotnet tool
@@ -20,18 +25,41 @@ jobs:
- name: Sign in to Nuget
run: dotnet nuget add source --username michael-j-green --password ${{ secrets.NUGETKEY }} --store-password-in-clear-text --name github "https://nuget.pkg.github.com/gaseous-project/index.json"
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v2
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v4
- name: Login to GitHub Package Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push standard image
uses: docker/build-push-action@v6
with:
context: .
file: ./build/Dockerfile
platforms: linux/amd64,linux/arm64
push: true
tags: gaseousgames/gaseousserver:latest,gaseousgames/gaseousserver:${{ github.ref_name}}
tags: |
gaseousgames/gaseousserver:latest
gaseousgames/gaseousserver:${{ github.ref_name}}
ghcr.io/gaseous-project/gaseousserver:latest
ghcr.io/gaseous-project/gaseousserver:${{ github.ref_name}}
- name: Build and push image with embedded mariadb
uses: docker/build-push-action@v6
with:
context: .
file: ./build/Dockerfile-EmbeddedDB
platforms: linux/amd64,linux/arm64
push: true
tags: |
gaseousgames/gaseousserver:latest-embeddeddb
gaseousgames/gaseousserver:${{ github.ref_name}}-embeddeddb
ghcr.io/gaseous-project/gaseousserver:latest-embeddeddb
ghcr.io/gaseous-project/gaseousserver:${{ github.ref_name}}-embeddeddb

3
.gitignore vendored
View File

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

View File

@@ -8,21 +8,34 @@ RUN echo "Target: $TARGETARCH"
RUN echo "Build: $BUILDPLATFORM"
# Copy everything
COPY . ./
COPY .. ./
# Restore as distinct layers
RUN dotnet restore "gaseous-server/gaseous-server.csproj" -a $TARGETARCH
# Build and publish a release
RUN dotnet publish "gaseous-server/gaseous-server.csproj" --use-current-runtime --self-contained true -c Release -o out -a $TARGETARCH
# download and unzip EmulatorJS from CDN
RUN apt-get update && apt-get install -y p7zip-full
# disabled for 1.7.4 as the next version EmulatorJS is not yet available
# # update apt-get
# RUN apt-get update
# # download and unzip EmulatorJS from CDN
# RUN apt-get install -y p7zip-full
# RUN mkdir -p out/wwwroot/emulators/EmulatorJS
# RUN wget https://cdn.emulatorjs.org/releases/4.0.12.7z
# RUN 7z x -y -oout/wwwroot/emulators/EmulatorJS 4.0.12.7z
RUN wget --recursive --no-parent https://cdn.emulatorjs.org/latest/
RUN mkdir -p out/wwwroot/emulators/EmulatorJS
RUN wget https://cdn.emulatorjs.org/releases/4.0.12.7z
RUN 7z x -y -oout/wwwroot/emulators/EmulatorJS 4.0.12.7z
RUN cp -fr cdn.emulatorjs.org/latest/* out/wwwroot/emulators/EmulatorJS
RUN rm -Rf cdn.emulatorjs.org
# clean up apt-get
RUN apt-get clean && rm -rf /var/lib/apt/lists
# Build runtime image
FROM mcr.microsoft.com/dotnet/aspnet:8.0
ENV INDOCKER=1
WORKDIR /App
COPY --from=build-env /App/out .
# start gaseous-server
ENTRYPOINT ["dotnet", "gaseous-server.dll"]

View File

@@ -0,0 +1,77 @@
FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:8.0 AS build-env
ARG TARGETARCH
ARG BUILDPLATFORM
WORKDIR /App
EXPOSE 80
RUN echo "Target: $TARGETARCH"
RUN echo "Build: $BUILDPLATFORM"
# Copy everything
COPY .. ./
# Restore as distinct layers
RUN dotnet restore "gaseous-server/gaseous-server.csproj" -a $TARGETARCH
# Build and publish a release
RUN dotnet publish "gaseous-server/gaseous-server.csproj" --use-current-runtime --self-contained true -c Release -o out -a $TARGETARCH
# disabled for 1.7.4 as the next version EmulatorJS is not yet available
# # update apt-get
# RUN apt-get update
# # download and unzip EmulatorJS from CDN
# RUN apt-get install -y p7zip-full
# RUN mkdir -p out/wwwroot/emulators/EmulatorJS
# RUN wget https://cdn.emulatorjs.org/releases/4.0.12.7z
# RUN 7z x -y -oout/wwwroot/emulators/EmulatorJS 4.0.12.7z
RUN wget --recursive --no-parent https://cdn.emulatorjs.org/latest/
RUN mkdir -p out/wwwroot/emulators/EmulatorJS
RUN cp -fr cdn.emulatorjs.org/latest/* out/wwwroot/emulators/EmulatorJS
RUN rm -Rf cdn.emulatorjs.org
# Build runtime image
FROM mcr.microsoft.com/dotnet/aspnet:8.0
ENV INDOCKER=1
WORKDIR /App
COPY --from=build-env /App/out .
# variables
ARG PUID=1000
ARG PGID=1000
ARG dbhost=localhost
ARG dbuser=root
ARG dbpass=gaseous
ARG MARIADB_ROOT_PASSWORD=$dbpass
ENV PUID=${PUID}
ENV PGID=${PGID}
ENV dbhost=${dbhost}
ENV dbuser=${dbuser}
ENV dbpass=${dbpass}
ENV MARIADB_ROOT_PASSWORD=${dbpass}
# install mariadb
RUN DEBIAN_FRONTEND=noninteractive && \
apt-get update && apt-get install -y mariadb-server
RUN mkdir -p /run/mysqld
COPY ../build/mariadb.sh /usr/sbin/start-mariadb.sh
RUN chmod +x /usr/sbin/start-mariadb.sh
# install supervisord
RUN apt-get install -y supervisor
COPY ../build/supervisord.conf /etc/supervisor/conf.d/supervisord.conf
RUN mkdir -p /var/run/supervisord
RUN mkdir -p /var/log/supervisord
# clean up apt-get
RUN apt-get clean && rm -rf /var/lib/apt/lists
# copy entrypoint
COPY ../build/entrypoint.sh /usr/sbin/entrypoint.sh
RUN chmod +x /usr/sbin/entrypoint.sh
# volumes
VOLUME /home/gaseous/.gaseous-server /var/lib/mysql
# start services
ENTRYPOINT [ "/usr/sbin/entrypoint.sh" ]

12
build/entrypoint.sh Normal file
View File

@@ -0,0 +1,12 @@
#!/bin/sh
# create the user
echo "Creating user gaseous with UID ${PUID} and GID ${PGID}"
groupadd -g ${PGID} gaseous
useradd -u ${PUID} -g ${PGID} -m gaseous -G sudo
usermod -p "*" gaseous
mkdir -p /home/gaseous/.gaseous-server /var/lib/mysql
chown -R ${PUID}:${PGID} /App /home/gaseous/.gaseous-server /var/lib/mysql /run/mysqld
# Start supervisord and services
/usr/bin/supervisord -c /etc/supervisor/conf.d/supervisord.conf

17
build/mariadb.sh Normal file
View File

@@ -0,0 +1,17 @@
#!/bin/sh
# start the database server without network or grant tables
/usr/sbin/mariadbd --datadir=/var/lib/mysql --skip-grant-tables --skip-networking &
# wait for the server to start
sleep 5
# change the root password
mariadb -u root -e "FLUSH PRIVILEGES; ALTER USER 'root'@'localhost' IDENTIFIED BY '$MARIADB_ROOT_PASSWORD'; FLUSH PRIVILEGES;"
# stop the server
sleep 5
killall mariadbd
# start the server normally
/usr/sbin/mariadbd --datadir=/var/lib/mysql

35
build/supervisord.conf Normal file
View File

@@ -0,0 +1,35 @@
[supervisord]
nodaemon=true
logfile=/var/log/supervisord/supervisord.log
logfile_maxbytes=50
logfile_backups=5
pidfile=/var/run/supervisord/supervisord.pid
loglevel = info
[unix_http_server]
file=/var/run/supervisord/supervisor.sock
chmod=0700
[rpcinterface:supervisor]
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
[supervisorctl]
serverurl=unix:///var/run/supervisord/supervisor.sock
[program:mariadb]
user=gaseous
command=bash -c "/usr/sbin/start-mariadb.sh"
autostart=true
autorestart=true
redirect_stderr=true
stdout_logfile=/dev/fd/1
stdout_logfile_maxbytes=0
[program:gaseous-server]
user=gaseous
command=dotnet /App/gaseous-server.dll
autostart=true
autorestart=true
redirect_stderr=true
stdout_logfile=/dev/fd/1
stdout_logfile_maxbytes=0

View File

@@ -1,4 +1,4 @@
using System.Collections.Concurrent;
using System.Collections.Concurrent;
using System.ComponentModel;
using System.Data;
using System.IO.Compression;
@@ -45,11 +45,13 @@ namespace gaseous_server.Classes
{
var xmlStream = File.OpenRead(FileName);
Logging.Log(Logging.LogType.Information, "Hash File", "Generating MD5 hash for file: " + FileName);
var md5 = MD5.Create();
byte[] md5HashByte = md5.ComputeHash(xmlStream);
string md5Hash = BitConverter.ToString(md5HashByte).Replace("-", "").ToLowerInvariant();
_md5hash = md5Hash;
Logging.Log(Logging.LogType.Information, "Hash File", "Generating SHA1 hash for file: " + FileName);
var sha1 = SHA1.Create();
xmlStream.Position = 0;
byte[] sha1HashByte = sha1.ComputeHash(xmlStream);
@@ -157,50 +159,6 @@ namespace gaseous_server.Classes
return defaultValue;
}
}
public static int GetLookupByCode(LookupTypes LookupType, string Code)
{
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "SELECT Id FROM " + LookupType.ToString() + " WHERE Code = @code";
Dictionary<string, object> dbDict = new Dictionary<string, object>{
{ "code", Code }
};
DataTable data = db.ExecuteCMD(sql, dbDict);
if (data.Rows.Count == 0)
{
return -1;
}
else
{
return (int)data.Rows[0]["Id"];
}
}
public static int GetLookupByValue(LookupTypes LookupType, string Value)
{
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "SELECT Id FROM " + LookupType.ToString() + " WHERE Value = @value";
Dictionary<string, object> dbDict = new Dictionary<string, object>{
{ "value", Value }
};
DataTable data = db.ExecuteCMD(sql, dbDict);
if (data.Rows.Count == 0)
{
return -1;
}
else
{
return (int)data.Rows[0]["Id"];
}
}
public enum LookupTypes
{
Country,
Language
}
}
/// <summary>

View File

@@ -645,7 +645,7 @@ namespace gaseous_server.Classes
return MetadataPath;
}
public string LibrarySignaturesDirectory
public string LibrarySignatureImportDirectory
{
get
{
@@ -653,14 +653,6 @@ namespace gaseous_server.Classes
}
}
public string LibrarySignaturesProcessedDirectory
{
get
{
return Path.Combine(LibraryRootDirectory, "Signatures - Processed");
}
}
public void InitLibrary()
{
if (!Directory.Exists(LibraryRootDirectory)) { Directory.CreateDirectory(LibraryRootDirectory); }
@@ -670,8 +662,7 @@ namespace gaseous_server.Classes
if (!Directory.Exists(LibraryMetadataDirectory)) { Directory.CreateDirectory(LibraryMetadataDirectory); }
if (!Directory.Exists(LibraryTempDirectory)) { Directory.CreateDirectory(LibraryTempDirectory); }
if (!Directory.Exists(LibraryCollectionsDirectory)) { Directory.CreateDirectory(LibraryCollectionsDirectory); }
if (!Directory.Exists(LibrarySignaturesDirectory)) { Directory.CreateDirectory(LibrarySignaturesDirectory); }
if (!Directory.Exists(LibrarySignaturesProcessedDirectory)) { Directory.CreateDirectory(LibrarySignaturesProcessedDirectory); }
if (!Directory.Exists(LibrarySignatureImportDirectory)) { Directory.CreateDirectory(LibrarySignatureImportDirectory); }
}
}
@@ -707,10 +698,6 @@ namespace gaseous_server.Classes
}
}
private static bool _HasheousSubmitFixes { get; set; } = false;
private static string _HasheousAPIKey { get; set; } = "";
private static int _MaxLibraryScanWorkers
{
get
@@ -745,10 +732,6 @@ namespace gaseous_server.Classes
public HasheousClient.Models.MetadataModel.SignatureSources SignatureSource = _SignatureSource;
public bool HasheousSubmitFixes = _HasheousSubmitFixes;
public string HasheousAPIKey = _HasheousAPIKey;
public int MaxLibraryScanWorkers = _MaxLibraryScanWorkers;
public string HasheousHost = _HasheousHost;

View File

@@ -1,9 +1,6 @@
using System;
using System.Data;
using System.Reflection;
using gaseous_server.Classes.Metadata;
using gaseous_server.Models;
using IGDB.Models;
namespace gaseous_server.Classes
{
@@ -67,8 +64,6 @@ namespace gaseous_server.Classes
public static void PostUpgradeScript(int TargetSchemaVersion, Database.databaseType? DatabaseType)
{
var assembly = Assembly.GetExecutingAssembly();
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "";
Dictionary<string, object> dbDict = new Dictionary<string, object>();
@@ -108,51 +103,6 @@ namespace gaseous_server.Classes
sql = "DELETE FROM Settings WHERE Setting LIKE 'LastRun_%';";
db.ExecuteNonQuery(sql);
break;
case 1022:
// load country list
Logging.Log(Logging.LogType.Information, "Database Upgrade", "Adding country look up table contents");
string countryResourceName = "gaseous_server.Support.Country.txt";
using (Stream stream = assembly.GetManifestResourceStream(countryResourceName))
using (StreamReader reader = new StreamReader(stream))
{
do
{
string[] line = reader.ReadLine().Split("|");
sql = "INSERT INTO Country (Code, Value) VALUES (@code, @value);";
dbDict = new Dictionary<string, object>{
{ "code", line[0] },
{ "value", line[1] }
};
db.ExecuteNonQuery(sql, dbDict);
} while (reader.EndOfStream == false);
}
// load language list
Logging.Log(Logging.LogType.Information, "Database Upgrade", "Adding language look up table contents");
string languageResourceName = "gaseous_server.Support.Language.txt";
using (Stream stream = assembly.GetManifestResourceStream(languageResourceName))
using (StreamReader reader = new StreamReader(stream))
{
do
{
string[] line = reader.ReadLine().Split("|");
sql = "INSERT INTO Language (Code, Value) VALUES (@code, @value);";
dbDict = new Dictionary<string, object>{
{ "code", line[0] },
{ "value", line[1] }
};
db.ExecuteNonQuery(sql, dbDict);
} while (reader.EndOfStream == false);
}
// this is a safe background task
BackgroundUpgradeTargetSchemaVersions.Add(1022);
break;
}
break;
}
@@ -167,10 +117,6 @@ namespace gaseous_server.Classes
case 1002:
MySql_1002_MigrateMetadataVersion();
break;
case 1022:
MySql_1022_MigrateMetadataVersion();
break;
}
}
}
@@ -271,36 +217,5 @@ namespace gaseous_server.Classes
}
}
}
public static void MySql_1022_MigrateMetadataVersion()
{
FileSignature fileSignature = new FileSignature();
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "SELECT * FROM Games_Roms WHERE RomDataVersion = 1;";
DataTable data = db.ExecuteCMD(sql);
foreach (DataRow row in data.Rows)
{
Logging.Log(Logging.LogType.Information, "Database Migration", "Updating ROM table for ROM: " + (string)row["Name"]);
GameLibrary.LibraryItem library = GameLibrary.GetLibrary((int)row["LibraryId"]);
Common.hashObject hash = new Common.hashObject()
{
md5hash = (string)row["MD5"],
sha1hash = (string)row["SHA1"]
};
Signatures_Games signature = fileSignature.GetFileSignature(
library,
hash,
new FileInfo((string)row["Path"]),
(string)row["Path"]
);
Platform platform = Platforms.GetPlatform((long)row["PlatformId"], false);
Game game = Games.GetGame((long)row["GameId"], false, false, false);
ImportGame.StoreROM(library, hash, game, platform, signature, (string)row["Path"], (long)row["Id"]);
}
}
}
}

View File

@@ -1,8 +1,6 @@
using System.Collections.Concurrent;
using System.IO.Compression;
using gaseous_server.Classes.Metadata;
using HasheousClient.Models;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using NuGet.Common;
using SevenZip;
using SharpCompress.Archives;
@@ -112,10 +110,8 @@ namespace gaseous_server.Classes
// loop through contents until we find the first signature match
List<ArchiveData> archiveFiles = new List<ArchiveData>();
bool signatureFound = false;
bool signatureSelectorAlreadyApplied = false;
foreach (string file in Directory.GetFiles(ExtractPath, "*.*", SearchOption.AllDirectories))
{
bool signatureSelector = false;
if (File.Exists(file))
{
FileInfo zfi = new FileInfo(file);
@@ -125,6 +121,16 @@ namespace gaseous_server.Classes
if (zfi != null)
{
ArchiveData archiveData = new ArchiveData
{
FileName = Path.GetFileName(file),
FilePath = zfi.Directory.FullName.Replace(ExtractPath, ""),
Size = zfi.Length,
MD5 = hash.md5hash,
SHA1 = hash.sha1hash
};
archiveFiles.Add(archiveData);
if (signatureFound == false)
{
gaseous_server.Models.Signatures_Games zDiscoveredSignature = _GetFileSignature(zhash, zfi.Name, zfi.Extension, zfi.Length, file, true);
@@ -146,37 +152,15 @@ namespace gaseous_server.Classes
discoveredSignature = zDiscoveredSignature;
signatureFound = true;
if (signatureSelectorAlreadyApplied == false)
{
signatureSelector = true;
signatureSelectorAlreadyApplied = true;
}
}
}
ArchiveData archiveData = new ArchiveData
{
FileName = Path.GetFileName(file),
FilePath = zfi.Directory.FullName.Replace(ExtractPath, ""),
Size = zfi.Length,
MD5 = zhash.md5hash,
SHA1 = zhash.sha1hash,
isSignatureSelector = signatureSelector
};
archiveFiles.Add(archiveData);
}
}
}
if (discoveredSignature.Rom.Attributes == null)
{
discoveredSignature.Rom.Attributes = new Dictionary<string, object>();
}
discoveredSignature.Rom.Attributes.Add(
discoveredSignature.Rom.Attributes.Add(new KeyValuePair<string, object>(
"ZipContents", Newtonsoft.Json.JsonConvert.SerializeObject(archiveFiles)
);
));
}
catch (Exception ex)
{
@@ -281,74 +265,46 @@ namespace gaseous_server.Classes
if (Config.MetadataConfiguration.SignatureSource == HasheousClient.Models.MetadataModel.SignatureSources.Hasheous)
{
HasheousClient.Hasheous hasheous = new HasheousClient.Hasheous();
Console.WriteLine(HasheousClient.WebApp.HttpHelper.BaseUri);
LookupItemModel? HasheousResult = null;
SignatureLookupItem? HasheousResult = null;
try
{
HasheousResult = hasheous.RetrieveFromHasheous(new HashLookupModel
HasheousResult = hasheous.RetrieveFromHasheousAsync(new HashLookupModel
{
MD5 = hash.md5hash,
SHA1 = hash.sha1hash
});
if (HasheousResult != null)
{
if (HasheousResult.Signature != null)
{
gaseous_server.Models.Signatures_Games signature = new Models.Signatures_Games();
signature.Game = HasheousResult.Signature.Game;
signature.Rom = HasheousResult.Signature.Rom;
if (HasheousResult.MetadataResults != null)
{
if (HasheousResult.MetadataResults.Count > 0)
{
foreach (SignatureLookupItem.MetadataResult metadataResult in HasheousResult.MetadataResults)
{
if (metadataResult.Source == MetadataModel.MetadataSources.IGDB)
{
signature.Flags.IGDBPlatformId = (long)metadataResult.PlatformId;
signature.Flags.IGDBGameId = (long)metadataResult.GameId;
}
}
}
}
return signature;
}
}
}
catch (Exception ex)
{
Logging.Log(Logging.LogType.Warning, "Import Game", "An error occurred while importing " + ImageName, ex);
return null;
}
if (HasheousResult != null)
{
if (HasheousResult.Signature != null)
{
gaseous_server.Models.Signatures_Games signature = new Models.Signatures_Games();
string gameJson = Newtonsoft.Json.JsonConvert.SerializeObject(HasheousResult.Signature.Game);
string romJson = Newtonsoft.Json.JsonConvert.SerializeObject(HasheousResult.Signature.Rom);
signature.Game = Newtonsoft.Json.JsonConvert.DeserializeObject<Models.Signatures_Games.GameItem>(gameJson);
signature.Rom = Newtonsoft.Json.JsonConvert.DeserializeObject<Models.Signatures_Games.RomItem>(romJson);
// get platform metadata
if (HasheousResult.Platform != null)
{
if (HasheousResult.Platform.metadata.Count > 0)
{
foreach (HasheousClient.Models.MetadataItem metadataResult in HasheousResult.Platform.metadata)
{
if (metadataResult.Id.Length > 0)
{
switch (metadataResult.Source)
{
case HasheousClient.Models.MetadataSources.IGDB:
signature.Flags.IGDBPlatformId = (long)Platforms.GetPlatform(metadataResult.Id, false).Id;
break;
}
}
}
}
}
// get game metadata
if (HasheousResult.Metadata != null)
{
if (HasheousResult.Metadata.Count > 0)
{
foreach (HasheousClient.Models.MetadataItem metadataResult in HasheousResult.Metadata)
{
if (metadataResult.Id.Length > 0)
{
switch (metadataResult.Source)
{
case HasheousClient.Models.MetadataSources.IGDB:
signature.Flags.IGDBGameId = (long)Games.GetGame(metadataResult.Id, false, false, false).Id;
break;
}
}
}
}
}
return signature;
}
Logging.Log(Logging.LogType.Warning, "Get Signature", "Error retrieving signature from Hasheous", ex);
}
}
@@ -423,7 +379,6 @@ namespace gaseous_server.Classes
public long Size { get; set; }
public string MD5 { get; set; }
public string SHA1 { get; set; }
public bool isSignatureSelector { get; set; } = false;
}
}
}

View File

@@ -308,11 +308,11 @@ namespace gaseous_server.Classes
if (UpdateId == 0)
{
sql = "INSERT INTO Games_Roms (PlatformId, GameId, Name, Size, CRC, MD5, SHA1, DevelopmentStatus, Attributes, RomType, RomTypeMedia, MediaLabel, Path, MetadataSource, MetadataGameName, MetadataVersion, LibraryId, RomDataVersion) VALUES (@platformid, @gameid, @name, @size, @crc, @md5, @sha1, @developmentstatus, @Attributes, @romtype, @romtypemedia, @medialabel, @path, @metadatasource, @metadatagamename, @metadataversion, @libraryid, @romdataversion); SELECT CAST(LAST_INSERT_ID() AS SIGNED);";
sql = "INSERT INTO Games_Roms (PlatformId, GameId, Name, Size, CRC, MD5, SHA1, DevelopmentStatus, Attributes, RomType, RomTypeMedia, MediaLabel, Path, MetadataSource, MetadataGameName, MetadataVersion, LibraryId) VALUES (@platformid, @gameid, @name, @size, @crc, @md5, @sha1, @developmentstatus, @Attributes, @romtype, @romtypemedia, @medialabel, @path, @metadatasource, @metadatagamename, @metadataversion, @libraryid); SELECT CAST(LAST_INSERT_ID() AS SIGNED);";
}
else
{
sql = "UPDATE Games_Roms SET PlatformId=@platformid, GameId=@gameid, Name=@name, Size=@size, DevelopmentStatus=@developmentstatus, Attributes=@Attributes, RomType=@romtype, RomTypeMedia=@romtypemedia, MediaLabel=@medialabel, MetadataSource=@metadatasource, MetadataGameName=@metadatagamename, MetadataVersion=@metadataversion, RomDataVersion=@romdataversion WHERE Id=@id;";
sql = "UPDATE Games_Roms SET PlatformId=@platformid, GameId=@gameid, Name=@name, Size=@size, DevelopmentStatus=@developmentstatus, Attributes=@Attributes, RomType=@romtype, RomTypeMedia=@romtypemedia, MediaLabel=@medialabel, MetadataSource=@metadatasource, MetadataGameName=@metadatagamename, MetadataVersion=@metadataversion WHERE Id=@id;";
dbDict.Add("id", UpdateId);
}
dbDict.Add("platformid", Common.ReturnValueIfNull(determinedPlatform.Id, 0));
@@ -327,7 +327,6 @@ namespace gaseous_server.Classes
dbDict.Add("metadatagamename", discoveredSignature.Game.Name);
dbDict.Add("metadataversion", 2);
dbDict.Add("libraryid", library.Id);
dbDict.Add("romdataversion", 2);
if (discoveredSignature.Rom.Attributes != null)
{

View File

@@ -5,7 +5,7 @@ using Microsoft.VisualStudio.Web.CodeGeneration;
namespace gaseous_server.Classes
{
public class Maintenance : QueueItemStatus
public class Maintenance : QueueItemStatus
{
const int MaxFileAge = 30;
@@ -16,6 +16,7 @@ namespace gaseous_server.Classes
Dictionary<string, object> dbDict = new Dictionary<string, object>();
// remove any entries from the library that have an invalid id
Logging.Log(Logging.LogType.Information, "Maintenance", "Removing any entries from the library that have an invalid id");
string LibraryWhereClause = "";
foreach (GameLibrary.LibraryItem library in GameLibrary.GetLibraries)
{
@@ -33,9 +34,27 @@ namespace gaseous_server.Classes
}
// delete old logs
sql = "DELETE FROM ServerLogs WHERE EventTime < @EventRetentionDate;";
dbDict.Add("EventRetentionDate", DateTime.UtcNow.AddDays(Config.LoggingConfiguration.LogRetention * -1));
db.ExecuteCMD(sql, dbDict);
Logging.Log(Logging.LogType.Information, "Maintenance", "Removing logs older than " + Config.LoggingConfiguration.LogRetention + " days");
long deletedCount = 1;
long deletedEventCount = 0;
long maxLoops = 1000;
while (deletedCount > 0)
{
sql = "DELETE FROM ServerLogs WHERE EventTime < @EventRetentionDate LIMIT 1000; SELECT ROW_COUNT() AS Count;";
dbDict.Add("EventRetentionDate", DateTime.UtcNow.AddDays(Config.LoggingConfiguration.LogRetention * -1));
DataTable deletedCountTable = db.ExecuteCMD(sql, dbDict);
deletedCount = (long)deletedCountTable.Rows[0][0];
deletedEventCount += deletedCount;
// check if we've hit the limit
maxLoops -= 1;
if (maxLoops <= 0)
{
Logging.Log(Logging.LogType.Warning, "Maintenance", "Hit the maximum number of loops for deleting logs. Stopping.");
break;
}
}
Logging.Log(Logging.LogType.Information, "Maintenance", "Deleted " + deletedEventCount + " log entries");
// delete files and directories older than 7 days in PathsToClean
List<string> PathsToClean = new List<string>();
@@ -87,7 +106,7 @@ namespace gaseous_server.Classes
SetStatus(StatusCounter, tables.Rows.Count, "Optimising table " + row[0].ToString());
sql = "OPTIMIZE TABLE " + row[0].ToString();
DataTable response = db.ExecuteCMD(sql);
DataTable response = db.ExecuteCMD(sql, new Dictionary<string, object>(), 240);
foreach (DataRow responseRow in response.Rows)
{
string retVal = "";

View File

@@ -7,7 +7,7 @@ using Microsoft.CodeAnalysis.Elfie.Model.Strings;
namespace gaseous_server.Classes.Metadata
{
public class Covers
public class Covers
{
const string fieldList = "fields alpha_channel,animated,checksum,game,game_localization,height,image_id,url,width;";
@@ -135,6 +135,6 @@ namespace gaseous_server.Classes.Metadata
return result;
}
}
}
}

View File

@@ -127,17 +127,17 @@ namespace gaseous_server.Classes.Metadata
private static void UpdateSubClasses(Game Game, bool getAllMetadata, bool followSubGames, bool forceRefresh)
{
// required metadata
if (Game.Cover != null)
{
try
{
Cover GameCover = Covers.GetCover(Game.Cover.Id, Config.LibraryConfiguration.LibraryMetadataDirectory_Game(Game), forceRefresh);
}
catch (Exception ex)
{
Logging.Log(Logging.LogType.Critical, "Game Metadata", "Unable to fetch cover artwork.", ex);
}
}
// if (Game.Cover != null)
// {
// try
// {
// Cover GameCover = Covers.GetCover(Game.Cover.Id, Config.LibraryConfiguration.LibraryMetadataDirectory_Game(Game), forceRefresh);
// }
// catch (Exception ex)
// {
// Logging.Log(Logging.LogType.Critical, "Game Metadata", "Unable to fetch cover artwork.", ex);
// }
// }
if (Game.Genres != null)
{

View File

@@ -5,15 +5,15 @@ using IGDB.Models;
namespace gaseous_server.Classes.Metadata
{
public class PlatformVersions
{
public class PlatformVersions
{
const string fieldList = "fields checksum,companies,connectivity,cpu,graphics,main_manufacturer,media,memory,name,online,os,output,platform_logo,platform_version_release_dates,resolutions,slug,sound,storage,summary,url;";
public PlatformVersions()
{
}
{
}
public static PlatformVersion? GetPlatformVersion(long Id, Platform ParentPlatform)
public static PlatformVersion? GetPlatformVersion(long Id, Platform ParentPlatform, bool GetImages = false)
{
if (Id == 0)
{
@@ -21,18 +21,18 @@ namespace gaseous_server.Classes.Metadata
}
else
{
Task<PlatformVersion> RetVal = _GetPlatformVersion(SearchUsing.id, Id, ParentPlatform);
Task<PlatformVersion> RetVal = _GetPlatformVersion(SearchUsing.id, Id, ParentPlatform, GetImages);
return RetVal.Result;
}
}
public static PlatformVersion GetPlatformVersion(string Slug, Platform ParentPlatform)
public static PlatformVersion GetPlatformVersion(string Slug, Platform ParentPlatform, bool GetImages)
{
Task<PlatformVersion> RetVal = _GetPlatformVersion(SearchUsing.slug, Slug, ParentPlatform);
Task<PlatformVersion> RetVal = _GetPlatformVersion(SearchUsing.slug, Slug, ParentPlatform, GetImages);
return RetVal.Result;
}
private static async Task<PlatformVersion> _GetPlatformVersion(SearchUsing searchUsing, object searchValue, Platform ParentPlatform)
private static async Task<PlatformVersion> _GetPlatformVersion(SearchUsing searchUsing, object searchValue, Platform ParentPlatform, bool GetImages)
{
// check database first
Storage.CacheStatus? cacheStatus = new Storage.CacheStatus();
@@ -67,7 +67,7 @@ namespace gaseous_server.Classes.Metadata
if (returnValue != null)
{
Storage.NewCacheValue(returnValue);
UpdateSubClasses(ParentPlatform, returnValue);
UpdateSubClasses(ParentPlatform, returnValue, GetImages);
}
return returnValue;
case Storage.CacheStatus.Expired:
@@ -75,7 +75,7 @@ namespace gaseous_server.Classes.Metadata
{
returnValue = await GetObjectFromServer(WhereClause);
Storage.NewCacheValue(returnValue, true);
UpdateSubClasses(ParentPlatform, returnValue);
UpdateSubClasses(ParentPlatform, returnValue, GetImages);
}
catch (Exception ex)
{
@@ -90,17 +90,20 @@ namespace gaseous_server.Classes.Metadata
}
}
private static void UpdateSubClasses(Platform ParentPlatform, PlatformVersion platformVersion)
private static void UpdateSubClasses(Platform ParentPlatform, PlatformVersion platformVersion, bool GetImages)
{
if (platformVersion.PlatformLogo != null)
if (GetImages == true)
{
try
if (platformVersion.PlatformLogo != null)
{
PlatformLogo platformLogo = PlatformLogos.GetPlatformLogo(platformVersion.PlatformLogo.Id, Path.Combine(Config.LibraryConfiguration.LibraryMetadataDirectory_Platform(ParentPlatform), "Versions", platformVersion.Slug));
}
catch (Exception ex)
{
Logging.Log(Logging.LogType.Warning, "Platform Update", "Unable to fetch platform logo", ex);
try
{
PlatformLogo platformLogo = PlatformLogos.GetPlatformLogo(platformVersion.PlatformLogo.Id, Path.Combine(Config.LibraryConfiguration.LibraryMetadataDirectory_Platform(ParentPlatform), "Versions", platformVersion.Slug));
}
catch (Exception ex)
{
Logging.Log(Logging.LogType.Warning, "Platform Update", "Unable to fetch platform logo", ex);
}
}
}
}

View File

@@ -15,7 +15,7 @@ namespace gaseous_server.Classes.Metadata
}
public static Platform? GetPlatform(long Id, bool forceRefresh = false)
public static Platform? GetPlatform(long Id, bool forceRefresh = false, bool GetImages = false)
{
if (Id == 0)
{
@@ -41,7 +41,7 @@ namespace gaseous_server.Classes.Metadata
{
try
{
Task<Platform> RetVal = _GetPlatform(SearchUsing.id, Id, forceRefresh);
Task<Platform> RetVal = _GetPlatform(SearchUsing.id, Id, forceRefresh, GetImages);
return RetVal.Result;
}
catch (Exception ex)
@@ -52,13 +52,13 @@ namespace gaseous_server.Classes.Metadata
}
}
public static Platform GetPlatform(string Slug, bool forceRefresh = false)
public static Platform GetPlatform(string Slug, bool forceRefresh = false, bool GetImages = false)
{
Task<Platform> RetVal = _GetPlatform(SearchUsing.slug, Slug, forceRefresh);
Task<Platform> RetVal = _GetPlatform(SearchUsing.slug, Slug, forceRefresh, GetImages);
return RetVal.Result;
}
private static async Task<Platform> _GetPlatform(SearchUsing searchUsing, object searchValue, bool forceRefresh)
private static async Task<Platform> _GetPlatform(SearchUsing searchUsing, object searchValue, bool forceRefresh, bool GetImages)
{
// check database first
Storage.CacheStatus? cacheStatus = new Storage.CacheStatus();
@@ -99,7 +99,7 @@ namespace gaseous_server.Classes.Metadata
case Storage.CacheStatus.NotPresent:
returnValue = await GetObjectFromServer(WhereClause);
Storage.NewCacheValue(returnValue);
UpdateSubClasses(returnValue);
UpdateSubClasses(returnValue, GetImages);
AddPlatformMapping(returnValue);
return returnValue;
case Storage.CacheStatus.Expired:
@@ -107,7 +107,7 @@ namespace gaseous_server.Classes.Metadata
{
returnValue = await GetObjectFromServer(WhereClause);
Storage.NewCacheValue(returnValue, true);
UpdateSubClasses(returnValue);
UpdateSubClasses(returnValue, GetImages);
AddPlatformMapping(returnValue);
return returnValue;
}
@@ -123,7 +123,7 @@ namespace gaseous_server.Classes.Metadata
}
}
private static void UpdateSubClasses(Platform platform)
private static void UpdateSubClasses(Platform platform, bool GetImages)
{
if (platform.Versions != null)
{
@@ -133,15 +133,18 @@ namespace gaseous_server.Classes.Metadata
}
}
if (platform.PlatformLogo != null)
if (GetImages == true)
{
try
if (platform.PlatformLogo != null)
{
PlatformLogo platformLogo = PlatformLogos.GetPlatformLogo(platform.PlatformLogo.Id, Config.LibraryConfiguration.LibraryMetadataDirectory_Platform(platform));
}
catch (Exception ex)
{
Logging.Log(Logging.LogType.Warning, "Platform Update", "Unable to fetch platform logo", ex);
try
{
PlatformLogo platformLogo = PlatformLogos.GetPlatformLogo(platform.PlatformLogo.Id, Config.LibraryConfiguration.LibraryMetadataDirectory_Platform(platform));
}
catch (Exception ex)
{
Logging.Log(Logging.LogType.Warning, "Platform Update", "Unable to fetch platform logo", ex);
}
}
}
}

View File

@@ -7,19 +7,19 @@ using Microsoft.Extensions.Caching.Memory;
namespace gaseous_server.Classes.Metadata
{
public class Storage
{
public enum CacheStatus
{
NotPresent,
Current,
Expired
}
public class Storage
{
public enum CacheStatus
{
NotPresent,
Current,
Expired
}
public static CacheStatus GetCacheStatus(string Endpoint, string Slug)
{
public static CacheStatus GetCacheStatus(string Endpoint, string Slug)
{
return _GetCacheStatus(Endpoint, "slug", Slug);
}
}
public static CacheStatus GetCacheStatus(string Endpoint, long Id)
{
@@ -47,124 +47,124 @@ namespace gaseous_server.Classes.Metadata
}
private static CacheStatus _GetCacheStatus(string Endpoint, string SearchField, object SearchValue)
{
{
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "SELECT lastUpdated FROM " + Endpoint + " WHERE " + SearchField + " = @" + SearchField;
string sql = "SELECT lastUpdated FROM " + Endpoint + " WHERE " + SearchField + " = @" + SearchField;
Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("Endpoint", Endpoint);
dbDict.Add(SearchField, SearchValue);
Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("Endpoint", Endpoint);
dbDict.Add(SearchField, SearchValue);
DataTable dt = db.ExecuteCMD(sql, dbDict);
if (dt.Rows.Count == 0)
{
// no data stored for this item, or lastUpdated
return CacheStatus.NotPresent;
}
else
{
DateTime CacheExpiryTime = DateTime.UtcNow.AddHours(-168);
if ((DateTime)dt.Rows[0]["lastUpdated"] < CacheExpiryTime)
{
return CacheStatus.Expired;
}
else
{
return CacheStatus.Current;
}
}
DataTable dt = db.ExecuteCMD(sql, dbDict);
if (dt.Rows.Count == 0)
{
// no data stored for this item, or lastUpdated
return CacheStatus.NotPresent;
}
else
{
DateTime CacheExpiryTime = DateTime.UtcNow.AddHours(-168);
if ((DateTime)dt.Rows[0]["lastUpdated"] < CacheExpiryTime)
{
return CacheStatus.Expired;
}
else
{
return CacheStatus.Current;
}
}
}
public static void NewCacheValue(object ObjectToCache, bool UpdateRecord = false)
{
// get the object type name
string ObjectTypeName = ObjectToCache.GetType().Name;
public static void NewCacheValue(object ObjectToCache, bool UpdateRecord = false)
{
// get the object type name
string ObjectTypeName = ObjectToCache.GetType().Name;
// build dictionary
string objectJson = Newtonsoft.Json.JsonConvert.SerializeObject(ObjectToCache);
Dictionary<string, object?> objectDict = Newtonsoft.Json.JsonConvert.DeserializeObject<Dictionary<string, object?>>(objectJson);
// build dictionary
string objectJson = Newtonsoft.Json.JsonConvert.SerializeObject(ObjectToCache);
Dictionary<string, object?> objectDict = Newtonsoft.Json.JsonConvert.DeserializeObject<Dictionary<string, object?>>(objectJson);
objectDict.Add("dateAdded", DateTime.UtcNow);
objectDict.Add("lastUpdated", DateTime.UtcNow);
// generate sql
string fieldList = "";
string valueList = "";
string updateFieldValueList = "";
foreach (KeyValuePair<string, object?> key in objectDict)
{
if (fieldList.Length > 0)
{
fieldList = fieldList + ", ";
valueList = valueList + ", ";
}
fieldList = fieldList + key.Key;
valueList = valueList + "@" + key.Key;
if ((key.Key != "id") && (key.Key != "dateAdded"))
// generate sql
string fieldList = "";
string valueList = "";
string updateFieldValueList = "";
foreach (KeyValuePair<string, object?> key in objectDict)
{
if (fieldList.Length > 0)
{
fieldList = fieldList + ", ";
valueList = valueList + ", ";
}
fieldList = fieldList + key.Key;
valueList = valueList + "@" + key.Key;
if ((key.Key != "id") && (key.Key != "dateAdded"))
{
if (updateFieldValueList.Length > 0)
{
updateFieldValueList = updateFieldValueList + ", ";
}
updateFieldValueList += key.Key + " = @" + key.Key;
}
}
// check property type
Type objectType = ObjectToCache.GetType();
if (objectType != null)
{
PropertyInfo objectProperty = objectType.GetProperty(key.Key);
if (objectProperty != null)
{
string compareName = objectProperty.PropertyType.Name.ToLower().Split("`")[0];
var objectValue = objectProperty.GetValue(ObjectToCache);
if (objectValue != null)
{
string newObjectValue;
Dictionary<string, object> newDict;
// check property type
Type objectType = ObjectToCache.GetType();
if (objectType != null)
{
PropertyInfo objectProperty = objectType.GetProperty(key.Key);
if (objectProperty != null)
{
string compareName = objectProperty.PropertyType.Name.ToLower().Split("`")[0];
var objectValue = objectProperty.GetValue(ObjectToCache);
if (objectValue != null)
{
string newObjectValue;
Dictionary<string, object> newDict;
switch (compareName)
{
case "identityorvalue":
{
case "identityorvalue":
newObjectValue = Newtonsoft.Json.JsonConvert.SerializeObject(objectValue);
newDict = Newtonsoft.Json.JsonConvert.DeserializeObject<Dictionary<string, object>>(newObjectValue);
newDict = Newtonsoft.Json.JsonConvert.DeserializeObject<Dictionary<string, object>>(newObjectValue);
objectDict[key.Key] = newDict["Id"];
break;
case "identitiesorvalues":
newObjectValue = Newtonsoft.Json.JsonConvert.SerializeObject(objectValue);
newDict = Newtonsoft.Json.JsonConvert.DeserializeObject<Dictionary<string, object>>(newObjectValue);
newObjectValue = Newtonsoft.Json.JsonConvert.SerializeObject(newDict["Ids"]);
newObjectValue = Newtonsoft.Json.JsonConvert.SerializeObject(newDict["Ids"]);
objectDict[key.Key] = newObjectValue;
StoreRelations(ObjectTypeName, key.Key, (long)objectDict["Id"], newObjectValue);
break;
case "int32[]":
case "int32[]":
newObjectValue = Newtonsoft.Json.JsonConvert.SerializeObject(objectValue);
objectDict[key.Key] = newObjectValue;
break;
}
}
}
}
}
string sql = "";
if (UpdateRecord == false)
{
sql = "INSERT INTO " + ObjectTypeName + " (" + fieldList + ") VALUES (" + valueList + ")";
}
}
}
}
string sql = "";
if (UpdateRecord == false)
{
sql = "INSERT INTO " + ObjectTypeName + " (" + fieldList + ") VALUES (" + valueList + ")";
}
else
{
sql = "UPDATE " + ObjectTypeName + " SET " + updateFieldValueList + " WHERE Id = @Id";
}
else
{
sql = "UPDATE " + ObjectTypeName + " SET " + updateFieldValueList + " WHERE Id = @Id";
}
// execute sql
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
db.ExecuteCMD(sql, objectDict);
db.ExecuteCMD(sql, objectDict);
}
public static T GetCacheValue<T>(T EndpointType, string SearchField, object SearchValue)
{
public static T GetCacheValue<T>(T EndpointType, string SearchField, object SearchValue)
{
string Endpoint = EndpointType.GetType().Name;
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
@@ -178,20 +178,20 @@ namespace gaseous_server.Classes.Metadata
DataTable dt = db.ExecuteCMD(sql, dbDict);
if (dt.Rows.Count == 0)
{
// no data stored for this item
throw new Exception("No record found that matches endpoint " + Endpoint + " with search value " + SearchValue);
// no data stored for this item
throw new Exception("No record found that matches endpoint " + Endpoint + " with search value " + SearchValue);
}
else
{
DataRow dataRow = dt.Rows[0];
DataRow dataRow = dt.Rows[0];
object returnObject = BuildCacheObject<T>(EndpointType, dataRow);
return (T)returnObject;
}
}
public static T BuildCacheObject<T>(T EndpointType, DataRow dataRow)
{
public static T BuildCacheObject<T>(T EndpointType, DataRow dataRow)
{
foreach (PropertyInfo property in EndpointType.GetType().GetProperties())
{
if (dataRow.Table.Columns.Contains(property.Name))
@@ -428,6 +428,30 @@ namespace gaseous_server.Classes.Metadata
}
}
public static void CreateRelationsTables<T>()
{
string PrimaryTable = typeof(T).Name;
foreach (PropertyInfo property in typeof(T).GetProperties())
{
string SecondaryTable = property.Name;
if (property.PropertyType.Name == "IdentitiesOrValues`1")
{
string TableName = "Relation_" + PrimaryTable + "_" + SecondaryTable;
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "SELECT * FROM information_schema.tables WHERE table_schema = '" + Config.DatabaseConfiguration.DatabaseName + "' AND table_name = '" + TableName + "';";
DataTable data = db.ExecuteCMD(sql);
if (data.Rows.Count == 0)
{
// table doesn't exist, create it
sql = "CREATE TABLE `" + Config.DatabaseConfiguration.DatabaseName + "`.`" + TableName + "` (`" + PrimaryTable + "Id` BIGINT NOT NULL, `" + SecondaryTable + "Id` BIGINT NOT NULL, PRIMARY KEY (`" + PrimaryTable + "Id`, `" + SecondaryTable + "Id`), INDEX `idx_PrimaryColumn` (`" + PrimaryTable + "Id` ASC) VISIBLE);";
db.ExecuteCMD(sql);
}
}
}
}
private class MemoryCacheObject
{
public object Object { get; set; }

View File

@@ -4,9 +4,6 @@ using gaseous_signature_parser.models.RomSignatureObject;
using static gaseous_server.Classes.RomMediaGroup;
using gaseous_server.Classes.Metadata;
using IGDB.Models;
using static HasheousClient.Models.FixMatchModel;
using NuGet.Protocol.Core.Types;
using static gaseous_server.Classes.FileSignature;
namespace gaseous_server.Classes
{
@@ -18,6 +15,12 @@ namespace gaseous_server.Classes
{ }
}
public class InvalidRomHash : Exception
{
public InvalidRomHash(String Hash) : base("Unable to find ROM by hash " + Hash)
{ }
}
public static GameRomObject GetRoms(long GameId, long PlatformId = -1, string NameSearch = "", int pageNumber = 0, int pageSize = 0, string userid = "")
{
GameRomObject GameRoms = new GameRomObject();
@@ -106,6 +109,26 @@ namespace gaseous_server.Classes
}
}
public static GameRomItem GetRom(string MD5)
{
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "SELECT Games_Roms.*, Platform.`Name` AS platformname FROM Games_Roms LEFT JOIN Platform ON Games_Roms.PlatformId = Platform.Id WHERE Games_Roms.MD5 = @id";
Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("id", MD5);
DataTable romDT = db.ExecuteCMD(sql, dbDict);
if (romDT.Rows.Count > 0)
{
DataRow romDR = romDT.Rows[0];
GameRomItem romItem = BuildRom(romDR);
return romItem;
}
else
{
throw new InvalidRomHash(MD5);
}
}
public static GameRomItem UpdateRom(long RomId, long PlatformId, long GameId)
{
// ensure metadata for platformid is present
@@ -124,53 +147,6 @@ namespace gaseous_server.Classes
GameRomItem rom = GetRom(RomId);
// send update to Hasheous if enabled
if (PlatformId != 0 && GameId != 0)
{
if (Config.MetadataConfiguration.HasheousSubmitFixes == true)
{
if (
Config.MetadataConfiguration.SignatureSource == HasheousClient.Models.MetadataModel.SignatureSources.Hasheous &&
(
Config.MetadataConfiguration.HasheousAPIKey != null &&
Config.MetadataConfiguration.HasheousAPIKey != "")
)
{
try
{
// find signature used for identifing the rom
string md5String = rom.Md5;
string sha1String = rom.Sha1;
if (rom.Attributes.ContainsKey("ZipContents"))
{
bool selectorFound = false;
List<ArchiveData> archiveDataValues = Newtonsoft.Json.JsonConvert.DeserializeObject<List<ArchiveData>>(rom.Attributes["ZipContents"].ToString());
foreach (ArchiveData archiveData in archiveDataValues)
{
if (archiveData.isSignatureSelector == true)
{
md5String = archiveData.MD5;
sha1String = archiveData.SHA1;
selectorFound = true;
break;
}
}
}
HasheousClient.WebApp.HttpHelper.AddHeader("X-API-Key", Config.MetadataConfiguration.HasheousAPIKey);
HasheousClient.Hasheous hasheousClient = new HasheousClient.Hasheous();
List<MetadataMatch> metadataMatchList = new List<MetadataMatch>();
metadataMatchList.Add(new MetadataMatch(HasheousClient.Models.MetadataSources.IGDB, platform.Slug, game.Slug));
hasheousClient.FixMatch(new HasheousClient.Models.FixMatchModel(md5String, sha1String, metadataMatchList));
}
catch (Exception ex)
{
Logging.Log(Logging.LogType.Critical, "Fix Match", "An error occurred while sending a fixed match to Hasheous.", ex);
}
}
}
}
return rom;
}
@@ -203,22 +179,6 @@ namespace gaseous_server.Classes
}
}
Dictionary<string, object> romAttributes = new Dictionary<string, object>();
if (romDR["attributes"] != DBNull.Value)
{
try
{
if ((string)romDR["attributes"] != "[ ]")
{
romAttributes = Newtonsoft.Json.JsonConvert.DeserializeObject<Dictionary<string, object>>((string)romDR["attributes"]);
}
}
catch (Exception ex)
{
Logging.Log(Logging.LogType.Warning, "Roms", "Error parsing rom attributes: " + ex.Message);
}
}
GameRomItem romItem = new GameRomItem
{
Id = (long)romDR["id"],
@@ -231,8 +191,8 @@ namespace gaseous_server.Classes
Md5 = ((string)romDR["md5"]).ToLower(),
Sha1 = ((string)romDR["sha1"]).ToLower(),
DevelopmentStatus = (string)romDR["developmentstatus"],
Attributes = romAttributes,
RomType = (HasheousClient.Models.SignatureModel.RomItem.RomTypes)(int)romDR["romtype"],
Attributes = Newtonsoft.Json.JsonConvert.DeserializeObject<List<KeyValuePair<string, object>>>((string)Common.ReturnValueIfNull(romDR["attributes"], "[ ]")),
RomType = (HasheousClient.Models.LookupResponseModel.RomItem.RomTypes)(int)romDR["romtype"],
RomTypeMedia = (string)romDR["romtypemedia"],
MediaLabel = (string)romDR["medialabel"],
Path = (string)romDR["path"],
@@ -263,7 +223,7 @@ namespace gaseous_server.Classes
public int Count { get; set; }
}
public class GameRomItem : HasheousClient.Models.SignatureModel.RomItem
public class GameRomItem : HasheousClient.Models.LookupResponseModel.RomItem
{
public long PlatformId { get; set; }
public string Platform { get; set; }
@@ -276,4 +236,3 @@ namespace gaseous_server.Classes
}
}
}

View File

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

View File

@@ -1,6 +1,5 @@
using System.Data;
using gaseous_signature_parser.models.RomSignatureObject;
using static gaseous_server.Classes.Common;
namespace gaseous_server.Classes
{
@@ -47,7 +46,7 @@ namespace gaseous_server.Classes
{
Game = new gaseous_server.Models.Signatures_Games.GameItem
{
Id = (long)(int)sigDbRow["Id"],
Id = (Int32)sigDbRow["Id"],
Name = (string)sigDbRow["Name"],
Description = (string)sigDbRow["Description"],
Year = (string)sigDbRow["Year"],
@@ -56,20 +55,20 @@ namespace gaseous_server.Classes
System = (string)sigDbRow["Platform"],
SystemVariant = (string)sigDbRow["SystemVariant"],
Video = (string)sigDbRow["Video"],
Countries = new Dictionary<string, string>(GetLookup(LookupTypes.Country, (long)(int)sigDbRow["Id"])),
Languages = new Dictionary<string, string>(GetLookup(LookupTypes.Language, (long)(int)sigDbRow["Id"])),
Country = (string)sigDbRow["Country"],
Language = (string)sigDbRow["Language"],
Copyright = (string)sigDbRow["Copyright"]
},
Rom = new gaseous_server.Models.Signatures_Games.RomItem
{
Id = (long)(int)sigDbRow["romid"],
Id = (Int32)sigDbRow["romid"],
Name = (string)sigDbRow["romname"],
Size = (Int64)sigDbRow["Size"],
Crc = (string)sigDbRow["CRC"],
Md5 = ((string)sigDbRow["MD5"]).ToLower(),
Sha1 = ((string)sigDbRow["SHA1"]).ToLower(),
DevelopmentStatus = (string)sigDbRow["DevelopmentStatus"],
Attributes = Newtonsoft.Json.JsonConvert.DeserializeObject<Dictionary<string, object>>((string)Common.ReturnValueIfNull(sigDbRow["Attributes"], "[]")),
Attributes = Newtonsoft.Json.JsonConvert.DeserializeObject<List<KeyValuePair<string, object>>>((string)Common.ReturnValueIfNull(sigDbRow["Attributes"], "[]")),
RomType = (gaseous_server.Models.Signatures_Games.RomItem.RomTypes)(int)sigDbRow["RomType"],
RomTypeMedia = (string)sigDbRow["RomTypeMedia"],
MediaLabel = (string)sigDbRow["MediaLabel"],
@@ -80,36 +79,5 @@ namespace gaseous_server.Classes
}
return GamesList;
}
public Dictionary<string, string> GetLookup(LookupTypes LookupType, long GameId)
{
string tableName = "";
switch (LookupType)
{
case LookupTypes.Country:
tableName = "Countries";
break;
case LookupTypes.Language:
tableName = "Languages";
break;
}
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "SELECT " + LookupType.ToString() + ".Code, " + LookupType.ToString() + ".Value FROM Signatures_Games_" + tableName + " JOIN " + LookupType.ToString() + " ON Signatures_Games_" + tableName + "." + LookupType.ToString() + "Id = " + LookupType.ToString() + ".Id WHERE Signatures_Games_" + tableName + ".GameId = @id;";
Dictionary<string, object> dbDict = new Dictionary<string, object>{
{ "id", GameId }
};
DataTable data = db.ExecuteCMD(sql, dbDict);
Dictionary<string, string> returnDict = new Dictionary<string, string>();
foreach (DataRow row in data.Rows)
{
returnDict.Add((string)row["Code"], (string)row["Value"]);
}
return returnDict;
}
}
}

View File

@@ -73,7 +73,8 @@ namespace gaseous_server.Controllers
private static async Task<List<GaseousGame>> _SearchForGame(long PlatformId, string SearchString)
{
string searchBody = "";
string searchFields = "fields cover,first_release_date,name,platforms,slug; ";
// string searchFields = "fields cover,first_release_date,name,platforms,slug; ";
string searchFields = "fields *; ";
searchBody += "search \"" + SearchString + "\";";
searchBody += "where platforms = (" + PlatformId + ");";
searchBody += "limit 100;";
@@ -91,7 +92,7 @@ namespace gaseous_server.Controllers
foreach (Game game in results.ToList())
{
Storage.CacheStatus cacheStatus = Storage.GetCacheStatus("Game", (long)game.Id);
switch(cacheStatus)
switch (cacheStatus)
{
case Storage.CacheStatus.NotPresent:
Storage.NewCacheValue(game, false);

View File

@@ -100,7 +100,7 @@ namespace gaseous_server.Controllers
string ver = "var AppVersion = \"" + Assembly.GetExecutingAssembly().GetName().Version.ToString() + "\";" + Environment.NewLine +
"var DBSchemaVersion = \"" + db.GetDatabaseSchemaVersion() + "\";" + Environment.NewLine +
"var FirstRunStatus = " + Config.ReadSetting<string>("FirstRunStatus", "0") + ";" + Environment.NewLine +
"var FirstRunStatus = \"" + Config.ReadSetting<string>("FirstRunStatus", "0") + "\";" + Environment.NewLine +
"var AgeRatingBoardsStrings = " + JsonSerializer.Serialize(ClassificationBoardsStrings, new JsonSerializerOptions
{
WriteIndented = true
@@ -260,14 +260,7 @@ namespace gaseous_server.Controllers
{
AlwaysLogToDisk = Config.LoggingConfiguration.AlwaysLogToDisk,
MinimumLogRetentionPeriod = Config.LoggingConfiguration.LogRetention,
EmulatorDebugMode = Boolean.Parse(Config.ReadSetting<string>("emulatorDebugMode", false.ToString())),
SignatureSource = new SystemSettingsModel.SignatureSourceItem()
{
Source = Config.MetadataConfiguration.SignatureSource,
HasheousHost = Config.MetadataConfiguration.HasheousHost,
HasheousSubmitFixes = (bool)Config.MetadataConfiguration.HasheousSubmitFixes,
HasheousAPIKey = Config.MetadataConfiguration.HasheousAPIKey
}
EmulatorDebugMode = Boolean.Parse(Config.ReadSetting<string>("emulatorDebugMode", false.ToString()))
};
return Ok(systemSettingsModel);
@@ -286,10 +279,6 @@ namespace gaseous_server.Controllers
Config.LoggingConfiguration.AlwaysLogToDisk = model.AlwaysLogToDisk;
Config.LoggingConfiguration.LogRetention = model.MinimumLogRetentionPeriod;
Config.SetSetting<string>("emulatorDebugMode", model.EmulatorDebugMode.ToString());
Config.MetadataConfiguration.SignatureSource = model.SignatureSource.Source;
Config.MetadataConfiguration.HasheousHost = model.SignatureSource.HasheousHost;
Config.MetadataConfiguration.HasheousAPIKey = model.SignatureSource.HasheousAPIKey;
Config.MetadataConfiguration.HasheousSubmitFixes = model.SignatureSource.HasheousSubmitFixes;
Config.UpdateConfig();
}
@@ -730,14 +719,5 @@ namespace gaseous_server.Controllers
public bool AlwaysLogToDisk { get; set; }
public int MinimumLogRetentionPeriod { get; set; }
public bool EmulatorDebugMode { get; set; }
public SignatureSourceItem SignatureSource { get; set; }
public class SignatureSourceItem
{
public HasheousClient.Models.MetadataModel.SignatureSources Source { get; set; }
public string HasheousHost { get; set; }
public string HasheousAPIKey { get; set; }
public bool HasheousSubmitFixes { get; set; }
}
}
}

View File

@@ -6,6 +6,7 @@ using Authentication;
using Microsoft.AspNetCore.Identity;
using System.Data;
using Asp.Versioning;
using System.IO.Compression;
namespace gaseous_server.Controllers.v1_1
{
@@ -13,7 +14,7 @@ namespace gaseous_server.Controllers.v1_1
[ApiVersion("1.0")]
[ApiVersion("1.1")]
[ApiController]
public class StateManagerController: ControllerBase
public class StateManagerController : ControllerBase
{
private readonly UserManager<ApplicationUser> _userManager;
private readonly SignInManager<ApplicationUser> _signInManager;
@@ -233,11 +234,11 @@ namespace gaseous_server.Controllers.v1_1
[ProducesResponseType(StatusCodes.Status404NotFound)]
[Route("{RomId}/{StateId}/State/")]
[Route("{RomId}/{StateId}/State/savestate.state")]
public async Task<ActionResult> GetStateDataAsync(long RomId, long StateId, bool IsMediaGroup = false)
public async Task<ActionResult> GetStateDataAsync(long RomId, long StateId, bool IsMediaGroup = false, bool StateOnly = false)
{
var user = await _userManager.GetUserAsync(User);
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "SELECT Zipped, State FROM GameState WHERE Id = @id AND RomId = @romid AND IsMediaGroup = @ismediagroup AND UserId = @userid;";
string sql = "SELECT * FROM GameState WHERE Id = @id AND RomId = @romid AND IsMediaGroup = @ismediagroup AND UserId = @userid;";
Dictionary<string, object> dbDict = new Dictionary<string, object>
{
{ "id", StateId },
@@ -254,7 +255,9 @@ namespace gaseous_server.Controllers.v1_1
}
else
{
string filename = "savestate.state";
// get rom data
Roms.GameRomItem romItem = Roms.GetRom(RomId);
byte[] bytes;
if ((bool)data.Rows[0]["Zipped"] == false)
{
@@ -264,7 +267,86 @@ namespace gaseous_server.Controllers.v1_1
{
bytes = Common.Decompress((byte[])data.Rows[0]["State"]);
}
string contentType = "application/octet-stream";
string contentType = "";
string filename = ((DateTime)data.Rows[0]["StateDateTime"]).ToString("yyyy-MM-ddTHH-mm-ss") + "-" + Path.GetFileNameWithoutExtension(romItem.Name);
if (StateOnly == true)
{
contentType = "application/octet-stream";
filename = filename + ".state";
}
else
{
contentType = "application/zip";
filename = filename + ".zip";
Dictionary<string, object> RomInfo = new Dictionary<string, object>
{
{ "Name", romItem.Name },
{ "StateDateTime", data.Rows[0]["StateDateTime"] },
{ "StateName", data.Rows[0]["Name"] }
};
if ((int)data.Rows[0]["IsMediaGroup"] == 0)
{
RomInfo.Add("MD5", romItem.Md5);
RomInfo.Add("SHA1", romItem.Sha1);
RomInfo.Add("Type", "ROM");
}
else
{
RomInfo.Add("Type", "Media Group");
RomInfo.Add("MediaGroupId", (long)data.Rows[0]["RomId"]);
}
string RomInfoString = Newtonsoft.Json.JsonConvert.SerializeObject(RomInfo, Newtonsoft.Json.Formatting.Indented, new Newtonsoft.Json.JsonSerializerSettings { NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore });
// compile zip file
using (var compressedFileStream = new MemoryStream())
{
List<Dictionary<string, object>> Attachments = new List<Dictionary<string, object>>();
Attachments.Add(new Dictionary<string, object>
{
{ "Name", "savestate.state" },
{ "Body", bytes }
});
// check if value is dbnull
if (data.Rows[0]["Screenshot"] != DBNull.Value)
{
Attachments.Add(new Dictionary<string, object>
{
{ "Name", "screenshot.jpg" },
{ "Body", (byte[])data.Rows[0]["Screenshot"] }
});
}
Attachments.Add(new Dictionary<string, object>
{
{ "Name", "rominfo.json" },
{ "Body", System.Text.Encoding.UTF8.GetBytes(RomInfoString) }
});
//Create an archive and store the stream in memory.
using (var zipArchive = new ZipArchive(compressedFileStream, ZipArchiveMode.Create, false))
{
foreach (var Attachment in Attachments)
{
//Create a zip entry for each attachment
var zipEntry = zipArchive.CreateEntry(Attachment["Name"].ToString());
//Get the stream of the attachment
using (var originalFileStream = new MemoryStream((byte[])Attachment["Body"]))
using (var zipEntryStream = zipEntry.Open())
{
//Copy the attachment stream to the zip entry stream
originalFileStream.CopyTo(zipEntryStream);
}
}
}
//return new FileContentResult(compressedFileStream.ToArray(), "application/zip") { FileDownloadName = filename };
bytes = compressedFileStream.ToArray();
}
}
var cd = new System.Net.Mime.ContentDisposition
{
@@ -279,6 +361,156 @@ namespace gaseous_server.Controllers.v1_1
}
}
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpPost]
[Authorize]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[RequestSizeLimit(long.MaxValue)]
[Consumes("multipart/form-data")]
[DisableRequestSizeLimit, RequestFormLimits(MultipartBodyLengthLimit = long.MaxValue, ValueLengthLimit = int.MaxValue)]
[Route("Upload")]
public async Task<ActionResult> UploadStateDataAsync(IFormFile file, long RomId = 0, bool IsMediaGroup = false)
{
// get user
var user = await _userManager.GetUserAsync(User);
if (file.Length > 0)
{
MemoryStream fileContent = new MemoryStream();
file.CopyTo(fileContent);
// test if file is a zip file
try
{
using (var zipArchive = new ZipArchive(fileContent, ZipArchiveMode.Read, false))
{
foreach (var entry in zipArchive.Entries)
{
if (entry.FullName == "rominfo.json")
{
using (var stream = entry.Open())
using (var reader = new StreamReader(stream))
{
string RomInfoString = reader.ReadToEnd();
Dictionary<string, object> RomInfo = Newtonsoft.Json.JsonConvert.DeserializeObject<Dictionary<string, object>>(RomInfoString);
// get rom data
Roms.GameRomItem romItem;
try
{
romItem = Roms.GetRom((string)RomInfo["MD5"]);
}
catch (Roms.InvalidRomHash)
{
return NotFound();
}
// get state data
byte[] StateData = null;
byte[] ScreenshotData = null;
string StateName = RomInfo["StateName"].ToString();
DateTime StateDateTime = DateTime.Parse(RomInfo["StateDateTime"].ToString());
IsMediaGroup = RomInfo["Type"].ToString() == "Media Group" ? true : false;
if (zipArchive.GetEntry("savestate.state") != null)
{
using (var stateStream = zipArchive.GetEntry("savestate.state").Open())
using (var stateReader = new MemoryStream())
{
stateStream.CopyTo(stateReader);
StateData = stateReader.ToArray();
}
}
if (zipArchive.GetEntry("screenshot.jpg") != null)
{
using (var screenshotStream = zipArchive.GetEntry("screenshot.jpg").Open())
using (var screenshotReader = new MemoryStream())
{
screenshotStream.CopyTo(screenshotReader);
ScreenshotData = screenshotReader.ToArray();
}
}
// save state
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "INSERT INTO GameState (UserId, RomId, IsMediaGroup, StateDateTime, Name, Screenshot, State, Zipped) VALUES (@userid, @romid, @ismediagroup, @statedatetime, @name, @screenshot, @state, @zipped); SELECT LAST_INSERT_ID();";
Dictionary<string, object> dbDict = new Dictionary<string, object>
{
{ "userid", user.Id },
{ "romid", romItem.Id },
{ "ismediagroup", IsMediaGroup },
{ "statedatetime", StateDateTime },
{ "name", StateName },
{ "screenshot", ScreenshotData },
{ "state", Common.Compress(StateData) },
{ "zipped", true }
};
DataTable data = db.ExecuteCMD(sql, dbDict);
RomInfo.Add("RomId", romItem.Id);
RomInfo.Add("Management", "Managed");
return Ok(RomInfo);
}
}
}
}
return BadRequest("File is not a valid Gaseous state file.");
}
catch
{
// not a zip file
if (RomId != 0)
{
// get rom data
Roms.GameRomItem romItem;
try
{
romItem = Roms.GetRom(RomId);
}
catch (Roms.InvalidRomHash)
{
return NotFound();
}
// save state
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "INSERT INTO GameState (UserId, RomId, IsMediaGroup, StateDateTime, Name, Screenshot, State, Zipped) VALUES (@userid, @romid, @ismediagroup, @statedatetime, @name, @screenshot, @state, @zipped); SELECT LAST_INSERT_ID();";
Dictionary<string, object> dbDict = new Dictionary<string, object>
{
{ "userid", user.Id },
{ "romid", RomId },
{ "ismediagroup", IsMediaGroup },
{ "statedatetime", DateTime.UtcNow },
{ "name", "" },
{ "screenshot", null },
{ "state", Common.Compress(fileContent.ToArray()) },
{ "zipped", true }
};
DataTable data = db.ExecuteCMD(sql, dbDict);
return Ok(new Dictionary<string, object>
{
{ "RomId", RomId },
{ "Management", "Unmanaged" }
});
}
else
{
return BadRequest("No rom id provided.");
}
}
}
else
{
return BadRequest("File is empty.");
}
}
private Models.GameStateItem BuildGameStateItem(DataRow dr)
{
bool HasScreenshot = true;

View File

@@ -16,7 +16,7 @@ namespace gaseous_server.Models
{
var targetType = this.GetType();
var sourceType = game.GetType();
foreach(var prop in targetType.GetProperties(BindingFlags.Instance | BindingFlags.Public| BindingFlags.SetProperty))
foreach (var prop in targetType.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.SetProperty))
{
// check whether source object has the the property
var sp = sourceType.GetProperty(prop.Name);
@@ -39,7 +39,11 @@ namespace gaseous_server.Models
{
if (this.Cover.Id != null)
{
IGDB.Models.Cover cover = Covers.GetCover(Cover.Id, Config.LibraryConfiguration.LibraryMetadataDirectory_Game(this), false);
// IGDB.Models.Cover cover = Covers.GetCover(Cover.Id, Config.LibraryConfiguration.LibraryMetadataDirectory_Game(this), false);
IGDB.Models.Cover cover = new IGDB.Models.Cover()
{
Id = this.Cover.Id
};
return cover;
}

View File

@@ -13,8 +13,8 @@ using Newtonsoft.Json;
namespace gaseous_server.Models
{
public class PlatformMapping
{
public class PlatformMapping
{
private static Dictionary<string, PlatformMapItem> PlatformMapCache = new Dictionary<string, PlatformMapItem>();
/// <summary>
@@ -27,7 +27,8 @@ namespace gaseous_server.Models
{
string rawJson = reader.ReadToEnd();
List<PlatformMapItem> platforms = new List<PlatformMapItem>();
Newtonsoft.Json.JsonSerializerSettings jsonSerializerSettings = new JsonSerializerSettings{
Newtonsoft.Json.JsonSerializerSettings jsonSerializerSettings = new JsonSerializerSettings
{
MaxDepth = 64
};
platforms = Newtonsoft.Json.JsonConvert.DeserializeObject<List<PlatformMapItem>>(rawJson, jsonSerializerSettings);
@@ -74,7 +75,7 @@ namespace gaseous_server.Models
foreach (PlatformMapItem mapItem in platforms)
{
// get the IGDB platform data
Platform platform = Platforms.GetPlatform(mapItem.IGDBId);
Platform platform = Platforms.GetPlatform(mapItem.IGDBId, false);
try
{
@@ -254,7 +255,7 @@ namespace gaseous_server.Models
}
}
public static void WriteAvailableEmulators (PlatformMapItem item)
public static void WriteAvailableEmulators(PlatformMapItem item)
{
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "";
@@ -286,7 +287,7 @@ namespace gaseous_server.Models
string sql = "";
// get platform data
IGDB.Models.Platform? platform = Platforms.GetPlatform(IGDBId);
IGDB.Models.Platform? platform = Platforms.GetPlatform(IGDBId, false);
if (platform != null)
{
@@ -369,12 +370,14 @@ namespace gaseous_server.Models
mapItem.IGDBName = platform.Name;
mapItem.IGDBSlug = platform.Slug;
mapItem.AlternateNames = alternateNames;
mapItem.Extensions = new PlatformMapItem.FileExtensions{
mapItem.Extensions = new PlatformMapItem.FileExtensions
{
SupportedFileExtensions = knownExtensions,
UniqueFileExtensions = uniqueExtensions
};
mapItem.RetroPieDirectoryName = (string)Common.ReturnValueIfNull(row["RetroPieDirectoryName"], "");
mapItem.WebEmulator = new PlatformMapItem.WebEmulatorItem{
mapItem.WebEmulator = new PlatformMapItem.WebEmulatorItem
{
Type = (string)Common.ReturnValueIfNull(row["WebEmulator_Type"], ""),
Core = (string)Common.ReturnValueIfNull(row["WebEmulator_Core"], ""),
AvailableWebEmulators = Newtonsoft.Json.JsonConvert.DeserializeObject<List<PlatformMapItem.WebEmulatorItem.AvailableWebEmulatorItem>>((string)Common.ReturnValueIfNull(row["AvailableWebEmulators"], "[]"))
@@ -503,6 +506,6 @@ namespace gaseous_server.Models
public string filename { get; set; }
}
}
}
}
}

View File

@@ -4,36 +4,12 @@ using gaseous_signature_parser.models.RomSignatureObject;
namespace gaseous_server.Models
{
public class Signatures_Games : HasheousClient.Models.SignatureModel
{
public Signatures_Games()
{
}
[JsonIgnore]
public int Score
public class Signatures_Games : HasheousClient.Models.LookupResponseModel
{
public Signatures_Games()
{
get
{
int _score = 0;
if (Game != null)
{
_score = _score + Game.Score;
}
if (Rom != null)
{
_score = _score + Rom.Score;
}
return _score;
}
}
public GameItem Game = new GameItem();
public RomItem Rom = new RomItem();
public SignatureFlags Flags = new SignatureFlags();
public class SignatureFlags
@@ -42,213 +18,5 @@ namespace gaseous_server.Models
public string IGDBPlatformName { get; set; }
public long IGDBGameId { get; set; }
}
public class GameItem : HasheousClient.Models.SignatureModel.GameItem
{
public GameItem()
{
}
[JsonIgnore]
public int Score
{
get
{
// calculate a score based on the availablility of data
int _score = 0;
var properties = this.GetType().GetProperties();
foreach (var prop in properties)
{
if (prop.GetGetMethod() != null)
{
switch (prop.Name.ToLower())
{
case "id":
case "score":
break;
case "name":
case "year":
case "publisher":
case "system":
if (prop.PropertyType == typeof(string))
{
if (prop.GetValue(this) != null)
{
string propVal = prop.GetValue(this).ToString();
if (propVal.Length > 0)
{
_score = _score + 10;
}
}
}
break;
default:
if (prop.PropertyType == typeof(string))
{
if (prop.GetValue(this) != null)
{
string propVal = prop.GetValue(this).ToString();
if (propVal.Length > 0)
{
_score = _score + 1;
}
}
}
break;
}
}
}
return _score;
}
}
}
public class RomItem : HasheousClient.Models.SignatureModel.RomItem
{
[JsonIgnore]
public int Score
{
get
{
// calculate a score based on the availablility of data
int _score = 0;
var properties = this.GetType().GetProperties();
foreach (var prop in properties)
{
if (prop.GetGetMethod() != null)
{
switch (prop.Name.ToLower())
{
case "name":
case "size":
case "crc":
case "developmentstatus":
case "flags":
case "attributes":
case "romtypemedia":
case "medialabel":
if (prop.PropertyType == typeof(string) || prop.PropertyType == typeof(Int64) || prop.PropertyType == typeof(List<string>))
{
if (prop.GetValue(this) != null)
{
string propVal = prop.GetValue(this).ToString();
if (propVal.Length > 0)
{
_score = _score + 10;
}
}
}
break;
default:
if (prop.PropertyType == typeof(string))
{
if (prop.GetValue(this) != null)
{
string propVal = prop.GetValue(this).ToString();
if (propVal.Length > 0)
{
_score = _score + 1;
}
}
}
break;
}
}
}
return _score;
}
}
public class MediaType
{
public MediaType(SignatureSourceType Source, string MediaTypeString)
{
switch (Source)
{
case RomItem.SignatureSourceType.TOSEC:
string[] typeString = MediaTypeString.Split(" ");
string inType = "";
foreach (string typeStringVal in typeString)
{
if (inType == "")
{
switch (typeStringVal.ToLower())
{
case "disk":
Media = RomItem.RomTypes.Disk;
inType = typeStringVal;
break;
case "disc":
Media = RomItem.RomTypes.Disc;
inType = typeStringVal;
break;
case "file":
Media = RomItem.RomTypes.File;
inType = typeStringVal;
break;
case "part":
Media = RomItem.RomTypes.Part;
inType = typeStringVal;
break;
case "tape":
Media = RomItem.RomTypes.Tape;
inType = typeStringVal;
break;
case "of":
inType = typeStringVal;
break;
case "side":
inType = typeStringVal;
break;
}
}
else {
switch (inType.ToLower())
{
case "disk":
case "disc":
case "file":
case "part":
case "tape":
Number = int.Parse(typeStringVal);
break;
case "of":
Count = int.Parse(typeStringVal);
break;
case "side":
Side = typeStringVal;
break;
}
inType = "";
}
}
break;
default:
break;
}
}
public RomItem.RomTypes? Media { get; set; }
public int? Number { get; set; }
public int? Count { get; set; }
public string? Side { get; set; }
}
}
}
}

View File

@@ -9,8 +9,8 @@ using NuGet.Packaging;
namespace gaseous_server
{
public static class ProcessQueue
{
public static class ProcessQueue
{
public static List<QueueItem> QueueItems = new List<QueueItem>();
public class QueueItem
@@ -245,32 +245,14 @@ namespace gaseous_server
CallingQueueItem = this
};
foreach (int i in Enum.GetValues(typeof(gaseous_signature_parser.parser.SignatureParser)))
{
gaseous_signature_parser.parser.SignatureParser parserType = (gaseous_signature_parser.parser.SignatureParser)i;
if (
parserType != gaseous_signature_parser.parser.SignatureParser.Auto &&
parserType != gaseous_signature_parser.parser.SignatureParser.Unknown
)
{
Logging.Log(Logging.LogType.Debug, "Signature Import", "Processing " + parserType + " files");
Logging.Log(Logging.LogType.Debug, "Signature Import", "Processing TOSEC files");
tIngest.Import(Path.Combine(Config.LibraryConfiguration.LibrarySignatureImportDirectory, "TOSEC"), gaseous_signature_parser.parser.SignatureParser.TOSEC);
string SignaturePath = Path.Combine(Config.LibraryConfiguration.LibrarySignaturesDirectory, parserType.ToString());
string SignatureProcessedPath = Path.Combine(Config.LibraryConfiguration.LibrarySignaturesProcessedDirectory, parserType.ToString());
Logging.Log(Logging.LogType.Debug, "Signature Import", "Processing MAME Arcade files");
tIngest.Import(Path.Combine(Config.LibraryConfiguration.LibrarySignatureImportDirectory, "MAME Arcade"), gaseous_signature_parser.parser.SignatureParser.MAMEArcade);
if (!Directory.Exists(SignaturePath))
{
Directory.CreateDirectory(SignaturePath);
}
if (!Directory.Exists(SignatureProcessedPath))
{
Directory.CreateDirectory(SignatureProcessedPath);
}
tIngest.Import(SignaturePath, SignatureProcessedPath, parserType);
}
}
Logging.Log(Logging.LogType.Debug, "Signature Import", "Processing MAME MESS files");
tIngest.Import(Path.Combine(Config.LibraryConfiguration.LibrarySignatureImportDirectory, "MAME MESS"), gaseous_signature_parser.parser.SignatureParser.MAMEMess);
_SaveLastRunTime = true;
@@ -368,7 +350,8 @@ namespace gaseous_server
case QueueItemType.DailyMaintainer:
Logging.Log(Logging.LogType.Debug, "Timered Event", "Starting Daily Maintenance");
Classes.Maintenance maintenance = new Maintenance{
Classes.Maintenance maintenance = new Maintenance
{
CallingQueueItem = this
};
maintenance.RunDailyMaintenance();
@@ -379,7 +362,8 @@ namespace gaseous_server
case QueueItemType.WeeklyMaintainer:
Logging.Log(Logging.LogType.Debug, "Timered Event", "Starting Weekly Maintenance");
Classes.Maintenance weeklyMaintenance = new Maintenance{
Classes.Maintenance weeklyMaintenance = new Maintenance
{
CallingQueueItem = this
};
weeklyMaintenance.RunWeeklyMaintenance();

View File

@@ -36,12 +36,19 @@ db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.Conn
// set up db
db.InitDB();
// create relation tables if they don't exist
Storage.CreateRelationsTables<IGDB.Models.Game>();
Storage.CreateRelationsTables<IGDB.Models.Platform>();
// populate db with static data for lookups
AgeRatings.PopulateAgeMap();
// load app settings
Config.InitSettings();
// disable hasheous
Config.MetadataConfiguration.SignatureSource = HasheousClient.Models.MetadataModel.SignatureSources.LocalOnly;
// write updated settings back to the config file
Config.UpdateConfig();
@@ -64,7 +71,6 @@ if (Directory.Exists(Config.LibraryConfiguration.LibraryUploadDirectory))
// kick off any delayed upgrade tasks
// run 1002 background updates in the background on every start
DatabaseMigration.BackgroundUpgradeTargetSchemaVersions.Add(1002);
DatabaseMigration.BackgroundUpgradeTargetSchemaVersions.Add(1022);
// start the task
ProcessQueue.QueueItem queueItem = new ProcessQueue.QueueItem(
ProcessQueue.QueueItemType.BackgroundDatabaseUpgrade,

View File

@@ -1,73 +0,0 @@
AE|United Arab Emirates
AL|Albania
AS|Asia
AT|Austria
AU|Australia
BA|Bosnia and Herzegovina
BE|Belgium
BG|Bulgaria
BR|Brazil
CA|Canada
CH|Switzerland
CL|Chile
CN|China
CS|Serbia and Montenegro
CY|Cyprus
CZ|Czech Republic
DE|Germany
DK|Denmark
EE|Estonia
EG|Egypt
ES|Spain
EU|Europe
FI|Finland
FR|France
GB|United Kingdom
GR|Greece
HK|Hong Kong
HR|Croatia
HU|Hungary
ID|Indonesia
IE|Ireland
IL|Israel
IN|India
IR|Iran
IS|Iceland
IT|Italy
JO|Jordan
JP|Japan
KR|Korea
KR|South Korea
LT|Lithuania
LU|Luxembourg
LV|Latvia
MN|Mongolia
MX|Mexico
MY|Malaysia
NL|Netherlands
NO|Norway
NP|Nepal
NZ|New Zealand
OM|Oman
PE|Peru
PH|Philippines
PL|Poland
PT|Portugal
QA|Qatar
RO|Romania
RU|Russia
SE|Sweden
SG|Singapore
SI|Slovenia
SK|Slovakia
TH|Thailand
TR|Turkey
TW|Taiwan
US|United States
USA|United States
VN|Vietnam
YU|Yugoslavia
ZA|South Africa
World|World
Europe|Europe
Asia|Asia

View File

@@ -1,40 +0,0 @@
CREATE TABLE `Signatures_RomToSource` (
`SourceId` int NOT NULL,
`RomId` int NOT NULL,
PRIMARY KEY (`SourceId`, `RomId`)
);
CREATE TABLE `Signatures_Games_Countries` (
`GameId` INT NOT NULL,
`CountryId` INT NOT NULL,
PRIMARY KEY (`GameId`, `CountryId`),
CONSTRAINT `GameCountry` FOREIGN KEY (`GameId`) REFERENCES `Signatures_Games` (`Id`) ON DELETE CASCADE ON UPDATE NO ACTION
);
CREATE TABLE `Signatures_Games_Languages` (
`GameId` INT NOT NULL,
`LanguageId` INT NOT NULL,
PRIMARY KEY (`GameId`, `LanguageId`),
CONSTRAINT `GameLanguage` FOREIGN KEY (`GameId`) REFERENCES `Signatures_Games` (`Id`) ON DELETE CASCADE ON UPDATE NO ACTION
);
CREATE TABLE `Country` (
`Id` INT NOT NULL AUTO_INCREMENT,
`Code` VARCHAR(20) NULL,
`Value` VARCHAR(255) NULL,
PRIMARY KEY (`Id`),
INDEX `id_Code` (`Code` ASC) VISIBLE,
INDEX `id_Value` (`Value` ASC) VISIBLE
);
CREATE TABLE `Language` (
`Id` INT NOT NULL AUTO_INCREMENT,
`Code` VARCHAR(20) NULL,
`Value` VARCHAR(255) NULL,
PRIMARY KEY (`Id`),
INDEX `id_Code` (`Code` ASC) VISIBLE,
INDEX `id_Value` (`Value` ASC) VISIBLE
);
ALTER TABLE `Games_Roms`
ADD COLUMN `RomDataVersion` INT DEFAULT 1;

View File

@@ -1,47 +0,0 @@
ar|Arabic
bg|Bulgarian
bs|Bosnian
cs|Czech
cy|Welsh
da|Danish
de|German
el|Greek
en|English
eo|Esperanto
es|Spanish
et|Estonian
fa|Persian
fi|Finnish
fr|French
fr-ca|French Canadian
ga|Irish
gd|Gaelic
gu|Gujarati
he|Hebrew
hi|Hindi
hr|Croatian
hu|Hungarian
is|Icelandic
it|Italian
ja|Japanese
ko|Korean
lt|Lithuanian
lv|Latvian
ms|Malay
nl|Dutch
no|Norwegian
pl|Polish
pt|Portuguese
ro|Romanian
ru|Russian
sk|Slovakian
sl|Slovenian
sq|Albanian
sr|Serbian
sv|Swedish
th|Thai
tr|Turkish
ur|Urdu
vi|Vietnamese
yi|Yiddish
zh|Chinese

File diff suppressed because it is too large Load Diff

View File

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

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 92 KiB

After

Width:  |  Height:  |  Size: 20 KiB

View File

@@ -1,5 +1,6 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<script src="/api/v1.1/System/VersionFile"></script>
@@ -44,6 +45,7 @@
var userProfile;
</script>
</head>
<body>
<!-- Notifications -->
<div id="notifications_target"></div>
@@ -54,7 +56,9 @@
<div id="banner_header">
<div id="bannerButtons">
<div id="banner_user" onclick="showMenu();" class="banner_button dropdown dropbtn">
<img src="/images/user.svg" alt="Account" title="Account" id="banner_user_image" class="banner_button_image" style="position: relative; top: 10px; right: 0px; pointer-events: none;" onclick="showMenu();" />
<img src="/images/user.svg" alt="Account" title="Account" id="banner_user_image"
class="banner_button_image" style="position: relative; top: 10px; right: 0px; pointer-events: none;"
onclick="showMenu();" />
<div id="myDropdown" class="dropdown-content">
<div id="banner_user_roles"></div>
<a href="#" onclick="showDialog('userprofile');">Profile</a>
@@ -63,22 +67,27 @@
</div>
<div id="banner_cog" onclick="window.location.href = '/index.html?page=settings';" class="banner_button">
<img src="/images/settings.svg" alt="Settings" title="Settings" id="banner_system_image" class="banner_button_image" />
<img src="/images/settings.svg" alt="Settings" title="Settings" id="banner_system_image"
class="banner_button_image" />
<span id="banner_system_label">Settings</span>
</div>
<div id="banner_upload" onclick="showDialog('upload');" class="banner_button">
<img src="/images/upload.svg" alt="Upload" title="Upload" id="banner_upload_image" class="banner_button_image" />
<img src="/images/upload.svg" alt="Upload" title="Upload" id="banner_upload_image"
class="banner_button_image" />
<span id="banner_upload_label">Upload</span>
</div>
<div id="banner_collections" onclick="window.location.href = '/index.html?page=collections';" class="banner_button">
<img src="/images/collections.svg" alt="Collections" title="Collections" id="banner_collections_image" class="banner_button_image" />
<div id="banner_collections" onclick="window.location.href = '/index.html?page=collections';"
class="banner_button">
<img src="/images/collections.svg" alt="Collections" title="Collections" id="banner_collections_image"
class="banner_button_image" />
<span id="banner_collections_label">Collections</span>
</div>
<div id="banner_library" onclick="window.location.href = '/index.html';" class="banner_button">
<img src="/images/library.svg" alt="Library" title="Library" id="banner_library_image" class="banner_button_image" />
<img src="/images/library.svg" alt="Library" title="Library" id="banner_library_image"
class="banner_button_image" />
<span id="banner_library_label">Library</span>
</div>
</div>
@@ -97,7 +106,9 @@
<!-- Modal content -->
<div class="modal-content">
<span class="close">&times;</span>
<div><h1 id="modal-heading">Modal heading</h1></div>
<div>
<h1 id="modal-heading">Modal heading</h1>
</div>
<div id="modal-content">Some text in the Modal..</div>
</div>
@@ -118,7 +129,7 @@
var modalVariables = null;
// redirect if first run status = 0
if (FirstRunStatus == 0) {
if (FirstRunStatus == 0 || FirstRunStatus == "0") {
window.location.replace("/pages/first.html");
}
@@ -126,7 +137,7 @@
ajaxCall(
'/api/v1.1/Account/Profile/Basic',
'GET',
function(result) {
function (result) {
console.log("User is logged in");
userProfile = result;
@@ -147,7 +158,7 @@
$('#content').load('/pages/' + myParam + '.html?v=' + AppVersion);
},
function(error) {
function (error) {
window.location.replace("/pages/login.html");
}
);
@@ -159,15 +170,15 @@
}
// Close the dropdown menu if the user clicks outside of it
window.onclick = function(event) {
window.onclick = function (event) {
if (!event.target.matches('.dropbtn')) {
var dropdowns = document.getElementsByClassName("dropdown-content");
var i;
for (i = 0; i < dropdowns.length; i++) {
var openDropdown = dropdowns[i];
if (openDropdown.classList.contains('show')) {
openDropdown.classList.remove('show');
}
var openDropdown = dropdowns[i];
if (openDropdown.classList.contains('show')) {
openDropdown.classList.remove('show');
}
}
}
}
@@ -186,4 +197,5 @@
}
</script>
</body>
</html>

View File

@@ -1,6 +1,9 @@
<div id="saved_states">
</div>
<div>
<span>Upload state file: </span><input type="file" id="stateFile" />
</div>
<script text="text/javascript">
document.getElementById('modal-heading').innerHTML = "Load saved state";
@@ -13,9 +16,10 @@
ajaxCall(
statesUrl,
'GET',
function(result) {
function (result) {
var statesBox = document.getElementById('saved_states');
statesBox.innerHTML = '';
document.getElementById('stateFile').value = '';
for (var i = 0; i < result.length; i++) {
var stateBox = document.createElement('div');
@@ -62,7 +66,7 @@
stateControls.id = 'stateControls_' + result[i].id;
stateControls.className = 'saved_state_controls';
var stateControlsLaunch= document.createElement('span');
var stateControlsLaunch = document.createElement('span');
stateControlsLaunch.id = 'stateControlsLaunch_' + result[i].id;
stateControlsLaunch.className = 'romstart';
var emulatorTarget = '/index.html?page=emulator&engine=@engine&core=@core&platformid=@platformid&gameid=@gameid&romid=@romid&mediagroup=@mediagroup&rompath=@rompath&stateid=' + result[i].id;
@@ -128,7 +132,7 @@
ajaxCall(
'/api/v1.1/StateManager/' + modalVariables.romId + '/' + StateId + '?IsMediaGroup=' + IsMediaGroup,
'DELETE',
function(success) {
function (success) {
LoadStates();
},
function (error) {
@@ -147,7 +151,7 @@
ajaxCall(
'/api/v1.1/StateManager/' + modalVariables.romId + '/' + StateId + '?IsMediaGroup=' + IsMediaGroup,
'PUT',
function(success) {
function (success) {
LoadStates();
},
function (error) {
@@ -156,4 +160,36 @@
JSON.stringify(model)
);
}
document.getElementById('stateFile').addEventListener('change', function () {
let file = document.getElementById('stateFile').files[0];
let formData = new FormData();
formData.append('file', file);
console.log("Uploading state file");
fetch('/api/v1.1/StateManager/Upload?RomId=' + modalVariables.romId + '&IsMediaGroup=' + modalVariables.IsMediaGroup, {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
console.log('Success:', data);
UploadAlert(data);
LoadStates();
})
.catch(error => {
console.error("Error:", error);
UploadAlert(error);
LoadStates();
});
});
function UploadAlert(data) {
if (data.Management == "Managed") {
alert("State uploaded successfully.");
} else {
alert("State uploaded successfully, but it might not function correctly for this platform and ROM.");
}
}
</script>

View File

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

View File

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

View File

@@ -13,7 +13,7 @@
if (IsMediaGroupInt == 1) { IsMediaGroup = true; }
var StateUrl = undefined;
if (getQueryString('stateid', 'int')) {
StateUrl = '/api/v1.1/StateManager/' + romId + '/' + getQueryString('stateid', 'int') + '/State/savestate.state?IsMediaGroup=' + IsMediaGroup;
StateUrl = '/api/v1.1/StateManager/' + romId + '/' + getQueryString('stateid', 'int') + '/State/savestate.state?StateOnly=true&IsMediaGroup=' + IsMediaGroup;
}
var gameData;
var artworks = null;

View File

@@ -1,5 +1,6 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<script src="/api/v1.1/System/VersionFile"></script>
@@ -40,8 +41,10 @@
}
</script>
</head>
<body>
<div id="bgImage" style="background-image: url('/images/LoginWallpaper.jpg'); background-position: center; background-repeat: no-repeat; background-size: cover; filter: blur(10px); -webkit-filter: blur(10px);">
<div id="bgImage"
style="background-image: url('/images/LoginWallpaper.jpg'); background-position: center; background-repeat: no-repeat; background-size: cover; filter: blur(10px); -webkit-filter: blur(10px);">
<div id="bgImage_Opacity"></div>
</div>
@@ -52,7 +55,9 @@
<div id="loginwindow_header_label" style="display: block; text-align: center;">Gaseous Games</div>
<button type="button" value="Get Started" onclick="document.getElementById('first_welcome').style.display = 'none'; document.getElementById('first_newadmin').style.display = '';" class="bigbutton">Get Started</button>
<button type="button" value="Get Started"
onclick="document.getElementById('first_welcome').style.display = 'none'; document.getElementById('first_newadmin').style.display = '';"
class="bigbutton">Get Started</button>
</div>
</div>
<div class="loginwindow" id="first_newadmin" style="display: none;">
@@ -67,15 +72,18 @@
</tr>
<tr>
<th>Email</th>
<td><input type="email" id="login_email" style="width: 95%;" onkeyup="checkPasswordsMatch();" /></td>
<td><input type="email" id="login_email" style="width: 95%;" onkeyup="checkPasswordsMatch();" />
</td>
</tr>
<tr>
<th>New Password</th>
<td><input type="password" id="login_password" style="width: 95%;" onkeyup="checkPasswordsMatch();" /></td>
<td><input type="password" id="login_password" style="width: 95%;"
onkeyup="checkPasswordsMatch();" /></td>
</tr>
<tr>
<th>Confirm Password</th>
<td><input type="password" id="login_confirmpassword" style="width: 95%;" onkeyup="checkPasswordsMatch();" /></td>
<td><input type="password" id="login_confirmpassword" style="width: 95%;"
onkeyup="checkPasswordsMatch();" /></td>
</tr>
<tr>
<td colspan="2" id="login_passwordnotice">&nbsp;</td>
@@ -85,7 +93,9 @@
</tr>
<tr>
<td colspan="2" style="padding-top: 20px;">
<button id="login_createaccount" type="button" value="Create Account" onclick="registerAccount();" disabled="disabled" class="bigbutton">Create Account</button>
<button id="login_createaccount" type="button" value="Create Account"
onclick="registerAccount();" disabled="disabled" class="bigbutton">Create
Account</button>
</td>
</tr>
</table>
@@ -94,12 +104,14 @@
</div>
<div id="settings_photocredit">
Wallpaper by <a href="https://unsplash.com/@spideyjoey" class="romlink">Joey Kwok</a> / <a href="https://unsplash.com/photos/a-room-filled-with-arcade-machines-and-neon-lights-jbIsTd7rdd8" class="romlink">Unsplash</a>
Wallpaper by <a href="https://unsplash.com/@spideyjoey" class="romlink">Joey Kwok</a> / <a
href="https://unsplash.com/photos/a-room-filled-with-arcade-machines-and-neon-lights-jbIsTd7rdd8"
class="romlink">Unsplash</a>
</div>
<script type="text/javascript">
// redirect if first run status != 0 as 0 indicates that first run needs to be run
if (FirstRunStatus != 0) {
if (FirstRunStatus != 0 && FirstRunStatus != "0") {
window.location.replace("/");
}
@@ -146,10 +158,10 @@
ajaxCall(
'/api/v1.1/FirstSetup/0',
'POST',
function(result){
function (result) {
loginCallback(result);
},
function(error){
function (error) {
loginCallback(error);
},
JSON.stringify(model)

View File

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

View File

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

View File

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

View File

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

View File

@@ -22,7 +22,7 @@
<p><strong>Database</strong></p>
<div id="system_database"></div>
<h3>Local Database Signatures</h3>
<h3>Signatures</h3>
<div id="system_signatures"></div>
<script type="text/javascript">
@@ -82,7 +82,7 @@
var nextRunTime = moment(result[i].nextRunTime).format("YYYY-MM-DD h:mm:ss a");
var startButton = '';
if (userProfile.roles.includes("Admin")) {
if (result[i].allowManualStart == true && !["Running"].includes(result[i].itemState) && result[i].isBlocked == false) {
if (result[i].allowManualStart == true && ![ "Running"].includes(result[i].itemState) && result[i].isBlocked == false) {
startButton = "<span id='startProcess' class='romstart' onclick='StartProcess(\"" + result[i].itemType + "\");'>Start</span>";
}
}

View File

@@ -39,7 +39,7 @@ function ajaxCall(endpoint, method, successFunction, errorFunction, body) {
function getQueryString(stringName, type) {
const urlParams = new URLSearchParams(window.location.search);
var myParam = urlParams.get(stringName);
var myParam = urlParams.get(stringName);
switch (type) {
case "int":
@@ -63,9 +63,9 @@ function getQueryString(stringName, type) {
function setCookie(cname, cvalue, exdays) {
const d = new Date();
d.setTime(d.getTime() + (exdays*24*60*60*1000));
d.setTime(d.getTime() + (exdays * 24 * 60 * 60 * 1000));
if (exdays) {
let expires = "expires="+ d.toUTCString();
let expires = "expires=" + d.toUTCString();
document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/";
} else {
document.cookie = cname + "=" + cvalue + ";path=/";
@@ -76,14 +76,14 @@ function getCookie(cname) {
let name = cname + "=";
let decodedCookie = decodeURIComponent(document.cookie);
let ca = decodedCookie.split(';');
for(let i = 0; i <ca.length; i++) {
let c = ca[i];
while (c.charAt(0) == ' ') {
c = c.substring(1);
}
if (c.indexOf(name) == 0) {
return c.substring(name.length, c.length);
}
for (let i = 0; i < ca.length; i++) {
let c = ca[i];
while (c.charAt(0) == ' ') {
c = c.substring(1);
}
if (c.indexOf(name) == 0) {
return c.substring(name.length, c.length);
}
}
return "";
}
@@ -207,7 +207,7 @@ function createTableRow(isHeader, row, rowClass, cellClass) {
}
var newCell = document.createElement(cellType);
if (typeof(row[i]) != "object") {
if (typeof (row[i]) != "object") {
newCell.innerHTML = row[i];
newCell.className = cellClass;
} else {
@@ -258,7 +258,7 @@ function DropDownRenderGameOption(state) {
if (state.cover) {
response = $(
'<table class="dropdown-div"><tr><td class="dropdown-cover"><img src="/api/v1.1/Games/' + state.id + '/cover/image/cover_small/' + state.cover.imageId + '.jpg" /></td><td class="dropdown-label"><span class="dropdown-title">' + state.text + '</span><span class="dropdown-releasedate">' + releaseDate + '</span></td></tr></table>'
'<table class="dropdown-div"><tr><td class="dropdown-cover"><img src="/api/v1.1/Games/' + state.id + '/cover/image/cover_small/' + state.id + '.jpg" class="game_tile_small_search" /></td><td class="dropdown-label"><span class="dropdown-title">' + state.text + '</span><span class="dropdown-releasedate">' + releaseDate + '</span></td></tr></table>'
);
} else {
response = $(
@@ -318,7 +318,7 @@ function CreateEditableTable(TableName, Headers) {
addButton.value = 'Add Row';
addButton.innerHTML = 'Add Row';
$(addButton).click(function() {
$(addButton).click(function () {
eTable.appendChild(AddEditableTableRow(Headers));
});
@@ -463,10 +463,10 @@ function SetPreference(Setting, Value) {
ajaxCall(
'/api/v1.1/Account/Preferences',
'POST',
function(result) {
function (result) {
SetPreference_Local(Setting, Value);
},
function(error) {
function (error) {
SetPreference_Local(Setting, Value);
},
JSON.stringify(model)
@@ -478,12 +478,12 @@ function SetPreference_Batch(model) {
ajaxCall(
'/api/v1.1/Account/Preferences',
'POST',
function(result) {
function (result) {
for (var i = 0; i < model.length; i++) {
SetPreference_Local(model[i].setting, model[i].value.toString());
}
},
function(error) {
function (error) {
for (var i = 0; i < model.length; i++) {
SetPreference_Local(model[i].setting, model[i].value.toString());
}
@@ -509,11 +509,11 @@ function SetPreference_Local(Setting, Value) {
}
}
function Uint8ToString(u8a){
function Uint8ToString(u8a) {
var CHUNK_SZ = 0x8000;
var c = [];
for (var i=0; i < u8a.length; i+=CHUNK_SZ) {
c.push(String.fromCharCode.apply(null, u8a.subarray(i, i+CHUNK_SZ)));
for (var i = 0; i < u8a.length; i += CHUNK_SZ) {
c.push(String.fromCharCode.apply(null, u8a.subarray(i, i + CHUNK_SZ)));
}
return c.join("");
}

View File

@@ -315,9 +315,7 @@ input[type='text'],
input[type='number'],
input[type="email"],
input[type="password"],
input[type="datetime-local"],
input[type="url"],
textarea {
input[type="datetime-local"] {
background-color: #2b2b2b;
color: white;
padding: 4px;
@@ -338,17 +336,10 @@ input[type='text']:hover,
input[type='number']:hover,
input[type="email"]:hover,
input[type="password"]:hover,
input[type="datetime-local"]:hover,
input[type="url"]:hover,
textarea:hover {
input[type="datetime-local"]:hover {
border-color: #939393;
}
textarea {
height: unset;
font-family: 'Courier New', Courier, monospace;
}
input[id='filter_panel_search'] {
width: 160px;
}
@@ -616,22 +607,28 @@ input[name='filter_panel_range_max'] {
.game_tile:hover {
cursor: pointer;
/* text-decoration: underline;
text-decoration: underline;
background-color: #2b2b2b;
border-radius: 10px 10px 10px 10px;
-webkit-border-radius: 10px 10px 10px 10px;
-moz-border-radius: 10px 10px 10px 10px;
border: 1px solid #2b2b2b; */
border: 1px solid #2b2b2b;
}
.game_tile_small:hover {
cursor: pointer;
/* text-decoration: underline;
text-decoration: underline;
background-color: #2b2b2b;
border-radius: 10px 10px 10px 10px;
-webkit-border-radius: 10px 10px 10px 10px;
-moz-border-radius: 10px 10px 10px 10px;
border: 1px solid #2b2b2b; */
border: 1px solid #2b2b2b;
}
.game_tile_small_search {
min-height: 50px;
min-width: 50px;
width: 80px;
}
.game_tile_row {
@@ -655,19 +652,6 @@ input[name='filter_panel_range_max'] {
display: inline-block;
max-width: 200px;
max-height: 200px;
border-radius: 7px;
border-width: 2px;
border-style: solid;
border-color: transparent;
overflow: hidden;
}
.game_tile:hover .game_tile_box {
border-color: yellow;
outline-width: 2px;
outline-style: solid;
outline-offset: -3px;
outline-color: black;
}
.game_tile_box_row {