Add support for loading CC2 ZIPs, and parse out C2G game titles
This commit is contained in:
parent
f5b1b4a83d
commit
0d35274d6a
@ -1980,6 +1980,16 @@ const MAX_SIMULTANEOUS_REQUESTS = 5;
|
|||||||
fetch_map(path, level_number);
|
fetch_map(path, level_number);
|
||||||
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);
|
statements.push(stmt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
27
js/main.js
27
js/main.js
@ -1,5 +1,7 @@
|
|||||||
// TODO bugs and quirks i'm aware of:
|
// 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
|
// - 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 { DIRECTIONS, INPUT_BITS, TICS_PER_SECOND } from './defs.js';
|
||||||
import * as c2g from './format-c2g.js';
|
import * as c2g from './format-c2g.js';
|
||||||
import * as dat from './format-dat.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') {
|
else if (magic === '\xac\xaa\x02\x00' || magic == '\xac\xaa\x02\x01') {
|
||||||
stored_game = dat.parse_game(buf);
|
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') {
|
else if (magic.toLowerCase() === 'game') {
|
||||||
// TODO this isn't really a magic number and isn't required to be first, so, maybe
|
// TODO this isn't really a magic number and isn't required to be first, so, maybe
|
||||||
// this one should just go by filename
|
// this one should just go by filename
|
||||||
@ -3149,12 +3158,28 @@ class Conductor {
|
|||||||
dir = path.replace(/[/][^/]+$/, '');
|
dir = path.replace(/[/][^/]+$/, '');
|
||||||
}
|
}
|
||||||
stored_game = await c2g.parse_game(buf, source, dir);
|
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 {
|
else {
|
||||||
throw new Error("Unrecognized file format");
|
throw new Error("Unrecognized file format");
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO load title for a C2G
|
|
||||||
if (! stored_game.title) {
|
if (! stored_game.title) {
|
||||||
stored_game.title = title ?? identifier ?? "Untitled pack";
|
stored_game.title = title ?? identifier ?? "Untitled pack";
|
||||||
}
|
}
|
||||||
|
|||||||
24
js/util.js
24
js/util.js
@ -1,3 +1,5 @@
|
|||||||
|
import * as fflate from 'https://unpkg.com/fflate/esm/index.mjs';
|
||||||
|
|
||||||
// Base class for custom errors
|
// Base class for custom errors
|
||||||
export class LLError extends Error {}
|
export class LLError extends Error {}
|
||||||
|
|
||||||
@ -413,3 +415,25 @@ export class EntryFileSource extends FileSource {
|
|||||||
return await file.arrayBuffer();
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user