Compare commits
3 Commits
v1.7.0-pre
...
v1.7.0-pre
Author | SHA1 | Date | |
---|---|---|---|
![]() |
9b8874902a | ||
![]() |
127eab683b | ||
![]() |
1efc47f9cd |
6
.github/dependabot.yml
vendored
6
.github/dependabot.yml
vendored
@@ -9,9 +9,3 @@ updates:
|
|||||||
directory: "/" # Location of package manifests
|
directory: "/" # Location of package manifests
|
||||||
schedule:
|
schedule:
|
||||||
interval: "weekly"
|
interval: "weekly"
|
||||||
- package-ecosystem: "gitsubmodule"
|
|
||||||
directory: "/"
|
|
||||||
allow:
|
|
||||||
- dependency-name: "gaseous-server/wwwroot/emulators/EmulatorJS"
|
|
||||||
schedule:
|
|
||||||
interval: "weekly"
|
|
1
.gitignore
vendored
1
.gitignore
vendored
@@ -404,3 +404,4 @@ ASALocalRun/
|
|||||||
# Local History for Visual Studio
|
# Local History for Visual Studio
|
||||||
.localhistory/
|
.localhistory/
|
||||||
gaseous-server/.DS_Store
|
gaseous-server/.DS_Store
|
||||||
|
gaseous-server/wwwroot/emulators/EmulatorJS
|
||||||
|
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -1,3 +0,0 @@
|
|||||||
[submodule "gaseous-server/wwwroot/emulators/EmulatorJS"]
|
|
||||||
path = gaseous-server/wwwroot/emulators/EmulatorJS
|
|
||||||
url = https://github.com/EmulatorJS/EmulatorJS.git
|
|
||||||
|
@@ -9,6 +9,12 @@ RUN dotnet restore "gaseous-server/gaseous-server.csproj"
|
|||||||
# Build and publish a release
|
# Build and publish a release
|
||||||
RUN dotnet publish "gaseous-server/gaseous-server.csproj" --use-current-runtime --self-contained false -c Release -o out
|
RUN dotnet publish "gaseous-server/gaseous-server.csproj" --use-current-runtime --self-contained false -c Release -o out
|
||||||
|
|
||||||
|
# download and unzip EmulatorJS from CDN
|
||||||
|
RUN apt-get update && apt-get install -y p7zip-full
|
||||||
|
RUN mkdir -p out/wwwroot/emulators/EmulatorJS
|
||||||
|
RUN wget https://cdn.emulatorjs.org/releases/4.0.9.7z
|
||||||
|
RUN 7z x -y -oout/wwwroot/emulators/EmulatorJS 4.0.9.7z
|
||||||
|
|
||||||
# Build runtime image
|
# Build runtime image
|
||||||
FROM mcr.microsoft.com/dotnet/aspnet:7.0
|
FROM mcr.microsoft.com/dotnet/aspnet:7.0
|
||||||
WORKDIR /App
|
WORKDIR /App
|
||||||
|
18
README.MD
18
README.MD
@@ -85,10 +85,9 @@ Dockerfile and docker-compose.yml files have been provided to make deployment of
|
|||||||
Dockerfile and docker-compose-build.yml files have been provided to make deployment of the server as easy as possible.
|
Dockerfile and docker-compose-build.yml files have been provided to make deployment of the server as easy as possible.
|
||||||
1. Clone the repo with ```git clone https://github.com/gaseous-project/gaseous-server.git```
|
1. Clone the repo with ```git clone https://github.com/gaseous-project/gaseous-server.git```
|
||||||
2. Change into the gaseous-server directory
|
2. Change into the gaseous-server directory
|
||||||
3. Clone the submodules with the command ```git submodule update --init```
|
3. Open the docker-compose-{database}-build.yml file and edit the igdbclientid and igdbclientsecret to the values retrieved from your IGDB account
|
||||||
4. Open the docker-compose-{database}-build.yml file and edit the igdbclientid and igdbclientsecret to the values retrieved from your IGDB account
|
4. Run the command ```docker-compose --file docker-compose-{database}-build.yml up -d```
|
||||||
5. Run the command ```docker-compose --file docker-compose-{database}-build.yml up -d```
|
5. Connect to the host on port 5198
|
||||||
6. Connect to the host on port 5198
|
|
||||||
|
|
||||||
## Source
|
## Source
|
||||||
### Build and deploy
|
### Build and deploy
|
||||||
@@ -100,8 +99,7 @@ Dockerfile and docker-compose-build.yml files have been provided to make deploym
|
|||||||
5. Change into the gaseous-server directory
|
5. Change into the gaseous-server directory
|
||||||
6. As the main branch is the development branch, you might want to change to a stable version - these are tagged with a version number. For example to change to the 1.5.0 release, use the command ```git checkout v1.5.0```
|
6. As the main branch is the development branch, you might want to change to a stable version - these are tagged with a version number. For example to change to the 1.5.0 release, use the command ```git checkout v1.5.0```
|
||||||
* Check the releases page for the version you would like to run: https://github.com/gaseous-project/gaseous-server/releases
|
* Check the releases page for the version you would like to run: https://github.com/gaseous-project/gaseous-server/releases
|
||||||
7. Clone the submodules with the command ```git submodule update --init --recursive```
|
7. Download the emulator files from ```https://cdn.emulatorjs.org/releases/4.0.9.zip``` and extract the files to ```gaseous-server/wwwroot/emulators/EmulatorJS```
|
||||||
* This command will clone the code that the server uses from other projects (currently only EmulatorJS)
|
|
||||||
8. Create a directory in the home directory of the user that will run the server. For example, if running as the user ```gaseous```, create the directory ```/home/gaseous/.gaseous-server```
|
8. Create a directory in the home directory of the user that will run the server. For example, if running as the user ```gaseous```, create the directory ```/home/gaseous/.gaseous-server```
|
||||||
9. Change into the ```.gaseous-server``` directory created in the previous step
|
9. Change into the ```.gaseous-server``` directory created in the previous step
|
||||||
10. Copy the JSON from the config file above into a new file named ```config.json```
|
10. Copy the JSON from the config file above into a new file named ```config.json```
|
||||||
@@ -115,11 +113,6 @@ Dockerfile and docker-compose-build.yml files have been provided to make deploym
|
|||||||
|
|
||||||
**Note**: The above instructions were tested on macOS Ventura, and Ubuntu 22.04.3. There was a report that Debian 11 had an issue with the git submodule commands (see: https://github.com/gaseous-project/gaseous-server/issues/71). This was possibly due to an older git package.
|
**Note**: The above instructions were tested on macOS Ventura, and Ubuntu 22.04.3. There was a report that Debian 11 had an issue with the git submodule commands (see: https://github.com/gaseous-project/gaseous-server/issues/71). This was possibly due to an older git package.
|
||||||
|
|
||||||
If the git submodule commands aren't working, you can:
|
|
||||||
1. change to the ```gaseous-server/wwwroot/emulators``` directory
|
|
||||||
2. delete the ```EmulatorJS``` directory
|
|
||||||
3. clone the EmulatorJS repository with ```git clone https://github.com/EmulatorJS/EmulatorJS.git```
|
|
||||||
|
|
||||||
### Updating from source
|
### Updating from source
|
||||||
1. Stop the server
|
1. Stop the server
|
||||||
2. Switch to the source directory
|
2. Switch to the source directory
|
||||||
@@ -128,8 +121,7 @@ If the git submodule commands aren't working, you can:
|
|||||||
* If running from another branch or tag, run:
|
* If running from another branch or tag, run:
|
||||||
* ```git fetch```
|
* ```git fetch```
|
||||||
* ```git checkout <branch or tag name>```
|
* ```git checkout <branch or tag name>```
|
||||||
4. Update the submodules with ```git submodule update --recursive```
|
4. Run steps 12 and 13 from the above Build guide
|
||||||
5. Run steps 12 and 13 from the above Build guide
|
|
||||||
|
|
||||||
# Adding Content
|
# Adding Content
|
||||||
While games can be added to the server without them, it is recommended adding some signature DAT files beforehand to allow for better matching of ROMs to games.
|
While games can be added to the server without them, it is recommended adding some signature DAT files beforehand to allow for better matching of ROMs to games.
|
||||||
|
BIN
gaseous-server/.DS_Store
vendored
BIN
gaseous-server/.DS_Store
vendored
Binary file not shown.
@@ -289,7 +289,7 @@ namespace Authentication
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
private int Delete(string userId)
|
private int Delete(string userId)
|
||||||
{
|
{
|
||||||
string commandText = "Delete from Users where Id = @userId; Delete from User_Settings where Id = @userId;";
|
string commandText = "Delete from Users where Id = @userId; Delete from User_Settings where Id = @userId; Delete from GameState where UserId = @userId;";
|
||||||
Dictionary<string, object> parameters = new Dictionary<string, object>();
|
Dictionary<string, object> parameters = new Dictionary<string, object>();
|
||||||
parameters.Add("@userId", userId);
|
parameters.Add("@userId", userId);
|
||||||
|
|
||||||
|
@@ -529,6 +529,11 @@ namespace gaseous_server.Classes.Metadata
|
|||||||
|
|
||||||
public class MinimalGameItem
|
public class MinimalGameItem
|
||||||
{
|
{
|
||||||
|
public MinimalGameItem()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public MinimalGameItem(Game gameObject)
|
public MinimalGameItem(Game gameObject)
|
||||||
{
|
{
|
||||||
this.Id = gameObject.Id;
|
this.Id = gameObject.Id;
|
||||||
@@ -558,6 +563,7 @@ namespace gaseous_server.Classes.Metadata
|
|||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
public double? TotalRating { get; set; }
|
public double? TotalRating { get; set; }
|
||||||
public int? TotalRatingCount { get; set; }
|
public int? TotalRatingCount { get; set; }
|
||||||
|
public bool HasSavedGame { get; set; } = false;
|
||||||
public DateTimeOffset? FirstReleaseDate { get; set; }
|
public DateTimeOffset? FirstReleaseDate { get; set; }
|
||||||
public IGDB.IdentityOrValue<IGDB.Models.Cover> Cover { get; set; }
|
public IGDB.IdentityOrValue<IGDB.Models.Cover> Cover { get; set; }
|
||||||
public IGDB.IdentitiesOrValues<IGDB.Models.Artwork> Artworks { get; set; }
|
public IGDB.IdentitiesOrValues<IGDB.Models.Artwork> Artworks { get; set; }
|
||||||
|
@@ -55,12 +55,15 @@ namespace gaseous_server.Classes
|
|||||||
return GetMediaGroup(mgId);
|
return GetMediaGroup(mgId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static GameRomMediaGroupItem GetMediaGroup(long Id)
|
public static GameRomMediaGroupItem GetMediaGroup(long Id, string userid = "")
|
||||||
{
|
{
|
||||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||||
string sql = "SELECT * FROM RomMediaGroup WHERE Id=@id;";
|
string sql = "SELECT DISTINCT RomMediaGroup.*, GameState.RomId AS GameStateRomId FROM gaseous.RomMediaGroup LEFT JOIN GameState ON RomMediaGroup.Id = GameState.RomId AND GameState.IsMediaGroup = 1 AND GameState.UserId = @userid WHERE RomMediaGroup.Id=@id;";
|
||||||
Dictionary<string, object> dbDict = new Dictionary<string, object>();
|
Dictionary<string, object> dbDict = new Dictionary<string, object>
|
||||||
dbDict.Add("id", Id);
|
{
|
||||||
|
{ "id", Id },
|
||||||
|
{ "userid", userid }
|
||||||
|
};
|
||||||
|
|
||||||
DataTable dataTable = db.ExecuteCMD(sql, dbDict);
|
DataTable dataTable = db.ExecuteCMD(sql, dbDict);
|
||||||
|
|
||||||
@@ -75,12 +78,15 @@ namespace gaseous_server.Classes
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<GameRomMediaGroupItem> GetMediaGroupsFromGameId(long GameId)
|
public static List<GameRomMediaGroupItem> GetMediaGroupsFromGameId(long GameId, string userid = "")
|
||||||
{
|
{
|
||||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||||
string sql = "SELECT * FROM RomMediaGroup WHERE GameId=@gameid;";
|
string sql = "SELECT DISTINCT RomMediaGroup.*, GameState.RomId AS GameStateRomId FROM gaseous.RomMediaGroup LEFT JOIN GameState ON RomMediaGroup.Id = GameState.RomId AND GameState.IsMediaGroup = 1 AND GameState.UserId = @userid WHERE RomMediaGroup.GameId=@gameid;";
|
||||||
Dictionary<string, object> dbDict = new Dictionary<string, object>();
|
Dictionary<string, object> dbDict = new Dictionary<string, object>
|
||||||
dbDict.Add("gameid", GameId);
|
{
|
||||||
|
{ "gameid", GameId },
|
||||||
|
{ "userid", userid }
|
||||||
|
};
|
||||||
|
|
||||||
DataTable dataTable = db.ExecuteCMD(sql, dbDict);
|
DataTable dataTable = db.ExecuteCMD(sql, dbDict);
|
||||||
|
|
||||||
@@ -156,7 +162,7 @@ namespace gaseous_server.Classes
|
|||||||
public static void DeleteMediaGroup(long Id)
|
public static void DeleteMediaGroup(long Id)
|
||||||
{
|
{
|
||||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||||
string sql = "DELETE FROM RomMediaGroup WHERE Id=@id;";
|
string sql = "DELETE FROM RomMediaGroup WHERE Id=@id; DELETE FROM GameState WHERE RomId=@id AND IsMediaGroup=1;";
|
||||||
Dictionary<string, object> dbDict = new Dictionary<string, object>();
|
Dictionary<string, object> dbDict = new Dictionary<string, object>();
|
||||||
dbDict.Add("id", Id);
|
dbDict.Add("id", Id);
|
||||||
db.ExecuteCMD(sql, dbDict);
|
db.ExecuteCMD(sql, dbDict);
|
||||||
@@ -170,6 +176,15 @@ namespace gaseous_server.Classes
|
|||||||
|
|
||||||
internal static GameRomMediaGroupItem BuildMediaGroupFromRow(DataRow row)
|
internal static GameRomMediaGroupItem BuildMediaGroupFromRow(DataRow row)
|
||||||
{
|
{
|
||||||
|
bool hasSaveStates = false;
|
||||||
|
if (row.Table.Columns.Contains("GameStateRomId"))
|
||||||
|
{
|
||||||
|
if (row["GameStateRomId"] != DBNull.Value)
|
||||||
|
{
|
||||||
|
hasSaveStates = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
GameRomMediaGroupItem mediaGroupItem = new GameRomMediaGroupItem();
|
GameRomMediaGroupItem mediaGroupItem = new GameRomMediaGroupItem();
|
||||||
mediaGroupItem.Id = (long)row["Id"];
|
mediaGroupItem.Id = (long)row["Id"];
|
||||||
mediaGroupItem.Status = (GameRomMediaGroupItem.GroupBuildStatus)row["Status"];
|
mediaGroupItem.Status = (GameRomMediaGroupItem.GroupBuildStatus)row["Status"];
|
||||||
@@ -177,6 +192,7 @@ namespace gaseous_server.Classes
|
|||||||
mediaGroupItem.GameId = (long)row["GameId"];
|
mediaGroupItem.GameId = (long)row["GameId"];
|
||||||
mediaGroupItem.RomIds = new List<long>();
|
mediaGroupItem.RomIds = new List<long>();
|
||||||
mediaGroupItem.Roms = new List<Roms.GameRomItem>();
|
mediaGroupItem.Roms = new List<Roms.GameRomItem>();
|
||||||
|
mediaGroupItem.HasSaveStates = hasSaveStates;
|
||||||
|
|
||||||
// get members
|
// get members
|
||||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||||
@@ -397,6 +413,7 @@ namespace gaseous_server.Classes
|
|||||||
public Models.PlatformMapping.PlatformMapItem.WebEmulatorItem? Emulator { get; set; }
|
public Models.PlatformMapping.PlatformMapItem.WebEmulatorItem? Emulator { get; set; }
|
||||||
public List<long> RomIds { get; set; }
|
public List<long> RomIds { get; set; }
|
||||||
public List<Roms.GameRomItem> Roms { get; set; }
|
public List<Roms.GameRomItem> Roms { get; set; }
|
||||||
|
public bool HasSaveStates { get; set; } = false;
|
||||||
private GroupBuildStatus _Status { get; set; }
|
private GroupBuildStatus _Status { get; set; }
|
||||||
public GroupBuildStatus Status {
|
public GroupBuildStatus Status {
|
||||||
get
|
get
|
||||||
|
@@ -15,7 +15,7 @@ namespace gaseous_server.Classes
|
|||||||
{}
|
{}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static GameRomObject GetRoms(long GameId, long PlatformId = -1, string NameSearch = "", int pageNumber = 0, int pageSize = 0)
|
public static GameRomObject GetRoms(long GameId, long PlatformId = -1, string NameSearch = "", int pageNumber = 0, int pageSize = 0, string userid = "")
|
||||||
{
|
{
|
||||||
GameRomObject GameRoms = new GameRomObject();
|
GameRomObject GameRoms = new GameRomObject();
|
||||||
|
|
||||||
@@ -25,6 +25,7 @@ namespace gaseous_server.Classes
|
|||||||
string sqlPlatform = "";
|
string sqlPlatform = "";
|
||||||
Dictionary<string, object> dbDict = new Dictionary<string, object>();
|
Dictionary<string, object> dbDict = new Dictionary<string, object>();
|
||||||
dbDict.Add("id", GameId);
|
dbDict.Add("id", GameId);
|
||||||
|
dbDict.Add("userid", userid);
|
||||||
|
|
||||||
string NameSearchWhere = "";
|
string NameSearchWhere = "";
|
||||||
if (NameSearch.Length > 0)
|
if (NameSearch.Length > 0)
|
||||||
@@ -38,13 +39,13 @@ namespace gaseous_server.Classes
|
|||||||
|
|
||||||
if (PlatformId == -1) {
|
if (PlatformId == -1) {
|
||||||
// data query
|
// data query
|
||||||
sql = "SELECT Games_Roms.*, Platform.`Name` AS platformname FROM Games_Roms LEFT JOIN Platform ON Games_Roms.PlatformId = Platform.Id WHERE Games_Roms.GameId = @id" + NameSearchWhere + " ORDER BY Platform.`Name`, Games_Roms.`Name` LIMIT 1000;";
|
sql = "SELECT DISTINCT Games_Roms.*, Platform.`Name` AS platformname, GameState.RomId AS SavedStateRomId FROM Games_Roms LEFT JOIN Platform ON Games_Roms.PlatformId = Platform.Id LEFT JOIN GameState ON (Games_Roms.Id = GameState.RomId AND GameState.UserId = @userid AND GameState.IsMediaGroup = 0) WHERE Games_Roms.GameId = @id" + NameSearchWhere + " ORDER BY Platform.`Name`, Games_Roms.`Name` LIMIT 1000;";
|
||||||
|
|
||||||
// count query
|
// count query
|
||||||
sqlCount = "SELECT COUNT(Games_Roms.Id) AS RomCount FROM Games_Roms WHERE Games_Roms.GameId = @id" + NameSearchWhere + ";";
|
sqlCount = "SELECT COUNT(Games_Roms.Id) AS RomCount FROM Games_Roms WHERE Games_Roms.GameId = @id" + NameSearchWhere + ";";
|
||||||
} else {
|
} else {
|
||||||
// data query
|
// data query
|
||||||
sql = "SELECT Games_Roms.*, Platform.`Name` AS platformname FROM Games_Roms LEFT JOIN Platform ON Games_Roms.PlatformId = Platform.Id WHERE Games_Roms.GameId = @id AND Games_Roms.PlatformId = @platformid" + NameSearchWhere + " ORDER BY Platform.`Name`, Games_Roms.`Name` LIMIT 1000;";
|
sql = "SELECT DISTINCT Games_Roms.*, Platform.`Name` AS platformname, GameState.RomId AS SavedStateRomId FROM Games_Roms LEFT JOIN Platform ON Games_Roms.PlatformId = Platform.Id LEFT JOIN GameState ON (Games_Roms.Id = GameState.RomId AND GameState.UserId = @userid AND GameState.IsMediaGroup = 0) WHERE Games_Roms.GameId = @id AND Games_Roms.PlatformId = @platformid" + NameSearchWhere + " ORDER BY Platform.`Name`, Games_Roms.`Name` LIMIT 1000;";
|
||||||
|
|
||||||
// count query
|
// count query
|
||||||
sqlCount = "SELECT COUNT(Games_Roms.Id) AS RomCount FROM Games_Roms WHERE Games_Roms.GameId = @id AND Games_Roms.PlatformId = @platformid" + NameSearchWhere + ";";
|
sqlCount = "SELECT COUNT(Games_Roms.Id) AS RomCount FROM Games_Roms WHERE Games_Roms.GameId = @id AND Games_Roms.PlatformId = @platformid" + NameSearchWhere + ";";
|
||||||
@@ -131,7 +132,7 @@ namespace gaseous_server.Classes
|
|||||||
}
|
}
|
||||||
|
|
||||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||||
string sql = "DELETE FROM Games_Roms WHERE Id = @id";
|
string sql = "DELETE FROM Games_Roms WHERE Id = @id; DELETE FROM GameState WHERE RomId = @id;";
|
||||||
Dictionary<string, object> dbDict = new Dictionary<string, object>();
|
Dictionary<string, object> dbDict = new Dictionary<string, object>();
|
||||||
dbDict.Add("id", RomId);
|
dbDict.Add("id", RomId);
|
||||||
db.ExecuteCMD(sql, dbDict);
|
db.ExecuteCMD(sql, dbDict);
|
||||||
@@ -140,6 +141,15 @@ namespace gaseous_server.Classes
|
|||||||
|
|
||||||
private static GameRomItem BuildRom(DataRow romDR)
|
private static GameRomItem BuildRom(DataRow romDR)
|
||||||
{
|
{
|
||||||
|
bool hasSaveStates = false;
|
||||||
|
if (romDR.Table.Columns.Contains("SavedStateRomId"))
|
||||||
|
{
|
||||||
|
if (romDR["SavedStateRomId"] != DBNull.Value)
|
||||||
|
{
|
||||||
|
hasSaveStates = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
GameRomItem romItem = new GameRomItem
|
GameRomItem romItem = new GameRomItem
|
||||||
{
|
{
|
||||||
Id = (long)romDR["id"],
|
Id = (long)romDR["id"],
|
||||||
@@ -159,6 +169,7 @@ namespace gaseous_server.Classes
|
|||||||
Path = (string)romDR["path"],
|
Path = (string)romDR["path"],
|
||||||
SignatureSource = (gaseous_server.Models.Signatures_Games.RomItem.SignatureSourceType)(Int32)romDR["metadatasource"],
|
SignatureSource = (gaseous_server.Models.Signatures_Games.RomItem.SignatureSourceType)(Int32)romDR["metadatasource"],
|
||||||
SignatureSourceGameTitle = (string)Common.ReturnValueIfNull(romDR["MetadataGameName"], ""),
|
SignatureSourceGameTitle = (string)Common.ReturnValueIfNull(romDR["MetadataGameName"], ""),
|
||||||
|
HasSaveStates = hasSaveStates,
|
||||||
Library = GameLibrary.GetLibrary((int)romDR["LibraryId"])
|
Library = GameLibrary.GetLibrary((int)romDR["LibraryId"])
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -191,6 +202,7 @@ namespace gaseous_server.Classes
|
|||||||
public long GameId { get; set; }
|
public long GameId { get; set; }
|
||||||
public string? Path { get; set; }
|
public string? Path { get; set; }
|
||||||
public string? SignatureSourceGameTitle { get; set;}
|
public string? SignatureSourceGameTitle { get; set;}
|
||||||
|
public bool HasSaveStates { get; set; } = false;
|
||||||
public GameLibrary.LibraryItem Library { get; set; }
|
public GameLibrary.LibraryItem Library { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -6,12 +6,14 @@ using System.IO;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Authentication;
|
||||||
using gaseous_server.Classes;
|
using gaseous_server.Classes;
|
||||||
using gaseous_server.Classes.Metadata;
|
using gaseous_server.Classes.Metadata;
|
||||||
using gaseous_server.Models;
|
using gaseous_server.Models;
|
||||||
using IGDB.Models;
|
using IGDB.Models;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Identity;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.CodeAnalysis.Scripting;
|
using Microsoft.CodeAnalysis.Scripting;
|
||||||
using static gaseous_server.Classes.Metadata.AgeRatings;
|
using static gaseous_server.Classes.Metadata.AgeRatings;
|
||||||
@@ -25,6 +27,18 @@ namespace gaseous_server.Controllers
|
|||||||
[ApiController]
|
[ApiController]
|
||||||
public class GamesController : Controller
|
public class GamesController : Controller
|
||||||
{
|
{
|
||||||
|
private readonly UserManager<ApplicationUser> _userManager;
|
||||||
|
private readonly SignInManager<ApplicationUser> _signInManager;
|
||||||
|
|
||||||
|
public GamesController(
|
||||||
|
UserManager<ApplicationUser> userManager,
|
||||||
|
SignInManager<ApplicationUser> signInManager
|
||||||
|
)
|
||||||
|
{
|
||||||
|
_userManager = userManager;
|
||||||
|
_signInManager = signInManager;
|
||||||
|
}
|
||||||
|
|
||||||
[MapToApiVersion("1.0")]
|
[MapToApiVersion("1.0")]
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
[ProducesResponseType(typeof(List<Game>), StatusCodes.Status200OK)]
|
[ProducesResponseType(typeof(List<Game>), StatusCodes.Status200OK)]
|
||||||
@@ -830,13 +844,15 @@ namespace gaseous_server.Controllers
|
|||||||
[ProducesResponseType(typeof(Classes.Roms.GameRomObject), StatusCodes.Status200OK)]
|
[ProducesResponseType(typeof(Classes.Roms.GameRomObject), StatusCodes.Status200OK)]
|
||||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
//[ResponseCache(CacheProfileName = "5Minute")]
|
//[ResponseCache(CacheProfileName = "5Minute")]
|
||||||
public ActionResult GameRom(long GameId, int pageNumber = 0, int pageSize = 0, long PlatformId = -1, string NameSearch = "")
|
public async Task<ActionResult> GameRomAsync(long GameId, int pageNumber = 0, int pageSize = 0, long PlatformId = -1, string NameSearch = "")
|
||||||
{
|
{
|
||||||
|
var user = await _userManager.GetUserAsync(User);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Game gameObject = Classes.Metadata.Games.GetGame(GameId, false, false, false);
|
Game gameObject = Classes.Metadata.Games.GetGame(GameId, false, false, false);
|
||||||
|
|
||||||
return Ok(Classes.Roms.GetRoms(GameId, PlatformId, NameSearch, pageNumber, pageSize));
|
return Ok(Classes.Roms.GetRoms(GameId, PlatformId, NameSearch, pageNumber, pageSize, user.Id));
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
@@ -1018,13 +1034,15 @@ namespace gaseous_server.Controllers
|
|||||||
[ProducesResponseType(typeof(Classes.RomMediaGroup.GameRomMediaGroupItem), StatusCodes.Status200OK)]
|
[ProducesResponseType(typeof(Classes.RomMediaGroup.GameRomMediaGroupItem), StatusCodes.Status200OK)]
|
||||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
//[ResponseCache(CacheProfileName = "5Minute")]
|
//[ResponseCache(CacheProfileName = "5Minute")]
|
||||||
public ActionResult GameRomGroup(long GameId, long RomGroupId)
|
public async Task<ActionResult> GameRomGroupAsync(long GameId, long RomGroupId)
|
||||||
{
|
{
|
||||||
|
var user = await _userManager.GetUserAsync(User);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Game gameObject = Classes.Metadata.Games.GetGame(GameId, false, false, false);
|
Game gameObject = Classes.Metadata.Games.GetGame(GameId, false, false, false);
|
||||||
|
|
||||||
Classes.RomMediaGroup.GameRomMediaGroupItem rom = Classes.RomMediaGroup.GetMediaGroup(RomGroupId);
|
Classes.RomMediaGroup.GameRomMediaGroupItem rom = Classes.RomMediaGroup.GetMediaGroup(RomGroupId, user.Id);
|
||||||
if (rom.GameId == GameId)
|
if (rom.GameId == GameId)
|
||||||
{
|
{
|
||||||
return Ok(rom);
|
return Ok(rom);
|
||||||
@@ -1047,15 +1065,17 @@ namespace gaseous_server.Controllers
|
|||||||
[Route("{GameId}/romgroup")]
|
[Route("{GameId}/romgroup")]
|
||||||
[ProducesResponseType(typeof(List<RomMediaGroup.GameRomMediaGroupItem>), StatusCodes.Status200OK)]
|
[ProducesResponseType(typeof(List<RomMediaGroup.GameRomMediaGroupItem>), StatusCodes.Status200OK)]
|
||||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
public ActionResult GetGameRomGroup(long GameId)
|
public async Task<ActionResult> GetGameRomGroupAsync(long GameId)
|
||||||
{
|
{
|
||||||
|
var user = await _userManager.GetUserAsync(User);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Game gameObject = Classes.Metadata.Games.GetGame(GameId, false, false, false);
|
Game gameObject = Classes.Metadata.Games.GetGame(GameId, false, false, false);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return Ok(RomMediaGroup.GetMediaGroupsFromGameId(GameId));
|
return Ok(RomMediaGroup.GetMediaGroupsFromGameId(GameId, user.Id));
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -1105,13 +1125,15 @@ namespace gaseous_server.Controllers
|
|||||||
[Route("{GameId}/romgroup/{RomId}")]
|
[Route("{GameId}/romgroup/{RomId}")]
|
||||||
[ProducesResponseType(typeof(Classes.RomMediaGroup.GameRomMediaGroupItem), StatusCodes.Status200OK)]
|
[ProducesResponseType(typeof(Classes.RomMediaGroup.GameRomMediaGroupItem), StatusCodes.Status200OK)]
|
||||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
public ActionResult GameRomGroupMembers(long GameId, long RomGroupId, [FromBody] List<long> RomIds)
|
public async Task<ActionResult> GameRomGroupMembersAsync(long GameId, long RomGroupId, [FromBody] List<long> RomIds)
|
||||||
{
|
{
|
||||||
|
var user = await _userManager.GetUserAsync(User);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Game gameObject = Classes.Metadata.Games.GetGame(GameId, false, false, false);
|
Game gameObject = Classes.Metadata.Games.GetGame(GameId, false, false, false);
|
||||||
|
|
||||||
Classes.RomMediaGroup.GameRomMediaGroupItem rom = Classes.RomMediaGroup.GetMediaGroup(RomGroupId);
|
Classes.RomMediaGroup.GameRomMediaGroupItem rom = Classes.RomMediaGroup.GetMediaGroup(RomGroupId, user.Id);
|
||||||
if (rom.GameId == GameId)
|
if (rom.GameId == GameId)
|
||||||
{
|
{
|
||||||
rom = Classes.RomMediaGroup.EditMediaGroup(RomGroupId, RomIds);
|
rom = Classes.RomMediaGroup.EditMediaGroup(RomGroupId, RomIds);
|
||||||
|
@@ -13,6 +13,7 @@ using Microsoft.AspNetCore.Http;
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.CodeAnalysis.Scripting;
|
using Microsoft.CodeAnalysis.Scripting;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
namespace gaseous_server.Controllers
|
namespace gaseous_server.Controllers
|
||||||
{
|
{
|
||||||
@@ -37,6 +38,32 @@ namespace gaseous_server.Controllers
|
|||||||
return Ok(PlatformMapping.PlatformMap);
|
return Ok(PlatformMapping.PlatformMap);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MapToApiVersion("1.0")]
|
||||||
|
[MapToApiVersion("1.1")]
|
||||||
|
[HttpGet]
|
||||||
|
[Route("PlatformMap.json")]
|
||||||
|
[ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)]
|
||||||
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
|
public ActionResult DownloadPlatformMap()
|
||||||
|
{
|
||||||
|
string srcJson = Newtonsoft.Json.JsonConvert.SerializeObject(PlatformMapping.PlatformMap, Newtonsoft.Json.Formatting.Indented);
|
||||||
|
|
||||||
|
string filename = "PlatformMap.json";
|
||||||
|
byte[] bytes = Encoding.UTF8.GetBytes(srcJson);
|
||||||
|
string contentType = "application/json";
|
||||||
|
|
||||||
|
var cd = new System.Net.Mime.ContentDisposition
|
||||||
|
{
|
||||||
|
FileName = filename,
|
||||||
|
Inline = true,
|
||||||
|
DispositionType = "attachment"
|
||||||
|
};
|
||||||
|
|
||||||
|
Response.Headers.Add("Content-Disposition", cd.ToString());
|
||||||
|
|
||||||
|
return File(bytes, contentType);
|
||||||
|
}
|
||||||
|
|
||||||
[MapToApiVersion("1.0")]
|
[MapToApiVersion("1.0")]
|
||||||
[MapToApiVersion("1.1")]
|
[MapToApiVersion("1.1")]
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
|
@@ -85,7 +85,7 @@ namespace gaseous_server.Controllers.v1_1
|
|||||||
model.GameAgeRating.IncludeUnrated = false;
|
model.GameAgeRating.IncludeUnrated = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok(GetGames(model, pageNumber, pageSize));
|
return Ok(GetGames(model, user.Id, pageNumber, pageSize));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -144,6 +144,7 @@ namespace gaseous_server.Controllers.v1_1
|
|||||||
public GameRatingItem GameRating { get; set; } = new GameRatingItem();
|
public GameRatingItem GameRating { get; set; } = new GameRatingItem();
|
||||||
public GameAgeRatingItem GameAgeRating { get; set; } = new GameAgeRatingItem();
|
public GameAgeRatingItem GameAgeRating { get; set; } = new GameAgeRatingItem();
|
||||||
public GameSortingItem Sorting { get; set; } = new GameSortingItem();
|
public GameSortingItem Sorting { get; set; } = new GameSortingItem();
|
||||||
|
public bool HasSavedGame { get; set; }
|
||||||
|
|
||||||
|
|
||||||
public class GameRatingItem
|
public class GameRatingItem
|
||||||
@@ -181,11 +182,12 @@ namespace gaseous_server.Controllers.v1_1
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static GameReturnPackage GetGames(GameSearchModel model, int pageNumber = 0, int pageSize = 0)
|
public static GameReturnPackage GetGames(GameSearchModel model, string userid, int pageNumber = 0, int pageSize = 0)
|
||||||
{
|
{
|
||||||
string whereClause = "";
|
string whereClause = "";
|
||||||
string havingClause = "";
|
string havingClause = "";
|
||||||
Dictionary<string, object> whereParams = new Dictionary<string, object>();
|
Dictionary<string, object> whereParams = new Dictionary<string, object>();
|
||||||
|
whereParams.Add("userid", userid);
|
||||||
|
|
||||||
List<string> whereClauses = new List<string>();
|
List<string> whereClauses = new List<string>();
|
||||||
List<string> havingClauses = new List<string>();
|
List<string> havingClauses = new List<string>();
|
||||||
@@ -202,6 +204,12 @@ namespace gaseous_server.Controllers.v1_1
|
|||||||
whereParams.Add("@Name", "(*" + model.Name + "*) (" + model.Name + ") ");
|
whereParams.Add("@Name", "(*" + model.Name + "*) (" + model.Name + ") ");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (model.HasSavedGame == true)
|
||||||
|
{
|
||||||
|
string hasSavesTemp = "(RomSavedStates.RomSaveCount IS NOT NULL OR RomGroupSavedStates.MediaGroupSaveCount IS NOT NULL)";
|
||||||
|
whereClauses.Add(hasSavesTemp);
|
||||||
|
}
|
||||||
|
|
||||||
if (model.GameRating != null)
|
if (model.GameRating != null)
|
||||||
{
|
{
|
||||||
List<string> ratingClauses = new List<string>();
|
List<string> ratingClauses = new List<string>();
|
||||||
@@ -444,11 +452,71 @@ namespace gaseous_server.Controllers.v1_1
|
|||||||
string orderByClause = "ORDER BY `" + orderByField + "` " + orderByOrder;
|
string orderByClause = "ORDER BY `" + orderByField + "` " + orderByOrder;
|
||||||
|
|
||||||
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||||
//string sql = "SELECT DISTINCT view_Games.* FROM view_Games LEFT JOIN Relation_Game_Platforms ON view_Games.Id = Relation_Game_Platforms.GameId AND (Relation_Game_Platforms.PlatformsId IN (SELECT DISTINCT PlatformId FROM Games_Roms WHERE Games_Roms.GameId = view_Games.Id)) LEFT JOIN Relation_Game_Genres ON view_Games.Id = Relation_Game_Genres.GameId LEFT JOIN Relation_Game_GameModes ON view_Games.Id = Relation_Game_GameModes.GameId LEFT JOIN Relation_Game_PlayerPerspectives ON view_Games.Id = Relation_Game_PlayerPerspectives.GameId LEFT JOIN Relation_Game_Themes ON view_Games.Id = Relation_Game_Themes.GameId " + whereClause + " " + havingClause + " " + orderByClause;
|
|
||||||
|
|
||||||
string sql = "SELECT DISTINCT Game.Id, Game.`Name`, Game.NameThe, Game.PlatformId, Game.TotalRating, Game.TotalRatingCount, Game.Cover, Game.Artworks, Game.FirstReleaseDate, Game.Category, Game.ParentGame, Game.AgeRatings, Game.AgeGroupId, Game.RomCount FROM (SELECT DISTINCT Game.*, CASE WHEN Game.`Name` LIKE 'The %' THEN CONCAT(TRIM(SUBSTR(Game.`Name` FROM 4)), ', The') ELSE Game.`Name` END AS NameThe, Games_Roms.PlatformId, AgeGroup.AgeGroupId, COUNT(Games_Roms.Id) AS RomCount FROM Game LEFT JOIN AgeGroup ON Game.Id = AgeGroup.GameId LEFT JOIN Games_Roms ON Game.Id = Games_Roms.GameId" + platformWhereClause + " LEFT JOIN AlternativeName ON Game.Id = AlternativeName.Game " + nameWhereClause + " GROUP BY Game.Id HAVING RomCount > 0) Game LEFT JOIN Relation_Game_Genres ON Game.Id = Relation_Game_Genres.GameId LEFT JOIN Relation_Game_GameModes ON Game.Id = Relation_Game_GameModes.GameId LEFT JOIN Relation_Game_PlayerPerspectives ON Game.Id = Relation_Game_PlayerPerspectives.GameId LEFT JOIN Relation_Game_Themes ON Game.Id = Relation_Game_Themes.GameId " + whereClause + " " + havingClause + " " + orderByClause;
|
string sql = @"
|
||||||
|
SELECT DISTINCT
|
||||||
List<IGDB.Models.Game> RetVal = new List<IGDB.Models.Game>();
|
Game.Id,
|
||||||
|
Game.`Name`,
|
||||||
|
Game.NameThe,
|
||||||
|
Game.PlatformId,
|
||||||
|
Game.TotalRating,
|
||||||
|
Game.TotalRatingCount,
|
||||||
|
Game.Cover,
|
||||||
|
Game.Artworks,
|
||||||
|
Game.FirstReleaseDate,
|
||||||
|
Game.Category,
|
||||||
|
Game.ParentGame,
|
||||||
|
Game.AgeRatings,
|
||||||
|
Game.AgeGroupId,
|
||||||
|
Game.RomCount,
|
||||||
|
RomSavedStates.RomSaveCount,
|
||||||
|
RomGroupSavedStates.MediaGroupSaveCount
|
||||||
|
FROM
|
||||||
|
(SELECT DISTINCT
|
||||||
|
Game.*,
|
||||||
|
CASE
|
||||||
|
WHEN Game.`Name` LIKE 'The %' THEN CONCAT(TRIM(SUBSTR(Game.`Name` FROM 4)), ', The')
|
||||||
|
ELSE Game.`Name`
|
||||||
|
END AS NameThe,
|
||||||
|
Games_Roms.PlatformId,
|
||||||
|
AgeGroup.AgeGroupId,
|
||||||
|
COUNT(Games_Roms.Id) AS RomCount
|
||||||
|
FROM
|
||||||
|
Game
|
||||||
|
LEFT JOIN AgeGroup ON Game.Id = AgeGroup.GameId
|
||||||
|
LEFT JOIN Games_Roms ON Game.Id = Games_Roms.GameId" + platformWhereClause + @"
|
||||||
|
LEFT JOIN AlternativeName ON Game.Id = AlternativeName.Game " + nameWhereClause + @"
|
||||||
|
GROUP BY Game.Id
|
||||||
|
HAVING RomCount > 0) Game
|
||||||
|
LEFT JOIN
|
||||||
|
(SELECT
|
||||||
|
Games_Roms.GameId, COUNT(GameState.Id) AS RomSaveCount
|
||||||
|
FROM
|
||||||
|
GameState
|
||||||
|
JOIN Games_Roms ON GameState.RomId = Games_Roms.Id
|
||||||
|
WHERE
|
||||||
|
GameState.IsMediaGroup = 0
|
||||||
|
AND GameState.UserId = @userid
|
||||||
|
GROUP BY Games_Roms.GameId) RomSavedStates ON Game.Id = RomSavedStates.GameId
|
||||||
|
LEFT JOIN
|
||||||
|
(SELECT
|
||||||
|
RomMediaGroup.GameId,
|
||||||
|
COUNT(RomMediaGroup.GameId) AS MediaGroupSaveCount
|
||||||
|
FROM
|
||||||
|
RomMediaGroup
|
||||||
|
JOIN GameState ON RomMediaGroup.Id = GameState.RomId
|
||||||
|
AND GameState.IsMediaGroup = 1
|
||||||
|
AND GameState.UserId = @userid
|
||||||
|
GROUP BY RomMediaGroup.GameId) RomGroupSavedStates ON Game.Id = RomGroupSavedStates.GameId
|
||||||
|
LEFT JOIN
|
||||||
|
Relation_Game_Genres ON Game.Id = Relation_Game_Genres.GameId
|
||||||
|
LEFT JOIN
|
||||||
|
Relation_Game_GameModes ON Game.Id = Relation_Game_GameModes.GameId
|
||||||
|
LEFT JOIN
|
||||||
|
Relation_Game_PlayerPerspectives ON Game.Id = Relation_Game_PlayerPerspectives.GameId
|
||||||
|
LEFT JOIN
|
||||||
|
Relation_Game_Themes ON Game.Id = Relation_Game_Themes.GameId " + whereClause + " " + havingClause + " " + orderByClause;
|
||||||
|
List<Games.MinimalGameItem> RetVal = new List<Games.MinimalGameItem>();
|
||||||
|
|
||||||
DataTable dbResponse = db.ExecuteCMD(sql, whereParams);
|
DataTable dbResponse = db.ExecuteCMD(sql, whereParams);
|
||||||
|
|
||||||
@@ -465,10 +533,24 @@ namespace gaseous_server.Controllers.v1_1
|
|||||||
}
|
}
|
||||||
|
|
||||||
Game retGame = Storage.BuildCacheObject<Game>(new Game() , dbResponse.Rows[i]);
|
Game retGame = Storage.BuildCacheObject<Game>(new Game() , dbResponse.Rows[i]);
|
||||||
RetVal.Add(retGame);
|
Games.MinimalGameItem retMinGame = new Games.MinimalGameItem(retGame);
|
||||||
|
if (dbResponse.Rows[i]["RomSaveCount"] != DBNull.Value || dbResponse.Rows[i]["MediaGroupSaveCount"] != DBNull.Value)
|
||||||
|
{
|
||||||
|
retMinGame.HasSavedGame = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
retMinGame.HasSavedGame = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
RetVal.Add(retMinGame);
|
||||||
}
|
}
|
||||||
|
|
||||||
GameReturnPackage gameReturn = new GameReturnPackage(RecordCount, RetVal);
|
GameReturnPackage gameReturn = new GameReturnPackage
|
||||||
|
{
|
||||||
|
Count = RecordCount,
|
||||||
|
Games = RetVal
|
||||||
|
};
|
||||||
|
|
||||||
return gameReturn;
|
return gameReturn;
|
||||||
}
|
}
|
||||||
|
279
gaseous-server/Controllers/V1.1/StateManagerController.cs
Normal file
279
gaseous-server/Controllers/V1.1/StateManagerController.cs
Normal file
@@ -0,0 +1,279 @@
|
|||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using gaseous_server.Models;
|
||||||
|
using gaseous_server.Classes;
|
||||||
|
using Authentication;
|
||||||
|
using Microsoft.AspNetCore.Identity;
|
||||||
|
using System.Data;
|
||||||
|
|
||||||
|
namespace gaseous_server.Controllers.v1_1
|
||||||
|
{
|
||||||
|
[Route("api/v{version:apiVersion}/[controller]")]
|
||||||
|
[ApiVersion("1.0")]
|
||||||
|
[ApiVersion("1.1")]
|
||||||
|
[ApiController]
|
||||||
|
public class StateManagerController: ControllerBase
|
||||||
|
{
|
||||||
|
private readonly UserManager<ApplicationUser> _userManager;
|
||||||
|
private readonly SignInManager<ApplicationUser> _signInManager;
|
||||||
|
|
||||||
|
public StateManagerController(
|
||||||
|
UserManager<ApplicationUser> userManager,
|
||||||
|
SignInManager<ApplicationUser> signInManager
|
||||||
|
)
|
||||||
|
{
|
||||||
|
_userManager = userManager;
|
||||||
|
_signInManager = signInManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MapToApiVersion("1.0")]
|
||||||
|
[MapToApiVersion("1.1")]
|
||||||
|
[HttpPost]
|
||||||
|
[Authorize]
|
||||||
|
[ProducesResponseType(typeof(Models.GameStateItem), StatusCodes.Status200OK)]
|
||||||
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
|
[Route("{RomId}")]
|
||||||
|
public async Task<ActionResult> SaveStateAsync(long RomId, UploadStateModel uploadState, bool IsMediaGroup = false)
|
||||||
|
{
|
||||||
|
var user = await _userManager.GetUserAsync(User);
|
||||||
|
|
||||||
|
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||||
|
string sql = "INSERT INTO GameState (UserId, RomId, IsMediaGroup, StateDateTime, Name, Screenshot, State) VALUES (@userid, @romid, @ismediagroup, @statedatetime, @name, @screenshot, @state); SELECT LAST_INSERT_ID();";
|
||||||
|
Dictionary<string, object> dbDict = new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
{ "userid", user.Id },
|
||||||
|
{ "romid", RomId },
|
||||||
|
{ "ismediagroup", IsMediaGroup },
|
||||||
|
{ "statedatetime", DateTime.UtcNow },
|
||||||
|
{ "name", "" },
|
||||||
|
{ "screenshot", uploadState.ScreenshotByteArray },
|
||||||
|
{ "state", uploadState.StateByteArray }
|
||||||
|
};
|
||||||
|
DataTable data = db.ExecuteCMD(sql, dbDict);
|
||||||
|
|
||||||
|
return Ok(await GetStateAsync(RomId, (long)(ulong)data.Rows[0][0], IsMediaGroup));
|
||||||
|
}
|
||||||
|
|
||||||
|
[MapToApiVersion("1.0")]
|
||||||
|
[MapToApiVersion("1.1")]
|
||||||
|
[HttpGet]
|
||||||
|
[Authorize]
|
||||||
|
[ProducesResponseType(typeof(List<Models.GameStateItem>), StatusCodes.Status200OK)]
|
||||||
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
|
[Route("{RomId}")]
|
||||||
|
public async Task<ActionResult> GetAllStateAsync(long RomId, bool IsMediaGroup = false)
|
||||||
|
{
|
||||||
|
var user = await _userManager.GetUserAsync(User);
|
||||||
|
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||||
|
string sql = "SELECT Id, StateDateTime, `Name`, Screenshot FROM GameState WHERE RomId = @romid AND IsMediaGroup = @ismediagroup AND UserId = @userid ORDER BY StateDateTime DESC;";
|
||||||
|
Dictionary<string, object> dbDict = new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
{ "romid", RomId },
|
||||||
|
{ "userid", user.Id },
|
||||||
|
{ "ismediagroup", IsMediaGroup }
|
||||||
|
};
|
||||||
|
DataTable data = db.ExecuteCMD(sql, dbDict);
|
||||||
|
|
||||||
|
List<Models.GameStateItem> gameStates = new List<GameStateItem>();
|
||||||
|
foreach (DataRow row in data.Rows)
|
||||||
|
{
|
||||||
|
gameStates.Add(BuildGameStateItem(row));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(gameStates);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MapToApiVersion("1.0")]
|
||||||
|
[MapToApiVersion("1.1")]
|
||||||
|
[HttpGet]
|
||||||
|
[Authorize]
|
||||||
|
[ProducesResponseType(typeof(Models.GameStateItem), StatusCodes.Status200OK)]
|
||||||
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
|
[Route("{RomId}/{StateId}")]
|
||||||
|
public async Task<ActionResult> GetStateAsync(long RomId, long StateId, bool IsMediaGroup = false)
|
||||||
|
{
|
||||||
|
var user = await _userManager.GetUserAsync(User);
|
||||||
|
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||||
|
string sql = "SELECT Id, StateDateTime, `Name`, Screenshot FROM GameState WHERE Id = @id AND RomId = @romid AND IsMediaGroup = @ismediagroup AND UserId = @userid;";
|
||||||
|
Dictionary<string, object> dbDict = new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
{ "id", StateId },
|
||||||
|
{ "romid", RomId },
|
||||||
|
{ "userid", user.Id },
|
||||||
|
{ "ismediagroup", IsMediaGroup }
|
||||||
|
};
|
||||||
|
DataTable data = db.ExecuteCMD(sql, dbDict);
|
||||||
|
|
||||||
|
if (data.Rows.Count == 0)
|
||||||
|
{
|
||||||
|
// invalid match - return not found
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
GameStateItem stateItem = BuildGameStateItem(data.Rows[0]);
|
||||||
|
|
||||||
|
return Ok(stateItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[MapToApiVersion("1.0")]
|
||||||
|
[MapToApiVersion("1.1")]
|
||||||
|
[HttpDelete]
|
||||||
|
[Authorize]
|
||||||
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||||
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
|
[Route("{RomId}/{StateId}")]
|
||||||
|
public async Task<ActionResult> DeleteStateAsync(long RomId, long StateId, bool IsMediaGroup = false)
|
||||||
|
{
|
||||||
|
var user = await _userManager.GetUserAsync(User);
|
||||||
|
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||||
|
string sql = "DELETE FROM GameState WHERE Id = @id AND RomId = @romid AND IsMediaGroup = @ismediagroup AND UserId = @userid;";
|
||||||
|
Dictionary<string, object> dbDict = new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
{ "id", StateId },
|
||||||
|
{ "romid", RomId },
|
||||||
|
{ "userid", user.Id },
|
||||||
|
{ "ismediagroup", IsMediaGroup }
|
||||||
|
};
|
||||||
|
db.ExecuteNonQuery(sql, dbDict);
|
||||||
|
|
||||||
|
return Ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
[MapToApiVersion("1.0")]
|
||||||
|
[MapToApiVersion("1.1")]
|
||||||
|
[HttpPut]
|
||||||
|
[Authorize]
|
||||||
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||||
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
|
[Route("{RomId}/{StateId}")]
|
||||||
|
public async Task<ActionResult> EditStateAsync(long RomId, long StateId, GameStateItemUpdateModel model, bool IsMediaGroup = false)
|
||||||
|
{
|
||||||
|
var user = await _userManager.GetUserAsync(User);
|
||||||
|
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||||
|
string sql = "UPDATE GameState SET `Name` = @name WHERE Id = @id AND RomId = @romid AND IsMediaGroup = @ismediagroup AND UserId = @userid;";
|
||||||
|
Dictionary<string, object> dbDict = new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
{ "id", StateId },
|
||||||
|
{ "romid", RomId },
|
||||||
|
{ "userid", user.Id },
|
||||||
|
{ "ismediagroup", IsMediaGroup },
|
||||||
|
{ "name", model.Name }
|
||||||
|
};
|
||||||
|
db.ExecuteNonQuery(sql, dbDict);
|
||||||
|
|
||||||
|
return Ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
[MapToApiVersion("1.0")]
|
||||||
|
[MapToApiVersion("1.1")]
|
||||||
|
[HttpGet]
|
||||||
|
[Authorize]
|
||||||
|
[ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)]
|
||||||
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
|
[Route("{RomId}/{StateId}/Screenshot/")]
|
||||||
|
[Route("{RomId}/{StateId}/Screenshot/image.png")]
|
||||||
|
public async Task<ActionResult> GetStateScreenshotAsync(long RomId, long StateId, bool IsMediaGroup = false)
|
||||||
|
{
|
||||||
|
var user = await _userManager.GetUserAsync(User);
|
||||||
|
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||||
|
string sql = "SELECT Screenshot FROM GameState WHERE Id = @id AND RomId = @romid AND IsMediaGroup = @ismediagroup AND UserId = @userid;";
|
||||||
|
Dictionary<string, object> dbDict = new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
{ "id", StateId },
|
||||||
|
{ "romid", RomId },
|
||||||
|
{ "userid", user.Id },
|
||||||
|
{ "ismediagroup", IsMediaGroup }
|
||||||
|
};
|
||||||
|
DataTable data = db.ExecuteCMD(sql, dbDict);
|
||||||
|
|
||||||
|
if (data.Rows.Count == 0)
|
||||||
|
{
|
||||||
|
// invalid match - return not found
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
string filename = "image.jpg";
|
||||||
|
byte[] bytes = (byte[])data.Rows[0][0];
|
||||||
|
string contentType = "image/png";
|
||||||
|
|
||||||
|
var cd = new System.Net.Mime.ContentDisposition
|
||||||
|
{
|
||||||
|
FileName = filename,
|
||||||
|
Inline = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
Response.Headers.Add("Content-Disposition", cd.ToString());
|
||||||
|
Response.Headers.Add("Cache-Control", "public, max-age=604800");
|
||||||
|
|
||||||
|
return File(bytes, contentType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[MapToApiVersion("1.0")]
|
||||||
|
[MapToApiVersion("1.1")]
|
||||||
|
[HttpGet]
|
||||||
|
[Authorize]
|
||||||
|
[ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)]
|
||||||
|
[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)
|
||||||
|
{
|
||||||
|
var user = await _userManager.GetUserAsync(User);
|
||||||
|
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
|
||||||
|
string sql = "SELECT State FROM GameState WHERE Id = @id AND RomId = @romid AND IsMediaGroup = @ismediagroup AND UserId = @userid;";
|
||||||
|
Dictionary<string, object> dbDict = new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
{ "id", StateId },
|
||||||
|
{ "romid", RomId },
|
||||||
|
{ "userid", user.Id },
|
||||||
|
{ "ismediagroup", IsMediaGroup }
|
||||||
|
};
|
||||||
|
DataTable data = db.ExecuteCMD(sql, dbDict);
|
||||||
|
|
||||||
|
if (data.Rows.Count == 0)
|
||||||
|
{
|
||||||
|
// invalid match - return not found
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
string filename = "savestate.state";
|
||||||
|
byte[] bytes = (byte[])data.Rows[0][0];
|
||||||
|
string contentType = "application/octet-stream";
|
||||||
|
|
||||||
|
var cd = new System.Net.Mime.ContentDisposition
|
||||||
|
{
|
||||||
|
FileName = filename,
|
||||||
|
Inline = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
Response.Headers.Add("Content-Disposition", cd.ToString());
|
||||||
|
Response.Headers.Add("Cache-Control", "public, max-age=604800");
|
||||||
|
|
||||||
|
return File(bytes, contentType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Models.GameStateItem BuildGameStateItem(DataRow dr)
|
||||||
|
{
|
||||||
|
bool HasScreenshot = true;
|
||||||
|
if (dr["Screenshot"] == DBNull.Value)
|
||||||
|
{
|
||||||
|
HasScreenshot = false;
|
||||||
|
}
|
||||||
|
GameStateItem stateItem = new GameStateItem
|
||||||
|
{
|
||||||
|
Id = (long)dr["Id"],
|
||||||
|
Name = (string)dr["Name"],
|
||||||
|
SaveTime = DateTime.Parse(((DateTime)dr["StateDateTime"]).ToString("yyyy-MM-ddThh:mm:ss") + 'Z'),
|
||||||
|
HasScreenshot = HasScreenshot
|
||||||
|
};
|
||||||
|
|
||||||
|
return stateItem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
35
gaseous-server/Models/GameState.cs
Normal file
35
gaseous-server/Models/GameState.cs
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
namespace gaseous_server.Models
|
||||||
|
{
|
||||||
|
public class UploadStateModel
|
||||||
|
{
|
||||||
|
public string ScreenshotByteArrayBase64 { get; set; }
|
||||||
|
public string StateByteArrayBase64 { get; set; }
|
||||||
|
public byte[] ScreenshotByteArray
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return Convert.FromBase64String(ScreenshotByteArrayBase64);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public byte[] StateByteArray
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return Convert.FromBase64String(StateByteArrayBase64);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class GameStateItem
|
||||||
|
{
|
||||||
|
public long Id { get; set; }
|
||||||
|
public string Name { get; set; } = "";
|
||||||
|
public DateTime SaveTime { get; set; }
|
||||||
|
public bool HasScreenshot { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class GameStateItemUpdateModel
|
||||||
|
{
|
||||||
|
public string Name { get; set; } = "";
|
||||||
|
}
|
||||||
|
}
|
@@ -29,6 +29,8 @@ namespace gaseous_server.Models
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool HasSavedGame { get; set; } = false;
|
||||||
|
|
||||||
public IGDB.Models.Cover? CoverItem
|
public IGDB.Models.Cover? CoverItem
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
@@ -46,49 +48,5 @@ namespace gaseous_server.Models
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// public List<IGDB.Models.Artwork>? ArtworksItem
|
|
||||||
// {
|
|
||||||
// get
|
|
||||||
// {
|
|
||||||
// if (this.Artworks != null)
|
|
||||||
// {
|
|
||||||
// if (this.Artworks.Ids != null)
|
|
||||||
// {
|
|
||||||
// List<IGDB.Models.Artwork> artworks = new List<IGDB.Models.Artwork>();
|
|
||||||
// foreach (long id in this.Artworks.Ids)
|
|
||||||
// {
|
|
||||||
// artworks.Add(gaseous_server.Classes.Metadata.Artworks.GetArtwork(id, Config.LibraryConfiguration.LibraryMetadataDirectory_Game(this), false));
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return artworks;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return null;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// public List<IGDB.Models.Screenshot>? ScreenshotsItem
|
|
||||||
// {
|
|
||||||
// get
|
|
||||||
// {
|
|
||||||
// if (this.Screenshots != null)
|
|
||||||
// {
|
|
||||||
// if (this.Screenshots.Ids != null)
|
|
||||||
// {
|
|
||||||
// List<IGDB.Models.Screenshot> screenshots = new List<IGDB.Models.Screenshot>();
|
|
||||||
// foreach (long id in this.Screenshots.Ids)
|
|
||||||
// {
|
|
||||||
// screenshots.Add(gaseous_server.Classes.Metadata.Screenshots.GetScreenshot(id, Config.LibraryConfiguration.LibraryMetadataDirectory_Game(this), false));
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return screenshots;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return null;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
11
gaseous-server/Support/Database/MySQL/gaseous-1015.sql
Normal file
11
gaseous-server/Support/Database/MySQL/gaseous-1015.sql
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
CREATE TABLE `GameState` (
|
||||||
|
`Id` BIGINT NOT NULL AUTO_INCREMENT,
|
||||||
|
`UserId` VARCHAR(45) NULL,
|
||||||
|
`RomId` BIGINT NULL,
|
||||||
|
`IsMediaGroup` INT NULL,
|
||||||
|
`StateDateTime` DATETIME NULL,
|
||||||
|
`Name` VARCHAR(100) NULL,
|
||||||
|
`Screenshot` LONGBLOB NULL,
|
||||||
|
`State` LONGBLOB NULL,
|
||||||
|
PRIMARY KEY (`Id`),
|
||||||
|
INDEX `idx_UserId` (`UserId` ASC) VISIBLE);
|
@@ -59,6 +59,7 @@
|
|||||||
<None Remove="Support\Database\MySQL\gaseous-1012.sql" />
|
<None Remove="Support\Database\MySQL\gaseous-1012.sql" />
|
||||||
<None Remove="Support\Database\MySQL\gaseous-1013.sql" />
|
<None Remove="Support\Database\MySQL\gaseous-1013.sql" />
|
||||||
<None Remove="Support\Database\MySQL\gaseous-1014.sql" />
|
<None Remove="Support\Database\MySQL\gaseous-1014.sql" />
|
||||||
|
<None Remove="Support\Database\MySQL\gaseous-1015.sql" />
|
||||||
<None Remove="Classes\Metadata\" />
|
<None Remove="Classes\Metadata\" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@@ -95,5 +96,6 @@
|
|||||||
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1012.sql" />
|
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1012.sql" />
|
||||||
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1013.sql" />
|
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1013.sql" />
|
||||||
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1014.sql" />
|
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1014.sql" />
|
||||||
|
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1015.sql" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
BIN
gaseous-server/wwwroot/.DS_Store
vendored
BIN
gaseous-server/wwwroot/.DS_Store
vendored
Binary file not shown.
Submodule gaseous-server/wwwroot/emulators/EmulatorJS deleted from c1a9d9b266
73
gaseous-server/wwwroot/emulators/EmulatorJS.html
Normal file
73
gaseous-server/wwwroot/emulators/EmulatorJS.html
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
<!-- <div style='width:640px;height:480px;max-width:100%'> -->
|
||||||
|
<div style='width:100%;height:100%;'>
|
||||||
|
<div id='game'></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script type='text/javascript'>
|
||||||
|
EJS_player = '#game';
|
||||||
|
|
||||||
|
// Can also be fceumm or nestopia
|
||||||
|
EJS_core = getQueryString('core', 'string');
|
||||||
|
|
||||||
|
// Lightgun
|
||||||
|
EJS_lightgun = false; // can be true or false
|
||||||
|
|
||||||
|
// URL to BIOS file
|
||||||
|
EJS_biosUrl = emuBios;
|
||||||
|
|
||||||
|
// URL to Game rom
|
||||||
|
EJS_gameUrl = decodeURIComponent(getQueryString('rompath', 'string'));
|
||||||
|
|
||||||
|
// load state if defined
|
||||||
|
if (StateUrl) {
|
||||||
|
console.log('Loading saved state from: ' + StateUrl);
|
||||||
|
EJS_loadStateURL = StateUrl;
|
||||||
|
EJS_startOnLoaded = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Path to the data directory
|
||||||
|
EJS_pathtodata = '/emulators/EmulatorJS/data/';
|
||||||
|
|
||||||
|
EJS_DEBUG_XX = false;
|
||||||
|
|
||||||
|
EJS_backgroundImage = emuBackground;
|
||||||
|
EJS_backgroundBlur = true;
|
||||||
|
|
||||||
|
EJS_fullscreenOnLoaded = false;
|
||||||
|
|
||||||
|
EJS_gameName = emuGameTitle;
|
||||||
|
|
||||||
|
EJS_threads = false;
|
||||||
|
|
||||||
|
EJS_onSaveState = function(e) {
|
||||||
|
var returnValue = {
|
||||||
|
"ScreenshotByteArrayBase64": btoa(Uint8ToString(e.screenshot)),
|
||||||
|
"StateByteArrayBase64": btoa(Uint8ToString(e.state))
|
||||||
|
};
|
||||||
|
|
||||||
|
var url = '/api/v1.1/StateManager/' + romId + '?IsMediaGroup=' + IsMediaGroup;
|
||||||
|
|
||||||
|
ajaxCall(
|
||||||
|
url,
|
||||||
|
'POST',
|
||||||
|
function (result) {
|
||||||
|
console.log("Upload complete");
|
||||||
|
console.log(result);
|
||||||
|
|
||||||
|
displayNotification('State Saved', 'Game state has been saved.', '/api/v1.1/StateManager/' + romId + '/' + result.value.id + '/Screenshot/image.png?IsMediaGroup=' + IsMediaGroup);
|
||||||
|
},
|
||||||
|
function (error) {
|
||||||
|
console.log("An error occurred");
|
||||||
|
console.log(error);
|
||||||
|
},
|
||||||
|
JSON.stringify(returnValue)
|
||||||
|
);
|
||||||
|
|
||||||
|
returnValue = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
EJS_onLoadState = function(e) {
|
||||||
|
showDialog('emulatorloadstate', { "romId": romId, "IsMediaGroup": IsMediaGroup });
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<script src='/emulators/EmulatorJS/data/loader.js'></script>
|
BIN
gaseous-server/wwwroot/images/SaveStates.png
Normal file
BIN
gaseous-server/wwwroot/images/SaveStates.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.2 KiB |
@@ -4,6 +4,7 @@
|
|||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<script src="/api/v1.1/System/VersionFile"></script>
|
<script src="/api/v1.1/System/VersionFile"></script>
|
||||||
<link type="text/css" rel="stylesheet" href="/styles/style.css" dat-href="/styles/style.css" />
|
<link type="text/css" rel="stylesheet" href="/styles/style.css" dat-href="/styles/style.css" />
|
||||||
|
<link type="text/css" rel="stylesheet" href="/styles/notifications.css" dat-href="/styles/notifications.css" />
|
||||||
<script src="/scripts/jquery-3.6.0.min.js"></script>
|
<script src="/scripts/jquery-3.6.0.min.js"></script>
|
||||||
<script src="/scripts/moment-with-locales.min.js"></script>
|
<script src="/scripts/moment-with-locales.min.js"></script>
|
||||||
<link href="/styles/select2.min.css" rel="stylesheet" />
|
<link href="/styles/select2.min.css" rel="stylesheet" />
|
||||||
@@ -14,6 +15,7 @@
|
|||||||
<script src="/scripts/dropzone.min.js"></script>
|
<script src="/scripts/dropzone.min.js"></script>
|
||||||
<script src="/scripts/simpleUpload.min.js"></script>
|
<script src="/scripts/simpleUpload.min.js"></script>
|
||||||
<script src="/scripts/main.js" type="text/javascript"></script>
|
<script src="/scripts/main.js" type="text/javascript"></script>
|
||||||
|
<script src="/scripts/notifications.js" type="text/javascript"></script>
|
||||||
<script src="/scripts/filterformating.js" type="text/javascript"></script>
|
<script src="/scripts/filterformating.js" type="text/javascript"></script>
|
||||||
<script src="/scripts/gamesformating.js" type="text/javascript"></script>
|
<script src="/scripts/gamesformating.js" type="text/javascript"></script>
|
||||||
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
|
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
|
||||||
@@ -43,6 +45,9 @@
|
|||||||
</script>
|
</script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
<!-- Notifications -->
|
||||||
|
<div id="notifications_target"></div>
|
||||||
|
|
||||||
<div id="banner_icon" onclick="window.location.href = '/index.html';">
|
<div id="banner_icon" onclick="window.location.href = '/index.html';">
|
||||||
<img src="/images/logo.png" alt="Gaseous" id="banner_icon_image" />
|
<img src="/images/logo.png" alt="Gaseous" id="banner_icon_image" />
|
||||||
</div>
|
</div>
|
||||||
|
@@ -1,35 +0,0 @@
|
|||||||
<!-- <div style='width:640px;height:480px;max-width:100%'> -->
|
|
||||||
<div style='width:100%;height:100%;'>
|
|
||||||
<div id='game'></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script type='text/javascript'>
|
|
||||||
EJS_player = '#game';
|
|
||||||
|
|
||||||
// Can also be fceumm or nestopia
|
|
||||||
EJS_core = getQueryString('core', 'string');
|
|
||||||
|
|
||||||
// Lightgun
|
|
||||||
EJS_lightgun = false; // can be true or false
|
|
||||||
|
|
||||||
// URL to BIOS file
|
|
||||||
EJS_biosUrl = emuBios;
|
|
||||||
|
|
||||||
// URL to Game rom
|
|
||||||
EJS_gameUrl = decodeURIComponent(getQueryString('rompath', 'string'));
|
|
||||||
|
|
||||||
// Path to the data directory
|
|
||||||
EJS_pathtodata = '/emulators/EmulatorJS/data/';
|
|
||||||
|
|
||||||
EJS_DEBUG_XX = false;
|
|
||||||
|
|
||||||
EJS_startOnLoaded = false;
|
|
||||||
|
|
||||||
EJS_backgroundImage = emuBackground;
|
|
||||||
EJS_backgroundBlur = true;
|
|
||||||
|
|
||||||
EJS_gameName = emuGameTitle;
|
|
||||||
|
|
||||||
EJS_threads = false;
|
|
||||||
</script>
|
|
||||||
<script src='/emulators/EmulatorJS/data/loader.js'></script>
|
|
159
gaseous-server/wwwroot/pages/dialogs/emulatorloadstate.html
Normal file
159
gaseous-server/wwwroot/pages/dialogs/emulatorloadstate.html
Normal file
@@ -0,0 +1,159 @@
|
|||||||
|
<div id="saved_states">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script text="text/javascript">
|
||||||
|
document.getElementById('modal-heading').innerHTML = "Load saved state";
|
||||||
|
|
||||||
|
console.log(modalVariables);
|
||||||
|
|
||||||
|
var statesUrl = '/api/v1.1/StateManager/' + modalVariables.romId + '?IsMediaGroup=' + modalVariables.IsMediaGroup;
|
||||||
|
console.log(statesUrl);
|
||||||
|
function LoadStates() {
|
||||||
|
ajaxCall(
|
||||||
|
statesUrl,
|
||||||
|
'GET',
|
||||||
|
function(result) {
|
||||||
|
var statesBox = document.getElementById('saved_states');
|
||||||
|
statesBox.innerHTML = '';
|
||||||
|
|
||||||
|
for (var i = 0; i < result.length; i++) {
|
||||||
|
var stateBox = document.createElement('div');
|
||||||
|
stateBox.id = 'stateBox_' + result[i].id;
|
||||||
|
stateBox.className = 'saved_state_box';
|
||||||
|
|
||||||
|
// screenshot panel
|
||||||
|
var stateImageBox = document.createElement('div');
|
||||||
|
stateImageBox.id = 'stateImageBox_' + result[i].id;
|
||||||
|
stateImageBox.className = 'saved_state_image_box';
|
||||||
|
|
||||||
|
if (result[i].hasScreenshot == true) {
|
||||||
|
var stateImage = document.createElement('img');
|
||||||
|
stateImage.className = 'saved_state_image_image';
|
||||||
|
stateImage.src = '/api/v1.1/StateManager/' + modalVariables.romId + '/' + result[i].id + '/Screenshot/image.png?IsMediaGroup=' + modalVariables.IsMediaGroup;
|
||||||
|
stateImageBox.appendChild(stateImage);
|
||||||
|
}
|
||||||
|
stateBox.appendChild(stateImageBox);
|
||||||
|
|
||||||
|
// main panel
|
||||||
|
var stateMainPanel = document.createElement('div');
|
||||||
|
stateMainPanel.id = 'stateMainPanel_' + result[i].id;
|
||||||
|
stateMainPanel.className = 'saved_state_main_box';
|
||||||
|
|
||||||
|
var stateName = document.createElement('input');
|
||||||
|
stateName.id = 'stateName_' + result[i].id;
|
||||||
|
stateName.type = 'text';
|
||||||
|
stateName.className = 'saved_state_name';
|
||||||
|
stateName.setAttribute('onblur', 'UpdateStateSave(' + result[i].id + ', ' + modalVariables.IsMediaGroup + ');');
|
||||||
|
if (result[i].name) {
|
||||||
|
stateName.value = result[i].name;
|
||||||
|
} else {
|
||||||
|
stateName.setAttribute('placeholder', "Untitled");
|
||||||
|
}
|
||||||
|
stateMainPanel.appendChild(stateName);
|
||||||
|
|
||||||
|
var stateTime = document.createElement('div');
|
||||||
|
stateTime.id = 'stateTime_' + result[i].id;
|
||||||
|
stateTime.className = 'saved_state_date';
|
||||||
|
stateTime.innerHTML = moment(result[i].saveTime).format("YYYY-MM-DD h:mm:ss a");
|
||||||
|
stateMainPanel.appendChild(stateTime);
|
||||||
|
|
||||||
|
var stateControls = document.createElement('div');
|
||||||
|
stateControls.id = 'stateControls_' + result[i].id;
|
||||||
|
stateControls.className = 'saved_state_controls';
|
||||||
|
|
||||||
|
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;
|
||||||
|
switch (getQueryString('page', 'string')) {
|
||||||
|
case 'emulator':
|
||||||
|
var mediagroupint = 0;
|
||||||
|
if (modalVariables.IsMediaGroup == true) {
|
||||||
|
mediagroupint = 1;
|
||||||
|
}
|
||||||
|
emulatorTarget = emulatorTarget.replaceAll('@engine', getQueryString('engine', 'string'));
|
||||||
|
emulatorTarget = emulatorTarget.replaceAll('@core', getQueryString('core', 'string'));
|
||||||
|
emulatorTarget = emulatorTarget.replaceAll('@platformid', getQueryString('platformid', 'string'));
|
||||||
|
emulatorTarget = emulatorTarget.replaceAll('@gameid', getQueryString('gameid', 'string'));
|
||||||
|
emulatorTarget = emulatorTarget.replaceAll('@romid', getQueryString('romid', 'string'));
|
||||||
|
emulatorTarget = emulatorTarget.replaceAll('@mediagroup', mediagroupint);
|
||||||
|
emulatorTarget = emulatorTarget.replaceAll('@rompath', getQueryString('rompath', 'string'));
|
||||||
|
stateControlsLaunch.setAttribute("onclick", 'window.location.replace("' + emulatorTarget + '")');
|
||||||
|
break;
|
||||||
|
case 'game':
|
||||||
|
console.log(modalVariables);
|
||||||
|
emulatorTarget = emulatorTarget.replaceAll('@engine', modalVariables.engine);
|
||||||
|
emulatorTarget = emulatorTarget.replaceAll('@core', modalVariables.core);
|
||||||
|
emulatorTarget = emulatorTarget.replaceAll('@platformid', modalVariables.platformid);
|
||||||
|
emulatorTarget = emulatorTarget.replaceAll('@gameid', modalVariables.gameid);
|
||||||
|
emulatorTarget = emulatorTarget.replaceAll('@romid', modalVariables.romId);
|
||||||
|
emulatorTarget = emulatorTarget.replaceAll('@mediagroup', modalVariables.mediagroup);
|
||||||
|
emulatorTarget = emulatorTarget.replaceAll('@rompath', modalVariables.rompath);
|
||||||
|
stateControlsLaunch.setAttribute("onclick", 'window.location.href = "' + emulatorTarget + '"');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
stateControlsLaunch.innerHTML = 'Launch';
|
||||||
|
stateControlsLaunch.style.float = 'right';
|
||||||
|
stateControls.appendChild(stateControlsLaunch);
|
||||||
|
|
||||||
|
var stateControlsDownload = document.createElement('a');
|
||||||
|
stateControlsDownload.id = 'stateControlsDownload_' + result[i].id;
|
||||||
|
stateControlsDownload.className = 'saved_state_buttonlink';
|
||||||
|
stateControlsDownload.href = '/api/v1.1/StateManager/' + modalVariables.romId + '/' + result[i].id + '/State/savestate.state?IsMediaGroup=' + modalVariables.IsMediaGroup;
|
||||||
|
stateControlsDownload.innerHTML = '<img src="/images/download.svg" class="banner_button_image" alt="Download" title="Download" />';
|
||||||
|
stateControls.appendChild(stateControlsDownload);
|
||||||
|
|
||||||
|
var stateControlsDelete = document.createElement('span');
|
||||||
|
stateControlsDelete.id = 'stateControlsDelete_' + result[i].id;
|
||||||
|
stateControlsDelete.className = 'saved_state_buttonlink';
|
||||||
|
stateControlsDelete.setAttribute('onclick', 'DeleteStateSave(' + result[i].id + ', ' + modalVariables.IsMediaGroup + ');');
|
||||||
|
stateControlsDelete.innerHTML = '<img src="/images/delete.svg" class="banner_button_image" alt="Delete" title="Delete" />';
|
||||||
|
stateControls.appendChild(stateControlsDelete);
|
||||||
|
|
||||||
|
stateMainPanel.appendChild(stateControls);
|
||||||
|
|
||||||
|
stateBox.appendChild(stateMainPanel);
|
||||||
|
|
||||||
|
statesBox.appendChild(stateBox);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
LoadStates();
|
||||||
|
|
||||||
|
function DeleteStateSave(StateId, IsMediaGroup) {
|
||||||
|
ajaxCall(
|
||||||
|
'/api/v1.1/StateManager/' + modalVariables.romId + '/' + StateId + '?IsMediaGroup=' + IsMediaGroup,
|
||||||
|
'DELETE',
|
||||||
|
function(success) {
|
||||||
|
LoadStates();
|
||||||
|
},
|
||||||
|
function (error) {
|
||||||
|
LoadStates();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function UpdateStateSave(StateId, IsMediaGroup) {
|
||||||
|
var stateName = document.getElementById('stateName_' + StateId);
|
||||||
|
|
||||||
|
var model = {
|
||||||
|
"name": stateName.value
|
||||||
|
};
|
||||||
|
|
||||||
|
ajaxCall(
|
||||||
|
'/api/v1.1/StateManager/' + modalVariables.romId + '/' + StateId + '?IsMediaGroup=' + IsMediaGroup,
|
||||||
|
'PUT',
|
||||||
|
function(success) {
|
||||||
|
LoadStates();
|
||||||
|
},
|
||||||
|
function (error) {
|
||||||
|
LoadStates();
|
||||||
|
},
|
||||||
|
JSON.stringify(model)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
</script>
|
@@ -1,4 +1,4 @@
|
|||||||
<p>Are you sure you want to delete this media group?</p>
|
<p>Are you sure you want to delete this media group and all associated saved states?</p>
|
||||||
<p><strong>Warning:</strong> This cannot be undone!</p>
|
<p><strong>Warning:</strong> This cannot be undone!</p>
|
||||||
<div style="width: 100%; text-align: center;">
|
<div style="width: 100%; text-align: center;">
|
||||||
<div style="display: inline-block; margin-right: 20px;">
|
<div style="display: inline-block; margin-right: 20px;">
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
<p>Are you sure you want to delete this ROM?</p>
|
<p>Are you sure you want to delete this ROM and all associated saved states?</p>
|
||||||
<p><strong>Warning:</strong> This cannot be undone!</p>
|
<p><strong>Warning:</strong> This cannot be undone!</p>
|
||||||
<div style="width: 100%; text-align: center;">
|
<div style="width: 100%; text-align: center;">
|
||||||
<div style="display: inline-block; margin-right: 20px;">
|
<div style="display: inline-block; margin-right: 20px;">
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
<p>Are you sure you want to delete the selected ROMs?</p>
|
<p>Are you sure you want to delete the selected ROMs and any associated saved states?</p>
|
||||||
<p><strong>Warning:</strong> This cannot be undone!</p>
|
<p><strong>Warning:</strong> This cannot be undone!</p>
|
||||||
<div style="width: 100%; text-align: center;">
|
<div style="width: 100%; text-align: center;">
|
||||||
<div style="display: inline-block; margin-right: 20px;">
|
<div style="display: inline-block; margin-right: 20px;">
|
||||||
|
@@ -6,7 +6,15 @@
|
|||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
var gameId = getQueryString('gameid', 'int');
|
var gameId = getQueryString('gameid', 'int');
|
||||||
|
var romId = getQueryString('romid', 'int');
|
||||||
var platformId = getQueryString('platformid', 'int');
|
var platformId = getQueryString('platformid', 'int');
|
||||||
|
var IsMediaGroupInt = getQueryString('mediagroup', 'int');
|
||||||
|
var IsMediaGroup = false;
|
||||||
|
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;
|
||||||
|
}
|
||||||
var gameData;
|
var gameData;
|
||||||
var artworks = null;
|
var artworks = null;
|
||||||
var artworksPosition = 0;
|
var artworksPosition = 0;
|
||||||
@@ -47,7 +55,7 @@
|
|||||||
|
|
||||||
switch (getQueryString('engine', 'string')) {
|
switch (getQueryString('engine', 'string')) {
|
||||||
case 'EmulatorJS':
|
case 'EmulatorJS':
|
||||||
$('#emulator').load('/pages/EmulatorJS.html?v=' + AppVersion);
|
$('#emulator').load('/emulators/EmulatorJS.html?v=' + AppVersion);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@@ -97,7 +97,7 @@
|
|||||||
<button value="Search" onclick="loadRoms();">Search</button>
|
<button value="Search" onclick="loadRoms();">Search</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="games_library_controlblock">
|
<div class="games_library_controlblock">
|
||||||
<span class="games_library_label">0 ROMs</span>
|
<span id="games_roms_count" class="games_library_label">0 ROMs</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="gamesummaryromscontent"></div>
|
<div id="gamesummaryromscontent"></div>
|
||||||
@@ -476,7 +476,7 @@
|
|||||||
mgTable.id = 'mediagrouptable';
|
mgTable.id = 'mediagrouptable';
|
||||||
mgTable.className = 'romtable';
|
mgTable.className = 'romtable';
|
||||||
mgTable.setAttribute('cellspacing', 0);
|
mgTable.setAttribute('cellspacing', 0);
|
||||||
mgTable.appendChild(createTableRow(true, ['Platform', 'Images', 'Size', '', '', '']));
|
mgTable.appendChild(createTableRow(true, ['Platform', 'Images', 'Size', '', '', '', '']));
|
||||||
|
|
||||||
lastPlatform = '';
|
lastPlatform = '';
|
||||||
for (var i = 0; i < result.length; i++) {
|
for (var i = 0; i < result.length; i++) {
|
||||||
@@ -484,9 +484,28 @@
|
|||||||
|
|
||||||
// get rom details including emulator and friendly platform name
|
// get rom details including emulator and friendly platform name
|
||||||
var launchButton = '';
|
var launchButton = '';
|
||||||
|
var saveStatesButton = '';
|
||||||
if (mediaGroup.emulator) {
|
if (mediaGroup.emulator) {
|
||||||
if (mediaGroup.emulator.type.length > 0) {
|
if (mediaGroup.emulator.type.length > 0) {
|
||||||
launchButton = '<a href="/index.html?page=emulator&engine=' + mediaGroup.emulator.type + '&core=' + mediaGroup.emulator.core + '&platformid=' + mediaGroup.platformId + '&gameid=' + gameId + '&rompath=' + encodeURIComponent('/api/v1.1/Games/' + gameId + '/romgroup/' + mediaGroup.id + '/' + gameData.name + '(' + mediaGroup.id + ')' + '.zip') + '" class="romstart">Launch</a>';
|
var romPath = encodeURIComponent('/api/v1.1/Games/' + gameId + '/romgroup/' + mediaGroup.id + '/' + gameData.name + '(' + mediaGroup.id + ')' + '.zip');
|
||||||
|
|
||||||
|
if (mediaGroup.hasSaveStates == true) {
|
||||||
|
var modalVariables = {
|
||||||
|
"romId": mediaGroup.id,
|
||||||
|
"IsMediaGroup": true,
|
||||||
|
"engine": mediaGroup.emulator.type,
|
||||||
|
"core": mediaGroup.emulator.core,
|
||||||
|
"platformid": mediaGroup.platformId,
|
||||||
|
"gameid": gameId,
|
||||||
|
"mediagroup": 1,
|
||||||
|
"rompath": romPath
|
||||||
|
};
|
||||||
|
saveStatesButton = document.createElement('div');
|
||||||
|
saveStatesButton.setAttribute('onclick', 'showDialog("emulatorloadstate", ' + JSON.stringify(modalVariables) + ');');
|
||||||
|
saveStatesButton.innerHTML = '<img src="/images/SaveStates.png" class="savedstateicon" />';
|
||||||
|
}
|
||||||
|
|
||||||
|
launchButton = '<a href="/index.html?page=emulator&engine=' + mediaGroup.emulator.type + '&core=' + mediaGroup.emulator.core + '&platformid=' + mediaGroup.platformId + '&gameid=' + gameId + '&romid=' + mediaGroup.id + '&mediagroup=1&rompath=' + romPath + '" class="romstart">Launch</a>';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -532,15 +551,19 @@
|
|||||||
mediaGroup.romIds.length,
|
mediaGroup.romIds.length,
|
||||||
packageSize,
|
packageSize,
|
||||||
statusText,
|
statusText,
|
||||||
|
saveStatesButton,
|
||||||
launchButtonContent,
|
launchButtonContent,
|
||||||
'<div style="text-align: right;">' + downloadLink + deleteButton + '</div>'
|
'<div style="text-align: right;">' + downloadLink + deleteButton + '</div>'
|
||||||
]
|
]
|
||||||
|
|
||||||
mgTable.appendChild(createTableRow(false, newRow, 'romrow', 'romcell'));
|
var mgRowBody = document.createElement('tbody');
|
||||||
|
mgRowBody.className = 'romrow';
|
||||||
|
|
||||||
|
mgRowBody.appendChild(createTableRow(false, newRow, '', 'romcell'));
|
||||||
|
|
||||||
var mgRomRow = document.createElement('tr');
|
var mgRomRow = document.createElement('tr');
|
||||||
var mgRomCell = document.createElement('td');
|
var mgRomCell = document.createElement('td');
|
||||||
mgRomCell.setAttribute('colspan', 6);
|
mgRomCell.setAttribute('colspan', 7);
|
||||||
mgRomCell.className = 'romGroupTitles';
|
mgRomCell.className = 'romGroupTitles';
|
||||||
|
|
||||||
|
|
||||||
@@ -557,7 +580,9 @@
|
|||||||
}
|
}
|
||||||
mgRomCell.innerHTML = groupMemberNames.join("<br />");
|
mgRomCell.innerHTML = groupMemberNames.join("<br />");
|
||||||
mgRomRow.appendChild(mgRomCell);
|
mgRomRow.appendChild(mgRomCell);
|
||||||
mgTable.appendChild(mgRomRow);
|
mgRowBody.appendChild(mgRomRow);
|
||||||
|
|
||||||
|
mgTable.appendChild(mgRowBody);
|
||||||
}
|
}
|
||||||
|
|
||||||
mediaGroupDiv.appendChild(mgTable);
|
mediaGroupDiv.appendChild(mgTable);
|
||||||
@@ -600,6 +625,13 @@
|
|||||||
var gameRoms = document.getElementById('gamesummaryromscontent');
|
var gameRoms = document.getElementById('gamesummaryromscontent');
|
||||||
var pageSize = 20;
|
var pageSize = 20;
|
||||||
ajaxCall('/api/v1.1/Games/' + gameId + '/roms?pageNumber=' + pageNumber + '&pageSize=' + pageSize + '&platformId=' + selectedPlatform + nameSearchQuery, 'GET', function (result) {
|
ajaxCall('/api/v1.1/Games/' + gameId + '/roms?pageNumber=' + pageNumber + '&pageSize=' + pageSize + '&platformId=' + selectedPlatform + nameSearchQuery, 'GET', function (result) {
|
||||||
|
var romCount = document.getElementById('games_roms_count');
|
||||||
|
if (result.count != 1) {
|
||||||
|
romCount.innerHTML = result.count + ' ROMs';
|
||||||
|
} else {
|
||||||
|
romCount.innerHTML = result.count + ' ROM';
|
||||||
|
}
|
||||||
|
|
||||||
if (result.gameRomItems) {
|
if (result.gameRomItems) {
|
||||||
var gameRomItems = result.gameRomItems;
|
var gameRomItems = result.gameRomItems;
|
||||||
|
|
||||||
@@ -608,7 +640,7 @@
|
|||||||
newTable.id = 'romtable';
|
newTable.id = 'romtable';
|
||||||
newTable.className = 'romtable';
|
newTable.className = 'romtable';
|
||||||
newTable.setAttribute('cellspacing', 0);
|
newTable.setAttribute('cellspacing', 0);
|
||||||
newTable.appendChild(createTableRow(true, [['<input id="rom_mastercheck" type="checkbox" onclick="selectAllChecks(); handleChecks();"/>', 'rom_checkbox_box_hidden', 'rom_edit_checkbox'], 'Name', 'Size', 'Media', '', '', '']));
|
newTable.appendChild(createTableRow(true, [['<input id="rom_mastercheck" type="checkbox" onclick="selectAllChecks(); handleChecks();"/>', 'rom_checkbox_box_hidden', 'rom_edit_checkbox'], 'Name', 'Size', 'Media', '', '', '', '']));
|
||||||
|
|
||||||
var lastPlatform = '';
|
var lastPlatform = '';
|
||||||
for (var i = 0; i < gameRomItems.length; i++) {
|
for (var i = 0; i < gameRomItems.length; i++) {
|
||||||
@@ -622,11 +654,28 @@
|
|||||||
newTable.appendChild(platformRow);
|
newTable.appendChild(platformRow);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var saveStatesButton = '';
|
||||||
var launchButton = '';
|
var launchButton = '';
|
||||||
if (result.gameRomItems[i].emulator) {
|
if (result.gameRomItems[i].emulator) {
|
||||||
if (gameRomItems[i].emulator.type) {
|
if (gameRomItems[i].emulator.type) {
|
||||||
if (gameRomItems[i].emulator.type.length > 0) {
|
if (gameRomItems[i].emulator.type.length > 0) {
|
||||||
launchButton = '<a href="/index.html?page=emulator&engine=' + gameRomItems[i].emulator.type + '&core=' + gameRomItems[i].emulator.core + '&platformid=' + gameRomItems[i].platformId + '&gameid=' + gameId + '&rompath=' + encodeURIComponent('/api/v1.1/Games/' + gameId + '/roms/' + gameRomItems[i].id + '/' + encodeURIComponent(gameRomItems[i].name)) + '" class="romstart">Launch</a>';
|
var romPath = encodeURIComponent('/api/v1.1/Games/' + gameId + '/roms/' + gameRomItems[i].id + '/' + gameRomItems[i].name);
|
||||||
|
if (gameRomItems[i].hasSaveStates == true) {
|
||||||
|
var modalVariables = {
|
||||||
|
"romId": gameRomItems[i].id,
|
||||||
|
"IsMediaGroup": false,
|
||||||
|
"engine": gameRomItems[i].emulator.type,
|
||||||
|
"core": gameRomItems[i].emulator.core,
|
||||||
|
"platformid": gameRomItems[i].platformId,
|
||||||
|
"gameid": gameId,
|
||||||
|
"mediagroup": 0,
|
||||||
|
"rompath": romPath
|
||||||
|
};
|
||||||
|
saveStatesButton = document.createElement('div');
|
||||||
|
saveStatesButton.setAttribute('onclick', 'showDialog("emulatorloadstate", ' + JSON.stringify(modalVariables) + ');');
|
||||||
|
saveStatesButton.innerHTML = '<img src="/images/SaveStates.png" class="savedstateicon" />';
|
||||||
|
}
|
||||||
|
launchButton = '<a href="/index.html?page=emulator&engine=' + gameRomItems[i].emulator.type + '&core=' + gameRomItems[i].emulator.core + '&platformid=' + gameRomItems[i].platformId + '&gameid=' + gameId + '&romid=' + gameRomItems[i].id + '&mediagroup=0&rompath=' + romPath + '" class="romstart">Launch</a>';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -637,10 +686,11 @@
|
|||||||
formatBytes(gameRomItems[i].size, 2),
|
formatBytes(gameRomItems[i].size, 2),
|
||||||
gameRomItems[i].romTypeMedia,
|
gameRomItems[i].romTypeMedia,
|
||||||
gameRomItems[i].mediaLabel,
|
gameRomItems[i].mediaLabel,
|
||||||
|
saveStatesButton,
|
||||||
launchButton,
|
launchButton,
|
||||||
'<div class="properties_button" onclick="showDialog(\'rominfo\', ' + gameRomItems[i].id + ');">i</div>'
|
'<div class="properties_button" onclick="showDialog(\'rominfo\', ' + gameRomItems[i].id + ');">i</div>'
|
||||||
];
|
];
|
||||||
newTable.appendChild(createTableRow(false, newRow, 'romrow', 'romcell'));
|
newTable.appendChild(createTableRow(false, newRow, 'romrow romrowgamepage', 'romcell'));
|
||||||
}
|
}
|
||||||
|
|
||||||
gameRoms.appendChild(newTable);
|
gameRoms.appendChild(newTable);
|
||||||
@@ -713,15 +763,17 @@
|
|||||||
bg.setAttribute('style', 'background-image: url("/images/gamebg' + randomInt + '.jpg"); background-position: center; background-repeat: no-repeat; background-size: cover; filter: blur(10px); -webkit-filter: blur(10px);');
|
bg.setAttribute('style', 'background-image: url("/images/gamebg' + randomInt + '.jpg"); background-position: center; background-repeat: no-repeat; background-size: cover; filter: blur(10px); -webkit-filter: blur(10px);');
|
||||||
}
|
}
|
||||||
|
|
||||||
// // load artwork
|
// load artwork
|
||||||
// if (game.artworks) {
|
if (!artworks) {
|
||||||
// ajaxCall('/api/v1.1/games/' + gameId + '/artwork', 'GET', function (result) {
|
if (game.artworks) {
|
||||||
// artworks = result;
|
ajaxCall('/api/v1.1/games/' + gameId + '/artwork', 'GET', function (result) {
|
||||||
// var startPos = randomIntFromInterval(0, result.length);
|
artworks = result;
|
||||||
// artworksPosition = startPos;
|
var startPos = randomIntFromInterval(0, result.length);
|
||||||
// rotateBackground();
|
artworksPosition = startPos;
|
||||||
// });
|
rotateBackground();
|
||||||
// }
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function rotateBackground() {
|
function rotateBackground() {
|
||||||
|
@@ -72,7 +72,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function DownloadJSON() {
|
function DownloadJSON() {
|
||||||
window.open('/api/v1.1/PlatformMaps', '_blank');
|
window.location = '/api/v1.1/PlatformMaps/PlatformMap.json';
|
||||||
}
|
}
|
||||||
|
|
||||||
document.getElementById('importjson').addEventListener('click', openDialog);
|
document.getElementById('importjson').addEventListener('click', openDialog);
|
||||||
|
@@ -21,6 +21,14 @@ function formatFilterPanel(containerElement, result) {
|
|||||||
containerPanelSearchField.id = 'filter_panel_search';
|
containerPanelSearchField.id = 'filter_panel_search';
|
||||||
containerPanelSearchField.type = 'text';
|
containerPanelSearchField.type = 'text';
|
||||||
containerPanelSearchField.placeholder = 'Search';
|
containerPanelSearchField.placeholder = 'Search';
|
||||||
|
containerPanelSearchField.addEventListener("keypress", function(event) {
|
||||||
|
if (event.key === "Enter") {
|
||||||
|
// Cancel the default action, if needed
|
||||||
|
event.preventDefault();
|
||||||
|
// Trigger the button element with a click
|
||||||
|
executeFilter1_1();
|
||||||
|
}
|
||||||
|
});
|
||||||
containerPanelSearch.appendChild(containerPanelSearchField);
|
containerPanelSearch.appendChild(containerPanelSearchField);
|
||||||
|
|
||||||
panel.appendChild(containerPanelSearch);
|
panel.appendChild(containerPanelSearch);
|
||||||
@@ -73,6 +81,8 @@ function formatFilterPanel(containerElement, result) {
|
|||||||
|
|
||||||
panel.appendChild(containerPanelUserRating);
|
panel.appendChild(containerPanelUserRating);
|
||||||
|
|
||||||
|
buildFilterPanel(panel, 'settings', 'Settings', [{ "id": "savestatesavailable", "name": "Game has save states avaialble", "gameCount": 0 }], true, true);
|
||||||
|
|
||||||
if (result.platforms) {
|
if (result.platforms) {
|
||||||
buildFilterPanel(panel, 'platform', 'Platforms', result.platforms, true, true);
|
buildFilterPanel(panel, 'platform', 'Platforms', result.platforms, true, true);
|
||||||
}
|
}
|
||||||
@@ -350,6 +360,9 @@ function executeFilter1_1(pageNumber, pageSize) {
|
|||||||
setCookie('games_library_orderby_direction_select', orderByDirectionSelect);
|
setCookie('games_library_orderby_direction_select', orderByDirectionSelect);
|
||||||
|
|
||||||
if (existingSearchModel == undefined || freshSearch == true) {
|
if (existingSearchModel == undefined || freshSearch == true) {
|
||||||
|
// search name
|
||||||
|
setCookie('filter_panel_search', document.getElementById('filter_panel_search').value);
|
||||||
|
|
||||||
// user ratings
|
// user ratings
|
||||||
var userRatingEnabled = document.getElementById('filter_panel_userrating_enabled');
|
var userRatingEnabled = document.getElementById('filter_panel_userrating_enabled');
|
||||||
|
|
||||||
@@ -386,6 +399,9 @@ function executeFilter1_1(pageNumber, pageSize) {
|
|||||||
setCookie("filter_panel_userrating_enabled", true);
|
setCookie("filter_panel_userrating_enabled", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// save cookies for settings
|
||||||
|
GetFilterQuery1_1('settings');
|
||||||
|
|
||||||
// build filter model
|
// build filter model
|
||||||
var ratingAgeGroups = GetFilterQuery1_1('agegroupings');
|
var ratingAgeGroups = GetFilterQuery1_1('agegroupings');
|
||||||
var ratingIncludeUnrated = false;
|
var ratingIncludeUnrated = false;
|
||||||
@@ -395,6 +411,7 @@ function executeFilter1_1(pageNumber, pageSize) {
|
|||||||
|
|
||||||
model = {
|
model = {
|
||||||
"Name": document.getElementById('filter_panel_search').value,
|
"Name": document.getElementById('filter_panel_search').value,
|
||||||
|
"HasSavedGame": document.getElementById('filter_panel_item_settings_checkbox_savestatesavailable').checked,
|
||||||
"Platform": GetFilterQuery1_1('platform'),
|
"Platform": GetFilterQuery1_1('platform'),
|
||||||
"Genre": GetFilterQuery1_1('genre'),
|
"Genre": GetFilterQuery1_1('genre'),
|
||||||
"GameMode": GetFilterQuery1_1('gamemode'),
|
"GameMode": GetFilterQuery1_1('gamemode'),
|
||||||
|
@@ -283,6 +283,14 @@ function renderGameIcon(gameObject, showTitle, showRatings, showClassification,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// add save game icon
|
||||||
|
if (gameObject.hasSavedGame == true) {
|
||||||
|
var gameSaveIcon = document.createElement('img');
|
||||||
|
gameSaveIcon.src = '/images/SaveStates.png';
|
||||||
|
gameSaveIcon.className = 'game_tile_box_savedgame savedstateicon';
|
||||||
|
gameImageBox.appendChild(gameSaveIcon);
|
||||||
|
}
|
||||||
|
|
||||||
if (gameObject.totalRating || displayClassification == true) {
|
if (gameObject.totalRating || displayClassification == true) {
|
||||||
var gameImageRatingBanner = document.createElement('div');
|
var gameImageRatingBanner = document.createElement('div');
|
||||||
gameImageRatingBanner.className = 'game_tile_box_ratingbanner';
|
gameImageRatingBanner.className = 'game_tile_box_ratingbanner';
|
||||||
|
@@ -496,3 +496,12 @@ function SetPreference_Local(Setting, Value) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)));
|
||||||
|
}
|
||||||
|
return c.join("");
|
||||||
|
}
|
58
gaseous-server/wwwroot/scripts/notifications.js
Normal file
58
gaseous-server/wwwroot/scripts/notifications.js
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
function displayNotification(heading, message, image, link) {
|
||||||
|
var noteId = Math.random().toString(36).substr(2, 9);
|
||||||
|
|
||||||
|
var noteBox = document.createElement('div');
|
||||||
|
noteBox.id = noteId;
|
||||||
|
noteBox.className = 'notification';
|
||||||
|
noteBox.style.display = 'none;'
|
||||||
|
|
||||||
|
if (link) {
|
||||||
|
noteBox.setAttribute('onclick', 'window.location.href = "' + link + '"');
|
||||||
|
} else {
|
||||||
|
noteBox.setAttribute('onclick', 'closeNotification("' + noteId + '");');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (image) {
|
||||||
|
var noteImageBox = document.createElement('div');
|
||||||
|
noteImageBox.className = 'notification_imagebox';
|
||||||
|
|
||||||
|
var noteImage = document.createElement('img');
|
||||||
|
noteImage.className = 'notification_image';
|
||||||
|
noteImage.src = image;
|
||||||
|
noteImageBox.appendChild(noteImage);
|
||||||
|
|
||||||
|
noteBox.appendChild(noteImageBox);
|
||||||
|
}
|
||||||
|
|
||||||
|
var noteMessageBox = document.createElement('div');
|
||||||
|
noteMessageBox.className = 'notification_messagebox';
|
||||||
|
|
||||||
|
if (heading) {
|
||||||
|
var noteMessageHeading = document.createElement('div');
|
||||||
|
noteMessageHeading.className = 'notification_title';
|
||||||
|
noteMessageHeading.innerHTML = heading;
|
||||||
|
noteMessageBox.appendChild(noteMessageHeading);
|
||||||
|
}
|
||||||
|
|
||||||
|
var noteMessageBody = document.createElement('div');
|
||||||
|
noteMessageBody.className = 'notification_message';
|
||||||
|
noteMessageBody.innerHTML = message;
|
||||||
|
noteMessageBox.appendChild(noteMessageBody);
|
||||||
|
|
||||||
|
noteBox.appendChild(noteMessageBox);
|
||||||
|
|
||||||
|
document.getElementById('notifications_target').appendChild(noteBox);
|
||||||
|
|
||||||
|
$(noteBox).hide().fadeIn(500);
|
||||||
|
|
||||||
|
setTimeout(function() {
|
||||||
|
closeNotification(noteId);
|
||||||
|
}, 5000);
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeNotification(id) {
|
||||||
|
var notificationObj = document.getElementById(id);
|
||||||
|
$(notificationObj).fadeOut(1000, function() {
|
||||||
|
notificationObj.parentElement.removeChild(notificationObj);
|
||||||
|
});
|
||||||
|
}
|
58
gaseous-server/wwwroot/styles/notifications.css
Normal file
58
gaseous-server/wwwroot/styles/notifications.css
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
#notification_target {
|
||||||
|
position: fixed;
|
||||||
|
top: 40px;
|
||||||
|
width: 400px;
|
||||||
|
right: 0px;
|
||||||
|
bottom: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notification {
|
||||||
|
position: absolute;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column nowrap;
|
||||||
|
top: 55px;
|
||||||
|
right: 15px;
|
||||||
|
width: 350px;
|
||||||
|
border-style: solid;
|
||||||
|
border-width: 1px;
|
||||||
|
border-radius: 7px;
|
||||||
|
border-color: rgba(0, 22, 56, 0.7);
|
||||||
|
padding-left: 15px;
|
||||||
|
padding-right: 20px;
|
||||||
|
padding-top: 15px;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
background-color: rgba(0, 22, 56, 0.6);
|
||||||
|
backdrop-filter: blur(8px);
|
||||||
|
-webkit-backdrop-filter: blur(8px);
|
||||||
|
box-shadow: 5px 5px 19px 0px rgba(0,0,0,0.44);
|
||||||
|
-webkit-box-shadow: 5px 5px 19px 0px rgba(0,0,0,0.44);
|
||||||
|
-moz-box-shadow: 5px 5px 19px 0px rgba(0,0,0,0.44);
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notification_imagebox {
|
||||||
|
flex-basis: 70px;
|
||||||
|
margin-bottom: 0px;
|
||||||
|
padding-bottom: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notification_image {
|
||||||
|
width: 80px;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notification_messagebox {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notification_title {
|
||||||
|
font-weight: bold;
|
||||||
|
text-transform: uppercase;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: rgb(160, 160, 186);
|
||||||
|
}
|
||||||
|
|
||||||
|
.notification_message {
|
||||||
|
|
||||||
|
}
|
@@ -213,13 +213,13 @@ h3 {
|
|||||||
position: -webkit-sticky;
|
position: -webkit-sticky;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
width: 180px;
|
width: 180px;
|
||||||
height: 18px;
|
height: 15px;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
background-color: #352879;
|
background-color: #352879;
|
||||||
color: white;
|
color: white;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-family: Commodore64;
|
font-family: Commodore64;
|
||||||
font-size: 16px;
|
font-size: 13px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#games_library_searchbutton:hover {
|
#games_library_searchbutton:hover {
|
||||||
@@ -232,13 +232,13 @@ h3 {
|
|||||||
position: -webkit-sticky;
|
position: -webkit-sticky;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
width: 180px;
|
width: 180px;
|
||||||
height: 18px;
|
height: 15px;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
background-color: #646464;
|
background-color: #646464;
|
||||||
color: white;
|
color: white;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-family: Commodore64;
|
font-family: Commodore64;
|
||||||
font-size: 16px;
|
font-size: 13px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#games_library_resetbutton:hover {
|
#games_library_resetbutton:hover {
|
||||||
@@ -557,6 +557,12 @@ input[id='filter_panel_userrating_max'] {
|
|||||||
-webkit-backdrop-filter: blur(8px);
|
-webkit-backdrop-filter: blur(8px);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.game_tile_box_savedgame {
|
||||||
|
position: absolute;
|
||||||
|
top: 1px;
|
||||||
|
right: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
.game_tile_image {
|
.game_tile_image {
|
||||||
max-width: 200px;
|
max-width: 200px;
|
||||||
min-width: 150px;
|
min-width: 150px;
|
||||||
@@ -792,10 +798,21 @@ table .romrow:nth-child(odd) {
|
|||||||
background: rgba(56, 56, 56, 0.3);
|
background: rgba(56, 56, 56, 0.3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.romrow.romrowgamepage {
|
||||||
|
height: 42px;
|
||||||
|
}
|
||||||
|
|
||||||
.romcell {
|
.romcell {
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.savedstateicon {
|
||||||
|
height: 24px;
|
||||||
|
width: 24px;
|
||||||
|
margin-top: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
th {
|
th {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
@@ -1417,3 +1434,48 @@ button:not(.select2-selection__choice__remove):not(.ejs_menu_button):disabled {
|
|||||||
margin-left: 5px;
|
margin-left: 5px;
|
||||||
margin-right: 5px;
|
margin-right: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#saved_states {
|
||||||
|
overflow-x: scroll;
|
||||||
|
height: 350px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.saved_state_box {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column nowrap;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.saved_state_image_box {
|
||||||
|
flex-basis: 128px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.saved_state_image_image {
|
||||||
|
width: 128px;
|
||||||
|
margin: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.saved_state_main_box {
|
||||||
|
flex-grow: 1;
|
||||||
|
padding-left: 20px;
|
||||||
|
padding-right: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.saved_state_name {
|
||||||
|
font-size: 15px;
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.saved_state_date {
|
||||||
|
color: #888;
|
||||||
|
}
|
||||||
|
|
||||||
|
.saved_state_controls {
|
||||||
|
margin-top: 10px;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.saved_state_buttonlink {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
Reference in New Issue
Block a user