2016-12-30 10:19:58 -05:00
|
|
|
/**
|
|
|
|
* @fileoverview
|
|
|
|
* Partial implementation of a SB3 serializer and deserializer. Parses provided
|
|
|
|
* JSON and then generates all needed scratch-vm runtime structures.
|
|
|
|
*/
|
|
|
|
|
2017-04-26 16:50:53 -04:00
|
|
|
const vmPackage = require('../../package.json');
|
|
|
|
const Blocks = require('../engine/blocks');
|
|
|
|
const Sprite = require('../sprites/sprite');
|
|
|
|
const Variable = require('../engine/variable');
|
|
|
|
const List = require('../engine/list');
|
2016-12-30 10:19:58 -05:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Serializes the specified VM runtime.
|
|
|
|
* @param {!Runtime} runtime VM runtime instance to be serialized.
|
|
|
|
* @return {string} Serialized runtime instance.
|
|
|
|
*/
|
2017-04-26 16:50:53 -04:00
|
|
|
const serialize = function (runtime) {
|
2016-12-30 10:19:58 -05:00
|
|
|
// Fetch targets
|
2017-04-26 16:50:53 -04:00
|
|
|
const obj = Object.create(null);
|
2016-12-30 10:19:58 -05:00
|
|
|
obj.targets = runtime.targets;
|
|
|
|
|
|
|
|
// Assemble metadata
|
2017-04-26 16:50:53 -04:00
|
|
|
const meta = Object.create(null);
|
2016-12-30 10:19:58 -05:00
|
|
|
meta.semver = '3.0.0';
|
2017-04-26 16:50:53 -04:00
|
|
|
meta.vm = vmPackage.version;
|
2016-12-30 10:19:58 -05:00
|
|
|
|
|
|
|
// Attach full user agent string to metadata if available
|
|
|
|
meta.agent = null;
|
|
|
|
if (typeof navigator !== 'undefined') meta.agent = navigator.userAgent;
|
|
|
|
|
|
|
|
// Assemble payload and return
|
|
|
|
obj.meta = meta;
|
|
|
|
return obj;
|
|
|
|
};
|
|
|
|
|
2017-01-27 20:05:54 -05:00
|
|
|
/**
|
|
|
|
* Parse a single "Scratch object" and create all its in-memory VM objects.
|
2017-04-26 16:50:53 -04:00
|
|
|
* @param {!object} object From-JSON "Scratch object:" sprite, stage, watcher.
|
2017-01-27 20:05:54 -05:00
|
|
|
* @param {!Runtime} runtime Runtime object to load all structures into.
|
|
|
|
* @return {?Target} Target created (stage or sprite).
|
|
|
|
*/
|
2017-04-26 16:50:53 -04:00
|
|
|
const parseScratchObject = function (object, runtime) {
|
2017-01-27 20:05:54 -05:00
|
|
|
if (!object.hasOwnProperty('name')) {
|
|
|
|
// Watcher/monitor - skip this object until those are implemented in VM.
|
|
|
|
// @todo
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// Blocks container for this object.
|
2017-04-26 16:50:53 -04:00
|
|
|
const blocks = new Blocks();
|
2017-01-27 20:05:54 -05:00
|
|
|
|
|
|
|
// @todo: For now, load all Scratch objects (stage/sprites) as a Sprite.
|
2017-04-26 16:50:53 -04:00
|
|
|
const sprite = new Sprite(blocks, runtime);
|
2017-01-27 20:05:54 -05:00
|
|
|
|
|
|
|
// Sprite/stage name from JSON.
|
|
|
|
if (object.hasOwnProperty('name')) {
|
|
|
|
sprite.name = object.name;
|
|
|
|
}
|
|
|
|
if (object.hasOwnProperty('blocks')) {
|
2017-04-26 16:50:53 -04:00
|
|
|
for (let blockId in object.blocks) {
|
2017-01-27 20:05:54 -05:00
|
|
|
blocks.createBlock(object.blocks[blockId]);
|
|
|
|
}
|
2017-04-26 16:50:53 -04:00
|
|
|
// console.log(blocks);
|
2017-01-27 20:05:54 -05:00
|
|
|
}
|
|
|
|
// Costumes from JSON.
|
|
|
|
if (object.hasOwnProperty('costumes') || object.hasOwnProperty('costume')) {
|
2017-04-26 16:50:53 -04:00
|
|
|
for (let i = 0; i < object.costumeCount; i++) {
|
|
|
|
const costume = object.costumes[i];
|
2017-01-27 20:05:54 -05:00
|
|
|
// @todo: Make sure all the relevant metadata is being pulled out.
|
|
|
|
sprite.costumes.push({
|
|
|
|
skin: costume.skin,
|
2017-02-07 20:12:55 -05:00
|
|
|
name: costume.name,
|
2017-01-27 20:05:54 -05:00
|
|
|
bitmapResolution: costume.bitmapResolution,
|
|
|
|
rotationCenterX: costume.rotationCenterX,
|
|
|
|
rotationCenterY: costume.rotationCenterY
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Sounds from JSON
|
|
|
|
if (object.hasOwnProperty('sounds')) {
|
2017-04-26 16:50:53 -04:00
|
|
|
for (let s = 0; s < object.sounds.length; s++) {
|
|
|
|
const sound = object.sounds[s];
|
2017-01-27 20:05:54 -05:00
|
|
|
sprite.sounds.push({
|
|
|
|
format: sound.format,
|
|
|
|
fileUrl: sound.fileUrl,
|
|
|
|
rate: sound.rate,
|
|
|
|
sampleCount: sound.sampleCount,
|
|
|
|
soundID: sound.soundID,
|
2017-02-09 10:44:28 -05:00
|
|
|
name: sound.name,
|
|
|
|
md5: sound.md5
|
2017-01-27 20:05:54 -05:00
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Create the first clone, and load its run-state from JSON.
|
2017-04-26 16:50:53 -04:00
|
|
|
const target = sprite.createClone();
|
2017-01-27 20:05:54 -05:00
|
|
|
// Add it to the runtime's list of targets.
|
|
|
|
runtime.targets.push(target);
|
|
|
|
// Load target properties from JSON.
|
|
|
|
if (object.hasOwnProperty('variables')) {
|
2017-04-26 16:50:53 -04:00
|
|
|
for (let j = 0; j < object.variables.length; j++) {
|
|
|
|
const variable = object.variables[j];
|
2017-01-27 20:05:54 -05:00
|
|
|
target.variables[variable.name] = new Variable(
|
|
|
|
variable.name,
|
|
|
|
variable.value,
|
|
|
|
variable.isPersistent
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (object.hasOwnProperty('lists')) {
|
2017-04-26 16:50:53 -04:00
|
|
|
for (let k = 0; k < object.lists.length; k++) {
|
|
|
|
const list = object.lists[k];
|
2017-01-27 20:05:54 -05:00
|
|
|
// @todo: monitor properties.
|
|
|
|
target.lists[list.listName] = new List(
|
|
|
|
list.listName,
|
|
|
|
list.contents
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (object.hasOwnProperty('x')) {
|
|
|
|
target.x = object.x;
|
|
|
|
}
|
|
|
|
if (object.hasOwnProperty('y')) {
|
|
|
|
target.y = object.y;
|
|
|
|
}
|
|
|
|
if (object.hasOwnProperty('direction')) {
|
|
|
|
target.direction = object.direction;
|
|
|
|
}
|
|
|
|
if (object.hasOwnProperty('size')) {
|
|
|
|
target.size = object.size;
|
|
|
|
}
|
|
|
|
if (object.hasOwnProperty('visible')) {
|
|
|
|
target.visible = object.visible;
|
|
|
|
}
|
|
|
|
if (object.hasOwnProperty('currentCostume')) {
|
|
|
|
target.currentCostume = object.currentCostume;
|
|
|
|
}
|
|
|
|
if (object.hasOwnProperty('rotationStyle')) {
|
|
|
|
target.rotationStyle = object.rotationStyle;
|
|
|
|
}
|
2017-02-07 18:38:44 -05:00
|
|
|
if (object.hasOwnProperty('isStage')) {
|
|
|
|
target.isStage = object.isStage;
|
2017-01-27 20:05:54 -05:00
|
|
|
}
|
2017-02-07 18:38:44 -05:00
|
|
|
target.updateAllDrawableProperties();
|
|
|
|
|
2017-04-26 16:50:53 -04:00
|
|
|
// console.log('returning target:');
|
|
|
|
// console.log(target);
|
2017-01-27 20:05:54 -05:00
|
|
|
return target;
|
|
|
|
};
|
|
|
|
|
2016-12-30 10:19:58 -05:00
|
|
|
/**
|
|
|
|
* Deserializes the specified representation of a VM runtime and loads it into
|
|
|
|
* the provided runtime instance.
|
|
|
|
* @param {string} json Stringified JSON representation of a VM runtime.
|
|
|
|
* @param {Runtime} runtime Runtime instance
|
|
|
|
*/
|
2017-04-26 16:50:53 -04:00
|
|
|
const deserialize = function (json, runtime) {
|
|
|
|
for (let i = 0; i < json.targets.length; i++) {
|
2017-01-27 20:05:54 -05:00
|
|
|
parseScratchObject(json.targets[i], runtime);
|
|
|
|
}
|
2016-12-30 10:19:58 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
module.exports = {
|
|
|
|
serialize: serialize,
|
|
|
|
deserialize: deserialize
|
|
|
|
};
|