Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- package edu.mit.scratch;
- public class ScratchFileReader {
- public static ScratchProject read (File file) {
- ZipFile zip = new ZipFile(file);
- ZipEntry entry = null;
- zip.stream().forEach((candidate) -> { if (candidate.getName().endsWith("project.json") && entry == null)
- entry = candidate; });
- if (entry == null) throw new NullPointerException("No project.json file found");
- StringWriter writer = new StringWriter();
- zip.getInputStream(entry).transferTo(writer);
- JSONObject json = new JSONObject(writer.toString());
- ScratchProject.Reference ref = new ScratchProject.Reference();
- List<ScratchSprite> sprites = readSprites(json.getJSONArray("targets"), ref, zip);
- ScratchStage stage = readStage(json.getJSONArray("targets"), ref, zip);
- List<ScratchMonitor> monitors = readMonitors(json.getJSONArray("monitors"), ref);
- List<String> extensions = List.<String>of();
- json.getJSONArray("extensions").toList().forEach((obj) -> { extensions.add(obj.toString()); });
- ScratchMetadata meta = readMetadata(json.getJSONObject("meta"));
- return new ScratchProject(targets, monitors, extensions, meta, ref);
- }
- public static List<ScratchSprite> readSprites (JSONArray json, ScratchProject.Reference ref, ZipFile zip) {
- List<ScratchSprite> sprites = List.<ScratchSprite>of();
- for (Object e : json)
- if (e instanceof JSONObject j && !j.getBoolean("isStage"))
- sprites.add(ScratchTargetParser.readSprite(e, ref, zip));
- return sprites;
- }
- public static ScratchStage readStage (JSONArray json, ScratchProject.Reference ref, ZipFile zip) {
- for (Object e : json)
- if (e instanceof JSONObject j && j.getBoolean("isStage"))
- return ScratchTargetParser.readStage(e, ref, zip);
- }
- public static List<ScratchMonitor> readMonitors (JSONArray json) {
- List<ScratchMonitor> monitors = List.<ScratchMonitor>of();
- for (Object e : json)
- if (e instanceof JSONObject j)
- monitors.add(ScratchMonitor.parse(json, ref));
- return monitors;
- }
- public static ScratchMetadata readMetadata (JSONObject json) {
- return new ScratchMetadata(json.getString("semver"), json.getString("vm"), json.getString("agent"));
- }
- }
- public class ScratchTargetParser {
- public static void readSprite (JSONObject json, ScratchProject.Reference ref, ZipFile zip) {
- ScratchTargetReference ref2 = ref.referenceSprite(json.getString("name"));
- ScratchSprite sprite = new ScratchSprite(json.getString("name"), readVariables(json.getJSONObject("variables"), ref2), readLists(json.getJSONObject("lists"), ref2), readBroadcasts(json.optJSONObject("broadcasts", new JSONObject()), ref2), readBlocks(json.getJSONObject("blocks"), ref2), readComments(json.getJSONObject("comments"), ref2), readCostumes(json.getJSONArray("costumes"), zip), readSounds(json.getJSONArray("sounds"), zip));
- sprite.setCostume(json.getInt("currentCostume"));
- sprite.setLayer(json.getInt("layerOrder"));
- sprite.setVolume(json.getFloat("volume"));
- sprite.initialize(ref2);
- return sprite;
- }
- public static List<ScratchVariable> readVariables (JSONObject json, ScratchTargetReference ref) {
- List<ScratchVariable> variables = List.<ScratchVariable>of();
- for (String key : json.keySet()) {
- JSONArray array = json.getJSONArray(key);
- ScratchVariable var = new ScratchVariable(array.getString(0), array.optBoolean(2, false));
- var.initialize(ref.referenceVariable(key, array.getString(1)));
- variables.add(var);
- }
- return variables;
- }
- public static void writeVariables (JSONObject json, ScratchTargetReference ref) {
- for (String s : ref.getVariables()) {
- JSONArray array = new JSONArray().put(ref.getVariable(s).name()).put(ref.getVariable(s).getValue()).put(ref.getVariable(s).cloud());
- json.put(s, array);
- }
- }
- public static List<ScratchList> readLists (JSONObject json, ScratchProject.Reference ref) {
- List<ScratchList> lists = List.<ScratchList>of();
- for (String key : json.keySet()) {
- JSONArray array = json.getJSONArray(key);
- List<ScratchVariable.Reference> list = List.<ScratchVariable.Reference>of();
- List<String> values = List.<String>of();
- array.get(1).toList().forEach((obj) -> { values.add(obj.toString()); });
- for (int i = 0; i <= values.size(); i++)
- list.put(ref.referenceVariable(key + "[" + i + "]", values.get(i)));
- ScratchList rList = new ScratchList(array.get(0));
- rList.initialize(ref.referenceList(key, list));
- lists.add(rList);
- }
- return lists;
- }
- public static void writeLists (JSONObject json, ScratchTargetReference ref) {
- for (String s : ref.getLists()) {
- List<String> list = List.<String>of();
- ref.getList(s).getValues().forEach((ref) -> { list.add(ref.getValue()); });
- JSONArray array = new JSONArray().put(ref.getList(s).getName()).put(new JSONArray(list));
- json.put(s, array);
- }
- }
- public static List<String> readBroadcasts (JSONObject json, ScratchTargetReference ref) {
- List<String> strings = List.<String>of();
- for (String key : json.keySet()) {
- ref.referenceBroadcast(key, json.getString(key));
- strings.put(json.getString(key));
- }
- return strings;
- }
- public static void writeBroadcasts (JSONObject json, ScratchTargetReference ref) {
- for (String s : ref.getBroadcasts())
- json.put(s, ref.getBroadcast(s));
- }
- public static List<ScratchBlock> readBlocks (JSONObject json, ScratchTargetReference ref) {
- List<ScratchBlock> blocks = List.<ScratchBlock>of();
- for (String s : json.keySet()) {
- try {
- json.getJSONObject(s);
- } catch (JSONException je) {
- JSONArray array = json.getJSONArray(s);
- int type = array.getInt(0);
- if (type < 9)
- type = 4;
- ScratchBlock block;
- switch (type) {
- case 4:
- block = ScratchPrimitiveBlock.intPrimitive(array.getInt(1));
- break;
- case 9:
- block = ScratchPrimitiveBlock.colorPrimitive(parseColor(array.getString(1)));
- break;
- case 10:
- block = ScratchPrimitiveBlock.stringPrimitive(array.getString(1));
- break;
- case 12:
- block = new ScratchBroadcastReporterBlock(array.getString(2));
- break;
- case 12:
- block = new ScratchVariableReporterBlock(array.getString(2));
- break;
- case 13:
- block = new ScratchListReporterBlock(array.getString(2));
- break;
- }
- if (array.has(3)) {
- block.setX(array.getInt(3));
- block.setY(array.getInt(4));
- }
- blocks.put(block);
- ref.referenceBlock(s, block);
- continue;
- }
- JSONObject obj = json.getJSONObject(s);
- ScratchBlock block = ScratchBlock.fromOpcode(obj.getString("opcode"), obj.getJSONObject("inputs"), obj.getJSONObject("fields"));
- String child = obj.getString("next");
- if (ref.getBlock(child) != null) {
- block.setChild(ref.getBlock(child));
- ref.getBlock(child).setParent(block);
- }
- if (obj.getBoolean("topLevel")) {
- String parent = obj.getString("parent");
- if (ref.getBlock(parent) != null) {
- block.setParent(ref.getBlock(parent));
- ref.getBlock(parent).setChild(block);
- }
- block.setX(obj.getInt("x"));
- block.setY(obj.getInt("y"));
- }
- block.setShadow(obj.getBoolean("shadow"));
- try {
- JSONObject j2 = obj.getJSONObject("mutation");
- block.parseMutation(j2);
- } catch (JSONException je) {}
- }
- return blocks;
- }
- public static void writeBlocks (JSONObject json, ScratchTargetReference ref) {
- for (String s : ref.getBlocks()) {
- ScratchBlock block = ref.getBlock(s);
- if (block instanceof ScratchPrimitiveBlock spb) {
- JSONArray array = new JSONArray();
- spb.save(array);
- json.put(s, array);
- continue;
- }
- JSONObject j2 = new JSONObject();
- j2.put("opcode", block.getOpcode());
- j2.put("next", ref.getID(block.getChild()));
- j2.put("parent", ref.getID(block.getParent()));
- block.saveInputs(j2);
- block.saveFields(j2);
- j2.put("shadow", block.isShadow());
- j2.put("topLevel", block.getParent() == null);
- if (block.getParent() == null) {
- j2.put("x", block.getX());
- j2.put("y", block.getY());
- }
- for (String t : ref.getComments()) {
- ScratchComment comment = ref.getComment(t);
- if (comment.block() == block) {
- j2.put("comment", ref.getID(comment));
- break;
- }
- }
- if (block.hasMutation()) block.saveMutation(j2);
- }
- }
- public static List<ScratchComment> readComments (JSONObject json, ScratchTargetReference ref) {
- List<ScratchComment> comments = List.<ScratchComment>of();
- for (String key : json.keySet()) {
- JSONObject j = json.getJSONObject(key);
- ScratchComment comment = new ScratchComment(ref.getBlock(j.getString("blockId")), new Rectangle(j.getInt("x"), j.getInt("y"), j.getInt("width"), j.getInt("height")), j.getBoolean("minimized"), j.getString("text"));
- ref.referenceComment(key, comment);
- comments.add(comment);
- }
- return comments;
- }
- public static JSONObject writeComments (JSONObject json, ScratchTargetReference ref) {
- Map<String, ScratchComment> comments = ref.getComments();
- for (String s : comments) {
- JSONObject obj = new JSONObject().put("blockId", ref.getID(comments.get(s).block())).put("x", comments.get(s).area().getX()).put("y", comments.get(s).area().getY()).put("width", comments.get(s).area().getWidth()).put("height", comments.get(s).area().getHeight()).put("minimized", comments.get(s).minimized()).put("text", comments.get(s).text());
- json.put(s, obj);
- }
- return json;
- }
- public static Map<String, Image> readCostumes (JSONArray json, ZipFile zip) {
- Map<String, Image> images = Map.<String, Image>of();
- for (int i = 0; i <= json.toList().size(); i++) {
- JSONObject asset = json.getJSONObject(0);
- ZipEntry entry = null;
- zip.stream().forEach((candidate) -> { if (candidate.getName().endsWith(asset.getString("md5ext")) && entry == null)
- entry = candidate; });
- if (entry == null) throw new NullPointerException("Asset " + asset.getString("assetId") + " not found");
- try {
- if (asset.getString("dataFormat") == "png") {
- images.put(asset.getString(name), readPNG(zip.getInputStream(entry)));
- } else if (asset.getString("dataFormat") == "svg") {
- images.put(asset.getString(name), readSVG(new InputStreamReader(zip.getInputStream(entry))));
- }
- } catch (IOException ioe) {
- ioe.printStackTrace();
- }
- }
- return images;
- }
- public static Holder<ZipOutputStream, JSONArray> writeCostumes (JSONArray json, ZipOutputStream out, Map<String, Image> images) {
- try {
- for (String s : images.keySet()) {
- byte[] bytes;
- zs.putNextEntry(new ZipEntry()
- .setLastAccessTime(FileTime.from(Instant.now()))
- .setLastModifiedTime(FileTime.from(Instant.now())));
- ByteArrayOutputStream o2 = new ByteArrayOutputStream();
- if (images.get(s) instanceof BufferedImage img) {
- writePNG(o2, img);
- bytes = o2.toByteArray();
- writePNG(zs, img);
- } else if (images.get(s) instanceof VectorImage svg) {
- OutputStreamWriter o3 = new OutputStreamWriter(o2);
- writeSVG(o3, svg);
- bytes = o2.toByteArray();
- writeSVG(new OutputStreamWriter(out));
- }
- JSONObject asset = new JSONObject().put("assetId", getMD5(bytes)).put("name", s).put("md5ext", getMD5(bytes) + ".png").put("dataFormat", "png").put("bitmapResolution", 2).put("rotationCenterX", 0).put("rotationCenterY", 0);
- json.put(asset);
- }
- return new Holder<ZipOutputStream, JSONArray>(out, json);
- } catch (IOException ioe) {
- ioe.printStackTrace();
- }
- }
- public static BufferedImage readPNG (InputStream stream) throws IOException {
- return ImageIO.read(stream);
- }
- public static void writePNG (OutputStream stream, BufferedImage image) throws IOException {
- ImageIO.write(stream, "png", image);
- }
- public static VectorImage readSVG (Reader reader) throws IOExcpetion {
- StringWriter writer = new StringWriter();
- reader.transferTo(writer);
- String svg = writer.toString();
- return VectorImage.parse(svg);
- }
- public static void writeSVG (Writer writer, VectorImage image) throws IOException {
- writer.write(image.toString());
- }
- public static Map<String, AudioInputStream> readSounds (JSONArray json, ZipFile zip) {
- Map<String, AudioInputStream> streams = Map.<String, AudioInputStream>of();
- for (int i = 0; i <= json.toList().size(); i++) {
- JSONObject asset = json.getJSONObject(0);
- ZipEntry entry = null;
- zip.stream().forEach((candidate) -> { if (candidate.getName().endsWith(asset.getString("md5ext")) && entry == null)
- entry = candidate; });
- if (entry == null) throw new NullPointerException("Asset " + asset.getString("name") + " not found");
- try {
- streams.put(asset.getString(name), AudioSystem.getAudioInputStream(zip.getInputStream(entry)));
- } catch (IOException ioe) {
- ioe.printStackTrace();
- }
- }
- return streams;
- }
- public static void writeSounds (JSONArray json, ZipOutputStream out, Map<String, AudioInputStream> audio) {
- try {
- for (String s : audio.keySet()) {
- byte[] data = audio.get(s).readAllBytes();
- zs.putNextEntry(new ZipEntry()
- .setLastAccessTime(FileTime.from(Instant.now()))
- .setLastModifiedTime(FileTime.from(Instant.now())));
- AudioSystem.write(audio.get(s), AudioFileFormat.Type.WAVE, zs);
- zs.closeEntry();
- if (audio.get(s).getFormat().toString())
- JSONObject j = new JSONObject().put("assetId", getMD5(data)).put("name", s).put("md5ext", getMD5(data) + ".wav").put("dataFormat", "wav").put("rate", audio.get(s).getFormat().getSampleRate()).put("sampleCount", getSampleCount(audio.get(s)));
- json.put(j);
- }
- return new Holder<ZipOutputStream, JSONArray>(out, json);
- } catch (IOException ioe) {
- ioe.printStackTrace();
- }
- }
- public static int getSampleCount (AudioInputStream ais) {
- return (int)((ais.readAllBytes().length * 8) / ais.getFormat().getSampleSizeInBits());
- }
- public static String getMD5 (byte[] data) {
- try {
- String hashtext = new BigInteger(1, MessageDigest.getInstance("MD5").digest(data)).toString(16);
- while (hashtext.length() < 32) {
- hashtext = 0 + hashtext;
- }
- return hashtext;
- } catch (NoSuchAlgorithmException e) {
- e.printStackTrace();
- System.exit(404);
- }
- }
- }
- public class ScratchProject {
- public ScratchProject (List<ScratchSprite> sprites, ScratchStage stage, List<ScratchMonitor> monitors, ScratchMetadata meta, ScratchProject.Reference ref) {
- this.sprites = sprites;
- this.stage = stage;
- this.monitors = monitors;
- this.meta = meta;
- this.ref = ref;
- }
- }
- public record ScratchSprite (String name, List<ScratchVariable> variables, List<ScratchList> lists, List<String> broadcasts, List<ScratchBlock> blocks, List<ScratchComment> comments, Map<String, Image> costumes, Map<String, AudioInputStream> sounds) {
- private int costume, layer;
- private float volume;
- private ScratchTargetReference ref;
- private ScratchBlockExecutor exe;
- public int getCostume () {
- return this.ref.getCostume();
- }
- protected void setCostume (int costume) {
- this.ref.setCostume(costume);
- }
- public int getLayer () {
- return this.ref.getLayer();
- }
- protected void setLayer (int layer) {
- this.ref.requestLayerChange(layer);
- }
- public float getVolume () {
- return this.ref.getVolume();
- }
- protected void setVolume (float volume) {
- this.ref.setVolume(volume);
- }
- }
- public class ScratchBlock<T> {
- private static Map<String, ScratchBlock> blocks = Map.<String, ScratchBlock>ofEntries(
- new AbstractMap.SimpleEntry("motion_movesteps", new ScratchBlock<Void>((sprite, inputs) -> { sprite.moveForward(inputs.get(0).getInt()); })),
- new AbstractMap.SimpleEntry("motion_turnright", new ScratchBlock<Void>((sprite, inputs) -> { sprite.rotate(inputs.get(0).getAngle()); })),
- new AbstractMap.SimpleEntry("motion_turnleft", new ScratchBlock<Void>((sprite, inputs) -> { sprite.rotate(360 - inputs.get(0).getAngle()); })),
- new AbstractMap.SimpleEntry("motion_goto", new ScratchBlock<Void>((sprite, inputs) -> { sprite.moveTo(((PointReporter)inputs.get(0)).get()); })),
- new AbstractMap.SimpleEntry("motion_gotoxy", new ScratchBlock<Void>((sprite, inputs) -> { sprite.moveTo(new Point(inputs.get(0).getInt(), inputs.get(1).getInt())); })),
- new AbstractMap.SimpleEntry("motion_glideto", new ScratchBlock<Void>((sprite, inputs) -> { sprite.glideTo(inputs.get(0).getFloat(), ((PointReporter)inputs.get(1)).get()); })),
- new AbstractMap.SimpleEntry("motion_glidesecstoxy", new ScratchBlock<Void>((sprite, inputs) -> { sprite.glideTo(inputs.get(0).getFloat(), new Point(inputs.get(1).getInt(), inputs.get(2).getInt())); })),
- new AbstractMap.SimpleEntry("motion_pointindirection", new ScratchBlock<Void>((sprite, inputs) -> { sprite.pointToward(inputs.get(0).getAngle()); })),
- new AbstractMap.SimpleEntry("motion_pointtowards", new ScratchBlock<Void>((sprite, inputs) -> { sprite.pointToward(MouseInfo.getPointerInfo().getLocation()); })),
- new AbstractMap.SimpleEntry("motion_changexby", new ScratchBlock<Void>((sprite, inputs) -> { sprite.moveTo(sprite.getX() + inputs.get(0).getInt(), sprite.getY()); })),
- new AbstractMap.SimpleEntry("motion_setx", new ScratchBlock<Void>((sprite, inputs) -> { sprite.moveTo(inputs.get(0).getInt(), sprite.getY()); })),
- new AbstractMap.SimpleEntry("motion_changeyby", new ScratchBlock<Void>((sprite, inputs) -> { sprite.moveTo(sprite.getX(), sprite.getY() + inputs.get(0).getInt()); })),
- new AbstractMap.SimpleEntry("motion_sety", new ScratchBlock<Void>((sprite, inputs) -> { sprite.moveTo(sprite.getX(), inputs.get(0).getInt()); })),
- new AbstractMap.SimpleEntry("motion_ifonedgebounce", new ScratchBlock<Void>((sprite, inputs) -> { if (sprite.onEdge()) sprite.setMovementModifier(180); })),
- new AbstractMap.SimpleEntry("motion_setrotationstyle", new ScratchBlock<Void>((sprite, inputs) -> { sprite.setRotationStyle(((RotationStyleReporter)inputs.get(0)).get()); })),
- new AbstractMap.SimpleEntry("motion_xposition", new ScratchBlock<Integer>((sprite, inputs) -> { return Integer.valueOf(sprite.getX()); })),
- new AbstractMap.SimpleEntry("motion_yposition", new ScratchBlock<Integer>((sprite, inputs) -> { return Integer.valueOf(sprite.getY()); })),
- new AbstractMap.SimpleEntry("motion_direction", new ScratchBlock<Integer>((sprite, inputs) -> { return Integer.valueOf(sprite.getDirection()); })),
- new AbstractMap.SimpleEntry("looks_sayforsecs", new ScratchBlock<Void>((sprite, inputs) -> { sprite.showSpeechBubble(inputs.get(0).getString(), inputs.get(1).getInt(), false); })),
- new AbstractMap.SimpleEntry("looks_say", new ScratchBlock<Void>((sprite, inputs) -> { sprite.showSpeechBubble(inputs.get(0).getString(), 2, false); })),
- new AbstractMap.SimpleEntry("looks_thinkforsecs", new ScratchBlock<Void>((sprite, inputs) -> { sprite.showSpeechBubble(inputs.get(0).getString(), inputs.get(1).getInt(), true); })),
- new AbstractMap.SimpleEntry("looks_think", new ScratchBlock<Void>((sprite, inputs) -> { sprite.showSpeechBubble(inputs.get(0).getString(), 2, true); })),
- new AbstractMap.SimpleEntry("looks_switchcostumeto", new ScratchBlock<Void>((sprite, inputs) -> { sprite.setCostume(((CostumeReporter)inputs.get(0)).getInt()); })),
- new AbstractMap.SimpleEntry("looks_nextcostume", new ScratchBlock<Void>((sprite, inputs) -> { sprite.setCostume(sprite.getCostume() + 1); })),
- new AbstractMap.SimpleEntry("looks_switchbackdropto", new ScratchBlock<Void>((sprite, inputs) -> { sprite.getProject().getStage().setBackdrop(((CostumeReporter)inputs.get(0)).getInt()); })),
- new AbstractMap.SimpleEntry("looks_switchbackdroptoandwait", new ScratchWaitBlock((sprite, inputs) -> { sprite.getProject().getStage().setBackdrop(((CostumeReporter)inputs.get(0)).getInt()); })),
- private BiConsumer<ScratchSprite, List<Input>> consumer = null;
- private BiFunction<ScratchSprite, List<Input>> function = null;
- private ScratchSprite.Reference ref = null;
- private List<Input> inputs = null;
- ScratchBlock (BiConsumer<ScratchSprite, List<Input>> call) {
- this.consumer = call;
- }
- ScratchBlock (BiFunction<ScratchSprite, List<Input>, T> call) {
- this.function = call;
- }
- public void execute () {
- if (this.consumer != null) {
- this.consumer.accept(this.ref.getSprite(), this.inputs);
- this.child.execute();
- } else {
- throw new IllegalStateException("No block code to execute");
- }
- }
- public T report () {
- if (this.function != null) {
- return this.function.accept(this.ref.getSprite(), this.inputs);
- } else {
- throw new IllegalStateException("No block code to execute");
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement