/*
* Copyright (C) 2012 IsmAvatar <IsmAvatar@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 java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.awt.image.ImageObserver;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Collections;
import java.util.EnumMap;
import java.util.Map;
import javax.imageio.ImageIO;
import org.lateralgm.libmaker.backend.Action;
import org.lateralgm.libmaker.backend.Action.Execution;
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.PLibrary;
import org.lateralgm.libmaker.backend.PropertyMap;
public class LibWriter
{
private static final String STR_EMPTY = new String();
private static final Map<Action.Kind,Integer> ACT_KINDS;
private static final Map<Action.InterfaceKind,Integer> IFACE_KINDS;
private static final Map<Action.Execution,Integer> EXECUTIONS;
private static final Map<Argument.Kind,Integer> ARG_KINDS;
static
{
EnumMap<Action.Kind,Integer> m = new EnumMap<Action.Kind,Integer>(Action.Kind.class);
for (int i = 0; i < LibReader.ACT_KINDS.length; i++)
m.put(LibReader.ACT_KINDS[i],i);
ACT_KINDS = Collections.unmodifiableMap(m);
EnumMap<Action.InterfaceKind,Integer> m2 = new EnumMap<Action.InterfaceKind,Integer>(
Action.InterfaceKind.class);
for (int i = 0; i < LibReader.IFACE_KINDS.length; i++)
if (LibReader.IFACE_KINDS[i] != null) m2.put(LibReader.IFACE_KINDS[i],i);
IFACE_KINDS = Collections.unmodifiableMap(m2);
EnumMap<Action.Execution,Integer> m3 = new EnumMap<Action.Execution,Integer>(
Action.Execution.class);
for (int i = 0; i < LibReader.EXECUTIONS.length; i++)
m3.put(LibReader.EXECUTIONS[i],i);
EXECUTIONS = Collections.unmodifiableMap(m3);
EnumMap<Argument.Kind,Integer> m4 = new EnumMap<Argument.Kind,Integer>(Argument.Kind.class);
for (int i = 0; i < LibReader.ARG_KINDS.length; i++)
m4.put(LibReader.ARG_KINDS[i],i);
ARG_KINDS = Collections.unmodifiableMap(m4);
}
/** Workhorse for writing a library of LIB format out of given GmStreamEncoder */
public static void saveLib(Library lib, GmStreamEncoder out, int ver) throws IOException
{
ver = ver >= 520 ? 520 : 500;
out.write4(ver);
out.writeStr(lib.properties,PLibrary.CAPTION);
out.write4(lib.properties,PLibrary.ID);
out.writeStr(lib.properties,PLibrary.AUTHOR);
out.write4(lib.properties,PLibrary.VERSION);
lib.put(PLibrary.CHANGED,Library.longTimeToGmTime(System.currentTimeMillis()));
out.writeD(lib.properties,PLibrary.CHANGED);
out.writeStr(lib.properties,PLibrary.INFO,PLibrary.INIT_CODE);
out.writeBool(lib.properties,PLibrary.ADVANCED);
out.write4(lib.actions.size()); // no of actions/official lib identifier thingy
out.write4(lib.actions.size());
for (Action act : lib.actions)
{
out.write4(ver);
out.writeStr(act.properties,PAction.NAME);
out.write4(act.properties,PAction.ID);
BufferedImage img = act.get(PAction.IMAGE);
ImageIO.write(img,"bmp",out); //write image data //$NON-NLS-1$
out.writeBool(act.properties,PAction.HIDDEN,PAction.ADVANCED);
if (ver == 520) out.writeBool(act.properties,PAction.REGISTERED);
out.writeStr(act.properties,PAction.DESCRIPTION,PAction.LIST,PAction.HINT);
out.write4(ACT_KINDS.get(act.get(PAction.KIND)));
out.write4(IFACE_KINDS.get(act.get(PAction.IFACE_KIND)));
out.writeBool(act.properties,PAction.QUESTION,PAction.APPLY,PAction.RELATIVE);
out.write4(act.properties,PAction.ARG_NUM);
out.write4(8); //8 vs act.argNum vs MAX_ARGS?
//This always writes MAX_ARGS arguments. Alternatively, we could just write
//argNum arguments, and truncate the remaining invisible/unused arguments.
for (Argument arg : act.arguments)
{
out.writeStr(arg.properties,PArgument.CAPTION);
out.write4(ARG_KINDS.get(arg.get(PArgument.KIND)));
out.writeStr(arg.properties,PArgument.DEF_VALUE,PArgument.MENU_OPTS);
}
for (int k = act.arguments.length; k < 8; k++)
{
out.writeStr(STR_EMPTY);
out.write4(0);
out.writeStr(STR_EMPTY);
out.writeStr(STR_EMPTY);
}
Execution execType = act.get(PAction.EXEC_TYPE);
String execInfo = act.get(PAction.EXEC_INFO);
out.write4(EXECUTIONS.get(execType));
out.writeStr(execType == Action.Execution.FUNCTION ? execInfo : STR_EMPTY);
out.writeStr(execType == Action.Execution.CODE ? execInfo : STR_EMPTY);
}
}
enum Size
{
I1,I2,I3,I4,S1,S4
}
private static <P extends Enum<P>>void write(GmStreamEncoder out, 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++)
write(out,sizes[i],map.get(keys[i]));
}
private static void write(GmStreamEncoder out, Size size, Object o) throws IOException
{
switch (size)
{
case I1:
out.write((Integer) o);
return;
case I2:
out.write2((Integer) o);
return;
case I3:
out.write3((Integer) o);
return;
case I4:
out.write4((Integer) o);
return;
case S1:
out.writeStr1((String) o);
return;
case S4:
out.writeStr((String) o);
return;
default:
throw new IllegalArgumentException();
}
}
/** Workhorse for write a library of LGL format out of given GmStreamEncoder */
public static void saveLgl(Library lib, GmStreamEncoder out, int iconColumns) throws IOException
{
final byte[] HDR = { 'L','G','L' };
final int VER = 160;
out.write(HDR);
out.write2(VER);
Size s[] = { Size.I3,Size.S1,Size.S1,Size.I4 };
PLibrary pl[] = { PLibrary.ID,PLibrary.CAPTION,PLibrary.AUTHOR,PLibrary.VERSION };
write(out,s,lib.properties,pl);
lib.put(PLibrary.CHANGED,Library.longTimeToGmTime(System.currentTimeMillis()));
out.writeD(lib.properties,PLibrary.CHANGED);
out.writeStr(lib.properties,PLibrary.INFO,PLibrary.INIT_CODE);
int acts = lib.get(PLibrary.ADVANCED) ? 128 : 0;
acts |= lib.actions.size();
out.write(acts);
for (Action act : lib.actions)
{
out.write2(VER);
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 };
write(out,s,act.properties,pa);
int mask = act.get(PAction.HIDDEN) ? 128 : 0;
mask |= act.get(PAction.ADVANCED) ? 64 : 0;
mask |= act.get(PAction.REGISTERED) ? 32 : 0;
mask |= act.get(PAction.QUESTION) ? 16 : 0;
mask |= act.get(PAction.APPLY) ? 8 : 0;
mask |= act.get(PAction.RELATIVE) ? 4 : 0;
mask |= EXECUTIONS.get(act.get(PAction.EXEC_TYPE)); //(0-2)
out.write(mask);
out.writeStr(act.properties,PAction.EXEC_INFO);
int kind = ACT_KINDS.get(act.get(PAction.KIND)) << 4;
kind |= IFACE_KINDS.get(act.get(PAction.IFACE_KIND));
out.write(kind);
int argNum = act.get(PAction.ARG_NUM);
out.write(argNum);
for (int k = 0; k < argNum; k++)
{
Argument arg = act.arguments[k];
out.writeStr1((String) arg.get(PArgument.CAPTION));
out.write(ARG_KINDS.get(arg.get(PArgument.KIND)));
out.writeStr1((String) arg.get(PArgument.DEF_VALUE));
out.writeStr1((String) arg.get(PArgument.MENU_OPTS));
}
}
saveLGLIcons(lib,out.getOutputStream(),iconColumns);
}
protected static void saveLGLIcons(Library lib, OutputStream out, int columns) throws IOException
{
//Oh god this hack
int actNum = 0;
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)
actNum++;
}
if (actNum == 0) //seriously, who does that?
{
ImageIO.write(new BufferedImage(1,1,BufferedImage.TYPE_INT_ARGB),"png",out); //$NON-NLS-1$
return;
}
int rows = (actNum - 1) / columns + 1; //yer a wizard, harry
//Don't premultiply alpha since we're not even drawing the image
BufferedImage icons = new BufferedImage(columns * 24,rows * 24,BufferedImage.TYPE_INT_ARGB);
Graphics g = icons.getGraphics();
int i = 0;
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)
{
BufferedImage img = a.get(PAction.IMAGE);
g.drawImage(img,24 * (i % columns),24 * (i / columns),(ImageObserver) null);
i++;
}
}
ImageIO.write(icons,"png",out); //$NON-NLS-1$
}
}