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