Add support for loading CC2 ZIPs, and parse out C2G game titles

This commit is contained in:
Eevee (Evelyn Woods) 2021-01-01 15:26:33 -07:00
parent f5b1b4a83d
commit 0d35274d6a
3 changed files with 60 additions and 1 deletions

View File

@ -1980,6 +1980,16 @@ const MAX_SIMULTANEOUS_REQUESTS = 5;
fetch_map(path, level_number);
level_number++;
}
else if (stmt.kind === 'directive' && stmt.name === 'game') {
// TODO apparently cc2 lets you change this mid-game and will then use a different save
// slot (?!), but i can't even consider that until i actually execute these things in
// order
if (game.identifier === undefined) {
let title = stmt.args[0].value;
game.identifier = title;
game.title = title;
}
}
statements.push(stmt);
}

View File

@ -1,5 +1,7 @@
// TODO bugs and quirks i'm aware of:
// - steam: if a player character starts on a force floor they won't be able to make any voluntary movements until they are no longer on a force floor
import * as fflate from 'https://unpkg.com/fflate/esm/index.mjs';
import { DIRECTIONS, INPUT_BITS, TICS_PER_SECOND } from './defs.js';
import * as c2g from './format-c2g.js';
import * as dat from './format-dat.js';
@ -3138,6 +3140,13 @@ class Conductor {
else if (magic === '\xac\xaa\x02\x00' || magic == '\xac\xaa\x02\x01') {
stored_game = dat.parse_game(buf);
}
else if (magic === 'PK\x03\x04') {
// That's the ZIP header
// FIXME move this here i guess and flesh it out some
// FIXME if this doesn't find something then we should abort
await this.splash.search_multi_source(new util.ZipFileSource(buf));
return;
}
else if (magic.toLowerCase() === 'game') {
// TODO this isn't really a magic number and isn't required to be first, so, maybe
// this one should just go by filename
@ -3149,12 +3158,28 @@ class Conductor {
dir = path.replace(/[/][^/]+$/, '');
}
stored_game = await c2g.parse_game(buf, source, dir);
if (stored_game.identifier) {
// Migrate any scores saved under the old path-based identifier
let new_identifier = stored_game.identifier;
if (this.stash.packs[identifier] && ! this.stash.packs[new_identifier]) {
this.stash.packs[new_identifier] = this.stash.packs[identifier];
delete this.stash.packs[identifier];
this.save_stash();
window.localStorage.setItem(
STORAGE_PACK_PREFIX + new_identifier,
window.localStorage.getItem(STORAGE_PACK_PREFIX + identifier));
window.localStorage.removeItem(STORAGE_PACK_PREFIX + identifier);
}
identifier = new_identifier;
}
}
else {
throw new Error("Unrecognized file format");
}
// TODO load title for a C2G
if (! stored_game.title) {
stored_game.title = title ?? identifier ?? "Untitled pack";
}

View File

@ -1,3 +1,5 @@
import * as fflate from 'https://unpkg.com/fflate/esm/index.mjs';
// Base class for custom errors
export class LLError extends Error {}
@ -413,3 +415,25 @@ export class EntryFileSource extends FileSource {
return await file.arrayBuffer();
}
}
// Zip files, using fflate
// TODO somewhat unfortunately fflate only supports unzipping the whole thing at once, not
// individual files as needed, but it's also pretty new so maybe later?
export class ZipFileSource extends FileSource {
constructor(buf) {
super();
// TODO async? has some setup time but won't freeze browser
let files = fflate.unzipSync(new Uint8Array(buf));
this.files = {};
for (let [path, file] of Object.entries(files)) {
this.files['/' + path.toLowerCase()] = file;
}
}
async get(path) {
let file = this.files[path.toLowerCase()];
if (! file)
throw new LLError(`No such file in zip: ${path}`);
return file.buffer;
}
}