diff --git a/js/main-editor.js b/js/main-editor.js index 350807d..7ca3e79 100644 --- a/js/main-editor.js +++ b/js/main-editor.js @@ -1,3 +1,5 @@ +import * as fflate from 'https://unpkg.com/fflate/esm/index.mjs'; + import { DIRECTIONS, TICS_PER_SECOND } from './defs.js'; import { TILES_WITH_PROPS } from './editor-tile-overlays.js'; import * as format_base from './format-base.js'; @@ -2549,7 +2551,7 @@ export class Editor extends PrimaryView { if (this.stored_level) { this.save_button.disabled = ! this.conductor.stored_game.editor_metadata; } - _make_button("Download", ev => { + _make_button("Download level as C2M", ev => { // TODO also allow download as CCL // TODO support getting warnings + errors out of synthesis let buf = c2g.synthesize_level(this.stored_level); @@ -2568,6 +2570,66 @@ export class Editor extends PrimaryView { URL.revokeObjectURL(url); }, 60 * 1000); }); + _make_button("Download pack", ev => { + let stored_pack = this.conductor.stored_game; + + // This is pretty heckin' best-effort for now; TODO move into format-c2g? + let lines = []; + let safe_title = (stored_pack.title || "untitled").replace(/[""]/g, "'").replace(/[\x00-\x1f]+/g, "_"); + lines.push(`game "${safe_title}"`); + + let files = {}; + let count = stored_pack.level_metadata.length; + let levelnumlen = String(count).length; + for (let [i, meta] of stored_pack.level_metadata.entries()) { + let c2m; + if (i === this.conductor.level_index) { + // Use the current state of the current level even if it's not been saved + c2m = new Uint8Array(c2g.synthesize_level(this.stored_level)); + } + else if (meta.key) { + // This is already in localStorage as a c2m + c2m = fflate.strToU8(localStorage.getItem(meta.key), true); + } + else { + let stored_level = stored_pack.load_level(i); + c2m = new Uint8Array(c2g.synthesize_level(stored_level)); + } + + let safe_title = meta.title.replace(/[\x00-\x1f<>:""\/\\|?*]+/g, '_'); + let dirchunk = i - i % 50; + let dirname = ( + String(dirchunk + 1).padStart(levelnumlen, '0') + '-' + + String(Math.min(count, dirchunk + 50)).padStart(levelnumlen, '0')); + let filename = `${dirname}/${i + 1} - ${safe_title}.c2m`; + files[filename] = c2m; + + lines.push(`map "${filename}"`); + } + + // TODO utf8 encode this + safe_title = safe_title.replace(/[\x00-\x1f<>:""\/\\|?*]+/g, '_'); + lines.push(""); + files[safe_title + '.c2g'] = fflate.strToU8(lines.join("\n")); + let u8array = fflate.zipSync(files); + + // TODO also allow download as CCL + // TODO support getting warnings + errors out of synthesis + let blob = new Blob([u8array]); + let url = URL.createObjectURL(blob); + // To download a file, um, make an and click it. Not kidding + let a = mk('a', { + href: url, + download: (stored_pack.title || 'untitled') + '.zip', + }); + document.body.append(a); + a.click(); + // Absolutely no idea when I'm allowed to revoke this, but surely a minute is safe + window.setTimeout(() => { + a.remove(); + URL.revokeObjectURL(url); + }, 60 * 1000); + }); _make_button("Share", ev => { let data = util.b64encode(c2g.synthesize_level(this.stored_level)); let url = new URL(location);