Add support for the RetroPie folder structure when building Collections (#88)

* Moved Bios info to the root of the platform map, started adding more content to the platform map to support collection naming options

* Major updates to the PlatformMap.json

* Added support for RetroPie directory structures and adding relevant BIOS files
This commit is contained in:
Michael Green
2023-09-07 15:55:41 +10:00
committed by GitHub
parent bd7124a5be
commit 7da17b91a0
8 changed files with 836 additions and 202 deletions

View File

@@ -16,16 +16,13 @@ namespace gaseous_server.Classes
{ {
foreach (Models.PlatformMapping.PlatformMapItem platformMapping in Models.PlatformMapping.PlatformMap) foreach (Models.PlatformMapping.PlatformMapItem platformMapping in Models.PlatformMapping.PlatformMap)
{ {
if (platformMapping.WebEmulator != null) if (platformMapping.Bios != null)
{ {
if (platformMapping.WebEmulator.Bios != null) foreach (Models.PlatformMapping.PlatformMapItem.EmulatorBiosItem emulatorBiosItem in platformMapping.Bios)
{ {
foreach (Models.PlatformMapping.PlatformMapItem.WebEmulatorItem.EmulatorBiosItem emulatorBiosItem in platformMapping.WebEmulator.Bios) if (emulatorBiosItem.hash.ToLower() == MD5.ToLower())
{ {
if (emulatorBiosItem.hash.ToLower() == MD5.ToLower()) return platformMapping;
{
return platformMapping;
}
} }
} }
} }
@@ -69,33 +66,29 @@ namespace gaseous_server.Classes
foreach (Models.PlatformMapping.PlatformMapItem platformMapping in Models.PlatformMapping.PlatformMap) foreach (Models.PlatformMapping.PlatformMapItem platformMapping in Models.PlatformMapping.PlatformMap)
{ {
if (platformMapping.WebEmulator != null) if (platformMapping.Bios != null)
{ {
if (platformMapping.WebEmulator.Bios != null) IGDB.Models.Platform platform = Metadata.Platforms.GetPlatform(platformMapping.IGDBId);
{
IGDB.Models.Platform platform = Metadata.Platforms.GetPlatform(platformMapping.IGDBId);
foreach (Models.PlatformMapping.PlatformMapItem.WebEmulatorItem.EmulatorBiosItem emulatorBios in platformMapping.WebEmulator.Bios) foreach (Models.PlatformMapping.PlatformMapItem.EmulatorBiosItem emulatorBios in platformMapping.Bios)
{
BiosItem biosItem = new BiosItem
{ {
BiosItem biosItem = new BiosItem platformid = platformMapping.IGDBId,
{ platformslug = platform.Slug,
platformid = platformMapping.IGDBId, platformname = platform.Name,
platformslug = platform.Slug, description = emulatorBios.description,
platformname = platform.Name, filename = emulatorBios.filename,
description = emulatorBios.description, hash = emulatorBios.hash
filename = emulatorBios.filename, };
region = emulatorBios.region, biosItems.Add(biosItem);
hash = emulatorBios.hash
};
biosItems.Add(biosItem);
}
} }
} }
} }
return biosItems; return biosItems;
} }
public class BiosItem : Models.PlatformMapping.PlatformMapItem.WebEmulatorItem.EmulatorBiosItem public class BiosItem : Models.PlatformMapping.PlatformMapItem.EmulatorBiosItem
{ {
public long platformid { get; set; } public long platformid { get; set; }
public string platformslug { get; set; } public string platformslug { get; set; }

View File

@@ -6,6 +6,7 @@ using System.Runtime.InteropServices;
using System.Security.Cryptography; using System.Security.Cryptography;
using gaseous_server.Classes.Metadata; using gaseous_server.Classes.Metadata;
using gaseous_server.Controllers; using gaseous_server.Controllers;
using gaseous_server.Models;
using gaseous_tools; using gaseous_tools;
using IGDB.Models; using IGDB.Models;
using Newtonsoft.Json; using Newtonsoft.Json;
@@ -57,7 +58,7 @@ namespace gaseous_server.Classes
public static CollectionItem NewCollection(CollectionItem item) public static CollectionItem NewCollection(CollectionItem item)
{ {
Database db = new gaseous_tools.Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); Database db = new gaseous_tools.Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "INSERT INTO RomCollections (`Name`, Description, Platforms, Genres, Players, PlayerPerspectives, Themes, MinimumRating, MaximumRating, MaximumRomsPerPlatform, MaximumBytesPerPlatform, MaximumCollectionSizeInBytes, BuiltStatus) VALUES (@name, @description, @platforms, @genres, @players, @playerperspectives, @themes, @minimumrating, @maximumrating, @maximumromsperplatform, @maximumbytesperplatform, @maximumcollectionsizeinbytes, @builtstatus); SELECT CAST(LAST_INSERT_ID() AS SIGNED);"; string sql = "INSERT INTO RomCollections (`Name`, Description, Platforms, Genres, Players, PlayerPerspectives, Themes, MinimumRating, MaximumRating, MaximumRomsPerPlatform, MaximumBytesPerPlatform, MaximumCollectionSizeInBytes, FolderStructure, IncludeBIOSFiles, BuiltStatus) VALUES (@name, @description, @platforms, @genres, @players, @playerperspectives, @themes, @minimumrating, @maximumrating, @maximumromsperplatform, @maximumbytesperplatform, @maximumcollectionsizeinbytes, @folderstructure, @includebiosfiles, @builtstatus); SELECT CAST(LAST_INSERT_ID() AS SIGNED);";
Dictionary<string, object> dbDict = new Dictionary<string, object>(); Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("name", item.Name); dbDict.Add("name", item.Name);
dbDict.Add("description", item.Description); dbDict.Add("description", item.Description);
@@ -71,6 +72,8 @@ namespace gaseous_server.Classes
dbDict.Add("maximumromsperplatform", Common.ReturnValueIfNull(item.MaximumRomsPerPlatform, -1)); dbDict.Add("maximumromsperplatform", Common.ReturnValueIfNull(item.MaximumRomsPerPlatform, -1));
dbDict.Add("maximumbytesperplatform", Common.ReturnValueIfNull(item.MaximumBytesPerPlatform, -1)); dbDict.Add("maximumbytesperplatform", Common.ReturnValueIfNull(item.MaximumBytesPerPlatform, -1));
dbDict.Add("maximumcollectionsizeinbytes", Common.ReturnValueIfNull(item.MaximumCollectionSizeInBytes, -1)); dbDict.Add("maximumcollectionsizeinbytes", Common.ReturnValueIfNull(item.MaximumCollectionSizeInBytes, -1));
dbDict.Add("folderstructure", Common.ReturnValueIfNull(item.FolderStructure, CollectionItem.FolderStructures.Gaseous));
dbDict.Add("includebiosfiles", Common.ReturnValueIfNull(item.IncludeBIOSFiles, 0));
dbDict.Add("builtstatus", CollectionItem.CollectionBuildStatus.WaitingForBuild); dbDict.Add("builtstatus", CollectionItem.CollectionBuildStatus.WaitingForBuild);
DataTable romDT = db.ExecuteCMD(sql, dbDict); DataTable romDT = db.ExecuteCMD(sql, dbDict);
long CollectionId = (long)romDT.Rows[0][0]; long CollectionId = (long)romDT.Rows[0][0];
@@ -85,7 +88,7 @@ namespace gaseous_server.Classes
public static CollectionItem EditCollection(long Id, CollectionItem item) public static CollectionItem EditCollection(long Id, CollectionItem item)
{ {
Database db = new gaseous_tools.Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); Database db = new gaseous_tools.Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "UPDATE RomCollections SET `Name`=@name, Description=@description, Platforms=@platforms, Genres=@genres, Players=@players, PlayerPerspectives=@playerperspectives, Themes=@themes, MinimumRating=@minimumrating, MaximumRating=@maximumrating, MaximumRomsPerPlatform=@maximumromsperplatform, MaximumBytesPerPlatform=@maximumbytesperplatform, MaximumCollectionSizeInBytes=@maximumcollectionsizeinbytes, BuiltStatus=@builtstatus WHERE Id=@id"; string sql = "UPDATE RomCollections SET `Name`=@name, Description=@description, Platforms=@platforms, Genres=@genres, Players=@players, PlayerPerspectives=@playerperspectives, Themes=@themes, MinimumRating=@minimumrating, MaximumRating=@maximumrating, MaximumRomsPerPlatform=@maximumromsperplatform, MaximumBytesPerPlatform=@maximumbytesperplatform, MaximumCollectionSizeInBytes=@maximumcollectionsizeinbytes, FolderStructure=@folderstructure, IncludeBIOSFiles=@includebiosfiles, BuiltStatus=@builtstatus WHERE Id=@id";
Dictionary<string, object> dbDict = new Dictionary<string, object>(); Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("id", Id); dbDict.Add("id", Id);
dbDict.Add("name", item.Name); dbDict.Add("name", item.Name);
@@ -100,6 +103,8 @@ namespace gaseous_server.Classes
dbDict.Add("maximumromsperplatform", Common.ReturnValueIfNull(item.MaximumRomsPerPlatform, -1)); dbDict.Add("maximumromsperplatform", Common.ReturnValueIfNull(item.MaximumRomsPerPlatform, -1));
dbDict.Add("maximumbytesperplatform", Common.ReturnValueIfNull(item.MaximumBytesPerPlatform, -1)); dbDict.Add("maximumbytesperplatform", Common.ReturnValueIfNull(item.MaximumBytesPerPlatform, -1));
dbDict.Add("maximumcollectionsizeinbytes", Common.ReturnValueIfNull(item.MaximumCollectionSizeInBytes, -1)); dbDict.Add("maximumcollectionsizeinbytes", Common.ReturnValueIfNull(item.MaximumCollectionSizeInBytes, -1));
dbDict.Add("folderstructure", Common.ReturnValueIfNull(item.FolderStructure, CollectionItem.FolderStructures.Gaseous));
dbDict.Add("includebiosfiles", Common.ReturnValueIfNull(item.IncludeBIOSFiles, 0));
dbDict.Add("builtstatus", CollectionItem.CollectionBuildStatus.WaitingForBuild); dbDict.Add("builtstatus", CollectionItem.CollectionBuildStatus.WaitingForBuild);
db.ExecuteCMD(sql, dbDict); db.ExecuteCMD(sql, dbDict);
@@ -299,11 +304,50 @@ namespace gaseous_server.Classes
// gather collection files // gather collection files
Directory.CreateDirectory(ZipFileTempPath); Directory.CreateDirectory(ZipFileTempPath);
string ZipBiosPath = Path.Combine(ZipFileTempPath, "BIOS");
// get the games
foreach (CollectionContents.CollectionPlatformItem collectionPlatformItem in collectionPlatformItems) foreach (CollectionContents.CollectionPlatformItem collectionPlatformItem in collectionPlatformItems)
{ {
// get platform bios files if present
if (collectionItem.IncludeBIOSFiles == true)
{
List<Bios.BiosItem> bios = Bios.GetBios(collectionPlatformItem.Id, true);
if (!Directory.Exists(ZipBiosPath)) {
Directory.CreateDirectory(ZipBiosPath);
}
foreach (Bios.BiosItem biosItem in bios)
{
if (File.Exists(biosItem.biosPath))
{
File.Copy(biosItem.biosPath, Path.Combine(ZipBiosPath, biosItem.filename));
}
}
}
// create platform directory // create platform directory
string ZipPlatformPath = Path.Combine(ZipFileTempPath, collectionPlatformItem.Slug); string ZipPlatformPath = "";
switch (collectionItem.FolderStructure)
{
case CollectionItem.FolderStructures.Gaseous:
ZipPlatformPath = Path.Combine(ZipFileTempPath, collectionPlatformItem.Slug);
break;
case CollectionItem.FolderStructures.RetroPie:
try
{
PlatformMapping.PlatformMapItem platformMapItem = PlatformMapping.GetPlatformMappingByIGDBid(collectionPlatformItem.Id);
ZipPlatformPath = Path.Combine(ZipFileTempPath, "roms", platformMapItem.RetroPieDirectoryName);
}
catch
{
ZipPlatformPath = Path.Combine(ZipFileTempPath, collectionPlatformItem.Slug);
}
break;
}
if (!Directory.Exists(ZipPlatformPath)) if (!Directory.Exists(ZipPlatformPath))
{ {
Directory.CreateDirectory(ZipPlatformPath); Directory.CreateDirectory(ZipPlatformPath);
@@ -311,13 +355,23 @@ namespace gaseous_server.Classes
foreach (CollectionContents.CollectionPlatformItem.CollectionGameItem collectionGameItem in collectionPlatformItem.Games) foreach (CollectionContents.CollectionPlatformItem.CollectionGameItem collectionGameItem in collectionPlatformItem.Games)
{ {
// create game directory string ZipGamePath = "";
string ZipGamePath = Path.Combine(ZipPlatformPath, collectionGameItem.Slug); switch (collectionItem.FolderStructure)
if (!Directory.Exists(ZipGamePath))
{ {
Directory.CreateDirectory(ZipGamePath); case CollectionItem.FolderStructures.Gaseous:
} // create game directory
ZipGamePath = Path.Combine(ZipPlatformPath, collectionGameItem.Slug);
if (!Directory.Exists(ZipGamePath))
{
Directory.CreateDirectory(ZipGamePath);
}
break;
case CollectionItem.FolderStructures.RetroPie:
ZipGamePath = ZipPlatformPath;
break;
}
// copy in roms // copy in roms
foreach (Roms.GameRomItem gameRomItem in collectionGameItem.Roms) foreach (Roms.GameRomItem gameRomItem in collectionGameItem.Roms)
{ {
@@ -386,6 +440,8 @@ namespace gaseous_server.Classes
item.MaximumRomsPerPlatform = (int)Common.ReturnValueIfNull(row["MaximumRomsPerPlatform"], (int)-1); item.MaximumRomsPerPlatform = (int)Common.ReturnValueIfNull(row["MaximumRomsPerPlatform"], (int)-1);
item.MaximumBytesPerPlatform = (long)Common.ReturnValueIfNull(row["MaximumBytesPerPlatform"], (long)-1); item.MaximumBytesPerPlatform = (long)Common.ReturnValueIfNull(row["MaximumBytesPerPlatform"], (long)-1);
item.MaximumCollectionSizeInBytes = (long)Common.ReturnValueIfNull(row["MaximumCollectionSizeInBytes"], (long)-1); item.MaximumCollectionSizeInBytes = (long)Common.ReturnValueIfNull(row["MaximumCollectionSizeInBytes"], (long)-1);
item.FolderStructure = (CollectionItem.FolderStructures)(int)Common.ReturnValueIfNull(row["FolderStructure"], 0);
item.IncludeBIOSFiles = (bool)row["IncludeBIOSFiles"];
item.BuildStatus = (CollectionItem.CollectionBuildStatus)(int)Common.ReturnValueIfNull(row["BuiltStatus"], 0); item.BuildStatus = (CollectionItem.CollectionBuildStatus)(int)Common.ReturnValueIfNull(row["BuiltStatus"], 0);
return item; return item;
@@ -411,6 +467,8 @@ namespace gaseous_server.Classes
public int? MaximumRomsPerPlatform { get; set; } public int? MaximumRomsPerPlatform { get; set; }
public long? MaximumBytesPerPlatform { get; set; } public long? MaximumBytesPerPlatform { get; set; }
public long? MaximumCollectionSizeInBytes { get; set; } public long? MaximumCollectionSizeInBytes { get; set; }
public FolderStructures FolderStructure { get; set; } = FolderStructures.Gaseous;
public bool IncludeBIOSFiles { get; set; } = true;
[JsonIgnore] [JsonIgnore]
public CollectionBuildStatus BuildStatus public CollectionBuildStatus BuildStatus
@@ -473,6 +531,12 @@ namespace gaseous_server.Classes
Completed = 3, Completed = 3,
Failed = 4 Failed = 4
} }
public enum FolderStructures
{
Gaseous = 0,
RetroPie = 1
}
} }
public class CollectionContents { public class CollectionContents {

View File

@@ -33,6 +33,19 @@ namespace gaseous_server.Models
} }
} }
public static PlatformMapItem GetPlatformMappingByIGDBid(long Id)
{
foreach (Models.PlatformMapping.PlatformMapItem platformMapping in PlatformMap)
{
if (platformMapping.IGDBId == Id)
{
return platformMapping;
}
}
throw new Exception("Platform id not found");
}
public static void GetIGDBPlatformMapping(ref Models.Signatures_Games Signature, FileInfo RomFileInfo, bool SetSystemName) public static void GetIGDBPlatformMapping(ref Models.Signatures_Games Signature, FileInfo RomFileInfo, bool SetSystemName)
{ {
bool PlatformFound = false; bool PlatformFound = false;
@@ -81,22 +94,22 @@ 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, object>? WebEmulator { get; set; } public string RetroPieDirectoryName { get; set; }
public WebEmulatorItem? WebEmulator { get; set; } public WebEmulatorItem? WebEmulator { get; set; }
public class WebEmulatorItem public class WebEmulatorItem
{ {
public string Type { get; set; } public string Type { get; set; }
public string Core { get; set; } public string Core { get; set; }
public List<EmulatorBiosItem> Bios { get; set; } }
public class EmulatorBiosItem public List<EmulatorBiosItem> Bios { get; set; }
{
public string hash { get; set; } public class EmulatorBiosItem
public string description { get; set; } {
public string filename { get; set; } public string hash { get; set; }
public string region { get; set; } public string description { get; set; }
} public string filename { get; set; }
} }
} }
} }

View File

@@ -25,11 +25,6 @@ if (Config.ReadSetting("API Key", "Test API Key") == "Test API Key")
Logging.Log(Logging.LogType.Information, "Startup", "Setting initial API key"); Logging.Log(Logging.LogType.Information, "Startup", "Setting initial API key");
Config.SetSetting("API Key", APIKey.ToString()); Config.SetSetting("API Key", APIKey.ToString());
} }
if (Config.ReadSetting("Emulator: Default BIOS Region", "Default Value") == "Default Value")
{
Logging.Log(Logging.LogType.Information, "Startup", "Setting default BIOS region to US");
Config.SetSetting("Emulator: Default BIOS Region", "US");
}
// set up server // set up server
var builder = WebApplication.CreateBuilder(args); var builder = WebApplication.CreateBuilder(args);

View File

@@ -1,7 +1,300 @@
[ [
{
"IGDBId": 50,
"IGDBName": "3DO Interactive Multiplayer",
"RetroPieDirectoryName": "3do",
"AlternateNames": [
"3DO",
"3DO Interactive Multiplayer"
],
"KnownFileExtensions": [
],
"WebEmulator": {
"Type": "EmulatorJS",
"Core": "3do"
},
"Bios": [
{
"hash": "f47264dd47fe30f73ab3c010015c155b",
"description": "Panasonic FZ-1",
"filename": "panafz1.bin"
},
{
"hash": "51f2f43ae2f3508a14d9f56597e2d3ce",
"description": "Panasonic FZ-10",
"filename": "panafz10.bin"
},
{
"hash": "1477bda80dc33731a65468c1f5bcbee9",
"description": "Panasonic FZ-10 [RSA Patch]",
"filename": "panafz10-norsa.bin"
},
{
"hash": "a48e6746bd7edec0f40cff078f0bb19f",
"description": "Panasonic FZ-10-E [Anvil]",
"filename": "panafz10e-anvil.bin"
},
{
"hash": "cf11bbb5a16d7af9875cca9de9a15e09",
"description": "Panasonic FZ-10-E [Anvil RSA Patch]",
"filename": "panafz10e-anvil-norsa.bin"
},
{
"hash": "a496cfdded3da562759be3561317b605",
"description": "Panasonic FZ-1J",
"filename": "panafz1j.bin"
},
{
"hash": "f6c71de7470d16abe4f71b1444883dc8",
"description": "Panasonic FZ-1J [RSA Patch]",
"filename": "panafz1j-norsa.bin"
},
{
"hash": "8639fd5e549bd6238cfee79e3e749114",
"description": "Goldstar GDO-101M",
"filename": "goldstar.bin"
},
{
"hash": "35fa1a1ebaaeea286dc5cd15487c13ea",
"description": "Sanyo IMP-21J TRY",
"filename": "sanyotry.bin"
},
{
"hash": "8970fc987ab89a7f64da9f8a8c4333ff",
"description": "Shootout At Old Tucson",
"filename": "3do_arcade_saot.bin"
}
]
},
{
"IGDBId": 16,
"IGDBName": "Amiga",
"RetroPieDirectoryName": "amiga",
"AlternateNames": [
"Amiga",
"Commodore Amiga"
],
"KnownFileExtensions": [
".ADF",
".ADZ",
".DMS",
".UAE",
".HDF",
".HDZ"
],
"Bios": [
{
"hash": "85ad74194e87c08904327de1a9443b7a",
"description": "Kickstart v1.2 rev 33.180",
"filename": "kick33180.A500"
},
{
"hash": "82a21c1890cae844b3df741f2762d48d",
"description": "Kickstart v1.3 rev 34.005",
"filename": "kick34005.A500"
},
{
"hash": "dc10d7bdd1b6f450773dfb558477c230",
"description": "Kickstart v2.04 rev 37.175",
"filename": "kick37175.A500"
},
{
"hash": "e40a5dfb3d017ba8779faba30cbd1c8e",
"description": "Kickstart v3.1 rev 40.063",
"filename": "kick40063.A600"
},
{
"hash": "646773759326fbac3b2311fd8c8793ee",
"description": "Kickstart v3.1 rev 40.068",
"filename": "kick40068.A1200"
},
{
"hash": "9bdedde6a4f33555b4a270c8ca53297d",
"description": "Kickstart v3.1 rev 40.068",
"filename": "kick40068.A4000"
},
{
"hash": "89da1838a24460e4b93f4f0c5d92d48d",
"description": "CDTV extended ROM v1.00",
"filename": "kick34005.CDTV"
},
{
"hash": "5f8924d013dd57a89cf349f4cdedc6b1",
"description": "CD32 Kickstart v3.1 rev 40.060",
"filename": "kick40060.CD32"
},
{
"hash": "bb72565701b1b6faece07d68ea5da639",
"description": "CD32 extended ROM rev 40.060",
"filename": "kick40060.CD32.ext"
},
{
"hash": "f2f241bf094168cfb9e7805dc2856433",
"description": "CD32 KS + extended v3.1 rev 40.060",
"filename": "kick40060.CD32"
}
]
},
{
"IGDBId": 25,
"IGDBName": "Amstrad CPC",
"RetroPieDirectoryName": "amstradcpc",
"AlternateNames": [
"Amstrad CPC"
],
"KnownFileExtensions": [
".CPC"
],
"Bios": [
]
},
{
"IGDBId": 75,
"IGDBName": "Apple II",
"RetroPieDirectoryName": "apple2",
"AlternateNames": [
"Apple II",
"Apple ][",
"Apple 2"
],
"KnownFileExtensions": [
],
"Bios": [
]
},
{
"IGDBId": 59,
"IGDBName": "Atari 2600",
"RetroPieDirectoryName": "atari2600",
"AlternateNames": [
"Atari 2600",
"Atari VCS"
],
"KnownFileExtensions": [
".A26"
],
"WebEmulator": {
"Type": "EmulatorJS",
"Core": "atari2600"
},
"Bios": [
]
},
{
"IGDBId": 60,
"IGDBName": "Atari 7800",
"RetroPieDirectoryName": "atari7800",
"AlternateNames": [
"Atari 7800 ProSystem",
"Atari VCS"
],
"KnownFileExtensions": [
".A78"
],
"WebEmulator": {
"Type": "EmulatorJS",
"Core": "atari7800"
},
"Bios": [
{
"hash": "0763f1ffb006ddbe32e52d497ee848ae",
"description": "7800 BIOS - Optional",
"filename": "7800 BIOS (U).rom"
}
]
},
{
"IGDBId": 66,
"IGDBName": "Atari 5200",
"RetroPieDirectoryName": "atari5200",
"AlternateNames": [
"Atari 5200",
"Atari 5200 SuperSystem"
],
"KnownFileExtensions": [
".A52"
],
"WebEmulator": {
"Type": "EmulatorJS",
"Core": "a5200"
},
"Bios": [
{
"hash": "281f20ea4320404ec820fb7ec0693b38",
"description": "BIOS for the Atari 5200",
"filename": "5200.rom"
}
]
},
{
"IGDBId": 62,
"IGDBName": "Atari Jaguar",
"RetroPieDirectoryName": "atarijaguar",
"AlternateNames": [
"Atari Jaguar",
"Jaguar"
],
"KnownFileExtensions": [
".J64",
".JAG"
],
"WebEmulator": {
"Type": "EmulatorJS",
"Core": "jaguar"
},
"Bios": [
]
},
{
"IGDBId": 61,
"IGDBName": "Atari Lynx",
"RetroPieDirectoryName": "atarilynx",
"AlternateNames": [
"Atari Lynx",
"Lynx"
],
"KnownFileExtensions": [
".LNX"
],
"WebEmulator": {
"Type": "EmulatorJS",
"Core": "lynx"
},
"Bios": [
{
"hash": "fcd403db69f54290b51035d82f835e7b",
"description": "BIOS for the Atari Lynx",
"filename": "lynxboot.img"
}
]
},
{
"IGDBId": 63,
"IGDBName": "Atari ST/STE",
"RetroPieDirectoryName": "atarist",
"AlternateNames": [
"Atari ST/STE",
"Atari ST",
"Atari STE"
],
"KnownFileExtensions": [
".ST",
".STX",
".MSA"
],
"Bios": [
{
"hash": "C1C57CE48E8EE4135885CEE9E63A68A2",
"description": "TOS",
"filename": "tos.img"
}
]
},
{ {
"IGDBId": 15, "IGDBId": 15,
"IGDBName": "Commodore C64/128/MAX", "IGDBName": "Commodore C64/128/MAX",
"RetroPieDirectoryName": "c64",
"AlternateNames": [ "AlternateNames": [
"C64", "C64",
"Commodore C64", "Commodore C64",
@@ -19,60 +312,151 @@
".PRG", ".PRG",
".T64", ".T64",
".TAP" ".TAP"
],
"Bios": [
{
"hash": "be09394f0576cf81fa8bacf634daf9a2",
"description": "JiffyDOS C64 Kernal",
"filename": "JiffyDOS_C64.bin"
},
{
"hash": "1b1e985ea5325a1f46eb7fd9681707bf",
"description": "JiffyDOS 1541 drive BIOS",
"filename": "JiffyDOS_1541-II.bin"
},
{
"hash": "41c6cc528e9515ffd0ed9b180f8467c0",
"description": "JiffyDOS 1571 drive BIOS",
"filename": "JiffyDOS_1571_repl310654.bin"
},
{
"hash": "20b6885c6dc2d42c38754a365b043d71",
"description": "JiffyDOS 1581 drive BIOS",
"filename": "JiffyDOS_1581.bin"
}
] ]
}, },
{ {
"IGDBId": 16, "IGDBId": 24,
"IGDBName": "Amiga", "IGDBName": "Game Boy Advance",
"RetroPieDirectoryName": "gba",
"AlternateNames": [ "AlternateNames": [
"Amiga", "GBA",
"Commodore Amiga" "Game Boy Advance"
], ],
"KnownFileExtensions": [ "KnownFileExtensions": [
".ADF", ".GBA"
".ADZ", ],
".DMS" "Bios": [
{
"hash": "a860e8c0b6d573d191e4ec7db1b1e4f6",
"description": "[BIOS] Game Boy Advance (World).gba",
"filename": "gba_bios.bin"
},
{
"hash": "32FBBD84168D3482956EB3C5051637F5",
"description": "[BIOS] Nintendo Game Boy Boot ROM (World) (Rev 1).gb",
"filename": "gb_bios.bin"
},
{
"hash": "DBFCE9DB9DEAA2567F6A84FDE55F9680",
"description": "[BIOS] Nintendo Game Boy Color Boot ROM (World).gbc",
"filename": "gbc_bios.bin"
},
{
"hash": "D574D4F9C12F305074798F54C091A8B4",
"description": "SGB-CPU (World) (Enhancement Chip).bin",
"filename": "sgb_bios.bin"
}
] ]
}, },
{ {
"IGDBId": 64, "IGDBId": 22,
"IGDBName": "Sega Master System/Mark III", "IGDBName": "Game Boy Color",
"RetroPieDirectoryName": "gbc",
"AlternateNames": [ "AlternateNames": [
"Sega Master System/Mark III", "GBC",
"Sega Master System", "Game Boy Color"
"Sega Mark III & Master System"
], ],
"KnownFileExtensions": [ "KnownFileExtensions": [
".SMS" ".GBC"
], ],
"WebEmulator": { "Bios": [
"Type": "EmulatorJS", {
"Core": "segaMS", "hash": "dbfce9db9deaa2567f6a84fde55f9680",
"Bios": [ "description": "BIOS for Game Boy Color",
{ "filename": "gbc_bios.bin"
"hash": "840481177270d5642a14ca71ee72844c", },
"description": "MasterSystem EU BIOS", {
"filename": "bios_E.sms", "hash": "d574d4f9c12f305074798f54c091a8b4",
"region": "EU" "description": "Super Game Boy",
}, "filename": "sgb_bios.bin"
{ }
"hash": "840481177270d5642a14ca71ee72844c", ]
"description": "MasterSystem US BIOS", },
"filename": "bios_U.sms", {
"region": "US" "IGDBId": 33,
}, "IGDBName": "Game Boy",
{ "RetroPieDirectoryName": "gb",
"hash": "24a519c53f67b00640d0048ef7089105", "AlternateNames": [
"description": "MasterSystem JP BIOS", "GB",
"filename": "bios_J.sms", "Game Boy"
"region": "JP" ],
} "KnownFileExtensions": [
] ".GB"
} ],
"Bios": [
{
"hash": "32fbbd84168d3482956eb3c5051637f5and",
"description": "BIOS for Game Boy",
"filename": "gb_bios.bin"
},
{
"hash": "d574d4f9c12f305074798f54c091a8b4",
"description": "Super Game Boy",
"filename": "sgb_bios.bin"
}
]
},
{
"IGDBId": 21,
"IGDBName": "Nintendo GameCube",
"RetroPieDirectoryName": "gc",
"AlternateNames": [
"GC",
"GameCube",
"Nintendo GameCube"
],
"KnownFileExtensions": [
".GC"
],
"Bios": [
]
},
{
"IGDBId": 35,
"IGDBName": "Sega Game Gear",
"RetroPieDirectoryName": "gamegear",
"AlternateNames": [
"GG",
"Game Gear",
"Sega Game Gear"
],
"KnownFileExtensions": [
".GG"
],
"Bios": [
{
"hash": "672e104c3be3a238301aceffc3b23fd6",
"description": "GameGear BIOS (bootrom) - Optional",
"filename": "bios.gg"
}
]
}, },
{ {
"IGDBId": 29, "IGDBId": 29,
"IGDBName": "Sega Mega Drive/Genesis", "IGDBName": "Sega Mega Drive/Genesis",
"RetroPieDirectoryName": "megadrive",
"AlternateNames": [ "AlternateNames": [
"Sega Mega Drive/Genesis", "Sega Mega Drive/Genesis",
"Sega Mega Drive", "Sega Mega Drive",
@@ -87,20 +471,57 @@
], ],
"WebEmulator": { "WebEmulator": {
"Type": "EmulatorJS", "Type": "EmulatorJS",
"Core": "segaMD", "Core": "segaMD"
"Bios": [ },
{ "Bios": [
"hash": "45e298905a08f9cfb38fd504cd6dbc84", {
"description": "MegaDrive TMSS startup ROM", "hash": "45e298905a08f9cfb38fd504cd6dbc84",
"filename": "bios_MD.bin", "description": "MegaDrive TMSS startup ROM",
"region": "" "filename": "bios_MD.bin"
} }
] ]
} },
{
"IGDBId": 64,
"IGDBName": "Sega Master System/Mark III",
"RetroPieDirectoryName": "mastersystem",
"AlternateNames": [
"Sega Master System/Mark III",
"Sega Master System",
"Sega Mark III & Master System"
],
"KnownFileExtensions": [
".SMS"
],
"WebEmulator": {
"Type": "EmulatorJS",
"Core": "segaMS"
},
"Bios": [
{
"hash": "840481177270d5642a14ca71ee72844c",
"description": "MasterSystem EU BIOS",
"filename": "bios_E.sms",
"region": "EU"
},
{
"hash": "840481177270d5642a14ca71ee72844c",
"description": "MasterSystem US BIOS",
"filename": "bios_U.sms",
"region": "US"
},
{
"hash": "24a519c53f67b00640d0048ef7089105",
"description": "MasterSystem JP BIOS",
"filename": "bios_J.sms",
"region": "JP"
}
]
}, },
{ {
"IGDBId": 4, "IGDBId": 4,
"IGDBName": "Nintendo 64", "IGDBName": "Nintendo 64",
"RetroPieDirectoryName": "n64",
"AlternateNames": [ "AlternateNames": [
"Nintendo 64", "Nintendo 64",
"N64" "N64"
@@ -118,6 +539,7 @@
{ {
"IGDBId": 18, "IGDBId": 18,
"IGDBName": "Nintendo Entertainment System", "IGDBName": "Nintendo Entertainment System",
"RetroPieDirectoryName": "nes",
"AlternateNames": [ "AlternateNames": [
"Nintendo Entertainment System", "Nintendo Entertainment System",
"NES", "NES",
@@ -131,26 +553,139 @@
], ],
"WebEmulator": { "WebEmulator": {
"Type": "EmulatorJS", "Type": "EmulatorJS",
"Core": "nes", "Core": "nes"
"Bios": [ },
{ "Bios": [
"hash": "ca30b50f880eb660a320674ed365ef7a", {
"description": "Family Computer Disk System BIOS - Required for Famicom Disk System emulation", "hash": "ca30b50f880eb660a320674ed365ef7a",
"filename": "disksys.rom", "description": "Family Computer Disk System BIOS - Required for Famicom Disk System emulation",
"region": "" "filename": "disksys.rom"
}, },
{ {
"hash": "7f98d77d7a094ad7d069b74bd553ec98", "hash": "7f98d77d7a094ad7d069b74bd553ec98",
"description": "Game Genie add-on cartridge - Required for Game Genei Add-on emulation (Only supported on the fceumm core)", "description": "Game Genie add-on cartridge - Required for Game Genei Add-on emulation (Only supported on the fceumm core)",
"filename": "gamegenie.nes", "filename": "gamegenie.nes"
"region": "" }
} ]
] },
} {
"IGDBId": 7,
"IGDBName": "PlayStation",
"RetroPieDirectoryName": "psx",
"AlternateNames": [
"Sony PlayStation",
"PS1",
"PSX",
"PSOne",
"PS"
],
"KnownFileExtensions": [],
"WebEmulator": {
"Type": "EmulatorJS",
"Core": "psx"
},
"Bios": [
{
"hash": "8dd7d5296a650fac7319bce665a6a53c",
"description": "PS1 JP BIOS - Required for JP games",
"filename": "scph5500.bin",
"region": "JP"
},
{
"hash": "490f666e1afb15b7362b406ed1cea246",
"description": "PS1 US BIOS - Required for US games",
"filename": "scph5501.bin",
"region": "US"
},
{
"hash": "32736f17079d0b2b7024407c39bd3050",
"description": "PS1 EU BIOS - Required for EU games",
"filename": "scph5502.bin",
"region": "EU"
}
]
},
{
"IGDBId": 32,
"IGDBName": "Sega Saturn",
"RetroPieDirectoryName": "saturn",
"AlternateNames": [
"Sega Saturn",
"JVC Saturn",
"Hi-Saturn",
"Samsung Saturn",
"V-Saturn",
"Saturn"
],
"KnownFileExtensions": [],
"WebEmulator": {
"Type": "EmulatorJS",
"Core": "segaSaturn"
},
"Bios": [
{
"hash": "af5828fdff51384f99b3c4926be27762",
"description": "Saturn BIOS",
"filename": "saturn_bios.bin"
}
]
},
{
"IGDBId": 30,
"IGDBName": "Sega 32X",
"RetroPieDirectoryName": "sega32x",
"AlternateNames": [
"Sega 32X",
"Sega32",
"Sega32X"
],
"KnownFileExtensions": [],
"WebEmulator": {
"Type": "EmulatorJS",
"Core": "sega32x"
},
"Bios": []
},
{
"IGDBId": 78,
"IGDBName": "Sega CD",
"RetroPieDirectoryName": "segacd",
"AlternateNames": [
"Sega CD",
"Mega CD",
"segacd",
"Sega Mega-CD & Sega CD"
],
"KnownFileExtensions": [],
"WebEmulator": {
"Type": "EmulatorJS",
"Core": "segaCD"
},
"Bios": [
{
"hash": "e66fa1dc5820d254611fdcdba0662372",
"description": "MegaCD EU BIOS - Required",
"filename": "bios_CD_E.bin",
"region": "EU"
},
{
"hash": "2efd74e3232ff260e371b99f84024f7f",
"description": "SegaCD US BIOS - Required",
"filename": "bios_CD_U.bin",
"region": "US"
},
{
"hash": "278a9397d192149e84e820ac621a8edd",
"description": "MegaCD JP BIOS - Required",
"filename": "bios_CD_J.bin",
"region": "JP"
}
]
}, },
{ {
"IGDBId": 19, "IGDBId": 19,
"IGDBName": "Super Nintendo Entertainment System", "IGDBName": "Super Nintendo Entertainment System",
"RetroPieDirectoryName": "snes",
"AlternateNames": [ "AlternateNames": [
"Nintendo Super Famicom & Super Entertainment System", "Nintendo Super Famicom & Super Entertainment System",
"Super Nintendo Entertainment System", "Super Nintendo Entertainment System",
@@ -167,40 +702,20 @@
} }
}, },
{ {
"IGDBId": 7, "IGDBId": 26,
"IGDBName": "PlayStation", "IGDBName": "ZX Spectrum",
"RetroPieDirectoryName": "zxspectrum",
"AlternateNames": [ "AlternateNames": [
"Sony PlayStation", "ZX Spectrum",
"PS1", "Sinclair ZX",
"PSX", "Sinclair ZX Spectrum"
"PSOne",
"PS"
], ],
"KnownFileExtensions": [], "KnownFileExtensions": [
"WebEmulator": { ".SNA",
"Type": "EmulatorJS", ".SZX",
"Core": "psx", ".Z80",
"Bios": [ ".SCL"
{ ]
"hash": "8dd7d5296a650fac7319bce665a6a53c",
"description": "PS1 JP BIOS - Required for JP games",
"filename": "scph5500.bin",
"region": "JP"
},
{
"hash": "490f666e1afb15b7362b406ed1cea246",
"description": "PS1 US BIOS - Required for US games",
"filename": "scph5501.bin",
"region": "US"
},
{
"hash": "32736f17079d0b2b7024407c39bd3050",
"description": "PS1 EU BIOS - Required for EU games",
"filename": "scph5502.bin",
"region": "EU"
}
]
}
}, },
{ {
"IGDBId": 52, "IGDBId": 52,
@@ -211,58 +726,8 @@
"KnownFileExtensions": [], "KnownFileExtensions": [],
"WebEmulator": { "WebEmulator": {
"Type": "EmulatorJS", "Type": "EmulatorJS",
"Core": "arcade", "Core": "arcade"
"Bios": [] },
} "Bios": []
},
{
"IGDBId": 30,
"IGDBName": "Sega 32X",
"AlternateNames": [
"Sega 32X",
"Sega32",
"Sega32X"
],
"KnownFileExtensions": [],
"WebEmulator": {
"Type": "EmulatorJS",
"Core": "sega32x",
"Bios": []
}
},
{
"IGDBId": 78,
"IGDBName": "Sega CD",
"AlternateNames": [
"Sega CD",
"Mega CD",
"segacd",
"Sega Mega-CD & Sega CD"
],
"KnownFileExtensions": [],
"WebEmulator": {
"Type": "EmulatorJS",
"Core": "segaCD",
"Bios": [
{
"hash": "e66fa1dc5820d254611fdcdba0662372",
"description": "MegaCD EU BIOS - Required",
"filename": "bios_CD_E.bin",
"region": "EU"
},
{
"hash": "2efd74e3232ff260e371b99f84024f7f",
"description": "SegaCD US BIOS - Required",
"filename": "bios_CD_U.bin",
"region": "US"
},
{
"hash": "278a9397d192149e84e820ac621a8edd",
"description": "MegaCD JP BIOS - Required",
"filename": "bios_CD_J.bin",
"region": "JP"
}
]
}
} }
] ]

View File

@@ -56,6 +56,47 @@
<th>Maximum collection size (bytes)</th> <th>Maximum collection size (bytes)</th>
<td><input id="collection_maxcollectionsize" type="number" placeholder="0" step="1048576" oninput="DisplayFormattedBytes('collection_maxcollectionsize', 'maxcollectionsize_label');"><span id="maxcollectionsize_label" style="margin-left: 10px;"></span></td></td> <td><input id="collection_maxcollectionsize" type="number" placeholder="0" step="1048576" oninput="DisplayFormattedBytes('collection_maxcollectionsize', 'maxcollectionsize_label');"><span id="maxcollectionsize_label" style="margin-left: 10px;"></span></td></td>
</tr> </tr>
<tr>
<th>Directory Layout</th>
<td>
<select id="collection_directorylayout" style="width: 100%;" onchange="DisplayDirectoryLabel();">
<option id="collection_directorylayout_gaseous" selected="selected" value="Gaseous">Standard</option>
<option id="collection_directorylayout_retropie" value="RetroPie">RetroPie</option>
</select>
</td>
</tr>
<tr>
<td>
</td>
<td>
<span id="collection_directorylayout_gaseous_label" name="collection_directorylayout_label">
<p>Standard layout: /&lt;IGDB Platform Slug&gt;/&lt;IGDB Game Slug&gt;/Game ROM's</p>
<p>Example: /genesis-slash-megadrive/sonic-the-hedgehog/Sonic the Hedgehog.smd</p>
</span>
<span id="collection_directorylayout_retropie_label" style="display: none;" name="collection_directorylayout_label">
<p>RetroPie layout: /roms/&lt;RetroPie Platform Label&gt;/Game ROM's</p>
<p>Example: /roms/megadrive/Sonic the Hedgehog.smd</p>
</span>
</td>
</tr>
<tr>
<th>
Include BIOS files (if available)
</th>
<td>
<select id="collection_includebios" style="width: 100%;">
<option id="collection_includebios_yes" selected="selected" value="true">Yes</option>
<option id="collection_includebios_no" value="false">No</option>
</select>
</td>
</tr>
<tr id="collection_includebios_label">
<td></td>
<td>
BIOS files for each platform will be stored in /BIOS
</td>
</tr>
</table> </table>
</div> </div>
<table style="position: absolute; top: 0px; right: 0px; bottom: 0px; width: 60%;"> <table style="position: absolute; top: 0px; right: 0px; bottom: 0px; width: 60%;">
@@ -208,6 +249,10 @@
} }
}); });
$('#collection_directorylayout').select2();
$('#collection_includebios').select2();
if (modalVariables) { if (modalVariables) {
// edit mode // edit mode
ajaxCall( ajaxCall(
@@ -221,6 +266,10 @@
if (result.maximumRomsPerPlatform != -1) { document.getElementById('collection_maxroms').value = result.maximumRomsPerPlatform; } if (result.maximumRomsPerPlatform != -1) { document.getElementById('collection_maxroms').value = result.maximumRomsPerPlatform; }
if (result.maximumBytesPerPlatform != -1) { document.getElementById('collection_maxplatformsize').value = result.maximumBytesPerPlatform; } if (result.maximumBytesPerPlatform != -1) { document.getElementById('collection_maxplatformsize').value = result.maximumBytesPerPlatform; }
if (result.maximumCollectionSizeInBytes != -1) { document.getElementById('collection_maxcollectionsize').value = result.maximumCollectionSizeInBytes; } if (result.maximumCollectionSizeInBytes != -1) { document.getElementById('collection_maxcollectionsize').value = result.maximumCollectionSizeInBytes; }
if (result.folderStructure) {
$('#collection_directorylayout').val(result.folderStructure).trigger('change');
}
$('#collection_includebios').val(result.includeBIOSFiles.toString()).trigger('change');
// fill select2 controls // fill select2 controls
$.ajax( $.ajax(
@@ -336,7 +385,9 @@
"maximumRating": GetNumberFieldValue('collection_userrating_max'), "maximumRating": GetNumberFieldValue('collection_userrating_max'),
"maximumRomsPerPlatform": GetNumberFieldValue('collection_maxroms'), "maximumRomsPerPlatform": GetNumberFieldValue('collection_maxroms'),
"maximumBytesPerPlatform": GetNumberFieldValue('collection_maxplatformsize'), "maximumBytesPerPlatform": GetNumberFieldValue('collection_maxplatformsize'),
"maximumCollectionSizeInBytes": GetNumberFieldValue('collection_maxcollectionsize') "maximumCollectionSizeInBytes": GetNumberFieldValue('collection_maxcollectionsize'),
"folderStructure": GetNumberFieldValue('collection_directorylayout', "Standard"),
"includeBIOSFiles": GetBooleanFieldValue('collection_includebios')
} }
return item; return item;
@@ -345,8 +396,6 @@
function GetPreview() { function GetPreview() {
var item = GenerateCollectionItem(); var item = GenerateCollectionItem();
console.log(JSON.stringify(item));
ajaxCall( ajaxCall(
'/api/v1/Collections/Preview', '/api/v1/Collections/Preview',
'POST', 'POST',
@@ -375,14 +424,29 @@
} }
} }
function GetNumberFieldValue(objectName) { function GetNumberFieldValue(objectName, defaultValue) {
var obj = document.getElementById(objectName); var obj = document.getElementById(objectName);
var objVal = obj.value; var objVal = obj.value;
if (objVal) { if (objVal) {
return objVal; return objVal;
} else { } else {
return -1; if (defaultValue) {
return defaultValue;
} else {
return -1;
}
}
}
function GetBooleanFieldValue(objectName) {
var obj = document.getElementById(objectName);
var objVal = obj.value;
if (objVal == "false") {
return false;
} else {
return true;
} }
} }
@@ -491,4 +555,32 @@
label.innerHTML = ''; label.innerHTML = '';
} }
} }
function DisplayDirectoryLabel() {
// hide all labels
var directoryLayoutLabels = document.getElementsByName('collection_directorylayout_label');
for (var i = 0; i < directoryLayoutLabels.length; i++) {
directoryLayoutLabels[i].style.display = 'none';
}
// display the label associated with the selection
var directoryLayoutMode = document.getElementById('collection_directorylayout').value;
var labelToDisplay = '';
if (directoryLayoutMode) {
switch(directoryLayoutMode) {
case "Gaseous":
// standard mode
labelToDisplay = 'collection_directorylayout_gaseous_label';
break;
case "RetroPie":
labelToDisplay = 'collection_directorylayout_retropie_label'
break;
}
document.getElementById(labelToDisplay).style.display = '';
}
}
</script> </script>

View File

@@ -36,9 +36,17 @@
if (result[i].available == true) { if (result[i].available == true) {
availableText.innerHTML = 'Available'; availableText.innerHTML = 'Available';
availableText.className = 'greentext'; availableText.className = 'greentext';
biosFilename = document.createElement('a');
biosFilename.href = '/api/v1/Bios/' + result[i].platformid + '/' + result[i].filename;
biosFilename.innerHTML = result[i].filename;
biosFilename.className = 'romlink';
} else { } else {
availableText.innerHTML = 'Unavailable'; availableText.innerHTML = 'Unavailable';
availableText.className = 'redtext'; availableText.className = 'redtext';
biosFilename = document.createElement('span');
biosFilename.innerHTML = result[i].filename;
} }
var newRow = [ var newRow = [

View File

@@ -8,4 +8,8 @@ ADD COLUMN `IngestorVersion` INT NULL DEFAULT 1;
ALTER TABLE `Games_Roms` ALTER TABLE `Games_Roms`
ADD COLUMN `Attributes` JSON NULL AFTER `Flags`, ADD COLUMN `Attributes` JSON NULL AFTER `Flags`,
ADD COLUMN `MetadataGameName` VARCHAR(255) NULL AFTER `MetadataSource`, ADD COLUMN `MetadataGameName` VARCHAR(255) NULL AFTER `MetadataSource`,
ADD COLUMN `MetadataVersion` INT NULL DEFAULT 1; ADD COLUMN `MetadataVersion` INT NULL DEFAULT 1;
ALTER TABLE `RomCollections`
ADD COLUMN `FolderStructure` INT NULL DEFAULT 0 AFTER `MaximumCollectionSizeInBytes`,
ADD COLUMN `IncludeBIOSFiles` BOOLEAN NULL DEFAULT 0 AFTER `FolderStructure`;