EmulatorJS - First Version (#23)
* feat: added EmulatorJS support for Mega Drive, NES, and N64 (see: #15) * doc: updated attribution in README.MD to include EmulatorJS * ci: updated action to include submodules
This commit is contained in:
2
.github/workflows/BuildDockerOnTag.yml
vendored
2
.github/workflows/BuildDockerOnTag.yml
vendored
@@ -12,6 +12,8 @@ jobs:
|
|||||||
-
|
-
|
||||||
name: Checkout
|
name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
submodules: 'true'
|
||||||
-
|
-
|
||||||
name: Set up QEMU
|
name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v2
|
uses: docker/setup-qemu-action@v2
|
||||||
|
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -0,0 +1,3 @@
|
|||||||
|
[submodule "gaseous-server/wwwroot/EmulatorJS"]
|
||||||
|
path = gaseous-server/wwwroot/EmulatorJS
|
||||||
|
url = https://github.com/EmulatorJS/EmulatorJS.git
|
||||||
|
@@ -16,6 +16,7 @@ The following projects are used by Gaseous
|
|||||||
* https://github.com/JamesNK/Newtonsoft.Json
|
* https://github.com/JamesNK/Newtonsoft.Json
|
||||||
* https://www.nuget.org/packages/MySql.Data/8.0.32.1
|
* https://www.nuget.org/packages/MySql.Data/8.0.32.1
|
||||||
* https://github.com/kamranayub/igdb-dotnet
|
* https://github.com/kamranayub/igdb-dotnet
|
||||||
|
* https://github.com/EmulatorJS/EmulatorJS
|
||||||
|
|
||||||
## Configuration File
|
## Configuration File
|
||||||
When Gaseous-Server is started for the first time, it creates a configuration file at ~/.gaseous-server/config.json if it doesn't exist. Some values can be filled in using environment variables (such as in the case of using docker).
|
When Gaseous-Server is started for the first time, it creates a configuration file at ~/.gaseous-server/config.json if it doesn't exist. Some values can be filled in using environment variables (such as in the case of using docker).
|
||||||
@@ -105,4 +106,4 @@ Loop through each of the search candidates searching using:
|
|||||||
2. "wherefuzzy" - partial match using wildcards
|
2. "wherefuzzy" - partial match using wildcards
|
||||||
3. "search" - uses a more flexible search method
|
3. "search" - uses a more flexible search method
|
||||||
|
|
||||||
Note: that if more than one result is found, the image will be set as "Unknown" as there is no way for Gaseous to know which title is the correct one.
|
Note: that if more than one result is found, the image will be set as "Unknown" as there is no way for Gaseous to know which title is the correct one.
|
||||||
|
BIN
gaseous-server/.DS_Store
vendored
BIN
gaseous-server/.DS_Store
vendored
Binary file not shown.
@@ -107,6 +107,19 @@ namespace gaseous_server.Classes
|
|||||||
Path = (string)romDR["path"],
|
Path = (string)romDR["path"],
|
||||||
Source = (GameRomItem.SourceType)(Int32)romDR["metadatasource"]
|
Source = (GameRomItem.SourceType)(Int32)romDR["metadatasource"]
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// check for a web emulator and update the romItem
|
||||||
|
foreach (Models.PlatformMapping.PlatformMapItem platformMapping in Models.PlatformMapping.PlatformMap)
|
||||||
|
{
|
||||||
|
if (platformMapping.IGDBId == romItem.PlatformId)
|
||||||
|
{
|
||||||
|
if (platformMapping.WebEmulator != null)
|
||||||
|
{
|
||||||
|
romItem.Emulator = platformMapping.WebEmulator;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return romItem;
|
return romItem;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,6 +128,7 @@ namespace gaseous_server.Classes
|
|||||||
public long Id { get; set; }
|
public long Id { get; set; }
|
||||||
public long PlatformId { get; set; }
|
public long PlatformId { get; set; }
|
||||||
public IGDB.Models.Platform Platform { get; set; }
|
public IGDB.Models.Platform Platform { get; set; }
|
||||||
|
public Dictionary<string, string>? Emulator { get; set; }
|
||||||
public long GameId { get; set; }
|
public long GameId { get; set; }
|
||||||
public string? Name { get; set; }
|
public string? Name { get; set; }
|
||||||
public long Size { get; set; }
|
public long Size { get; set; }
|
||||||
|
@@ -749,6 +749,7 @@ namespace gaseous_server.Controllers
|
|||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
|
[HttpHead]
|
||||||
[Route("{GameId}/roms/{RomId}/file")]
|
[Route("{GameId}/roms/{RomId}/file")]
|
||||||
[ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)]
|
[ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)]
|
||||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
|
@@ -78,6 +78,8 @@ namespace gaseous_server.Models
|
|||||||
public string IGDBName { get; set; }
|
public string IGDBName { get; set; }
|
||||||
public List<string> AlternateNames { get; set; } = new List<string>();
|
public List<string> AlternateNames { get; set; } = new List<string>();
|
||||||
public List<string> KnownFileExtensions { get; set; } = new List<string>();
|
public List<string> KnownFileExtensions { get; set; } = new List<string>();
|
||||||
|
public Dictionary<string, string>? WebEmulator { get; set; }
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -76,7 +76,11 @@ app.UseResponseCaching();
|
|||||||
app.UseAuthorization();
|
app.UseAuthorization();
|
||||||
|
|
||||||
app.UseDefaultFiles();
|
app.UseDefaultFiles();
|
||||||
app.UseStaticFiles();
|
app.UseStaticFiles(new StaticFileOptions
|
||||||
|
{
|
||||||
|
ServeUnknownFileTypes = true, //allow unkown file types also to be served
|
||||||
|
DefaultContentType = "plain/text" //content type to returned if fileType is not known.
|
||||||
|
});
|
||||||
|
|
||||||
app.MapControllers();
|
app.MapControllers();
|
||||||
|
|
||||||
|
@@ -60,7 +60,11 @@
|
|||||||
".MD",
|
".MD",
|
||||||
".SG",
|
".SG",
|
||||||
".SMD"
|
".SMD"
|
||||||
]
|
],
|
||||||
|
"WebEmulator": {
|
||||||
|
"Type": "EmulatorJS",
|
||||||
|
"Core": "segaMD"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"IGDBId": 4,
|
"IGDBId": 4,
|
||||||
@@ -71,7 +75,11 @@
|
|||||||
],
|
],
|
||||||
"KnownFileExtensions": [
|
"KnownFileExtensions": [
|
||||||
".Z64"
|
".Z64"
|
||||||
]
|
],
|
||||||
|
"WebEmulator": {
|
||||||
|
"Type": "EmulatorJS",
|
||||||
|
"Core": "n64"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"IGDBId": 18,
|
"IGDBId": 18,
|
||||||
@@ -88,6 +96,10 @@
|
|||||||
".SFC",
|
".SFC",
|
||||||
".SMC",
|
".SMC",
|
||||||
".SWC"
|
".SWC"
|
||||||
]
|
],
|
||||||
|
"WebEmulator": {
|
||||||
|
"Type": "EmulatorJS",
|
||||||
|
"Core": "nes"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@@ -119,6 +119,10 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Content Remove="Support\PlatformMap.json" />
|
<Content Remove="Support\PlatformMap.json" />
|
||||||
|
<Content Remove="wwwroot\styles\select2.min.css" />
|
||||||
|
<Content Remove="wwwroot\scripts\select2.min.js" />
|
||||||
|
<Content Remove="wwwroot\scripts\moment.js" />
|
||||||
|
<Content Remove="wwwroot\scripts\jquery-3.6.0.min.js" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<EmbeddedResource Include="Support\PlatformMap.json" Condition="'$(ExcludeConfigFilesFromBuildOutput)'!='true'">
|
<EmbeddedResource Include="Support\PlatformMap.json" Condition="'$(ExcludeConfigFilesFromBuildOutput)'!='true'">
|
||||||
@@ -165,4 +169,9 @@
|
|||||||
<EmbeddedResource Include="Assets\Ratings\CLASS_IND\CLASS_IND_Ten.svg" />
|
<EmbeddedResource Include="Assets\Ratings\CLASS_IND\CLASS_IND_Ten.svg" />
|
||||||
<EmbeddedResource Include="Assets\Ratings\CLASS_IND\CLASS_IND_Twelve.svg" />
|
<EmbeddedResource Include="Assets\Ratings\CLASS_IND\CLASS_IND_Twelve.svg" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Update="wwwroot\EmulatorJS\data\old\sega-old-wasm.data">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
BIN
gaseous-server/wwwroot/.DS_Store
vendored
BIN
gaseous-server/wwwroot/.DS_Store
vendored
Binary file not shown.
1
gaseous-server/wwwroot/EmulatorJS
Submodule
1
gaseous-server/wwwroot/EmulatorJS
Submodule
Submodule gaseous-server/wwwroot/EmulatorJS added at f7fa5d4148
25
gaseous-server/wwwroot/pages/EmulatorJS.html
Normal file
25
gaseous-server/wwwroot/pages/EmulatorJS.html
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
<div style='width:640px;height:480px;max-width:100%'>
|
||||||
|
<div id='game'></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script type='text/javascript'>
|
||||||
|
EJS_player = '#game';
|
||||||
|
|
||||||
|
// Can also be fceumm or nestopia
|
||||||
|
EJS_core = urlParams.get('core');
|
||||||
|
|
||||||
|
// Lightgun
|
||||||
|
EJS_lightgun = false; // can be true or false
|
||||||
|
|
||||||
|
// URL to BIOS file
|
||||||
|
EJS_biosUrl = ''; // example: https://dl.dropboxusercontent.com/s/[random-code]/bios.bin
|
||||||
|
|
||||||
|
// URL to Game rom
|
||||||
|
EJS_gameUrl = decodeURIComponent(urlParams.get('rompath'));
|
||||||
|
|
||||||
|
// Path to the data directory
|
||||||
|
EJS_pathtodata = '/EmulatorJS/data/';
|
||||||
|
|
||||||
|
EJS_DEBUG_XX = false;
|
||||||
|
</script>
|
||||||
|
<script src='/EmulatorJS/data/loader.js'></script>
|
48
gaseous-server/wwwroot/pages/emulator.html
Normal file
48
gaseous-server/wwwroot/pages/emulator.html
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
<div id="bgImage">
|
||||||
|
<div id="bgImage_Opacity"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="emulator"></div>
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
|
|
||||||
|
var gameId = urlParams.get('gameid');
|
||||||
|
var gameData;
|
||||||
|
var artworks = null;
|
||||||
|
var artworksPosition = 0;
|
||||||
|
|
||||||
|
ajaxCall('/api/v1/Games/' + gameId, 'GET', function (result) {
|
||||||
|
gameData = result;
|
||||||
|
|
||||||
|
// load artwork
|
||||||
|
if (result.artworks) {
|
||||||
|
artworks = result.artworks.ids;
|
||||||
|
var startPos = randomIntFromInterval(0, result.artworks.ids.length);
|
||||||
|
artworksPosition = startPos;
|
||||||
|
rotateBackground();
|
||||||
|
} else {
|
||||||
|
if (result.cover) {
|
||||||
|
var bg = document.getElementById('bgImage');
|
||||||
|
bg.setAttribute('style', 'background-image: url("/api/v1/Games/' + gameId + '/cover/image"); background-position: center; background-repeat: no-repeat; background-size: cover; filter: blur(10px); -webkit-filter: blur(10px);');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function rotateBackground() {
|
||||||
|
if (artworks) {
|
||||||
|
artworksPosition += 1;
|
||||||
|
if (artworks[artworksPosition] == null) {
|
||||||
|
artworksPosition = 0;
|
||||||
|
}
|
||||||
|
var bg = document.getElementById('bgImage');
|
||||||
|
bg.setAttribute('style', 'background-image: url("/api/v1/Games/' + gameId + '/artwork/' + artworks[artworksPosition] + '/image"); background-position: center; background-repeat: no-repeat; background-size: cover; filter: blur(10px); -webkit-filter: blur(10px);');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (urlParams.get('engine')) {
|
||||||
|
case 'EmulatorJS':
|
||||||
|
$('#emulator').load('/pages/EmulatorJS.html');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
</script>
|
@@ -304,7 +304,7 @@
|
|||||||
var newTable = document.createElement('table');
|
var newTable = document.createElement('table');
|
||||||
newTable.className = 'romtable';
|
newTable.className = 'romtable';
|
||||||
newTable.setAttribute('cellspacing', 0);
|
newTable.setAttribute('cellspacing', 0);
|
||||||
newTable.appendChild(createTableRow(true, ['Name', 'Size', 'Media', '', '']));
|
newTable.appendChild(createTableRow(true, ['Name', 'Size', 'Media', '', '', '']));
|
||||||
|
|
||||||
var lastPlatform = '';
|
var lastPlatform = '';
|
||||||
for (var i = 0; i < result.length; i++) {
|
for (var i = 0; i < result.length; i++) {
|
||||||
@@ -318,11 +318,17 @@
|
|||||||
newTable.appendChild(platformRow);
|
newTable.appendChild(platformRow);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var launchButton = '';
|
||||||
|
if (result[i].emulator) {
|
||||||
|
launchButton = '<a href="/index.html?page=emulator&engine=' + result[i].emulator.Type + '&core=' + result[i].emulator.Core + '&gameid=' + gameId + '&rompath=' + encodeURIComponent('/api/v1/Games/' + gameId + '/roms/' + result[i].id + '/file') + '" class="romlink">Start</a>';
|
||||||
|
}
|
||||||
|
|
||||||
var newRow = [
|
var newRow = [
|
||||||
'<a href="/api/v1/Games/' + gameId + '/roms/' + result[i].id + '/file" class="romlink">' + result[i].name + '</a>',
|
'<a href="/api/v1/Games/' + gameId + '/roms/' + result[i].id + '/file" class="romlink">' + result[i].name + '</a>',
|
||||||
formatBytes(result[i].size, 2),
|
formatBytes(result[i].size, 2),
|
||||||
result[i].romTypeMedia,
|
result[i].romTypeMedia,
|
||||||
result[i].mediaLabel,
|
result[i].mediaLabel,
|
||||||
|
launchButton,
|
||||||
'<span class="romlink" onclick="showDialog(\'rominfo\', ' + result[i].id + ');">...</span>'
|
'<span class="romlink" onclick="showDialog(\'rominfo\', ' + result[i].id + ');">...</span>'
|
||||||
];
|
];
|
||||||
newTable.appendChild(createTableRow(false, newRow, 'romrow', 'romcell'));
|
newTable.appendChild(createTableRow(false, newRow, 'romrow', 'romcell'));
|
||||||
|
@@ -578,4 +578,10 @@ button:disabled {
|
|||||||
|
|
||||||
.redbutton:disabled {
|
.redbutton:disabled {
|
||||||
background-color: #555;
|
background-color: #555;
|
||||||
|
}
|
||||||
|
|
||||||
|
#emulator {
|
||||||
|
margin: 0 auto;
|
||||||
|
width: 640px;
|
||||||
|
padding-top: 100px;
|
||||||
}
|
}
|
Reference in New Issue
Block a user