mirror of
https://github.com/kichikuou/xsystem35-sdl2.git
synced 2026-10-10 16:18:33 +03:00
Implement savedata download/upload
This commit is contained in:
+1
-1
@@ -2,7 +2,7 @@
|
||||
*.o
|
||||
*.lo
|
||||
*.la
|
||||
*.js
|
||||
/docs/*.js
|
||||
/config.h
|
||||
/config.log
|
||||
/config.status
|
||||
|
||||
+3
-2
@@ -81,8 +81,8 @@
|
||||
<h5>セーブデータ</h5>
|
||||
<div>
|
||||
<p>セーブデータを ZIP ファイルに保存したり、ZIP ファイルから復元できます。</p>
|
||||
<button class="btn mr-10">保存</button>
|
||||
<button class="btn">復元</button>
|
||||
<button id="downloadSaveData" class="btn mr-10">保存</button>
|
||||
<button id="uploadSaveData" class="btn">復元</button>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
@@ -97,5 +97,6 @@
|
||||
</script>
|
||||
<script src="shell.js"></script>
|
||||
<script src="xsystem35.js" async></script>
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/jszip/3.1.3/jszip.min.js" async></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+6
-23
@@ -1,3 +1,4 @@
|
||||
/// <reference path="util.ts" />
|
||||
/// <reference path="text-decoder.ts" />
|
||||
|
||||
namespace CDImage {
|
||||
@@ -122,24 +123,6 @@ namespace CDImage {
|
||||
}
|
||||
}
|
||||
|
||||
function readAsArrayBuffer(blob: Blob): Promise<ArrayBuffer> {
|
||||
return new Promise((resolve, reject) => {
|
||||
let reader = new FileReader();
|
||||
reader.onload = () => { resolve(reader.result); };
|
||||
reader.onerror = () => { reject(reader.error); };
|
||||
reader.readAsArrayBuffer(blob);
|
||||
});
|
||||
}
|
||||
|
||||
function readAsText(blob: Blob): Promise<string> {
|
||||
return new Promise((resolve, reject) => {
|
||||
let reader = new FileReader();
|
||||
reader.onload = () => { resolve(reader.result); };
|
||||
reader.onerror = () => { reject(reader.error); };
|
||||
reader.readAsText(blob);
|
||||
});
|
||||
}
|
||||
|
||||
class ImageReaderBase {
|
||||
constructor(public image: File) { }
|
||||
|
||||
@@ -150,7 +133,7 @@ namespace CDImage {
|
||||
sectorOffset: number): Promise<Uint8Array[]> {
|
||||
let sectors = Math.ceil(bytesToRead / sectorSize);
|
||||
let blob = this.image.slice(startOffset, startOffset + sectors * blockSize);
|
||||
let buf = await readAsArrayBuffer(blob);
|
||||
let buf = await readFileAsArrayBuffer(blob);
|
||||
let bufs: Uint8Array[] = [];
|
||||
for (let i = 0; i < sectors; i++) {
|
||||
bufs.push(new Uint8Array(buf, i * blockSize + sectorOffset, Math.min(bytesToRead, sectorSize)));
|
||||
@@ -170,7 +153,7 @@ namespace CDImage {
|
||||
readSector(sector: number): Promise<ArrayBuffer> {
|
||||
let start = sector * 2352 + 16;
|
||||
let end = start + 2048;
|
||||
return readAsArrayBuffer(this.image.slice(start, end));
|
||||
return readFileAsArrayBuffer(this.image.slice(start, end));
|
||||
}
|
||||
|
||||
readSequentialSectors(startSector: number, length: number): Promise<Uint8Array[]> {
|
||||
@@ -178,7 +161,7 @@ namespace CDImage {
|
||||
}
|
||||
|
||||
async parseCue(cueFile: File) {
|
||||
let lines = (await readAsText(cueFile)).split('\n');
|
||||
let lines = (await readFileAsText(cueFile)).split('\n');
|
||||
this.tracks = [];
|
||||
let currentTrack: number = null;
|
||||
for (let line of lines) {
|
||||
@@ -234,7 +217,7 @@ namespace CDImage {
|
||||
}
|
||||
|
||||
async parseMds(mdsFile: File) {
|
||||
let buf = await readAsArrayBuffer(mdsFile);
|
||||
let buf = await readFileAsArrayBuffer(mdsFile);
|
||||
|
||||
let signature = new TextDecoder().decode(new DataView(buf, 0, 16));
|
||||
if (signature !== 'MEDIA DESCRIPTOR')
|
||||
@@ -262,7 +245,7 @@ namespace CDImage {
|
||||
readSector(sector: number): Promise<ArrayBuffer> {
|
||||
let start = sector * this.tracks[1].sectorSize + 16;
|
||||
let end = start + 2048;
|
||||
return readAsArrayBuffer(this.image.slice(start, end));
|
||||
return readFileAsArrayBuffer(this.image.slice(start, end));
|
||||
}
|
||||
|
||||
readSequentialSectors(startSector: number, length: number): Promise<Uint8Array[]> {
|
||||
|
||||
+1
-1
@@ -52,7 +52,7 @@ namespace xsystem35 {
|
||||
}
|
||||
if (this.imgFile && this.cueFile) {
|
||||
this.imageReader = await CDImage.createReader(this.imgFile, this.cueFile);
|
||||
await this.shell.fileSystemReady;
|
||||
await xsystem35.fileSystemReady;
|
||||
this.install();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
"description": "",
|
||||
"dependencies": {
|
||||
"@types/emscripten": "0.0.29",
|
||||
"@types/jszip": "0.0.32",
|
||||
"@types/text-encoding": "0.0.30"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,9 @@ namespace xsystem35 {
|
||||
this.closeModal();
|
||||
};
|
||||
$('.modal-overlay').addEventListener('click', this.closeModal.bind(this));
|
||||
|
||||
$('#downloadSaveData').addEventListener('click', this.downloadSaveData.bind(this));
|
||||
$('#uploadSaveData').addEventListener('click', this.uploadSaveData.bind(this));
|
||||
}
|
||||
|
||||
private openModal() {
|
||||
@@ -22,5 +25,60 @@ namespace xsystem35 {
|
||||
$('#settings-modal').classList.remove('active');
|
||||
document.removeEventListener('keydown', this.keyDownHandler);
|
||||
}
|
||||
|
||||
private async downloadSaveData() {
|
||||
await xsystem35.saveDirReady;
|
||||
let zip = new JSZip();
|
||||
let folder = zip.folder('save');
|
||||
for (let name of FS.readdir('/save')) {
|
||||
if (!name.toLowerCase().endsWith('.asd'))
|
||||
continue;
|
||||
let content = FS.readFile('/save/' + name, { encoding: 'binary' });
|
||||
folder.file(name, content);
|
||||
}
|
||||
let blob = await zip.generateAsync({type: 'blob', compression: 'DEFLATE'});
|
||||
let elem = document.createElement('a');
|
||||
elem.setAttribute('download', 'savedata.zip');
|
||||
elem.setAttribute('href', URL.createObjectURL(blob));
|
||||
elem.click();
|
||||
}
|
||||
|
||||
private uploadSaveData() {
|
||||
let input = document.createElement('input');
|
||||
input.type = 'file';
|
||||
input.addEventListener('change', (evt: Event) => {
|
||||
this.extractSaveData(input.files[0]);
|
||||
document.body.removeChild(input);
|
||||
});
|
||||
input.style.display = 'none';
|
||||
document.body.appendChild(input);
|
||||
input.click();
|
||||
}
|
||||
|
||||
private async extractSaveData(file: File) {
|
||||
function basename(path: string) {
|
||||
return path.slice(path.lastIndexOf('/') + 1);
|
||||
}
|
||||
function addSaveFile(name: string, content: ArrayBuffer) {
|
||||
console.log(name);
|
||||
FS.writeFile('/save/' + name, new Uint8Array(content), { encoding: 'binary' });
|
||||
}
|
||||
try {
|
||||
await xsystem35.saveDirReady;
|
||||
if (file.name.toLowerCase().endsWith('.zip')) {
|
||||
let zip = new JSZip();
|
||||
await zip.loadAsync(await readFileAsArrayBuffer(file));
|
||||
for (let f of zip.file(/\.asd$/i))
|
||||
addSaveFile(basename(f.name), await f.async('arraybuffer'));
|
||||
} else if (file.name.toLowerCase().endsWith('.asd')) {
|
||||
addSaveFile(file.name, await readFileAsArrayBuffer(file));
|
||||
} else {
|
||||
throw('ファイルを認識できません。');
|
||||
}
|
||||
xsystem35.shell.syncfs(0);
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+13
-7
@@ -13,12 +13,13 @@ namespace xsystem35 {
|
||||
'ttfont_mincho: ' + Font.fname,
|
||||
'ttfont_gothic: ' + Font.fname, '',
|
||||
].join('\n');
|
||||
export let fileSystemReady: Promise<any>;
|
||||
export let saveDirReady: Promise<any>;
|
||||
export let cdPlayer: CDPlayer;
|
||||
export let audio: AudioManager;
|
||||
export let settings: Settings;
|
||||
|
||||
export class System35Shell {
|
||||
readonly fileSystemReady: Promise<any>;
|
||||
private imageLoader: ImageLoader;
|
||||
status: HTMLElement = document.getElementById('status');
|
||||
private zoom: ZoomManager;
|
||||
@@ -26,8 +27,10 @@ namespace xsystem35 {
|
||||
private antialiasCheckbox: HTMLInputElement;
|
||||
|
||||
constructor() {
|
||||
let fileSystemReady: () => void;
|
||||
this.fileSystemReady = new Promise((resolve) => { fileSystemReady = resolve; });
|
||||
let fsReady: () => void;
|
||||
fileSystemReady = new Promise((resolve) => { fsReady = resolve; });
|
||||
let idbfsReady: () => void;
|
||||
saveDirReady = new Promise((resolve) => { idbfsReady = resolve; });
|
||||
|
||||
this.imageLoader = new ImageLoader(this);
|
||||
this.setStatus('Downloading...');
|
||||
@@ -45,7 +48,7 @@ namespace xsystem35 {
|
||||
Module.noInitialRun = true;
|
||||
Module.setStatus = this.setStatus.bind(this);
|
||||
Module.preRun = [
|
||||
fileSystemReady,
|
||||
fsReady,
|
||||
function loadFont() {
|
||||
FS.createPreloadedFile('/', Font.fname, Font.url, true, false);
|
||||
},
|
||||
@@ -53,7 +56,10 @@ namespace xsystem35 {
|
||||
FS.mkdir('/save');
|
||||
FS.mount(IDBFS, {}, '/save');
|
||||
Module.addRunDependency('syncfs');
|
||||
FS.syncfs(true, (err) => { Module.removeRunDependency('syncfs'); });
|
||||
FS.syncfs(true, (err) => {
|
||||
Module.removeRunDependency('syncfs');
|
||||
idbfsReady();
|
||||
});
|
||||
},
|
||||
];
|
||||
this.volumeControl = new VolumeControl();
|
||||
@@ -92,14 +98,14 @@ namespace xsystem35 {
|
||||
}
|
||||
|
||||
private fsyncTimer: number;
|
||||
syncfs(fname: string) {
|
||||
syncfs(timeout = 100) {
|
||||
window.clearTimeout(this.fsyncTimer);
|
||||
this.fsyncTimer = window.setTimeout(() => {
|
||||
FS.syncfs(false, (err) => {
|
||||
if (err)
|
||||
console.log('FS.syncfs error: ', err);
|
||||
});
|
||||
}, 100);
|
||||
}, timeout);
|
||||
}
|
||||
|
||||
private antialiasChanged() {
|
||||
|
||||
@@ -1,5 +1,23 @@
|
||||
let $: (selector: string) => HTMLElement = document.querySelector.bind(document);
|
||||
|
||||
function readFileAsArrayBuffer(blob: Blob): Promise<ArrayBuffer> {
|
||||
return new Promise((resolve, reject) => {
|
||||
let reader = new FileReader();
|
||||
reader.onload = () => { resolve(reader.result); };
|
||||
reader.onerror = () => { reject(reader.error); };
|
||||
reader.readAsArrayBuffer(blob);
|
||||
});
|
||||
}
|
||||
|
||||
function readFileAsText(blob: Blob): Promise<string> {
|
||||
return new Promise((resolve, reject) => {
|
||||
let reader = new FileReader();
|
||||
reader.onload = () => { resolve(reader.result); };
|
||||
reader.onerror = () => { reject(reader.error); };
|
||||
reader.readAsText(blob);
|
||||
});
|
||||
}
|
||||
|
||||
// xsystem35 exported functions
|
||||
declare function _musfade_setvolval_all(vol: number): void;
|
||||
declare function _ags_setAntialiasedStringMode(on: number): void;
|
||||
@@ -19,6 +37,7 @@ declare namespace Module {
|
||||
}
|
||||
|
||||
declare namespace FS {
|
||||
function readFile(path: string, opts?: {encoding?: string; flags?: string}): any;
|
||||
function writeFile(path: string, data: ArrayBufferView | string,
|
||||
opts?: {encoding?: string; flags?: string; canOwn?: boolean}): void;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user