diff --git a/src/virtual-machine.js b/src/virtual-machine.js index cf82327f5..a078b98a8 100644 --- a/src/virtual-machine.js +++ b/src/virtual-machine.js @@ -212,7 +212,7 @@ class VirtualMachine extends EventEmitter { } const validationPromise = new Promise((resolve, reject) => { - validate(input, (error, res) => { + validate(input, false /* this is not a single sprite */, (error, res) => { if (error) { reject(error); } @@ -379,23 +379,64 @@ class VirtualMachine extends EventEmitter { } /** - * Add a single sprite from the "Sprite2" (i.e., SB2 sprite) format. - * @param {string} json JSON string representing the sprite. - * @returns {Promise} Promise that resolves after the sprite is added + * Add a sprite, this could be .sprite2 or .sprite3. Unpack and validate + * such a file first. + * @param {string | object} input A json string, object, or ArrayBuffer representing the project to load. + * @return {!Promise} Promise that resolves after targets are installed. */ - addSprite2 (json) { - // Validate & parse - if (typeof json !== 'string') { - log.error('Failed to parse sprite. Non-string supplied to addSprite2.'); - return; - } - json = JSON.parse(json); - if (typeof json !== 'object') { - log.error('Failed to parse sprite. JSON supplied to addSprite2 is not an object.'); - return; + addSprite (input) { + if (typeof input === 'object' && !(input instanceof ArrayBuffer) && + !ArrayBuffer.isView(input)) { + // If the input is an object and not any ArrayBuffer + // or an ArrayBuffer view (this includes all typed arrays and DataViews) + // turn the object into a JSON string, because we suspect + // this is a project.json as an object + // validate expects a string or buffer as input + // TODO not sure if we need to check that it also isn't a data view + input = JSON.stringify(input); } - return sb2.deserialize(json, this.runtime, true) + const validationPromise = new Promise((resolve, reject) => { + validate(input, true /* single sprite */, (error, res) => { + if (error) { + reject(error); + } + if (res) { + resolve(res); + } + }); + }); + + return validationPromise + .then(validatedInput => { + const projectVersion = validatedInput[0].projectVersion; + if (projectVersion === 2) { + return this.addSprite2(validatedInput[0], validatedInput[1]); + } + if (projectVersion === 3) { + return this.addSprite3(validatedInput[0], validatedInput[1]); + } + return Promise.reject('Unable to verify sprite version.'); + }) + .catch(error => { + // Intentionally rejecting here (want errors to be handled by caller) + if (error.hasOwnProperty('validationError')) { + return Promise.reject(JSON.stringify(error)); + } + return Promise.reject(error); + }); + } + + /** + * Add a single sprite from the "Sprite2" (i.e., SB2 sprite) format. + * @param {string} json JSON string representing the sprite. + * @param {?ArrayBuffer} zip Optional zip of assets being referenced by json + * @returns {Promise} Promise that resolves after the sprite is added + */ + addSprite2 (json, zip) { + // Validate & parse + + return sb2.deserialize(json, this.runtime, true, zip) .then(({targets, extensions}) => this.installTargets(targets, extensions, false)); } @@ -403,26 +444,14 @@ class VirtualMachine extends EventEmitter { /** * Add a single sb3 sprite. * @param {string} target JSON string representing the sprite/target. + * @param {?ArrayBuffer} zip Optional zip of assets being referenced by target json * @returns {Promise} Promise that resolves after the sprite is added */ - addSprite3 (target) { + addSprite3 (target, zip) { // Validate & parse - if (typeof target !== 'string') { - log.error('Failed to parse sprite. Non-string supplied to addSprite3.'); - return; - } - target = JSON.parse(target); - if (typeof target !== 'object') { - log.error('Failed to parse sprite. JSON supplied to addSprite3 is not an object.'); - return; - } - - const jsonFormatted = { - targets: [target] - }; return sb3 - .deserialize(jsonFormatted, this.runtime, null) + .deserialize(target, this.runtime, zip) .then(({targets, extensions}) => this.installTargets(targets, extensions, false)); } @@ -614,7 +643,7 @@ class VirtualMachine extends EventEmitter { canvas.height = bitmap.height; const context = canvas.getContext('2d'); context.putImageData(bitmap, 0, 0); - + // Divide by resolution because the renderer's definition of the rotation center // is the rotation center divided by the bitmap resolution this.runtime.renderer.updateBitmapSkin(