/* * Copyright (C) 2007, 2008, 2012 IsmAvatar <IsmAvatar@gmail.com> * Copyright (C) 2006, 2007 Clam <clamisgood@gmail.com> * * This file is part of LibMaker. * LibMaker is free software and comes with ABSOLUTELY NO WARRANTY. * See LICENSE for details. */ package org.lateralgm.libmaker.file; import static org.lateralgm.libmaker.file.GmStreamDecoder.mask; import java.awt.image.BufferedImage; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import javax.imageio.ImageIO; import org.lateralgm.libmaker.Messages; import org.lateralgm.libmaker.backend.Action; import org.lateralgm.libmaker.backend.Action.Execution; import org.lateralgm.libmaker.backend.Action.InterfaceKind; import org.lateralgm.libmaker.backend.Action.PAction; import org.lateralgm.libmaker.backend.Argument; import org.lateralgm.libmaker.backend.Argument.PArgument; import org.lateralgm.libmaker.backend.Library; import org.lateralgm.libmaker.backend.Library.LglFormat; import org.lateralgm.libmaker.backend.Library.LibFormat; import org.lateralgm.libmaker.backend.Library.PLibrary; import org.lateralgm.libmaker.backend.PropertyMap; public class LibReader { public static class LibFormatException extends Exception { private static final long serialVersionUID = 1L; public LibFormatException(String message) { super(message); } public String stackAsString() { StackTraceElement[] els = getStackTrace(); String res = ""; //$NON-NLS-1$ for (int i = 0; i < els.length; i++) { res += els[i].toString(); if (i != els.length - 1) res += "\n"; //$NON-NLS-1$ } return res; } } /*package*/static final Action.Kind ACT_KINDS[] = { Action.Kind.NORMAL,Action.Kind.BEGIN_GROUP, Action.Kind.END_GROUP,Action.Kind.ELSE,Action.Kind.EXIT,Action.Kind.REPEAT, Action.Kind.VARIABLE,Action.Kind.CODE,Action.Kind.PLACEHOLDER,Action.Kind.SEPARATOR, Action.Kind.LABEL }; /*package*/static final Action.InterfaceKind IFACE_KINDS[] = { InterfaceKind.NORMAL, InterfaceKind.NONE,InterfaceKind.ARROWS,null,null,InterfaceKind.CODE,InterfaceKind.TEXT }; /*package*/static final Action.Execution EXECUTIONS[] = { Execution.NOTHING,Execution.FUNCTION, Execution.CODE }; /*package*/static final Argument.Kind ARG_KINDS[] = { Argument.Kind.EXPRESSION, Argument.Kind.STRING,Argument.Kind.BOTH,Argument.Kind.BOOLEAN,Argument.Kind.MENU, Argument.Kind.SPRITE,Argument.Kind.SOUND,Argument.Kind.BACKGROUND,Argument.Kind.PATH, Argument.Kind.SCRIPT,Argument.Kind.OBJECT,Argument.Kind.ROOM,Argument.Kind.FONT, Argument.Kind.COLOR,Argument.Kind.TIMELINE,Argument.Kind.FONT_STRING }; public static Library loadFile(File f) throws LibFormatException { try { Library lib = loadFile(new GmStreamDecoder(f),f.getName()); lib.sourceFile = f; return lib; } catch (FileNotFoundException e) { throw new LibFormatException(Messages.format("LibReader.ERROR_NOTFOUND",f.getName())); //$NON-NLS-1$ } } /** * Loads a library file of given fileName of either LIB or LGL format * @param in * @param filename for error reporting * @return the library * @throws LibFormatException */ public static Library loadFile(GmStreamDecoder in, String filename) throws LibFormatException { Library lib = null; try { int header = in.read3(); if (header == (('L' << 16) | ('G' << 8) | 'L')) { lib = loadLgl(in); lib.format = new LglFormat(6); } else if (header == 500 || header == 520) { lib = loadLib(in); lib.format = header == 520 ? LibFormat.LIB520 : LibFormat.LIB500; } else throw new LibFormatException(Messages.format("LibReader.ERROR_INVALIDFILE",filename)); //$NON-NLS-1$ } catch (IOException ex) { throw new LibFormatException(Messages.format("LibReader.ERROR_READING",filename, //$NON-NLS-1$ ex.getMessage())); } catch (LibFormatException ex) { //Fill in the %s with the filename, now that we know it. throw new LibFormatException(String.format(ex.getMessage(),filename)); } finally { try { if (in != null) in.close(); } catch (IOException ex) { String msg = Messages.getString("LibReader.ERROR_CLOSEFAILED"); //$NON-NLS-1$ throw new LibFormatException(msg); } } return lib; } /** * Workhorse for constructing a library out of given StreamDecoder of LIB format * @param in * @return the library (not yet added to the libs list) * @throws LibFormatException * @throws IOException */ public static Library loadLib(GmStreamDecoder in) throws LibFormatException,IOException { if (in.read() != 0) throw new LibFormatException(Messages.format("LibReader.ERROR_INVALIDFILE","%s")); //$NON-NLS-1$ //$NON-NLS-2$ Library lib = new Library(); in.readStr(lib.properties,PLibrary.CAPTION); in.read4(lib.properties,PLibrary.ID); in.readStr(lib.properties,PLibrary.AUTHOR); in.read4(lib.properties,PLibrary.VERSION); in.readD(lib.properties,PLibrary.CHANGED); in.readStr(lib.properties,PLibrary.INFO,PLibrary.INIT_CODE); in.readBool(lib.properties,PLibrary.ADVANCED); in.skip(4); // no of actions/official lib identifier thingy int acts = in.read4(); for (int j = 0; j < acts; j++) { int ver = in.read4(); if (ver != 500 && ver != 520) throw new LibFormatException(Messages.format("LibReader.ERROR_INVALIDACTION",j,"%s",ver)); //$NON-NLS-1$ //$NON-NLS-2$ Action act = new Action(); lib.actions.add(act); act.parent = lib; in.readStr(act.properties,PAction.NAME); in.read4(act.properties,PAction.ID); byte[] data = new byte[in.read4()]; in.read(data); act.put(PAction.IMAGE,ImageIO.read(new ByteArrayInputStream(data))); in.readBool(act.properties,PAction.HIDDEN,PAction.ADVANCED); if (ver == 520) in.readBool(act.properties,PAction.REGISTERED); in.readStr(act.properties,PAction.DESCRIPTION,PAction.LIST,PAction.HINT); act.put(PAction.KIND,ACT_KINDS[in.read4()]); act.put(PAction.IFACE_KIND,IFACE_KINDS[in.read4()]); in.readBool(act.properties,PAction.QUESTION,PAction.APPLY,PAction.RELATIVE); in.read4(act.properties,PAction.ARG_NUM); int args = in.read4(); for (int k = 0; k < args; k++) { if (k < act.arguments.length) { Argument arg = act.arguments[k]; in.readStr(arg.properties,PArgument.CAPTION); arg.put(PArgument.KIND,ARG_KINDS[in.read4()]); in.readStr(arg.properties,PArgument.DEF_VALUE,PArgument.MENU_OPTS); } else { in.skip(in.read4()); in.skip(4); in.skip(in.read4()); in.skip(in.read4()); } } Execution execType = EXECUTIONS[in.read4()]; act.put(PAction.EXEC_TYPE,execType); if (execType == Action.Execution.FUNCTION) in.readStr(act.properties,PAction.EXEC_INFO); else in.skip(in.read4()); if (execType == Action.Execution.CODE) in.readStr(act.properties,PAction.EXEC_INFO); else in.skip(in.read4()); } return lib; } enum Size { I1,I2,I3,I4,S1,S4 } private static <P extends Enum<P>>void read(GmStreamDecoder in, Size[] sizes, PropertyMap<P> map, P...keys) throws IOException { if (sizes.length != keys.length) throw new IllegalArgumentException(); for (int i = 0; i < sizes.length; i++) map.put(keys[i],read(in,sizes[i])); } private static Object read(GmStreamDecoder in, Size size) throws IOException { switch (size) { case I1: return in.read(); case I2: return in.read2(); case I3: return in.read3(); case I4: return in.read4(); case S1: return in.readStr1(); case S4: return in.readStr(); default: return null; } } /** * Workhorse for constructing a library out of given StreamDecoder of LGL format * @param in * @return the library (not yet added to the libs list) * @throws LibFormatException * @throws IOException */ public static Library loadLgl(GmStreamDecoder in) throws LibFormatException,IOException { if (in.read2() != 160) { String invalidFile = Messages.getString("LibReader.ERROR_INVALIDFILE"); //$NON-NLS-1$ throw new LibFormatException(invalidFile); } Library lib = new Library(); Size s[] = { Size.I3,Size.S1,Size.S1,Size.I4 }; PLibrary pl[] = { PLibrary.ID,PLibrary.CAPTION,PLibrary.AUTHOR,PLibrary.VERSION }; read(in,s,lib.properties,pl); in.readD(lib.properties,PLibrary.CHANGED); in.readStr(lib.properties,PLibrary.INFO,PLibrary.INIT_CODE); int acts = in.read(); lib.properties.put(PLibrary.ADVANCED,mask(acts,128)); acts &= 127; for (int j = 0; j < acts; j++) { if (in.read2() != 160) throw new LibFormatException(Messages.format("LibReader.ERROR_INVALIDACTION",j,"%s",160)); //$NON-NLS-1$ //$NON-NLS-2$ Action act = new Action(); lib.actions.add(act); act.parent = lib; s = new Size[] { Size.I2,Size.S1,Size.S1,Size.S1,Size.S1 }; PAction[] pa = { PAction.ID,PAction.NAME,PAction.DESCRIPTION,PAction.LIST,PAction.HINT }; read(in,s,act.properties,pa); int tags = in.read(); act.put(PAction.HIDDEN,mask(tags,128)); act.put(PAction.ADVANCED,mask(tags,64)); act.put(PAction.REGISTERED,mask(tags,32)); act.put(PAction.QUESTION,mask(tags,16)); act.put(PAction.APPLY,mask(tags,8)); act.put(PAction.RELATIVE,mask(tags,4)); act.put(PAction.EXEC_TYPE,EXECUTIONS[tags & 3]); act.put(PAction.EXEC_INFO,in.readStr()); tags = in.read(); act.put(PAction.KIND,ACT_KINDS[tags >> 4]); act.put(PAction.IFACE_KIND,IFACE_KINDS[tags & 15]); int argNum = in.read(); act.put(PAction.ARG_NUM,argNum); for (int k = 0; k < argNum; k++) { Argument arg = act.arguments[k]; arg.put(PArgument.CAPTION,in.readStr1()); arg.put(PArgument.KIND,ARG_KINDS[in.read()]); arg.put(PArgument.DEF_VALUE,in.readStr1()); arg.put(PArgument.MENU_OPTS,in.readStr1()); } } loadLGLIcons(lib,in.getInputStream()); return lib; } protected static void loadLGLIcons(Library lib, InputStream in) throws IOException { BufferedImage icons = ImageIO.read(in); int i = 0; int cc = icons.getWidth() / 24; for (Action a : lib.actions) { Action.Kind k = a.get(PAction.KIND); if (k != Action.Kind.PLACEHOLDER && k != Action.Kind.SEPARATOR && k != Action.Kind.LABEL) { a.put(PAction.IMAGE,icons.getSubimage(24 * (i % cc),24 * (i / cc),24,24)); i++; } } } }