mirror of
https://github.com/scratchfoundation/scratch-parser.git
synced 2025-07-11 22:03:58 -04:00
feat(schemas): add support for sprite files for 2.0 and 3.0
refactor schemas to support sprite2 and sprite3 files, while reusing rules in common with sb2 and sb3 files BREAKING CHANGE: Change to main API which now takes an additional boolean argument indicating whether a sprite or project is being validated
This commit is contained in:
parent
5daad968a4
commit
b808e7447f
11 changed files with 759 additions and 616 deletions
7
index.js
7
index.js
|
@ -7,20 +7,21 @@ var validate = require('./lib/validate');
|
|||
/**
|
||||
* Unpacks, parses, validates, and analyzes Scratch projects. If successful,
|
||||
* will return a valid Scratch project object with appended metadata.
|
||||
* @param {boolean} isSprite Whether this is a sprite (true) or whole project (false)
|
||||
* @param {Buffer | string} input Buffer or string representing project
|
||||
* @param {Function} callback Returns error or project data
|
||||
*/
|
||||
module.exports = function (input, callback) {
|
||||
module.exports = function (isSprite, input, callback) {
|
||||
// First unpack the input (need this outside of the async waterfall so that
|
||||
// unpackedProject can be refered to again)
|
||||
unpack(input, function (err, unpackedProject) {
|
||||
unpack(isSprite, input, function (err, unpackedProject) {
|
||||
if (err) return callback(err);
|
||||
|
||||
async.waterfall([
|
||||
function (cb) {
|
||||
parse(unpackedProject[0], cb);
|
||||
},
|
||||
validate
|
||||
validate.bind(null, isSprite)
|
||||
], function (error, validatedInput) {
|
||||
// One more callback wrapper so that we can re-package everything
|
||||
// with the possible zip returned from unpack
|
||||
|
|
|
@ -8,10 +8,11 @@
|
|||
* @return {void}
|
||||
*/
|
||||
module.exports = function (input, callback) {
|
||||
var result;
|
||||
try {
|
||||
var result = JSON.parse(input);
|
||||
callback(null, result);
|
||||
result = JSON.parse(input);
|
||||
} catch (e) {
|
||||
return callback(e.toString());
|
||||
}
|
||||
return callback(null, result);
|
||||
};
|
||||
|
|
191
lib/sb2_definitions.json
Normal file
191
lib/sb2_definitions.json
Normal file
|
@ -0,0 +1,191 @@
|
|||
{
|
||||
"$id": "https://scratch.mit.edu/sb2_definitions.json",
|
||||
"$schema": "http://json-schema.org/schema#",
|
||||
"description": "Scratch 2.0 Project and Sprite Definitions",
|
||||
"definitions": {
|
||||
"scripts": {
|
||||
"type": "array"
|
||||
},
|
||||
"sounds": {
|
||||
"type": "array",
|
||||
"properties": {
|
||||
"soundName": {
|
||||
"type": "string"
|
||||
},
|
||||
"soundID": {
|
||||
"type": "number"
|
||||
},
|
||||
"md5": {
|
||||
"type": "string"
|
||||
},
|
||||
"sampleCount": {
|
||||
"type": "number"
|
||||
},
|
||||
"rate": {
|
||||
"type": "number"
|
||||
},
|
||||
"format": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"soundName",
|
||||
"soundID",
|
||||
"md5",
|
||||
"sampleCount",
|
||||
"rate",
|
||||
"format"
|
||||
]
|
||||
},
|
||||
"costumes": {
|
||||
"type": "array",
|
||||
"properties": {
|
||||
"costumeName": {
|
||||
"type": "string"
|
||||
},
|
||||
"baseLayerID": {
|
||||
"type": "number"
|
||||
},
|
||||
"baseLayerMD5": {
|
||||
"type": "string"
|
||||
},
|
||||
"bitmapResolution": {
|
||||
"type": "number"
|
||||
},
|
||||
"rotationCenterX": {
|
||||
"type": "number"
|
||||
},
|
||||
"rotationCenterY": {
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"costumeName",
|
||||
"baseLayerID",
|
||||
"baseLayerMD5",
|
||||
"bitmapResolution",
|
||||
"rotationCenterX",
|
||||
"rotationCenterY"
|
||||
]
|
||||
},
|
||||
"variables": {
|
||||
"type": "array",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"value": {
|
||||
"type": "string"
|
||||
},
|
||||
"isPersistent": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name",
|
||||
"value"
|
||||
]
|
||||
},
|
||||
"sprite_object": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"objName": {
|
||||
"type": "string"
|
||||
},
|
||||
"variables": {"$ref": "#/definitions/variables"},
|
||||
"sounds": {"$ref":"#/definitions/sounds"},
|
||||
"costumes": {"$ref":"#/definitions/costumes"},
|
||||
"currentCostumeIndex": {
|
||||
"type": "number",
|
||||
"minimum": 0
|
||||
}
|
||||
},
|
||||
"additionalProperties": true,
|
||||
"required": [
|
||||
"objName",
|
||||
"sounds",
|
||||
"costumes",
|
||||
"currentCostumeIndex"
|
||||
]
|
||||
},
|
||||
"stage_object" : {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"objName": {
|
||||
"type": "string"
|
||||
},
|
||||
"variables": {"$ref": "#/definitions/variables"},
|
||||
"lists": {
|
||||
"type": "array",
|
||||
"properties": {
|
||||
"listName": {
|
||||
"type": "string"
|
||||
},
|
||||
"contents": {
|
||||
"type": "array"
|
||||
},
|
||||
"isPersistent": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"x": {
|
||||
"type": "number"
|
||||
},
|
||||
"y": {
|
||||
"type": "number"
|
||||
},
|
||||
"width": {
|
||||
"type": "number"
|
||||
},
|
||||
"height": {
|
||||
"type": "number"
|
||||
},
|
||||
"visible": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"listName",
|
||||
"contents"
|
||||
]
|
||||
},
|
||||
"scripts": {"$ref": "#/definitions/scripts"},
|
||||
"sounds": {"$ref": "#/definitions/sounds"},
|
||||
"costumes": {"$ref": "#/definitions/costumes"},
|
||||
"currentCostumeIndex": {
|
||||
"type": "number",
|
||||
"minimum": 0
|
||||
},
|
||||
"penLayerMD5": {
|
||||
"oneOf":[
|
||||
{"type": "string"},
|
||||
{"type": "null"}
|
||||
]
|
||||
},
|
||||
"penLayerID": {
|
||||
"type": "number",
|
||||
"minimum": -1
|
||||
},
|
||||
"tempoBPM": {
|
||||
"type": "number"
|
||||
},
|
||||
"videoAlpha": {
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 1
|
||||
},
|
||||
"children": {
|
||||
"type": "array",
|
||||
"items": {"$ref": "#/definitions/sprite_object"}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"objName",
|
||||
"costumes",
|
||||
"currentCostumeIndex",
|
||||
"penLayerMD5",
|
||||
"tempoBPM",
|
||||
"children"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,207 +1,54 @@
|
|||
{
|
||||
"$id": "https://scratch.mit.edu",
|
||||
"description": "Scratch project schema",
|
||||
"$id": "https://scratch.mit.edu/sb2_schema.json",
|
||||
"$schema": "http://json-schema.org/schema#",
|
||||
"description": "Scratch 2.0 project schema",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"objName": {
|
||||
"type": "string"
|
||||
},
|
||||
"variables": {
|
||||
"type": "array",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"value": {
|
||||
"type": "string"
|
||||
},
|
||||
"isPersistent": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name",
|
||||
"value"
|
||||
]
|
||||
},
|
||||
"lists": {
|
||||
"type": "array",
|
||||
"properties": {
|
||||
"listName": {
|
||||
"type": "string"
|
||||
},
|
||||
"contents": {
|
||||
"type": "array"
|
||||
},
|
||||
"isPersistent": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"x": {
|
||||
"type": "number"
|
||||
},
|
||||
"y": {
|
||||
"type": "number"
|
||||
},
|
||||
"width": {
|
||||
"type": "number"
|
||||
},
|
||||
"height": {
|
||||
"type": "number"
|
||||
},
|
||||
"visible": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"listName",
|
||||
"contents"
|
||||
]
|
||||
},
|
||||
"scripts": {
|
||||
"type": "array"
|
||||
},
|
||||
"sounds": {
|
||||
"type": "array",
|
||||
"properties": {
|
||||
"soundName": {
|
||||
"type": "string"
|
||||
},
|
||||
"soundID": {
|
||||
"type": "number"
|
||||
},
|
||||
"md5": {
|
||||
"type": "string"
|
||||
},
|
||||
"sampleCount": {
|
||||
"type": "number"
|
||||
},
|
||||
"rate": {
|
||||
"type": "number"
|
||||
},
|
||||
"format": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"soundName",
|
||||
"soundID",
|
||||
"md5",
|
||||
"sampleCount",
|
||||
"rate",
|
||||
"format"
|
||||
]
|
||||
},
|
||||
"costumes": {
|
||||
"type": "array",
|
||||
"properties": {
|
||||
"costumeName": {
|
||||
"type": "string"
|
||||
},
|
||||
"baseLayerID": {
|
||||
"type": "number"
|
||||
},
|
||||
"baseLayerMD5": {
|
||||
"type": "string"
|
||||
},
|
||||
"bitmapResolution": {
|
||||
"type": "number"
|
||||
},
|
||||
"rotationCenterX": {
|
||||
"type": "number"
|
||||
},
|
||||
"rotationCenterY": {
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"costumeName",
|
||||
"baseLayerID",
|
||||
"baseLayerMD5",
|
||||
"bitmapResolution",
|
||||
"rotationCenterX",
|
||||
"rotationCenterY"
|
||||
]
|
||||
},
|
||||
"currentCostumeIndex": {
|
||||
"type": "number",
|
||||
"minimum": 0
|
||||
},
|
||||
"penLayerMD5": {
|
||||
"oneOf":[
|
||||
{"type": "string"},
|
||||
{"type": "null"}
|
||||
]
|
||||
},
|
||||
"penLayerID": {
|
||||
"type": "number",
|
||||
"minimum": -1
|
||||
},
|
||||
"tempoBPM": {
|
||||
"type": "number"
|
||||
},
|
||||
"videoAlpha": {
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 1
|
||||
},
|
||||
"children": {
|
||||
"type": "array",
|
||||
"properties": {},
|
||||
"required": [
|
||||
"objName",
|
||||
"sounds",
|
||||
"costumes",
|
||||
"currentCostumeIndex"
|
||||
]
|
||||
},
|
||||
"info": {
|
||||
"allOf": [
|
||||
{"$ref": "sb2_definitions.json#/definitions/stage_object"},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"flashVersion": {
|
||||
"type": "string"
|
||||
},
|
||||
"swfVersion": {
|
||||
"type": "string"
|
||||
},
|
||||
"userAgent": {
|
||||
"type": "string"
|
||||
},
|
||||
"videoOn": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"savedExtensions": {
|
||||
"type": "array",
|
||||
"info": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"extensionName": {
|
||||
"flashVersion": {
|
||||
"type": "string"
|
||||
},
|
||||
"blockSpecs": {
|
||||
"type": "array"
|
||||
},
|
||||
"menus": {
|
||||
"type": "object"
|
||||
},
|
||||
"javascriptURL": {
|
||||
"swfVersion": {
|
||||
"type": "string"
|
||||
},
|
||||
"userAgent": {
|
||||
"type": "string"
|
||||
},
|
||||
"videoOn": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"savedExtensions": {
|
||||
"type": "array",
|
||||
"properties": {
|
||||
"extensionName": {
|
||||
"type": "string"
|
||||
},
|
||||
"blockSpecs": {
|
||||
"type": "array"
|
||||
},
|
||||
"menus": {
|
||||
"type": "object"
|
||||
},
|
||||
"javascriptURL": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"extensionName",
|
||||
"blockSpecs",
|
||||
"menus",
|
||||
"javascriptURL"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"extensionName",
|
||||
"blockSpecs",
|
||||
"menus",
|
||||
"javascriptURL"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"objName",
|
||||
"costumes",
|
||||
"currentCostumeIndex",
|
||||
"penLayerMD5",
|
||||
"tempoBPM",
|
||||
"children",
|
||||
"info"
|
||||
]
|
||||
}
|
||||
|
|
404
lib/sb3_definitions.json
Normal file
404
lib/sb3_definitions.json
Normal file
|
@ -0,0 +1,404 @@
|
|||
{
|
||||
"$id": "https://scratch.mit.edu/sb3_definitions.json",
|
||||
"$schema": "http://json-schema.org/schema#",
|
||||
"description": "Scratch 3.0 Project and Sprite Schema Definitions",
|
||||
"definitions": {
|
||||
"optionalString": {
|
||||
"oneOf": [
|
||||
{"type": "string"},
|
||||
{"type": "null"}
|
||||
]
|
||||
},
|
||||
"boolOrBoolString": {
|
||||
"oneOf": [
|
||||
{"type": "string",
|
||||
"enum": ["true", "false"]},
|
||||
{"type": "boolean"}
|
||||
]
|
||||
},
|
||||
"stringOrNumber": {
|
||||
"oneOf": [
|
||||
{"type": "string"},
|
||||
{"type": "number"}
|
||||
]
|
||||
},
|
||||
"scalarVal": {
|
||||
"oneOf": [
|
||||
{"$ref":"#/definitions/stringOrNumber"},
|
||||
{"type": "boolean"}
|
||||
]
|
||||
},
|
||||
"assetId": {
|
||||
"type": "string",
|
||||
"pattern": "^[a-fA-F0-9]{32}$"
|
||||
},
|
||||
"costume": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"assetId": { "$ref": "#/definitions/assetId"},
|
||||
"bitmapResolution": {
|
||||
"type": "integer"
|
||||
},
|
||||
"dataFormat": {
|
||||
"type": "string",
|
||||
"enum": ["png", "svg", "jpeg", "jpg", "bmp", "gif"]
|
||||
},
|
||||
"md5ext": {
|
||||
"type": "string",
|
||||
"pattern": "^[a-fA-F0-9]{32}\\.[a-zA-Z]+$"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"rotationCenterX": {
|
||||
"type": "number",
|
||||
"description": "This property is not required, but is highly recommended."
|
||||
},
|
||||
"rotationCenterY": {
|
||||
"type": "number",
|
||||
"description": "This property is not required, but is highly recommended."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"assetId",
|
||||
"dataFormat",
|
||||
"name"
|
||||
]
|
||||
},
|
||||
"sound": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"assetId": { "$ref": "#/definitions/assetId"},
|
||||
"dataFormat": {
|
||||
"type": "string",
|
||||
"enum": ["wav", "wave", "mp3"]
|
||||
},
|
||||
"md5ext": {
|
||||
"type": "string",
|
||||
"pattern": "^[a-fA-F0-9]{32}\\.[a-zA-Z0-9]+$"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"rate": {
|
||||
"type": "integer"
|
||||
},
|
||||
"sampleCount": {
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"assetId",
|
||||
"dataFormat",
|
||||
"name"
|
||||
]
|
||||
},
|
||||
"scalar_variable": {
|
||||
"type": "array",
|
||||
"items": [
|
||||
{"type": "string", "description": "name of the variable"},
|
||||
{"$ref":"#/definitions/scalarVal", "description": "value of the variable"}
|
||||
],
|
||||
"additionalItems": {"type": "boolean", "enum": [true], "description": "Whether this is a cloud variable"},
|
||||
"maxItems": 3
|
||||
},
|
||||
"list": {
|
||||
"type": "array",
|
||||
"items": [
|
||||
{"type":"string", "description": "name of the list"},
|
||||
{
|
||||
"type": "array",
|
||||
"description": "contents of the list",
|
||||
"items": {"$ref":"#/definitions/scalarVal"}
|
||||
}
|
||||
],
|
||||
"additionalItems": false
|
||||
},
|
||||
"broadcast_message": {
|
||||
"type": "string",
|
||||
"description": "the message being broadcasted"
|
||||
},
|
||||
"num_primitive": {
|
||||
"type": "array",
|
||||
"items": [
|
||||
{
|
||||
"type": "number",
|
||||
"enum": [4,5,6,7,8]
|
||||
},
|
||||
{"$ref":"#/definitions/stringOrNumber"}
|
||||
],
|
||||
"additionalItems": false
|
||||
},
|
||||
"color_primitive": {
|
||||
"type": "array",
|
||||
"items": [
|
||||
{
|
||||
"type": "number",
|
||||
"enum": [9]
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"pattern": "^#[a-fA-F0-9]{6}$"
|
||||
}
|
||||
],
|
||||
"additionalItems": false
|
||||
},
|
||||
"text_primitive": {
|
||||
"type": "array",
|
||||
"items": [
|
||||
{
|
||||
"type": "number",
|
||||
"enum": [10]
|
||||
},
|
||||
{"$ref":"#/definitions/stringOrNumber"}
|
||||
],
|
||||
"additionalItems": false
|
||||
},
|
||||
"broadcast_primitive": {
|
||||
"type": "array",
|
||||
"items": [
|
||||
{
|
||||
"type": "number",
|
||||
"enum": [11]
|
||||
},
|
||||
{"type": "string", "description": "broadcast message"},
|
||||
{"type": "string", "description": "broadcast message id"}
|
||||
],
|
||||
"additionalItems": false
|
||||
},
|
||||
"variable_primitive": {
|
||||
"type": "array",
|
||||
"items": [
|
||||
{
|
||||
"type": "number",
|
||||
"enum": [12]
|
||||
},
|
||||
{"type": "string", "description": "variable name"},
|
||||
{"type": "string", "description": "variable id"}
|
||||
],
|
||||
"additionalItems": {
|
||||
"type": "number"
|
||||
},
|
||||
"minItems": 3,
|
||||
"maxItems": 5
|
||||
},
|
||||
"list_primitive": {
|
||||
"type": "array",
|
||||
"items": [
|
||||
{
|
||||
"type": "number",
|
||||
"enum": [13]
|
||||
},
|
||||
{"type": "string", "description": "list name"},
|
||||
{"type": "string", "description": "list id"}
|
||||
],
|
||||
"additionalItems": {
|
||||
"type": "number"
|
||||
},
|
||||
"minItems": 3,
|
||||
"maxItems": 5
|
||||
},
|
||||
"topLevelPrimitive": {
|
||||
"oneOf": [
|
||||
{"$ref":"#/definitions/variable_primitive"},
|
||||
{"$ref":"#/definitions/list_primitive"}
|
||||
]
|
||||
},
|
||||
"inputPrimitive": {
|
||||
"oneOf": [
|
||||
{"$ref":"#/definitions/num_primitive"},
|
||||
{"$ref":"#/definitions/color_primitive"},
|
||||
{"$ref":"#/definitions/text_primitive"},
|
||||
{"$ref":"#/definitions/broadcast_primitive"},
|
||||
{"$ref":"#/definitions/variable_primitive"},
|
||||
{"$ref":"#/definitions/list_primitive"}
|
||||
]
|
||||
},
|
||||
"block": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"opcode": {
|
||||
"type": "string"
|
||||
},
|
||||
"inputs": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "array",
|
||||
"items": [
|
||||
{
|
||||
"type":"number",
|
||||
"enum":[1,2,3],
|
||||
"description": "1 = unobscured shadow, 2 = no shadow, 3 = obscured shadow"
|
||||
}
|
||||
],
|
||||
"additionalItems": {
|
||||
"oneOf": [
|
||||
{"$ref":"#/definitions/optionalString"},
|
||||
{"$ref":"#/definitions/inputPrimitive"}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"fields": {
|
||||
"type": "object"
|
||||
},
|
||||
"next": {"$ref":"#/definitions/optionalString"},
|
||||
"topLevel": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"parent": {"$ref":"#/definitions/optionalString"},
|
||||
"shadow": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"x": {
|
||||
"type": "number"
|
||||
},
|
||||
"y": {
|
||||
"type": "number"
|
||||
},
|
||||
"mutation": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"tagName": {
|
||||
"type": "string",
|
||||
"enum": ["mutation"]
|
||||
},
|
||||
"children": {
|
||||
"type": "array"
|
||||
},
|
||||
"proccode": {
|
||||
"type": "string"
|
||||
},
|
||||
"argumentids": {
|
||||
"type": "string"
|
||||
},
|
||||
"warp": {"$ref":"#/definitions/boolOrBoolString"},
|
||||
"hasnext": {"$ref":"#/definitions/boolOrBoolString"}
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"opcode"
|
||||
]
|
||||
},
|
||||
"stage": {
|
||||
"type": "object",
|
||||
"description": "Description of property (and/or property/value pairs) that are unique to the stage.",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"enum": ["Stage"]
|
||||
},
|
||||
"isStage": {
|
||||
"type": "boolean",
|
||||
"enum": [true]
|
||||
},
|
||||
"tempo": {
|
||||
"type": "number"
|
||||
},
|
||||
"videoTransparency": {
|
||||
"type": "number"
|
||||
},
|
||||
"videoState": {
|
||||
"type": "string",
|
||||
"enum": ["on", "off", "on-flipped"]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name",
|
||||
"isStage"
|
||||
]
|
||||
},
|
||||
"sprite": {
|
||||
"type": "object",
|
||||
"description": "Description of property (and/or property/value pairs) for sprites.",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"not": {"enum": ["Stage", "stage"]}
|
||||
},
|
||||
"isStage": {
|
||||
"type": "boolean",
|
||||
"enum": [false]
|
||||
},
|
||||
"visible": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"x": {
|
||||
"type": "number"
|
||||
},
|
||||
"y": {
|
||||
"type": "number"
|
||||
},
|
||||
"size": {
|
||||
"type": "number"
|
||||
},
|
||||
"direction": {
|
||||
"type": "number"
|
||||
},
|
||||
"draggable": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"rotationStyle": {
|
||||
"type": "string",
|
||||
"enum": ["all around", "don't rotate", "left-right"]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name",
|
||||
"isStage"
|
||||
]
|
||||
},
|
||||
"target": {
|
||||
"type": "object",
|
||||
"description" : "Properties common to both Scratch 3.0 Stage and Sprite",
|
||||
"properties": {
|
||||
"currentCostume": {
|
||||
"type": "integer",
|
||||
"minimum": 0
|
||||
},
|
||||
"blocks": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"oneOf": [
|
||||
{"$ref":"#/definitions/block"},
|
||||
{"$ref":"#/definitions/topLevelPrimitive"}
|
||||
]
|
||||
}
|
||||
},
|
||||
"variables": {
|
||||
"type": "object",
|
||||
"additionalProperties": {"$ref":"#/definitions/scalar_variable"}
|
||||
},
|
||||
"lists": {
|
||||
"type": "object",
|
||||
"additionalProperties": {"$ref":"#/definitions/list"}
|
||||
},
|
||||
"broadcasts": {
|
||||
"type": "object",
|
||||
"additionalProperties": {"$ref":"#/definitions/broadcast_message"}
|
||||
},
|
||||
"costumes": {
|
||||
"type": "array",
|
||||
"items": {"$ref":"#/definitions/costume"},
|
||||
"minItems": 1,
|
||||
"uniqueItems": true
|
||||
},
|
||||
"sounds": {
|
||||
"type": "array",
|
||||
"items": {"$ref":"#/definitions/sound"},
|
||||
"uniqueItems": true
|
||||
},
|
||||
"volume": {
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"variables",
|
||||
"costumes",
|
||||
"sounds",
|
||||
"blocks"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,410 +2,6 @@
|
|||
"$id": "https://scratch.mit.edu/sb3_schema.json",
|
||||
"$schema": "http://json-schema.org/schema#",
|
||||
"description": "Scratch 3.0 Project Schema",
|
||||
"definitions": {
|
||||
"optionalString": {
|
||||
"oneOf": [
|
||||
{"type": "string"},
|
||||
{"type": "null"}
|
||||
]
|
||||
},
|
||||
"boolOrBoolString": {
|
||||
"oneOf": [
|
||||
{"type": "string",
|
||||
"enum": ["true", "false"]},
|
||||
{"type": "boolean"}
|
||||
]
|
||||
},
|
||||
"stringOrNumber": {
|
||||
"oneOf": [
|
||||
{"type": "string"},
|
||||
{"type": "number"}
|
||||
]
|
||||
},
|
||||
"scalarVal": {
|
||||
"oneOf": [
|
||||
{"$ref":"#/definitions/stringOrNumber"},
|
||||
{"type": "boolean"}
|
||||
]
|
||||
},
|
||||
"assetId": {
|
||||
"type": "string",
|
||||
"pattern": "^[a-fA-F0-9]{32}$"
|
||||
},
|
||||
"costume": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"assetId": { "$ref": "#/definitions/assetId"},
|
||||
"bitmapResolution": {
|
||||
"type": "integer"
|
||||
},
|
||||
"dataFormat": {
|
||||
"type": "string",
|
||||
"enum": ["png", "svg", "jpeg", "jpg", "bmp", "gif"]
|
||||
},
|
||||
"md5ext": {
|
||||
"type": "string",
|
||||
"pattern": "^[a-fA-F0-9]{32}\\.[a-zA-Z]+$"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"rotationCenterX": {
|
||||
"type": "number"
|
||||
},
|
||||
"rotationCenterY": {
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"assetId",
|
||||
"dataFormat",
|
||||
"name",
|
||||
"rotationCenterX",
|
||||
"rotationCenterY"
|
||||
]
|
||||
},
|
||||
"sound": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"assetId": { "$ref": "#/definitions/assetId"},
|
||||
"dataFormat": {
|
||||
"type": "string",
|
||||
"enum": ["wav", "wave", "mp3"]
|
||||
},
|
||||
"format": {
|
||||
"type": "string",
|
||||
"enum": ["", "adpcm"]
|
||||
},
|
||||
"md5ext": {
|
||||
"type": "string",
|
||||
"pattern": "^[a-fA-F0-9]{32}\\.[a-zA-Z0-9]+$"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"rate": {
|
||||
"type": "integer"
|
||||
},
|
||||
"sampleCount": {
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"assetId",
|
||||
"dataFormat",
|
||||
"format",
|
||||
"name"
|
||||
]
|
||||
},
|
||||
"scalar_variable": {
|
||||
"type": "array",
|
||||
"items": [
|
||||
{"type": "string", "description": "name of the variable"},
|
||||
{"$ref":"#/definitions/scalarVal", "description": "value of the variable"}
|
||||
],
|
||||
"additionalItems": {"type": "boolean", "enum": [true], "description": "Whether this is a cloud variable"},
|
||||
"maxItems": 3
|
||||
},
|
||||
"list": {
|
||||
"type": "array",
|
||||
"items": [
|
||||
{"type":"string", "description": "name of the list"},
|
||||
{
|
||||
"type": "array",
|
||||
"description": "contents of the list",
|
||||
"items": {"$ref":"#/definitions/scalarVal"}
|
||||
}
|
||||
],
|
||||
"additionalItems": false
|
||||
},
|
||||
"broadcast_message": {
|
||||
"type": "string",
|
||||
"description": "the message being broadcasted"
|
||||
},
|
||||
"num_primitive": {
|
||||
"type": "array",
|
||||
"items": [
|
||||
{
|
||||
"type": "number",
|
||||
"enum": [4,5,6,7,8]
|
||||
},
|
||||
{"$ref":"#/definitions/stringOrNumber"}
|
||||
],
|
||||
"additionalItems": false
|
||||
},
|
||||
"color_primitive": {
|
||||
"type": "array",
|
||||
"items": [
|
||||
{
|
||||
"type": "number",
|
||||
"enum": [9]
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"pattern": "^#[a-fA-F0-9]{6}$"
|
||||
}
|
||||
],
|
||||
"additionalItems": false
|
||||
},
|
||||
"text_primitive": {
|
||||
"type": "array",
|
||||
"items": [
|
||||
{
|
||||
"type": "number",
|
||||
"enum": [10]
|
||||
},
|
||||
{"$ref":"#/definitions/stringOrNumber"}
|
||||
],
|
||||
"additionalItems": false
|
||||
},
|
||||
"broadcast_primitive": {
|
||||
"type": "array",
|
||||
"items": [
|
||||
{
|
||||
"type": "number",
|
||||
"enum": [11]
|
||||
},
|
||||
{"type": "string", "description": "broadcast message"},
|
||||
{"type": "string", "description": "broadcast message id"}
|
||||
],
|
||||
"additionalItems": false
|
||||
},
|
||||
"variable_primitive": {
|
||||
"type": "array",
|
||||
"items": [
|
||||
{
|
||||
"type": "number",
|
||||
"enum": [12]
|
||||
},
|
||||
{"type": "string", "description": "variable name"},
|
||||
{"type": "string", "description": "variable id"}
|
||||
],
|
||||
"additionalItems": {
|
||||
"type": "number"
|
||||
},
|
||||
"minItems": 3,
|
||||
"maxItems": 5
|
||||
},
|
||||
"list_primitive": {
|
||||
"type": "array",
|
||||
"items": [
|
||||
{
|
||||
"type": "number",
|
||||
"enum": [13]
|
||||
},
|
||||
{"type": "string", "description": "list name"},
|
||||
{"type": "string", "description": "list id"}
|
||||
],
|
||||
"additionalItems": {
|
||||
"type": "number"
|
||||
},
|
||||
"minItems": 3,
|
||||
"maxItems": 5
|
||||
},
|
||||
"topLevelPrimitive": {
|
||||
"oneOf": [
|
||||
{"$ref":"#/definitions/variable_primitive"},
|
||||
{"$ref":"#/definitions/list_primitive"}
|
||||
]
|
||||
},
|
||||
"inputPrimitive": {
|
||||
"oneOf": [
|
||||
{"$ref":"#/definitions/num_primitive"},
|
||||
{"$ref":"#/definitions/color_primitive"},
|
||||
{"$ref":"#/definitions/text_primitive"},
|
||||
{"$ref":"#/definitions/broadcast_primitive"},
|
||||
{"$ref":"#/definitions/variable_primitive"},
|
||||
{"$ref":"#/definitions/list_primitive"}
|
||||
]
|
||||
},
|
||||
"block": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"opcode": {
|
||||
"type": "string"
|
||||
},
|
||||
"inputs": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "array",
|
||||
"items": [
|
||||
{
|
||||
"type":"number",
|
||||
"enum":[1,2,3],
|
||||
"description": "1 = unobscured shadow, 2 = no shadow, 3 = obscured shadow"
|
||||
}
|
||||
],
|
||||
"additionalItems": {
|
||||
"oneOf": [
|
||||
{"$ref":"#/definitions/optionalString"},
|
||||
{"$ref":"#/definitions/inputPrimitive"}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"fields": {
|
||||
"type": "object"
|
||||
},
|
||||
"next": {"$ref":"#/definitions/optionalString"},
|
||||
"topLevel": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"parent": {"$ref":"#/definitions/optionalString"},
|
||||
"shadow": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"x": {
|
||||
"type": "number"
|
||||
},
|
||||
"y": {
|
||||
"type": "number"
|
||||
},
|
||||
"mutation": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"tagName": {
|
||||
"type": "string",
|
||||
"enum": ["mutation"]
|
||||
},
|
||||
"children": {
|
||||
"type": "array"
|
||||
},
|
||||
"proccode": {
|
||||
"type": "string"
|
||||
},
|
||||
"argumentids": {
|
||||
"type": "string"
|
||||
},
|
||||
"warp": {"$ref":"#/definitions/boolOrBoolString"},
|
||||
"hasnext": {"$ref":"#/definitions/boolOrBoolString"}
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"opcode"
|
||||
]
|
||||
},
|
||||
"stage": {
|
||||
"type": "object",
|
||||
"description": "Description of property (and/or property/value pairs) that are unique to the stage.",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"enum": ["Stage"]
|
||||
},
|
||||
"isStage": {
|
||||
"type": "boolean",
|
||||
"enum": [true]
|
||||
},
|
||||
"tempo": {
|
||||
"type": "number"
|
||||
},
|
||||
"videoTransparency": {
|
||||
"type": "number"
|
||||
},
|
||||
"videoState": {
|
||||
"type": "string",
|
||||
"enum": ["on", "off", "on-flipped"]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name",
|
||||
"isStage"
|
||||
]
|
||||
},
|
||||
"sprite": {
|
||||
"type": "object",
|
||||
"description": "Description of property (and/or property/value pairs) for sprites.",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"not": {"enum": ["Stage", "stage"]}
|
||||
},
|
||||
"isStage": {
|
||||
"type": "boolean",
|
||||
"enum": [false]
|
||||
},
|
||||
"visible": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"x": {
|
||||
"type": "number"
|
||||
},
|
||||
"y": {
|
||||
"type": "number"
|
||||
},
|
||||
"size": {
|
||||
"type": "number"
|
||||
},
|
||||
"direction": {
|
||||
"type": "number"
|
||||
},
|
||||
"draggable": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"rotationStyle": {
|
||||
"type": "string",
|
||||
"enum": ["all around", "don't rotate", "left-right"]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name",
|
||||
"isStage"
|
||||
]
|
||||
},
|
||||
"target": {
|
||||
"type": "object",
|
||||
"description" : "Properties common to both Scratch 3.0 Stage and Sprite",
|
||||
"properties": {
|
||||
"currentCostume": {
|
||||
"type": "integer",
|
||||
"minimum": 0
|
||||
},
|
||||
"blocks": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"oneOf": [
|
||||
{"$ref":"#/definitions/block"},
|
||||
{"$ref":"#/definitions/topLevelPrimitive"}
|
||||
]
|
||||
}
|
||||
},
|
||||
"variables": {
|
||||
"type": "object",
|
||||
"additionalProperties": {"$ref":"#/definitions/scalar_variable"}
|
||||
},
|
||||
"lists": {
|
||||
"type": "object",
|
||||
"additionalProperties": {"$ref":"#/definitions/list"}
|
||||
},
|
||||
"broadcasts": {
|
||||
"type": "object",
|
||||
"additionalProperties": {"$ref":"#/definitions/broadcast_message"}
|
||||
},
|
||||
"costumes": {
|
||||
"type": "array",
|
||||
"items": {"$ref":"#/definitions/costume"},
|
||||
"minItems": 1,
|
||||
"uniqueItems": true
|
||||
},
|
||||
"sounds": {
|
||||
"type": "array",
|
||||
"items": {"$ref":"#/definitions/sound"},
|
||||
"uniqueItems": true
|
||||
},
|
||||
"volume": {
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"variables",
|
||||
"costumes",
|
||||
"sounds",
|
||||
"blocks"
|
||||
]
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"meta": {
|
||||
|
@ -427,21 +23,20 @@
|
|||
"semver"
|
||||
]
|
||||
},
|
||||
|
||||
"targets": {
|
||||
"type": "array",
|
||||
"items": [
|
||||
{
|
||||
"allOf": [
|
||||
{"$ref": "#/definitions/stage" },
|
||||
{"$ref": "#/definitions/target"}
|
||||
{"$ref": "sb3_definitions.json#/definitions/stage" },
|
||||
{"$ref": "sb3_definitions.json#/definitions/target"}
|
||||
]
|
||||
}
|
||||
],
|
||||
"additionalItems": {
|
||||
"allOf": [
|
||||
{"$ref": "#/definitions/sprite"},
|
||||
{"$ref": "#/definitions/target"}
|
||||
{"$ref": "sb3_definitions.json#/definitions/sprite"},
|
||||
{"$ref": "sb3_definitions.json#/definitions/target"}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
53
lib/sprite2_schema.json
Normal file
53
lib/sprite2_schema.json
Normal file
|
@ -0,0 +1,53 @@
|
|||
{
|
||||
"$id": "https://scratch.mit.edu/sprite2_schema.json",
|
||||
"description": "Scratch 2.0 project schema",
|
||||
"type": "object",
|
||||
"allOf": [
|
||||
{"$ref": "sb2_definitions.json#/definitions/sprite_object"},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"info": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"flashVersion": {
|
||||
"type": "string"
|
||||
},
|
||||
"swfVersion": {
|
||||
"type": "string"
|
||||
},
|
||||
"userAgent": {
|
||||
"type": "string"
|
||||
},
|
||||
"videoOn": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"savedExtensions": {
|
||||
"type": "array",
|
||||
"properties": {
|
||||
"extensionName": {
|
||||
"type": "string"
|
||||
},
|
||||
"blockSpecs": {
|
||||
"type": "array"
|
||||
},
|
||||
"menus": {
|
||||
"type": "object"
|
||||
},
|
||||
"javascriptURL": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"extensionName",
|
||||
"blockSpecs",
|
||||
"menus",
|
||||
"javascriptURL"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
42
lib/sprite3_schema.json
Normal file
42
lib/sprite3_schema.json
Normal file
|
@ -0,0 +1,42 @@
|
|||
{
|
||||
"$id": "https://scratch.mit.edu/sprite3_schema.json",
|
||||
"$schema": "http://json-schema.org/schema#",
|
||||
"description": "Scratch 3.0 Sprite Schema",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"meta": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"semver": {
|
||||
"type": "string",
|
||||
"pattern": "^(3.[0-9]+.[0-9]+)$"
|
||||
},
|
||||
"vm": {
|
||||
"type": "string",
|
||||
"pattern": "^([0-9]+.[0-9]+.[0-9]+)($|-)"
|
||||
},
|
||||
"agent": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"semver"
|
||||
]
|
||||
},
|
||||
"targets": {
|
||||
"type": "array",
|
||||
"items": [
|
||||
{
|
||||
"allOf": [
|
||||
{"$ref": "sb3_definitions.json#/definitions/sprite"},
|
||||
{"$ref": "sb3_definitions.json#/definitions/target"}
|
||||
]
|
||||
}
|
||||
],
|
||||
"additionalItems": false
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"targets"
|
||||
]
|
||||
}
|
|
@ -4,11 +4,13 @@ var unzip = require('./unzip');
|
|||
* If input a buffer, transforms buffer into a UTF-8 string.
|
||||
* If input is encoded in zip or gzip format, the input will be extracted and decoded.
|
||||
* If input is a string, passes that string along to the given callback.
|
||||
* @param {boolean} isSprite Whether the input should be treated as
|
||||
* a sprite (true) or a whole project (false)
|
||||
* @param {Buffer | string} input Project data
|
||||
* @param {Function} callback Error or stringified project data
|
||||
* @return {void}
|
||||
*/
|
||||
module.exports = function (input, callback) {
|
||||
module.exports = function (isSprite, input, callback) {
|
||||
if (typeof input === 'string') {
|
||||
// Pass string to callback
|
||||
return callback(null, [input, null]);
|
||||
|
@ -46,5 +48,5 @@ module.exports = function (input, callback) {
|
|||
// Return error if legacy encoding detected
|
||||
if (isLegacy) return callback('Parser only supports Scratch 2.X and above');
|
||||
|
||||
unzip(input, isGZip, callback);
|
||||
unzip(isSprite, input, isGZip, callback);
|
||||
};
|
||||
|
|
|
@ -3,12 +3,14 @@ var GZip = require('gzip-js');
|
|||
|
||||
/**
|
||||
* Unpacks a zip or gzip file.
|
||||
* @param {boolean} isSprite Whether the input should be treated as
|
||||
* a sprite (true) or whole project (false)
|
||||
* @param {string} input Zip file provided as a string
|
||||
* @param {boolean} isGZip Whether the input is a GZip file, otherwise treat as zip
|
||||
* @param {array} callback Array including both the project and zip archive
|
||||
* @return {void}
|
||||
*/
|
||||
module.exports = function (input, isGZip, callback) {
|
||||
module.exports = function (isSprite, input, isGZip, callback) {
|
||||
var msg = 'Failed to unzip and extract project.json, with error: ';
|
||||
if (isGZip) {
|
||||
var unpackedProject = null;
|
||||
|
@ -21,7 +23,7 @@ module.exports = function (input, isGZip, callback) {
|
|||
}
|
||||
return JSZip.loadAsync(input)
|
||||
.then(function (zip) {
|
||||
return zip.file('project.json').async('string')
|
||||
return zip.file(isSprite ? 'sprite.json' : 'project.json').async('string')
|
||||
.then(function (project) {
|
||||
return callback(null, [project, zip]);
|
||||
});
|
||||
|
|
|
@ -1,10 +1,15 @@
|
|||
var ajv = require('ajv')();
|
||||
var sb2schema = require('./sb2_schema.json');
|
||||
var sb3schema = require('./sb3_schema.json');
|
||||
var sb2Defs = require('./sb2_definitions.json');
|
||||
var sb3Defs = require('./sb3_definitions.json');
|
||||
var sb2Schema = require('./sb2_schema.json');
|
||||
var sb3Schema = require('./sb3_schema.json');
|
||||
var sprite2Schema = require('./sprite2_schema.json');
|
||||
var sprite3Schema = require('./sprite3_schema.json');
|
||||
ajv.addSchema(sb2Defs).addSchema(sb3Defs);
|
||||
|
||||
module.exports = function (input, callback) {
|
||||
var validateSb2 = ajv.compile(sb2schema);
|
||||
var validateSb3 = ajv.compile(sb3schema);
|
||||
module.exports = function (isSprite, input, callback) {
|
||||
var validateSb2 = ajv.compile(isSprite ? sprite2Schema : sb2Schema);
|
||||
var validateSb3 = ajv.compile(isSprite ? sprite3Schema : sb3Schema);
|
||||
|
||||
var isValidSb2 = validateSb2(input);
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue