package greenfoot.importer.scratch;

import greenfoot.core.GreenfootMain;

import java.awt.Color;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.math.BigDecimal;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Properties;
import java.util.Set;

import bluej.pkgmgr.PackageFile;
import bluej.pkgmgr.PackageFileFactory;
import bluej.utility.Debug;

public class ScratchImport
{          

| Reads a fixed number of bytes, treats them as ASCII, and returns them as a String | private static String readFixedASCII(FileInputStream input, int num) throws IOException { byte[] b = new byte[num]; input.read(b); return new String(b, Charset.forName("US-ASCII")); }
| Reads a fixed number of bytes, treats them as UTF8, and returns them as a String | private static String readUTF8(FileInputStream input, int num) throws IOException { byte[] b = new byte[num]; input.read(b); return new String(b, Charset.forName("UTF-8")); }
| Reads the version string from the file | private static void readVersion(FileInputStream input) throws IOException { String ver = readFixedASCII(input, 10); if ("ScratchV01".equals(ver)) { } else if ("ScratchV02".equals(ver)) { } else { Debug.message("Unknown Scratch version: " + ver); } }
| Reads a big-endian int using the given number of bytes | | The Scratch format has all sorts of integer sizes, including 3 bytes. | private static long readInt(FileInputStream input, int bytes) throws IOException { long x = 0; for (int i = 0; i < bytes; i++) { x <<= 8; x |= input.read(); } if (x >> ((8 * bytes) - 1) != 0 && bytes < 8) { x |= 0xFFFFFFFFFFFFFFFFL << (8 * bytes); } return x; }
| Reads the header from a Scratch file (the version, and the info block, which is skipped) | private static void readHeader(FileInputStream input) throws IOException { readVersion(input); int infoSize = (int)readInt(input, 4); input.skip(infoSize); } private static ScratchObject readObject(FileInputStream input) throws IOException { int id = input.read(); if (id == -1) return null; if (id >= 100) { return readUserObject(id, input); } else { return readPrimitiveOrReferenceWithGivenId(id, input); } } private static ScratchUserObject readUserObject(int id, FileInputStream input) throws IOException { int version = input.read(); int fieldAmount = input.read(); List<ScratchObject> scratchObjects = Arrays.asList(readFields(input, fieldAmount)); switch (id) { case ScratchUserObject.IMAGE_MEDIA: return new ImageMedia(version, scratchObjects); case ScratchUserObject.SOUND_MEDIA: return new SoundMedia(version, scratchObjects); case ScratchUserObject.SCRATCH_STAGE_MORPH: return new ScratchStageMorph(version, scratchObjects); case ScratchUserObject.SCRATCH_SPRITE_MORPH: return new ScratchSpriteMorph(version, scratchObjects); default: return new ScratchUserObject(id, version, scratchObjects); } } private static ScratchObject readPrimitiveOrReference(FileInputStream input) throws IOException { int id = input.read(); return readPrimitiveOrReferenceWithGivenId(id, input); } private static ScratchObject readPrimitiveOrReferenceWithGivenId(int id, FileInputStream input) throws IOException { switch (id) { case 99: return new ScratchObjectReference((int)readInt(input, 3)); case 1: return null; case 2: case 3: return new ScratchPrimitive(Boolean.valueOf(id == 2)); case 4: return new ScratchPrimitive(new BigDecimal(readInt(input, 4))); case 5: return new ScratchPrimitive(new BigDecimal(readInt(input, 2))); case 8: {
| Float | long bits = readInt(input, 8); return new ScratchPrimitive(new BigDecimal(Double.longBitsToDouble(bits))); } case 9: case 10: { int size = (int)readInt(input, 4); return new ScratchPrimitive(readFixedASCII(input, size)); } case 11: { int size = (int)readInt(input, 4); byte[] b = new byte[size]; input.read(b); return new ScratchPrimitive(b); } case 12: { int size = (int)readInt(input, 4); byte[] b = new byte[size * 2]; input.read(b); return new ScratchPrimitive(b); } case 13: { int size = (int)readInt(input, 4); int[] arr = new int[size]; for (int i = 0; i < size; i++) { arr[i] = (int)readInt(input, 4); } return new ScratchPrimitive(arr); } case 14: { int size = (int)readInt(input, 4); return new ScratchPrimitive(readUTF8(input, size)); } case 20: case 21: { int size = (int)readInt(input, 4); ScratchObject[] scratchObjects = readFields(input, size); return new ScratchObjectArray(scratchObjects); } case 24: { int size = (int)readInt(input, 4); ScratchObject[] keyValues = readFields(input, size*2); HashMap<ScratchObject, ScratchObject> map = new HashMap<ScratchObject, ScratchObject>(); for (int i = 0; i < size; i++) { map.put(keyValues[i*2], keyValues[i*2+1]); } return new ScratchPrimitive(map); } case 30: case 31: { int colour =(int)readInt(input, 4); int alpha = id == 31 ? (int)readInt(input, 1) : 255; Color c = new Color((colour >> 22) & 255, (colour >> 12) & 255, (colour >> 2) & 255, alpha); return new ScratchPrimitive(c); } case 32: { ScratchObject[] fields = readFields(input, 2); BigDecimal x = (BigDecimal)fields[0].getValue(); BigDecimal y = (BigDecimal)fields[1].getValue(); return new ScratchPoint(x, y); } case 33: { ScratchObject[] fields = readFields(input, 4); BigDecimal x = (BigDecimal)fields[0].getValue(); BigDecimal y = (BigDecimal)fields[1].getValue(); BigDecimal x2 = (BigDecimal)fields[2].getValue(); BigDecimal y2 = (BigDecimal)fields[3].getValue(); return new ScratchRectangle(x, y, x2, y2); } case 34: case 35: { ScratchObject[] fields = readFields(input, id == 35 ? 6 : 5); int w = ((BigDecimal)fields[0].getValue()).intValue(); int h = ((BigDecimal)fields[1].getValue()).intValue(); int d = ((BigDecimal)fields[2].getValue()).intValue(); int offset = fields[3] == null ? 0 : (Integer)fields[3].getValue(); ScratchObject bits = fields[4]; ScratchObject palette = id == 35 ? fields[5] : null; return new ScratchImage(w,h,d,offset,bits, palette); } default: Debug.message("*** UNKNOWN SCRATCH FIELD: " + id + " ***"); return null; } } private static ScratchObject[] readFields(FileInputStream input, int size) throws IOException { List<ScratchObject> scratchObjects = new ArrayList<ScratchObject>(); for (int i = 0; i < size; i++) { scratchObjects.add(readPrimitiveOrReference(input)); } return scratchObjects.toArray(new ScratchObject[0]); } private static List readObjectStore(FileInputStream input) throws IOException { String header = readFixedASCII(input, 10); if (!"ObjS\001Stch\001".equals(header)) { Debug.message("Unknown Scratch object store header: " + header); return null; } int numObjects = (int)readInt(input, 4); ArrayList<ScratchObject> objects = new ArrayList<ScratchObject>(numObjects); for (int i = 0; i < numObjects; i++) { objects.add(readObject(input)); } for (int i = 0; i < objects.size(); i++) { if (objects.get(i) != null) { objects.set(i, objects.get(i).resolve(objects)); } } return objects; } private static void importScratch(File src, File dest) { try { FileInputStream input = new FileInputStream(src); readHeader(input); List<ScratchObject> objects = readObjectStore(input); Properties props = new Properties(); props.setProperty("version", GreenfootMain.getAPIVersion().toString()); for (ScratchObject o : objects) { o.saveInto(dest, props, null); } File javaFile = new File(dest, "Bubble.java"); FileWriter javaFileWriter = new FileWriter(javaFile); javaFileWriter.write("import greenfoot.*;\nimport java.awt.Color;\npublic class Bubble extends Actor \n{\n"); javaFileWriter.write("public Bubble(String s)\n{\nsetImage(new GreenfootImage(s, 15, Color.BLACK, Color.WHITE));\n}\n"); javaFileWriter.write("public void act()\n{\n}\n"); javaFileWriter.write("}\n"); javaFileWriter.close(); PackageFile packageFile = PackageFileFactory.getPackageFile(dest); packageFile.create(); packageFile.save(props); } catch (IOException e) { Debug.reportError("Problem during Scratch import", e); } } public static File convert(File scratchFile) { String archiveName = scratchFile.getName(); int dotIndex = archiveName.lastIndexOf('.'); String strippedName = null; if (dotIndex != -1) { strippedName = archiveName.substring(0, dotIndex); } else { strippedName = archiveName; } File dest = new File(scratchFile.getParentFile(), strippedName); int i = 0; while (dest.exists()){ dest = new File(scratchFile.getParentFile(), strippedName + i); i++; } existingNames = new HashSet<String>(); existingNames.add("World"); existingNames.add("Actor"); importScratch(scratchFile, dest); return dest; } private static Set<String> existingNames; p.public static String mungeUnique(String orig) { StringBuilder r = new StringBuilder(); if (orig.length() > 0) { if (Character.isJavaIdentifierStart(orig.charAt(0))) { r.append(orig.charAt(0)); } else { r.append("C"); } for (char c : Arrays.copyOfRange(orig.toCharArray(),1, orig.toCharArray().length)) { if (Character.isJavaIdentifierPart(c)) { r.append(c); } } } String initial = r.toString(); String result; if (initial.length() == 0 || existingNames.contains(initial)) { int i = 0; while (existingNames.contains(initial + i)){ i += 1; } result = initial + i; } else { result = initial; } existingNames.add(result); return result; } }
top, use, map, class ScratchImport

.   readFixedASCII
.   readUTF8
.   readVersion
.   readInt
.   readHeader
.   readObject
.   readUserObject
.   readPrimitiveOrReference
.   readPrimitiveOrReferenceWithGivenId
.   readFields
.   readObjectStore
.   importScratch
.   convert
.   mungeUnique




496 neLoCode + 7 LoComm