Implement savedata download/upload

This commit is contained in:
kichikuou
2017-03-28 09:16:51 +09:00
parent 9f4d6cefbf
commit ab6112b0bf
8 changed files with 102 additions and 34 deletions
+1 -1
View File
@@ -2,7 +2,7 @@
*.o
*.lo
*.la
*.js
/docs/*.js
/config.h
/config.log
/config.status
+3 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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();
}
}
+1
View File
@@ -4,6 +4,7 @@
"description": "",
"dependencies": {
"@types/emscripten": "0.0.29",
"@types/jszip": "0.0.32",
"@types/text-encoding": "0.0.30"
}
}
+58
View File
@@ -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
View File
@@ -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() {
+19
View File
@@ -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;
}