Various improvements; options, version, info

Adds options to PNG importing so that any valid DMI file can be entirely built from PNG files and DMITool.
Adds version string and "version" command
Adds info command to get a list of states easily (the only way previously was to print the descriptor with "dmitool -vvvv verify $file" and parse with e.g. grep/sed)
This commit is contained in:
=
2015-01-07 17:26:21 +00:00
parent c49f0f2f89
commit 73da1156a2
4 changed files with 170 additions and 48 deletions

Binary file not shown.

View File

@@ -419,6 +419,12 @@ public class DMI implements Comparator<IconState> {
public void printInfo() { public void printInfo() {
System.out.println(totalImages + " images, " + images.size() + " states, size "+w+"x"+h); System.out.println(totalImages + " images, " + images.size() + " states, size "+w+"x"+h);
} }
public void printStateList() {
for(IconState s: images) {
System.out.println(s.getInfoLine());
}
}
@Override public boolean equals(Object obj) { @Override public boolean equals(Object obj) {
if(obj == this) return true; if(obj == this) return true;

View File

@@ -19,6 +19,21 @@ public class IconState {
int loop; int loop;
String hotspot; String hotspot;
boolean movement; boolean movement;
public String getInfoLine() {
String extraInfo = "";
if(rewind) extraInfo += " rewind";
if(frames != 1) {
extraInfo += " loop(" + (loop==-1 ? "infinite" : loop) + ")";
}
if(hotspot != null) extraInfo += " hotspot('" + hotspot + "')";
if(movement) extraInfo += " movement";
if(extraInfo.equals("")) {
return String.format("state \"%s\", %d dir(s), %d frame(s)", name, dirs, frames);
} else {
return String.format("state \"%s\", %d dir(s), %d frame(s),%s", name, dirs, frames, extraInfo);
}
}
@Override public IconState clone() { @Override public IconState clone() {
IconState is = new IconState(name, dirs, frames, images.clone(), delays==null ? null : delays.clone(), rewind, loop, hotspot, movement); IconState is = new IconState(name, dirs, frames, images.clone(), delays==null ? null : delays.clone(), rewind, loop, hotspot, movement);
@@ -148,7 +163,7 @@ public class IconState {
out.end(); out.end();
} }
public static IconState importFromPNG(DMI dmi, InputStream inS, String name) throws DMIException { public static IconState importFromPNG(DMI dmi, InputStream inS, String name, float[] delays, boolean rewind, int loop, String hotspot, boolean movement) throws DMIException {
int w = dmi.w; int w = dmi.w;
int h = dmi.h; int h = dmi.h;
@@ -192,15 +207,8 @@ public class IconState {
} }
} }
float[] delays = null;
if(frames != 1) {
delays = new float[frames];
for(int i=0; i<delays.length; i++) {
delays[i] = 1;
}
}
//public IconState(String name, int dirs, int frames, Image[] images, float[] delays, boolean rewind, int loop, String hotspot, boolean movement) { //public IconState(String name, int dirs, int frames, Image[] images, float[] delays, boolean rewind, int loop, String hotspot, boolean movement) {
return new IconState(name, dirs, frames, images, delays, false, -1, null, false); return new IconState(name, dirs, frames, images, delays, rewind, loop, hotspot, movement);
} }
} }

View File

@@ -11,40 +11,56 @@ import java.util.Set;
public class Main { public class Main {
public static int VERBOSITY = 0; public static int VERBOSITY = 0;
public static boolean STRICT = false; public static boolean STRICT = false;
public static final String VERSION = "v0.6 (7 Jan 2015)";
public static final String[] dirs = new String[] { public static final String[] dirs = new String[] {
"S", "N", "E", "W", "SE", "SW", "NE", "NW" "S", "N", "E", "W", "SE", "SW", "NE", "NW"
}; };
public static final String helpStr = public static final String helpStr =
"help\n" + "help\n" +
"\tthis text\n" + "\tthis text\n" +
"version\n" +
"\tprint version and exit\n" +
"verify [file]\n" + "verify [file]\n" +
"\tattempt to load the given file to check format\n" + "\tattempt to load the given file to check format\n" +
"info [file]\n" +
"\tprint information about [file], including a list of states\n" +
"diff [file1] [file2]\n" + "diff [file1] [file2]\n" +
"\tdiff between [file1] and [file2]\n" + "\tdiff between [file1] and [file2]\n" +
"sort [file]\n" + "sort [file]\n" +
"\tsort the icon_states in [file] into ASCIIbetical order\n" + "\tsort the icon_states in [file] into ASCIIbetical order\n" +
"merge [base] [file1] [file2] [out]\n" + "merge [base] [file1] [file2] [out]\n" +
"\tmerge [file1] and [file2]'s changes from a common ancestor [base], saving the result in [out]\n" + "\tmerge [file1] and [file2]'s changes from a common ancestor [base], saving the result in [out]\n" +
"\tconflicts will be placed in [out].conflict.dmi\n" + "\tconflicts will be placed in [out].conflict.dmi\n" +
"extract [file] [state] [out] {args}\n"+ "extract [file] [state] [out] {args}\n"+
"\textract [state] from [file] in PNG format to [out]\n" + "\textract [state] from [file] in PNG format to [out]\n" +
"\targs specify direction and frame; input 'f' followed by a frame specifier, and/or 'd' followed by a direction specifier\n" + "\targs specify direction and frame; input 'f' followed by a frame specifier, and/or 'd' followed by a direction specifier\n" +
"\tframe specifier can be a single number or number-number for a range\n" + "\tframe specifier can be a single number or number-number for a range\n" +
"\tdirection specifier can be a single direction, or direction-direction\n" + "\tdirection specifier can be a single direction, or direction-direction\n" +
"\tdirection can be 0-7 or S, N, E, W, SE, SW, NE, NW (non-case-sensitive)\n" + "\tdirection can be 0-7 or S, N, E, W, SE, SW, NE, NW (non-case-sensitive)\n" +
"import [file] [state] [in]\n" + "import [file] [state] [in] [options]\n" +
"\timport a PNG image from [in] into [file], with the name [state]\n" + "\timport a PNG image from [in] into [file], with the name [state]\n" +
"\tinput should be in the same format given by the 'extract' command with no direction or frame arguments\n" + "\tinput should be in the same format given by the 'extract' command with no direction or frame arguments\n" +
"\t(i.e. frames should be on the x-axis, and directions on the y)\n" +
"\tpossible options:\n" +
"\t nodup | nd | n : if the state [state] already exists in [file], replace it instead of append\n" +
"\t rewind | rw | r : if there is more than one frame, the animation should be played forwards-backwards-forwards-[...]\n" +
"\t loop | lp | l : loop the animation infinitely; equivalent to \"loopn -1\"\n" +
"\t loopn N | lpn N | ln N : loop the animation N times; for infinite animations, use 'loop' or N = -1\n" +
"\t movement | move | mov | m : [state] should be marked as a movement state\n" +
"\t delays L | delay L | del L | d L : use the list L as a comma-separated list of delays (e.g. '1,1,2,2,1')\n" +
"\t hotspot H | hs H | h H : use H as the hotspot for this state\n" +
""; "";
public static void main(String[] args) throws FileNotFoundException, IOException, DMIException { public static void main(String[] args) throws FileNotFoundException, IOException, DMIException {
Deque<String> argq = new ArrayDeque<>(); Deque<String> argq = new ArrayDeque<>();
for(String s: args) { for(String s: args) {
@@ -66,7 +82,7 @@ public class Main {
argq.pollFirst(); argq.pollFirst();
} }
String op = argq.pollFirst(); String op = argq.pollFirst();
switch(op) { switch(op) {
case "diff": { case "diff": {
if(argq.size() < 2) { if(argq.size() < 2) {
@@ -76,7 +92,7 @@ public class Main {
} }
String a = argq.pollFirst(); String a = argq.pollFirst();
String b = argq.pollFirst(); String b = argq.pollFirst();
if(VERBOSITY >= 0) System.out.println("Loading " + a); if(VERBOSITY >= 0) System.out.println("Loading " + a);
DMI dmi = doDMILoad(a); DMI dmi = doDMILoad(a);
if(VERBOSITY >= 0) dmi.printInfo(); if(VERBOSITY >= 0) dmi.printInfo();
@@ -96,11 +112,11 @@ public class Main {
return; return;
} }
String f = argq.pollFirst(); String f = argq.pollFirst();
if(VERBOSITY >= 0) System.out.println("Loading " + f); if(VERBOSITY >= 0) System.out.println("Loading " + f);
DMI dmi = doDMILoad(f); DMI dmi = doDMILoad(f);
if(VERBOSITY >= 0) dmi.printInfo(); if(VERBOSITY >= 0) dmi.printInfo();
if(VERBOSITY >= 0) System.out.println("Saving " + f); if(VERBOSITY >= 0) System.out.println("Saving " + f);
dmi.writeDMI(new FileOutputStream(f), true); dmi.writeDMI(new FileOutputStream(f), true);
break; break;
@@ -118,26 +134,26 @@ public class Main {
if(VERBOSITY >= 0) System.out.println("Loading " + baseF); if(VERBOSITY >= 0) System.out.println("Loading " + baseF);
DMI base = doDMILoad(baseF); DMI base = doDMILoad(baseF);
if(VERBOSITY >= 0) base.printInfo(); if(VERBOSITY >= 0) base.printInfo();
if(VERBOSITY >= 0) System.out.println("Loading " + aF); if(VERBOSITY >= 0) System.out.println("Loading " + aF);
DMI aDMI = doDMILoad(aF); DMI aDMI = doDMILoad(aF);
if(VERBOSITY >= 0) aDMI.printInfo(); if(VERBOSITY >= 0) aDMI.printInfo();
if(VERBOSITY >= 0) System.out.println("Loading " + bF); if(VERBOSITY >= 0) System.out.println("Loading " + bF);
DMI bDMI = doDMILoad(bF); DMI bDMI = doDMILoad(bF);
if(VERBOSITY >= 0) bDMI.printInfo(); if(VERBOSITY >= 0) bDMI.printInfo();
DMIDiff aDiff = new DMIDiff(base, aDMI); DMIDiff aDiff = new DMIDiff(base, aDMI);
DMIDiff bDiff = new DMIDiff(base, bDMI); DMIDiff bDiff = new DMIDiff(base, bDMI);
DMIDiff mergedDiff = new DMIDiff(); DMIDiff mergedDiff = new DMIDiff();
DMI conflictDMI = new DMI(32, 32); DMI conflictDMI = new DMI(32, 32);
Set<String> cf = aDiff.mergeDiff(bDiff, conflictDMI, mergedDiff, aF, bF); Set<String> cf = aDiff.mergeDiff(bDiff, conflictDMI, mergedDiff, aF, bF);
mergedDiff.applyToDMI(base); mergedDiff.applyToDMI(base);
base.writeDMI(new FileOutputStream(mergedF)); base.writeDMI(new FileOutputStream(mergedF));
if(!cf.isEmpty()) { if(!cf.isEmpty()) {
if(VERBOSITY >= 0) for(String s: cf) { if(VERBOSITY >= 0) for(String s: cf) {
System.out.println(s); System.out.println(s);
@@ -160,10 +176,10 @@ public class Main {
String file = argq.pollFirst(), String file = argq.pollFirst(),
state = argq.pollFirst(), state = argq.pollFirst(),
outFile = argq.pollFirst(); outFile = argq.pollFirst();
DMI dmi = doDMILoad(file); DMI dmi = doDMILoad(file);
if(VERBOSITY >= 0) dmi.printInfo(); if(VERBOSITY >= 0) dmi.printInfo();
IconState is = dmi.getIconState(state); IconState is = dmi.getIconState(state);
if(is == null) { if(is == null) {
System.out.println("icon_state '"+state+"' does not exist!"); System.out.println("icon_state '"+state+"' does not exist!");
@@ -172,10 +188,10 @@ public class Main {
// minDir, Maxdir, minFrame, Maxframe // minDir, Maxdir, minFrame, Maxframe
int mDir=0, Mdir=is.dirs-1; int mDir=0, Mdir=is.dirs-1;
int mFrame=0, Mframe=is.frames-1; int mFrame=0, Mframe=is.frames-1;
while(argq.size() > 1) { while(argq.size() > 1) {
String arg = argq.pollFirst(); String arg = argq.pollFirst();
switch(arg) { switch(arg) {
case "d": case "d":
case "dir": case "dir":
@@ -247,29 +263,105 @@ public class Main {
String dmiFile = argq.pollFirst(), String dmiFile = argq.pollFirst(),
stateName = argq.pollFirst(), stateName = argq.pollFirst(),
pngFile = argq.pollFirst(); pngFile = argq.pollFirst();
boolean noDup = false; boolean noDup = false;
if(!argq.isEmpty()) { boolean rewind = false;
switch(argq.pollFirst().toLowerCase()) { int loop = 0;
boolean movement = false;
String hotspot = null;
float[] delays = null;
while(!argq.isEmpty()) {
String s = argq.pollFirst();
switch(s.toLowerCase()) {
case "nodup": case "nodup":
case "nd":
case "n":
noDup = true; noDup = true;
break; break;
case "rewind":
case "rw":
case "r":
rewind = true;
break;
case "loop":
case "lp":
case "l":
loop = -1;
break;
case "loopn":
case "lpn":
case "ln":
if(!argq.isEmpty()) {
String loopTimes = argq.pollFirst();
try {
loop = Integer.parseInt(loopTimes);
} catch(NumberFormatException nfe) {
System.out.println("Illegal number '" + loopTimes + "' as argument to '" + s + "'!");
return;
}
} else {
System.out.println("Argument '" + s + "' requires a numeric argument following it!");
return;
}
break;
case "movement":
case "move":
case "mov":
case "m":
movement = true;
break;
case "delays":
case "delay":
case "del":
case "d":
if(!argq.isEmpty()) {
String delaysString = argq.pollFirst();
String[] delaysSplit = delaysString.split(",");
delays = new float[delaysSplit.length];
for(int i=0; i<delaysSplit.length; i++) {
try {
delays[i] = Integer.parseInt(delaysSplit[i]);
} catch(NumberFormatException nfe) {
System.out.println("Illegal number '" + delaysSplit[i] + "' as argument to '" + s + "'!");
return;
}
}
} else {
System.out.println("Argument '" + s + "' requires a list of delays (in the format 'a,b,c,d,[...]') following it!");
return;
}
break;
case "hotspot":
case "hs":
case "h":
if(!argq.isEmpty()) {
hotspot = argq.pollFirst();
} else {
System.out.println("Argument '" + s + "' requires a hotspot string following it!");
return;
}
break;
default:
System.out.println("Unknown import argument '" + s + "', ignoring.");
break;
} }
} }
if(VERBOSITY >= 0) System.out.println("Loading " + dmiFile); if(VERBOSITY >= 0) System.out.println("Loading " + dmiFile);
DMI toImportTo = doDMILoad(dmiFile); DMI toImportTo = doDMILoad(dmiFile);
if(VERBOSITY >= 0) toImportTo.printInfo(); if(VERBOSITY >= 0) toImportTo.printInfo();
//public static IconState importFromPNG(DMI dmi, InputStream inS, String name) throws DMIException { IconState is = IconState.importFromPNG(toImportTo, new FileInputStream(pngFile), stateName, delays, rewind, loop, hotspot, movement);
IconState is = IconState.importFromPNG(toImportTo, new FileInputStream(pngFile), stateName);
if(noDup) {
if(!toImportTo.setIconState(is)) {
if(!(noDup && toImportTo.setIconState(is))) { toImportTo.addIconState(null, is);
}
} else {
toImportTo.addIconState(null, is); toImportTo.addIconState(null, is);
} }
if(VERBOSITY >= 0) toImportTo.printInfo(); if(VERBOSITY >= 0) toImportTo.printInfo();
if(VERBOSITY >= 0) System.out.println("Saving " + dmiFile); if(VERBOSITY >= 0) System.out.println("Saving " + dmiFile);
toImportTo.writeDMI(new FileOutputStream(dmiFile)); toImportTo.writeDMI(new FileOutputStream(dmiFile));
break; break;
@@ -286,6 +378,22 @@ public class Main {
if(VERBOSITY >= 0) v.printInfo(); if(VERBOSITY >= 0) v.printInfo();
break; break;
} }
case "info": {
if(argq.size() < 1) {
System.out.println("Insufficient arguments for command!");
System.out.println(helpStr);
return;
}
String infoFile = argq.pollFirst();
if(VERBOSITY >= 0) System.out.println("Loading " + infoFile);
DMI info = doDMILoad(infoFile);
info.printInfo();
info.printStateList();
break;
}
case "version":
System.out.println(VERSION);
return;
default: default:
System.out.println("Command '" + op + "' not found!"); System.out.println("Command '" + op + "' not found!");
case "help": case "help":
@@ -293,7 +401,7 @@ public class Main {
break; break;
} }
} }
static int parseDir(String s, IconState is) { static int parseDir(String s, IconState is) {
try { try {
int i = Integer.parseInt(s); int i = Integer.parseInt(s);
@@ -319,7 +427,7 @@ public class Main {
return -1; return -1;
} }
} }
static int parseFrame(String s, IconState is) { static int parseFrame(String s, IconState is) {
try { try {
int i = Integer.parseInt(s); int i = Integer.parseInt(s);
@@ -334,7 +442,7 @@ public class Main {
return -1; return -1;
} }
} }
static DMI doDMILoad(String file) { static DMI doDMILoad(String file) {
try { try {
DMI dmi = new DMI(file); DMI dmi = new DMI(file);