Multiple Updates (#251)

* Added more error logging to zip expansion

* Added more logging

* More logging, and archive contents can now be seen in rom info

* Bug fixes and caching enhancements

* Import path now cleaned after import
This commit is contained in:
Michael Green
2024-01-07 01:07:10 +11:00
committed by GitHub
parent ce9ab91e5b
commit 7d5419d33c
13 changed files with 392 additions and 112 deletions

View File

@@ -1,7 +1,9 @@
using System.IO.Compression;
using HasheousClient.Models;
using SevenZip;
using SharpCompress.Archives;
using SharpCompress.Archives.Rar;
using SharpCompress.Archives.Zip;
using SharpCompress.Common;
namespace gaseous_server.Classes
@@ -10,6 +12,7 @@ namespace gaseous_server.Classes
{
public static gaseous_server.Models.Signatures_Games GetFileSignature(Common.hashObject hash, FileInfo fi, string GameFileImportPath)
{
Logging.Log(Logging.LogType.Information, "Get Signature", "Getting signature for file: " + GameFileImportPath);
gaseous_server.Models.Signatures_Games discoveredSignature = new gaseous_server.Models.Signatures_Games();
discoveredSignature = _GetFileSignature(hash, fi, GameFileImportPath, false);
@@ -20,23 +23,46 @@ namespace gaseous_server.Classes
{
// file is a zip and less than 1 GiB
// extract the zip file and search the contents
Logging.Log(Logging.LogType.Information, "Get Signature", "Decompressing " + GameFileImportPath + " to examine contents");
string ExtractPath = Path.Combine(Config.LibraryConfiguration.LibraryTempDirectory, Path.GetRandomFileName());
Logging.Log(Logging.LogType.Information, "Get Signature", "Decompressing " + GameFileImportPath + " to " + ExtractPath + " examine contents");
if (!Directory.Exists(ExtractPath)) { Directory.CreateDirectory(ExtractPath); }
try
{
switch(ImportedFileExtension)
{
case ".zip":
ZipFile.ExtractToDirectory(GameFileImportPath, ExtractPath);
Logging.Log(Logging.LogType.Information, "Get Signature", "Decompressing using zip");
try
{
using (var archive = SharpCompress.Archives.Zip.ZipArchive.Open(GameFileImportPath))
{
foreach (var entry in archive.Entries.Where(entry => !entry.IsDirectory))
{
Logging.Log(Logging.LogType.Information, "Get Signature", "Extracting file: " + entry.Key);
entry.WriteToDirectory(ExtractPath, new ExtractionOptions()
{
ExtractFullPath = true,
Overwrite = true
});
}
}
}
catch (Exception zipEx)
{
Logging.Log(Logging.LogType.Warning, "Get Signature", "Unzip error", zipEx);
throw;
}
break;
case ".rar":
Logging.Log(Logging.LogType.Information, "Get Signature", "Decompressing using rar");
try
{
using (var archive = RarArchive.Open(GameFileImportPath))
{
foreach (var entry in archive.Entries.Where(entry => !entry.IsDirectory))
{
Logging.Log(Logging.LogType.Information, "Get Signature", "Extracting file: " + entry.Key);
entry.WriteToDirectory(ExtractPath, new ExtractionOptions()
{
ExtractFullPath = true,
@@ -44,13 +70,23 @@ namespace gaseous_server.Classes
});
}
}
}
catch (Exception zipEx)
{
Logging.Log(Logging.LogType.Warning, "Get Signature", "Unrar error", zipEx);
throw;
}
break;
case ".7z":
Logging.Log(Logging.LogType.Information, "Get Signature", "Decompressing using 7z");
try
{
using (var archive = SharpCompress.Archives.SevenZip.SevenZipArchive.Open(GameFileImportPath))
{
foreach (var entry in archive.Entries.Where(entry => !entry.IsDirectory))
{
Logging.Log(Logging.LogType.Information, "Get Signature", "Extracting file: " + entry.Key);
entry.WriteToDirectory(ExtractPath, new ExtractionOptions()
{
ExtractFullPath = true,
@@ -58,15 +94,41 @@ namespace gaseous_server.Classes
});
}
}
}
catch (Exception zipEx)
{
Logging.Log(Logging.LogType.Warning, "Get Signature", "7z error", zipEx);
throw;
}
break;
}
Logging.Log(Logging.LogType.Information, "Get Signature", "Processing decompressed files for signature matches");
// loop through contents until we find the first signature match
List<ArchiveData> archiveFiles = new List<ArchiveData>();
bool signatureFound = false;
foreach (string file in Directory.GetFiles(ExtractPath, "*.*", SearchOption.AllDirectories))
{
if (File.Exists(file))
{
FileInfo zfi = new FileInfo(file);
Common.hashObject zhash = new Common.hashObject(file);
Logging.Log(Logging.LogType.Information, "Get Signature", "Checking signature of decompressed file " + file);
if (zfi != null)
{
ArchiveData archiveData = new ArchiveData{
FileName = Path.GetFileName(file),
FilePath = zfi.Directory.FullName.Replace(ExtractPath, ""),
Size = zfi.Length,
MD5 = hash.md5hash,
SHA1 = hash.sha1hash
};
archiveFiles.Add(archiveData);
if (signatureFound == false)
{
gaseous_server.Models.Signatures_Games zDiscoveredSignature = _GetFileSignature(zhash, zfi, file, true);
zDiscoveredSignature.Rom.Name = Path.ChangeExtension(zDiscoveredSignature.Rom.Name, ImportedFileExtension);
@@ -85,16 +147,28 @@ namespace gaseous_server.Classes
zDiscoveredSignature.Rom.Size = discoveredSignature.Rom.Size;
discoveredSignature = zDiscoveredSignature;
break;
signatureFound = true;
}
}
}
}
}
discoveredSignature.Rom.Attributes.Add(new KeyValuePair<string, object>(
"ZipContents", Newtonsoft.Json.JsonConvert.SerializeObject(archiveFiles)
));
}
catch (Exception ex)
{
Logging.Log(Logging.LogType.Critical, "Get Signature", "Error processing zip file: " + GameFileImportPath, ex);
Logging.Log(Logging.LogType.Critical, "Get Signature", "Error processing compressed file: " + GameFileImportPath, ex);
}
if (Directory.Exists(ExtractPath)) { Directory.Delete(ExtractPath, true); }
if (Directory.Exists(ExtractPath))
{
Logging.Log(Logging.LogType.Information, "Get Signature", "Deleting temporary decompress folder: " + ExtractPath);
Directory.Delete(ExtractPath, true);
}
}
return discoveredSignature;
@@ -110,6 +184,7 @@ namespace gaseous_server.Classes
if (dbSignature != null)
{
// local signature found
Logging.Log(Logging.LogType.Information, "Import Game", "Signature found in local database for game: " + dbSignature.Game.Name);
discoveredSignature = dbSignature;
}
else
@@ -120,12 +195,16 @@ namespace gaseous_server.Classes
if (dbSignature != null)
{
// signature retrieved from Hasheous
Logging.Log(Logging.LogType.Information, "Import Game", "Signature retrieved from Hasheous for game: " + dbSignature.Game.Name);
discoveredSignature = dbSignature;
}
else
{
// construct a signature from file data
dbSignature = _GetFileSignatureFromFileData(hash, fi, GameFileImportPath);
Logging.Log(Logging.LogType.Information, "Import Game", "Signature generated from provided file for game: " + dbSignature.Game.Name);
discoveredSignature = dbSignature;
}
}
@@ -282,5 +361,14 @@ namespace gaseous_server.Classes
return discoveredSignature;
}
}
public class ArchiveData
{
public string FileName { get; set; }
public string FilePath { get; set; }
public long Size { get; set; }
public string MD5 { get; set; }
public string SHA1 { get; set; }
}
}
}

View File

@@ -16,30 +16,28 @@ using HasheousClient.Models;
namespace gaseous_server.Classes
{
public class ImportGames : QueueItemStatus
public class ImportGame : QueueItemStatus
{
public ImportGames(string ImportPath)
public void ProcessDirectory(string ImportPath)
{
if (Directory.Exists(ImportPath))
{
string[] importContents_Files = Directory.GetFiles(ImportPath);
string[] importContents_Directories = Directory.GetDirectories(ImportPath);
string[] importContents = Directory.GetFiles(ImportPath, "*.*", SearchOption.AllDirectories);
Logging.Log(Logging.LogType.Information, "Import Games", "Found " + importContents.Length + " files to process in import directory: " + ImportPath);
// import files first
int importCount = 1;
foreach (string importContent in importContents_Files) {
SetStatus(importCount, importContents_Files.Length, "Importing file: " + importContent);
foreach (string importContent in importContents) {
SetStatus(importCount, importContents.Length, "Importing file: " + importContent);
ImportGame.ImportGameFile(importContent, null);
ImportGameFile(importContent, null);
importCount += 1;
}
ClearStatus();
// import sub directories
foreach (string importDir in importContents_Directories) {
Classes.ImportGames importGames = new Classes.ImportGames(importDir);
}
DeleteOrphanedDirectories(ImportPath);
}
else
{
@@ -48,12 +46,7 @@ namespace gaseous_server.Classes
}
}
}
public class ImportGame : QueueItemStatus
{
public static void ImportGameFile(string GameFileImportPath, IGDB.Models.Platform? OverridePlatform)
public void ImportGameFile(string GameFileImportPath, IGDB.Models.Platform? OverridePlatform)
{
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "";
@@ -443,7 +436,7 @@ namespace gaseous_server.Classes
}
}
public static void OrganiseLibrary()
public void OrganiseLibrary()
{
Logging.Log(Logging.LogType.Information, "Organise Library", "Starting default library organisation");
@@ -477,8 +470,12 @@ namespace gaseous_server.Classes
foreach (var directory in Directory.GetDirectories(startLocation))
{
DeleteOrphanedDirectories(directory);
if (Directory.GetFiles(directory).Length == 0 &&
Directory.GetDirectories(directory).Length == 0)
string[] files = Directory.GetFiles(directory);
string[] directories = Directory.GetDirectories(directory);
if (files.Length == 0 &&
directories.Length == 0)
{
Directory.Delete(directory, false);
}
@@ -621,13 +618,13 @@ namespace gaseous_server.Classes
if (romFound == false)
{
// file is not in database - process it
Logging.Log(Logging.LogType.Information, "Library Scan", "Orphaned file found in library: " + LibraryFile);
Common.hashObject hash = new Common.hashObject(LibraryFile);
FileInfo fi = new FileInfo(LibraryFile);
gaseous_server.Models.Signatures_Games sig = GetFileSignature(hash, fi, LibraryFile);
Logging.Log(Logging.LogType.Information, "Library Scan", "Orphaned file found in library: " + LibraryFile);
// get discovered platform
IGDB.Models.Platform determinedPlatform = Metadata.Platforms.GetPlatform(sig.Flags.IGDBPlatformId);
@@ -687,8 +684,13 @@ namespace gaseous_server.Classes
{
if (romPath != ComputeROMPath(romId))
{
Logging.Log(Logging.LogType.Information, "Library Scan", "ROM at path " + romPath + " found, but needs to be moved");
MoveGameFile(romId);
}
else
{
Logging.Log(Logging.LogType.Information, "Library Scan", "ROM at path " + romPath + " found");
}
}
}
else

View File

@@ -1,4 +1,5 @@
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Net;
using Humanizer;
@@ -422,6 +423,56 @@ namespace gaseous_server.Classes.Metadata
return returnPath;
}
public static T? GetSearchCache<T>(string SearchFields, string SearchString)
{
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "SELECT * FROM SearchCache WHERE SearchFields = @searchfields AND SearchString = @searchstring;";
Dictionary<string, object> dbDict = new Dictionary<string, object>
{
{ "searchfields", SearchFields },
{ "searchstring", SearchString }
};
DataTable data = db.ExecuteCMD(sql, dbDict);
if (data.Rows.Count > 0)
{
// cache hit
string rawString = data.Rows[0]["Content"].ToString();
T ReturnValue = Newtonsoft.Json.JsonConvert.DeserializeObject<T>(rawString);
if (ReturnValue != null)
{
Logging.Log(Logging.LogType.Information, "Search Cache", "Found search result in cache. Search string: " + SearchString);
return ReturnValue;
}
else
{
Logging.Log(Logging.LogType.Information, "Search Cache", "Search result not found in cache.");
return default;
}
}
else
{
// cache miss
Logging.Log(Logging.LogType.Information, "Search Cache", "Search result not found in cache.");
return default;
}
}
public static void SetSearchCache<T>(string SearchFields, string SearchString, T SearchResult)
{
Logging.Log(Logging.LogType.Information, "Search Cache", "Storing search results in cache. Search string: " + SearchString);
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "INSERT INTO SearchCache (SearchFields, SearchString, Content, LastSearch) VALUES (@searchfields, @searchstring, @content, @lastsearch);";
Dictionary<string, object> dbDict = new Dictionary<string, object>
{
{ "searchfields", SearchFields },
{ "searchstring", SearchString },
{ "content", Newtonsoft.Json.JsonConvert.SerializeObject(SearchResult) },
{ "lastsearch", DateTime.UtcNow }
};
db.ExecuteNonQuery(sql, dbDict);
}
/// <summary>
/// See https://api-docs.igdb.com/?javascript#images for more information about the image url structure
/// </summary>

View File

@@ -477,7 +477,12 @@ namespace gaseous_server.Classes.Metadata
break;
}
// check search cache
List<Game>? games = Communications.GetSearchCache<List<Game>>(searchFields, searchBody);
if (games == null)
{
// cache miss
// get Game metadata
Communications comms = new Communications();
Game[]? results = new Game[0];
@@ -488,6 +493,11 @@ namespace gaseous_server.Classes.Metadata
return results;
}
else
{
return games.ToArray();
}
}
public enum SearchType
{

View File

@@ -77,7 +77,8 @@ namespace gaseous_server.Controllers
// Process uploaded files
foreach (Dictionary<string, object> UploadedFile in UploadedFiles)
{
Classes.ImportGame.ImportGameFile((string)UploadedFile["fullpath"], OverridePlatform);
Classes.ImportGame uploadImport = new ImportGame();
uploadImport.ImportGameFile((string)UploadedFile["fullpath"], OverridePlatform);
}
if (Directory.Exists(workPath))

View File

@@ -1,13 +1,16 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Threading.Tasks;
using gaseous_server.Classes;
using gaseous_server.Classes.Metadata;
using gaseous_server.Models;
using IGDB;
using IGDB.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using NuGet.Common;
using static gaseous_server.Classes.Metadata.Games;
@@ -37,36 +40,88 @@ namespace gaseous_server.Controllers
string searchFields = "fields abbreviation,alternative_name,category,checksum,created_at,generation,name,platform_family,platform_logo,slug,summary,updated_at,url,versions,websites; ";
searchBody += "where name ~ *\"" + SearchString + "\"*;";
// get Platform metadata
List<Platform>? searchCache = Communications.GetSearchCache<List<Platform>>(searchFields, searchBody);
if (searchCache == null)
{
// cache miss
// get Platform metadata from data source
Communications comms = new Communications();
var results = await comms.APIComm<Platform>(IGDBClient.Endpoints.Platforms, searchFields, searchBody);
Communications.SetSearchCache<List<Platform>>(searchFields, searchBody, results.ToList());
return results.ToList();
}
else
{
return searchCache;
}
}
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet]
[Route("Game")]
[ProducesResponseType(typeof(List<Game>), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(List<GaseousGame>), StatusCodes.Status200OK)]
public async Task<ActionResult> SearchGame(long PlatformId, string SearchString)
{
List<Game> RetVal = await _SearchForGame(PlatformId, SearchString);
List<GaseousGame> RetVal = await _SearchForGame(PlatformId, SearchString);
return Ok(RetVal);
}
private static async Task<List<Game>> _SearchForGame(long PlatformId, string SearchString)
private static async Task<List<GaseousGame>> _SearchForGame(long PlatformId, string SearchString)
{
string searchBody = "";
string searchFields = "fields cover.*,first_release_date,name,platforms,slug; ";
string searchFields = "fields cover,first_release_date,name,platforms,slug; ";
searchBody += "search \"" + SearchString + "\";";
searchBody += "where platforms = (" + PlatformId + ");";
// get Platform metadata
List<GaseousGame>? searchCache = Communications.GetSearchCache<List<GaseousGame>>(searchFields, searchBody);
if (searchCache == null)
{
// cache miss
// get Game metadata from data source
Communications comms = new Communications();
var results = await comms.APIComm<Game>(IGDBClient.Endpoints.Games, searchFields, searchBody);
return results.ToList();
List<GaseousGame> games = new List<GaseousGame>();
foreach (Game game in results.ToList())
{
Storage.CacheStatus cacheStatus = Storage.GetCacheStatus("Game", (long)game.Id);
switch(cacheStatus)
{
case Storage.CacheStatus.NotPresent:
Storage.NewCacheValue(game, false);
break;
case Storage.CacheStatus.Expired:
Storage.NewCacheValue(game, true);
break;
}
games.Add(new GaseousGame(game));
}
Communications.SetSearchCache<List<GaseousGame>>(searchFields, searchBody, games);
return games;
}
else
{
// get full version of results from database
// this is a hacky workaround due to the readonly nature of IGDB.Model.Game IdentityOrValue fields
List<GaseousGame> gamesToReturn = new List<GaseousGame>();
foreach (GaseousGame game in searchCache)
{
Game tempGame = Games.GetGame((long)game.Id, false, false, false);
gamesToReturn.Add(new GaseousGame(tempGame));
}
return gamesToReturn;
}
}
}
}

View File

@@ -1,7 +1,7 @@
using System.Reflection;
using gaseous_server.Classes;
using gaseous_server.Classes.Metadata;
using IGDB;
using Swashbuckle.AspNetCore.SwaggerGen;
namespace gaseous_server.Models
{

View File

@@ -144,10 +144,11 @@ namespace gaseous_server
case QueueItemType.TitleIngestor:
Logging.Log(Logging.LogType.Debug, "Timered Event", "Starting Title Ingestor");
Classes.ImportGames importGames = new Classes.ImportGames(Config.LibraryConfiguration.LibraryImportDirectory)
Classes.ImportGame import = new ImportGame
{
CallingQueueItem = this
};
import.ProcessDirectory(Config.LibraryConfiguration.LibraryImportDirectory);
// clean up
Classes.ImportGame.DeleteOrphanedDirectories(Config.LibraryConfiguration.LibraryImportDirectory);
@@ -170,7 +171,11 @@ namespace gaseous_server
case QueueItemType.OrganiseLibrary:
Logging.Log(Logging.LogType.Debug, "Timered Event", "Starting Library Organiser");
Classes.ImportGame.OrganiseLibrary();
Classes.ImportGame importLibraryOrg = new ImportGame
{
CallingQueueItem = this
};
importLibraryOrg.OrganiseLibrary();
_SaveLastRunTime = true;
@@ -178,11 +183,11 @@ namespace gaseous_server
case QueueItemType.LibraryScan:
Logging.Log(Logging.LogType.Debug, "Timered Event", "Starting Library Scanners");
Classes.ImportGame import = new ImportGame
Classes.ImportGame libScan = new ImportGame
{
CallingQueueItem = this
};
import.LibraryScan();
libScan.LibraryScan();
_SaveLastRunTime = true;

View File

@@ -0,0 +1,5 @@
ALTER TABLE `Games_Roms`
ADD INDEX `id_IdAndLibraryId` (`Id` ASC, `LibraryId` ASC) VISIBLE;
ALTER TABLE `ServerLogs`
ADD INDEX `idx_EventDate` (`EventTime` ASC) VISIBLE;

View File

@@ -0,0 +1,9 @@
CREATE TABLE `SearchCache` (
`SearchFields` varchar(384) NOT NULL,
`SearchString` varchar(128) NOT NULL,
`Content` longtext DEFAULT NULL,
`LastSearch` datetime DEFAULT NULL,
PRIMARY KEY (`SearchFields`,`SearchString`),
KEY `idx_SearchString` (`SearchFields`,`SearchString`),
KEY `idx_LastSearch` (`LastSearch`)
);

View File

@@ -56,6 +56,8 @@
<None Remove="Support\Database\MySQL\gaseous-1009.sql" />
<None Remove="Support\Database\MySQL\gaseous-1010.sql" />
<None Remove="Support\Database\MySQL\gaseous-1011.sql" />
<None Remove="Support\Database\MySQL\gaseous-1012.sql" />
<None Remove="Support\Database\MySQL\gaseous-1013.sql" />
<None Remove="Classes\Metadata\" />
</ItemGroup>
<ItemGroup>
@@ -89,5 +91,7 @@
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1009.sql" />
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1010.sql" />
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1011.sql" />
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1012.sql" />
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1013.sql" />
</ItemGroup>
</Project>

View File

@@ -1,6 +1,7 @@
<div id="properties_toc">
<div id="properties_toc_general" name="properties_toc_item" onclick="SelectTab('general');">General</div>
<div id="properties_toc_attributes" name="properties_toc_item" onclick="SelectTab('attributes');">Attributes</div>
<div id="properties_toc_archive" name="properties_toc_item" onclick="SelectTab('archive');" style="display: none;">Archive Contents</div>
<div id="properties_toc_attributes" name="properties_toc_item" onclick="SelectTab('attributes');" style="display: none;">Attributes</div>
<div id="properties_toc_match" name="properties_toc_item" onclick="SelectTab('match');">Title Match</div>
<!--<div id="properties_toc_manage" name="properties_toc_item" onclick="SelectTab('manage');">Manage</div>-->
</div>
@@ -53,6 +54,10 @@
</table>
</div>
<div id="properties_bodypanel_archive" name="properties_tab" style="display: none;">
<div id="properties_bodypanel_archive_content" style="height: 315px; overflow-x: scroll;"></div>
</div>
<div id="properties_bodypanel_attributes" name="properties_tab" style="display: none;">
</div>
@@ -120,7 +125,6 @@
ajaxCall('/api/v1.1/Games/' + gameId + '/roms/' + modalVariables, 'GET', function (result) {
romData = result;
console.log(romData);
document.getElementById('modal-heading').innerHTML = result.name;
document.getElementById('rominfo_library').innerHTML = result.library.name;
document.getElementById('rominfo_platform').innerHTML = result.platform;
@@ -142,8 +146,7 @@
if (result.attributes.length > 0) {
document.getElementById('properties_bodypanel_attributes').appendChild(BuildAttributesTable(result.attributes, result.source));
} else {
document.getElementById('properties_toc_attributes').style.display = 'none';
document.getElementById('properties_bodypanel_archive_content').appendChild(BuildArchiveTable(result.attributes, result.source));
}
});
@@ -199,12 +202,9 @@
$('#properties_fixplatform').on('select2:select', function (e) {
var platformData = e.params.data;
console.log(platformData);
var gameValue = $('#properties_fixgame').select2('data');
if (gameValue) {
console.log(gameValue[0]);
setFixGameDropDown();
}
});
@@ -264,6 +264,9 @@
aTable.style.width = '100%';
for (var i = 0; i < attributes.length; i++) {
if (attributes[i].key != "ZipContents") {
// show attributes button
document.getElementById('properties_toc_attributes').style.display = '';
var aRow = document.createElement('tr');
var aTitleCell = document.createElement('th');
@@ -282,6 +285,55 @@
aTable.appendChild(aRow);
}
}
return aTable;
}
function BuildArchiveTable(attributes, sourceName) {
for (var i = 0; i < attributes.length; i++) {
if (attributes[i].key == "ZipContents") {
var archiveContent = JSON.parse(attributes[i].value);
// show archive button
document.getElementById('properties_toc_archive').style.display = '';
var aTable = document.createElement('table');
aTable.className = 'romtable';
aTable.setAttribute('cellspacing', 0);
aTable.style.width = '100%';
for (var r = 0; r < archiveContent.length; r++) {
var aBody = document.createElement('tbody');
aBody.className = 'romrow';
var aRow = document.createElement('tr');
var aNameCell = document.createElement('th');
aNameCell.className = 'romcell';
aNameCell.innerHTML = archiveContent[r].FilePath + '/' + archiveContent[r].FileName;
aRow.appendChild(aNameCell);
var aSizeCell = document.createElement('td');
aSizeCell.className = 'romcell';
aSizeCell.innerHTML = formatBytes(archiveContent[r].Size);
aRow.appendChild(aSizeCell);
aBody.appendChild(aRow);
var hRow = document.createElement('tr');
var aHashCell = document.createElement('td');
aHashCell.setAttribute('colspan', 2);
aHashCell.style.paddingLeft = '20px';
aHashCell.innerHTML = "MD5: " + archiveContent[r].MD5 + "<br />SHA1: " + archiveContent[r].SHA1;
hRow.appendChild(aHashCell);
aBody.appendChild(hRow);
aTable.appendChild(aBody);
}
}
}
return aTable;
}

View File

@@ -245,8 +245,6 @@ function intToRGB(i) {
}
function DropDownRenderGameOption(state) {
console.log(state);
if (state.loading) {
return state;
}
@@ -260,7 +258,7 @@ function DropDownRenderGameOption(state) {
if (state.cover) {
response = $(
'<table class="dropdown-div"><tr><td class="dropdown-cover"><img src="/api/v1.1/Games/' + state.id + '/cover/image/cover_small/' + state.cover.value.imageId + '.jpg" /></td><td class="dropdown-label"><span class="dropdown-title">' + state.text + '</span><span class="dropdown-releasedate">' + releaseDate + '</span></td></tr></table>'
'<table class="dropdown-div"><tr><td class="dropdown-cover"><img src="/api/v1.1/Games/' + state.id + '/cover/image/cover_small/' + state.cover.imageId + '.jpg" /></td><td class="dropdown-label"><span class="dropdown-title">' + state.text + '</span><span class="dropdown-releasedate">' + releaseDate + '</span></td></tr></table>'
);
} else {
response = $(