mirror of
https://github.com/linuxserver/emulatorjs.git
synced 2026-02-23 00:01:26 +08:00
259 lines
7.6 KiB
JavaScript
259 lines
7.6 KiB
JavaScript
//Default vars
|
|
var storeName = 'RetroArch';
|
|
var afs;
|
|
BrowserFS.install(window);
|
|
var fs = require('fs');
|
|
var mfs = new BrowserFS.FileSystem.MountableFileSystem();
|
|
|
|
// Render file list
|
|
async function renderFiles(directory) {
|
|
directory = directory.replace("|","'");
|
|
let directoryClean = directory.replace("'","|");
|
|
if ((directory !== '/') && (directory.endsWith('/'))) {
|
|
directory = directory.slice(0, -1);
|
|
}
|
|
$('#filebrowser').empty();
|
|
$('#filebrowser').data('directory', directory);
|
|
let items = await fs.readdirSync(directory);
|
|
let baseName = directory.split('/').at(-1);
|
|
let parentFolder = directory.replace(baseName,'');
|
|
let parentLink = $('<td>').addClass('directory').attr('onclick', 'renderFiles(\'' + parentFolder + '\');').text('..');
|
|
if (directoryClean == '/') {
|
|
directoryClean = '';
|
|
}
|
|
let table = $('<table>').addClass('fileTable');
|
|
let tableHeader = $('<tr>');
|
|
for await (name of ['Name', 'Type', 'Delete (NO WARNING)']) {
|
|
tableHeader.append($('<th>').text(name));
|
|
}
|
|
let parentRow = $('<tr>');
|
|
for await (item of [parentLink, $('<td>').text('Parent'), $('<td>')]) {
|
|
parentRow.append(item);
|
|
}
|
|
table.append(tableHeader,parentRow);
|
|
$('#filebrowser').append(table);
|
|
items.sort();
|
|
if (items.length > 0) {
|
|
let dirs = [];
|
|
let files = [];
|
|
for await (let item of items) {
|
|
if (fs.lstatSync(directory + '/' + item).isDirectory()) {
|
|
dirs.push(item)
|
|
} else {
|
|
files.push(item)
|
|
}
|
|
}
|
|
if (dirs.length > 0) {
|
|
for await (let dir of dirs) {
|
|
let tableRow = $('<tr>');
|
|
let dirClean = dir.replace("'","|");
|
|
let link = $('<td>').addClass('directory').attr('onclick', 'renderFiles(\'' + directoryClean + '/' + dirClean + '\');').text(dir);
|
|
let type = $('<td>').text('Dir');
|
|
let del = $('<td>').append($('<button>').addClass('deleteButton').attr('onclick', 'deleter(\'' + directoryClean + '/' + dirClean + '\');').text('Delete'));
|
|
for await (item of [link, type, del]) {
|
|
tableRow.append(item);
|
|
}
|
|
table.append(tableRow);
|
|
}
|
|
}
|
|
if (files.length > 0) {
|
|
for await (let file of files) {
|
|
let tableRow = $('<tr>');
|
|
let fileClean = file.replace("'","|");
|
|
let link = $('<td>').addClass('file').attr('onclick', 'downloadFile(\'' + directoryClean + '/' + fileClean + '\');').text(file);
|
|
let type = $('<td>').text('File');
|
|
let del = $('<td>').append($('<button>').addClass('deleteButton').attr('onclick', 'deleter(\'' + directoryClean + '/' + fileClean + '\');').text('Delete'));
|
|
for await (item of [link, type, del]) {
|
|
tableRow.append(item);
|
|
}
|
|
table.append(tableRow);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Download file when clicked
|
|
async function downloadFile(file) {
|
|
file = file.replace("|","'");
|
|
let fileName = file.split('/').at(-1);
|
|
let data = fs.readFileSync(file);
|
|
let blob = new Blob([data], { type: "application/octetstream" });
|
|
let url = window.URL || window.webkitURL;
|
|
link = url.createObjectURL(blob);
|
|
let a = $("<a />");
|
|
a.attr("download", fileName);
|
|
a.attr("href", link);
|
|
$("body").append(a);
|
|
a[0].click();
|
|
$("body").remove(a);
|
|
}
|
|
|
|
// Upload file to current directory
|
|
async function upload(input) {
|
|
let directory = $('#filebrowser').data('directory');
|
|
if (directory == '/') {
|
|
directoryUp = '';
|
|
} else {
|
|
directoryUp = directory;
|
|
}
|
|
if (input.files && input.files[0]) {
|
|
let reader = new FileReader();
|
|
reader.onload = function(e) {
|
|
let fileName = input.files[0].name;
|
|
let data = e.target.result;
|
|
fs.writeFileSync(directoryUp + '/' + fileName, Buffer.from(data));
|
|
renderFiles(directory);
|
|
}
|
|
reader.readAsArrayBuffer(input.files[0]);
|
|
}
|
|
}
|
|
|
|
// Create a directory
|
|
async function createFolder() {
|
|
let folderName = $('#folderName').val();
|
|
$('#folderName').val('');
|
|
if ((folderName.length == 0) || (folderName.includes('/'))) {
|
|
alert('Bad or Null Directory Name');
|
|
return '';
|
|
}
|
|
let directory = $('#filebrowser').data('directory');
|
|
if (directory == '/') {
|
|
directoryUp = '';
|
|
} else {
|
|
directoryUp = directory;
|
|
}
|
|
let createD = directoryUp + '/' + folderName;
|
|
if (!fs.existsSync(createD)){
|
|
fs.mkdirSync(createD);
|
|
}
|
|
renderFiles(directory);
|
|
}
|
|
|
|
// Delete file or folder
|
|
async function deleter(item) {
|
|
let directory = $('#filebrowser').data('directory');
|
|
item = item.replace("|","'");
|
|
if (fs.lstatSync(item).isDirectory()) {
|
|
await rmDir(item);
|
|
} else {
|
|
fs.unlinkSync(item);
|
|
}
|
|
renderFiles(directory);
|
|
}
|
|
|
|
// Download a full backup of all files
|
|
async function downloadBackup() {
|
|
var zip = new JSZip();
|
|
let items = await fs.readdirSync('/');
|
|
async function addToZip(item) {
|
|
if (fs.lstatSync(item).isDirectory()) {
|
|
let items = await fs.readdirSync(item);
|
|
if (items.length > 0) {
|
|
for await (let subPath of items) {
|
|
await addToZip(item + '/' + subPath);
|
|
}
|
|
}
|
|
} else {
|
|
let data = fs.readFileSync(item);
|
|
let zipPath = item.replace(/^\//,'');
|
|
zip.file(zipPath, data);
|
|
}
|
|
return ''
|
|
}
|
|
for await (let item of items) {
|
|
await addToZip(item);
|
|
}
|
|
zip.generateAsync({type:"blob"}).then(function callback(blob) {
|
|
let url = window.URL || window.webkitURL;
|
|
link = url.createObjectURL(blob);
|
|
let a = $("<a />");
|
|
a.attr("download", storeName + '.zip');
|
|
a.attr("href", link);
|
|
$("body").append(a);
|
|
a[0].click();
|
|
$("body").remove(a);
|
|
});
|
|
}
|
|
|
|
// Full delete directory
|
|
async function rmDir(dirPath, removeSelf) {
|
|
try { var files = fs.readdirSync(dirPath); }
|
|
catch(e) { return; }
|
|
if (files.length > 0) {
|
|
for await (let file of files) {
|
|
var filePath = dirPath + '/' + file;
|
|
if (fs.statSync(filePath).isFile()) {
|
|
fs.unlinkSync(filePath);
|
|
} else {
|
|
rmDir(filePath);
|
|
}
|
|
}
|
|
}
|
|
if (dirPath !== '/') {
|
|
fs.rmdirSync(dirPath)
|
|
}
|
|
return '';
|
|
}
|
|
|
|
|
|
// Upload a full backup
|
|
async function uploadBackup(input) {
|
|
if (input.files && input.files[0]) {
|
|
$('#filebrowser').empty();
|
|
$('#filebrowser').append($('<div>').attr('id','loading'));
|
|
let reader = new FileReader();
|
|
reader.onload = async function(e) {
|
|
let data = e.target.result;
|
|
var zip = new JSZip();
|
|
// Load zip from data
|
|
zip.loadAsync(data).then(async function(contents) {
|
|
// Purge current storage
|
|
await rmDir('/');
|
|
// Unzip the files to the FS by name
|
|
for await (let fileName of Object.keys(contents.files)) {
|
|
if (fileName.endsWith('/')) {
|
|
if (! fs.existsSync('/' + fileName)) {
|
|
fs.mkdirSync('/' + fileName);
|
|
}
|
|
}
|
|
}
|
|
for await (let fileName of Object.keys(contents.files)) {
|
|
if (! fileName.endsWith('/')) {
|
|
zip.file(fileName).async('arraybuffer').then(function(content) {
|
|
fs.writeFileSync('/' + fileName, Buffer.from(content));
|
|
});
|
|
}
|
|
}
|
|
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
renderFiles('/');
|
|
});
|
|
}
|
|
reader.readAsArrayBuffer(input.files[0]);
|
|
}
|
|
}
|
|
|
|
// Create Async filestore
|
|
async function setupFileSystem() {
|
|
var imfs = new BrowserFS.FileSystem.InMemory();
|
|
afs = new BrowserFS.FileSystem.AsyncMirror(imfs,
|
|
new BrowserFS.FileSystem.IndexedDB(async function(e, fs) {
|
|
afs.initialize(async function(e) {
|
|
console.log('IndexedDB setup successful');
|
|
setupMounts();
|
|
});
|
|
},
|
|
storeName));
|
|
};
|
|
|
|
// Setup mounts
|
|
async function setupMounts() {
|
|
mfs.mount('/', afs);
|
|
BrowserFS.initialize(mfs);
|
|
renderFiles('/');
|
|
}
|
|
|
|
// On page load
|
|
window.onload = function() {
|
|
setupFileSystem();
|
|
}
|