Hey Guys,
I am currently working (or better: want to start working) on a Scratch Player for the new WP7. But the only Project-File-Format-Specification I can find is for the V01 of the project-file format. Is there any newer specification? And can you get the Java-source the Scratch Applet?
Would be very glad if you could tell me
Thank you,
FlySoft
Offline
I already did, now I am reading the source, but I think it'll take some time. The documentation isn't very exact, so I have no chance but reading the source
Offline
Here is the base of a player I starter in JavaScript but never finished:
(I hope this post isn't too big.
)
Reader file:
var class_id = new Array();
class_id[1] = "UndefinedObject";class_id[2] = "True";class_id[3] = "False";class_id[4] = "SmallInteger";
class_id[5] = "SmallInteger16";class_id[6] = "LargePositiveInteger";class_id[7] = "LargeNegativeInteger";
class_id[8] = "Float";class_id[9] = "String";class_id[10] = "Symbol";class_id[11] = "ByteArray";
class_id[12] = "SoundBuffer";class_id[13] = "Bitmap";class_id[14] = "UTF8";class_id[20] = "Array";
class_id[21] = "OrderedCollection";class_id[22] = "Set";
class_id[23] = "IdentitySet";class_id[24] = "Dictionary";class_id[25] = "IdentityDictionary";class_id[30] = "Color";
class_id[31] = "TranslucentColor";class_id[32] = "Point";class_id[33] = "Rectangle";class_id[34] = "Form";class_id[35] = "ColorForm";
class_id[99] = "ObjectRef";class_id[100] = "Morph";class_id[101] = "BorderedMorph";class_id[102] = "RectangleMorph";
class_id[103] = "EllipseMorph";class_id[104] = "AlignmentMorph";class_id[105] = "StringMorph";class_id[106] = "UpdatingStringMorph";
class_id[107] = "SimpleSliderMorph";
class_id[108] = "SimpleButtonMorph";class_id[109] = "SampledSound";class_id[110] = "ImageMorph";class_id[111] = "SketchMorph";
class_id[123] = "SensorBoardMorph";class_id[124] = "ScratchSpriteMorph";
class_id[125] = "ScratchStageMorph";class_id[140] = "ChoiceArgMorph";class_id[141] = "ColorArgMorph";
class_id[142] = "ExpressionArgMorph";class_id[145] = "SpriteArgMorph";class_id[147] = "BlockMorph";
class_id[148] = "CommandBlockMorph";class_id[149] = "CBlockMorph";
class_id[151] = "HatBlockMorph";class_id[153] = "ScratchScriptsMorph";class_id[154] = "ScratchSliderMorph";
class_id[155] = "WatcherMorph";class_id[157] = "SetterBlockMorph";class_id[158] = "EventHatMorph";class_id[160] = "VariableBlockMorph";
class_id[162] = "ImageMedia";class_id[163] = "MovieMedia";class_id[164] = "SoundMedia";class_id[165] = "KeyEventHatMorph";
class_id[166] = "BooleanArgMorph";class_id[167] = "EventTitleMorph";class_id[168] = "MouseClickEventHatMorph";
class_id[169] = "ExpressionArgMorphWithMenu";class_id[170] = "ReporterBlockMorph";class_id[171] = "MultilineStringMorph";
class_id[172] = "ToggleButton";class_id[173] = "WatcherReadoutFrameMorph";class_id[174] = "WatcherSliderMorph";
class_id[175] = "ScratchListMorph";class_id[176] = "ScrollingStringMorph";
//var file = [];
var info;
var object;
var index = 0;
var file;
var fields;
var image;
function read(file)
{
//file = this.file;
if (read_header())
{
if (next_string(2) == 0)
{
println("This project is too old!");
}
else
{
println("This file is a Scratch file!");
next_uint(4);
info = read_object();
var notes = info.at("comment");
setNotes(notes);
setHistory(info.at("history"));
object = read_object();
}
}
else
{
println("This file is not a Scratch file!");
}
}
function next_byte()
{
var b = file[index];
index++;
return b;
}
function next_length(length)
{
bytes = [];
if (length > file.length - index)
{
throw "Trying to read " + length + " bytes. It is longer than the file itself. Index: " + index;
if(!confirm("Warning, this may be an error: The viewer wants to read " + length + " bytes of data. Do you wish to continue?")){return;}
}
for (var i = 0; i < length; i++)
{
bytes[i] = next_byte();
}
return bytes;
}
function read_header()
{
next = next_string(8);
println(next);
return next == "ScratchV";
}
function next_string(length)
{
string = "";
for (var i = 0; i < length; i++)
{
string += String.fromCharCode(next_byte());
}
return string;
}
function next_uint(length)
{
num = 0;
j = 1;
bytes = next_length(length).reverse();
for (var i = 0; i < bytes.length; i++)
{
var b = bytes[i];
num += (b * j);
j *= 256;
}
return num;
}
function next_int(length)
{
num = 0;
j = 1;
bytes = next_length(length);
for (var i = 0; i < bytes.length; i++)
{
var b = bytes[i];
num += (b * j);
j *= 256;
}
return num;
}
function read_obj_header()
{
return next_string(4) == "ObjS" && next_byte() == 1 && next_string(4) == "Stch" && next_byte() == 1;
}
function read_object()
{
if (read_obj_header())
{
objectSize = next_uint(4);
println("Object size: " + objectSize)
fields = [];
for (var fieldNum = 0; fieldNum < objectSize; fieldNum++)
{
field = read_field();
fields[fieldNum] = field;
}
return fields[0].fix().fixClass();
}
else
{
println("Not an Object!");
}
}
function read_field()
{
return new FieldObject().read();
}
function FieldObject()
{
var id;
var name;
var userDefined;
var variables;
var fixed = false;
var fixedClass = false;
var version;
this.read = function()
{
id = next_byte();
name = class_id[id];
userDefined = false;
variables = [];
println("Reading: " + name + " id: " + id + " index: " + index);
switch (name)
{
case "ObjectRef":
variables = [next_uint(3)];
break;
case "UndefinedObject":
variables = new Array();
break;
case "True":
variables = new Array();
break;
case "False":
variables = new Array();
break;
case "SmallInteger":
variables = [next_uint(4)];
break;
case "SmallInteger16":
variables = [next_uint(2)];
break;
case "Float":
variables = [next_uint(4), next_uint(4)];
break;
case "String":
variables = [next_string(next_uint(4))];
break;
case "Symbol":
variables = [next_string(next_uint(4))];
break;
case "ByteArray":
variables = next_length(next_uint(4));
break;
case "SoundBuffer":
variables = next_length(next_uint(4)*2);
break;
case "Bitmap":
size = next_uint(4);
variables = new Array();
for (i = 0; i < size; i++)
{
variables[i] = next_uint(4);
}
break;
case "UTF8":
variables = [next_string(next_uint(4))];
break;
case "Array":
size = next_uint(4);
variables = new Array();
for (i = 0; i < size; i++)
{
variables[i] = read_field();
}
break;
case "OrderedCollection":
size = next_uint(4);
variables = new Array();
for (i = 0; i < size; i++)
{
variables[i] = read_field();
}
break;
case "Dictionary":
size = next_uint(4);
variables = new Array();
for (i = 0; i < size; i++)
{
key = read_field();
value = read_field();
variables[i] = [key, value];
}
break;
case "Color":
var color = next_uint(4);
variables = [color >> 22 & 0xFF, color >> 12 & 0xFF, color >> 2 & 0xFF];
break;
case "TranslucentColor":
var color = next_uint(4);
variables = [color >> 22 & 0xFF, color >> 12 & 0xFF, color >> 2 & 0xFF, next_byte()];
break;
case "Point":
variables = [read_field(), read_field()];
break;
case "Rectangle":
variables = [read_field(), read_field(), read_field(), read_field()];
break;
case "Form":
width = read_field();
height = read_field();
depth = read_field();
offset = read_field();
bits = read_field();
variables = [width, height, depth, offset, bits];
break;
case "ColorForm":
width = read_field();
height = read_field();
depth = read_field();
offset = read_field();
bits = read_field();
colors = read_field();
variables = [width, height, depth, offset, bits, colors];
break;
default:
if(id >= 100 && id <= 176)
{
version = next_byte();
size = next_byte();
variables = new Array();
for (i = 0; i < size; i++)
{
value = read_field();
variables[i] = value;
}
userDefined = true;
}
else
{
throw "Unknown class: " + id;
}
}
return this;
};
this.fix = function()
{
println("Fixing: " + name);
if(!fixed)
{
fixed = true;
switch (name)
{
case "ObjectRef":
return fields[variables[0] - 1].fix();
break;
case "Array":
for (var i = 0; i < variables.length; i++)
{
variables[i] = variables[i].fix();
}
break;
case "OrderedCollection":
for (var i = 0; i < variables.length; i++)
{
variables[i] = variables[i].fix();
}
break;
case "Dictionary":
for (var i = 0; i < variables.length; i++)
{
variables[i][0] = variables[i][0].fix();
variables[i][1] = variables[i][1].fix();
}
break;
case "Form":
for (var i = 0; i < variables.length; i++)
{
variables[i] = variables[i].fix();
}
break;
case "ColorForm":
for (var i = 0; i < variables.length; i++)
{
variables[i] = variables[i].fix();
}
break;
default:
if(userDefined)
{
for (var i = 0; i < variables.length; i++)
{
variables[i] = variables[i].fix();
}
}
}
}
return this;
};
this.fixClass = function()
{
println("Fixing class: " + name);
if(!fixedClass)
{
fixedClass = true;
switch (name)
{
case "Array":
for (var i = 0; i < variables.length; i++)
{
variables[i] = variables[i].fixClass();
}
break;
case "OrderedCollection":
for (var i = 0; i < variables.length; i++)
{
variables[i] = variables[i].fixClass();
}
break;
case "Dictionary":
for (var i = 0; i < variables.length; i++)
{
variables[i][0] = variables[i][0].fixClass();
variables[i][1] = variables[i][1].fixClass();
}
break;
case "Form":
for (var i = 0; i < variables.length; i++)
{
variables[i] = variables[i].fixClass();
}
break;
case "ColorForm":
for (var i = 0; i < variables.length; i++)
{
variables[i] = variables[i].fixClass();
}
break;
case "Point":
variables[0] = variables[0].fixClass();
variables[1] = variables[1].fixClass();
break;
case "Rectangle":
variables[0] = variables[0].fixClass();
variables[1] = variables[1].fixClass();
variables[2] = variables[2].fixClass();
variables[3] = variables[3].fixClass();
break;
default:
if(userDefined)
{
for (var i = 0; i < variables.length; i++)
{
variables[i] = variables[i].fixClass();
}
}
}
}
return classFor(this);
}
this.toString = function()
{
switch (name)
{
case "ObjectRef":
return "ObjectRef: " + variables[0];
break;
case "String":
return "String: " + variables[0];
break;
case "UTF8":
return "UTF8: " + variables[0];
break;
case "Array":
return "Dictionary ln: " + variables.length;
break;
case "OrderedCollection":
return "Dictionary ln: " + variables.length;
break;
case "Dictionary":
return "Dictionary ln: " + variables.length;
break;
case "Form":
return "Form";
break;
case "ColorForm":
return "ColorForm ln: " + variables.length;
break;
default:
return name + " ln: " + variables.length;
}
}
this.getName = function() {return name};
this.getVariables = function() {return variables};
this.getFixed = function() {return fixed};
this.getFixedClass = function() {return fixedClass};
this.getId = function() {return id};
this.getVersion = function() {return version};
this.getUserDefined = function() {return userDefined};
}Classes:
function classFor(obj)
{
var objName = obj.getName();
switch(objName)
{
case "String":
return obj.getVariables()[0];
break;
case "UTF8":
return obj.getVariables()[0];
break;
case "Symbol":
return obj.getVariables()[0];
break;
case "UndefinedObject":
return null;
break;
case "True":
return true;
break;
case "False":
return false;
break;
case "SmallInteger":
return obj.getVariables()[0];
break;
case "SmallInteger16":
return obj.getVariables()[0];
break;
case "Float":
return obj.getVariables()[0];
break;
case "Array":
return obj.getVariables();
break;
case "OrderedCollection":
return obj.getVariables();
break;
case "ByteArray":
return obj.getVariables();
break;
case "Dictionary":
var result = new Dictionary();
for (var i = 0; i < obj.getVariables().length; i++)
{
result.keys.push(obj.getVariables()[i][0]);
result.values.push(obj.getVariables()[i][1]);
}
return result;
break;
case "Color":
var result = new Color();
result.r = obj.getVariables()[0];
result.g = obj.getVariables()[1];
result.b = obj.getVariables()[2];
return result;
break;
case "TranslucentColor":
var result = new TranslucentColor();
result.r = obj.getVariables()[0];
result.g = obj.getVariables()[1];
result.b = obj.getVariables()[2];
result.a = obj.getVariables()[3];
return result;
break;
case "Point":
var result = new Point();
result.x = obj.getVariables()[0];
result.y = obj.getVariables()[1];
return result;
break;
case "Rectangle":
var result = new Rectangle();
result.x = obj.getVariables()[0];
result.y = obj.getVariables()[1];
result.cornerX = obj.getVariables()[2];
result.cornerY = obj.getVariables()[3];
return result;
break;
case "Form":
var result = new Form();
result.width = obj.getVariables()[0];
result.height = obj.getVariables()[1];
result.depth = obj.getVariables()[2];
result.offset = obj.getVariables()[3];
result.bits = obj.getVariables()[4];
return result;
break;
case "ColorForm":
var result = new ColorForm();
result.width = obj.getVariables()[0];
result.height = obj.getVariables()[1];
result.depth = obj.getVariables()[2];
result.offset = obj.getVariables()[3];
result.bits = obj.getVariables()[4];
result.colors = obj.getVariables()[5];
return result;
break;
case "Bitmap":
return obj.getVariables();
break;
case "SoundBuffer":
return obj.getVariables();
break;
default:
if(obj.getUserDefined())
{
if(false)//obj.getName() == "ScratchStageMorph")
{
var result = new ScratchStageMorph();
result.init(obj.getVariables());
}
else
{
var result = new Morph();
result.version = obj.getVersion();
result.variables = obj.getVariables();
}
return result;
}
}
throw "Unknown class: " + obj.getId();
}
function Dictionary()
{
this.keys = [];
this.values = [];
this.at = function(key)
{
for(var i = 0; i < this.keys.length; i++)
{
if(key == this.keys[i])
{
return this.values[i];
}
}
return null;
}
}
function Morph()
{
this.version = 0;
this.variables = [];
}
function ScratchStageMorph()
{
this.init = function(vars)
{
this.bounds = vars[0];
this.owner = vars[1];
this.submorphs = vars[2];
this.color = vars[3];
this.flags = vars[4];
this.objName = vars[5];
this.vars = vars[6];
this.blocksBin = vars[7];
this.isClone = vars[8];
this.media = vars[9];
this.costume = vars[10];
this.zoom = vars[11];
this.hPan = vars[12];
this.vPan = vars[13];
this.obsoleteSavedState = vars[14];
this.sprites = vars[15];
this.volume = vars[16];
this.tempoBPM = vars[17];
this.sceneStates = vars[18];
this.lists = vars[19];
}
}
function Color()
{
this.r = 0;
this.g = 0;
this.b = 0;
}
function TranslucentColor()
{
this.r = 0;
this.g = 0;
this.b = 0;
this.a = 0;
}
function Point()
{
this.x = 0;
this.y = 0;
}
function Rectangle()
{
this.x = 0;
this.y = 0;
this.cornerX = 0;
this.cornerY = 0;
}
function Form()
{
this.width = 0;
this.height = 0;
this.depth = 0;
this.offset = 0;
this.bits = [];
}
function ColorForm()
{
this.width = 0;
this.height = 0;
this.depth = 0;
this.offset = 0;
this.bits = [];
this.colors = [];
}Last edited by MathWizz (2011-04-03 17:30:18)
Offline
I dicedet to make my own project format, with a more or less comfortable editor ;-)
Thank you all anyway!
Offline