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:
Michael Green
2023-07-13 13:31:38 +10:00
committed by GitHub
parent b010f9742b
commit 7a8e445471
17 changed files with 140 additions and 6 deletions

BIN
.DS_Store vendored

Binary file not shown.

View File

@@ -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
View File

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

View File

@@ -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.

Binary file not shown.

View File

@@ -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; }

View File

@@ -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)]

View File

@@ -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; }
} }
} }
} }

View File

@@ -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();

View File

@@ -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"
}
} }
] ]

View File

@@ -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>

Binary file not shown.

View 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>

View 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>

View File

@@ -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'));

View File

@@ -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;
} }