diff --git a/tools/dmi/Resolve Icon Conflicts.bat b/tools/dmi/Resolve Icon Conflicts.bat new file mode 100644 index 00000000000..8def88442c6 --- /dev/null +++ b/tools/dmi/Resolve Icon Conflicts.bat @@ -0,0 +1,2 @@ +@call "%~dp0\..\bootstrap\python.bat" -m dmi.merge_driver --posthoc %* +@pause diff --git a/tools/dmi/__init__.py b/tools/dmi/__init__.py new file mode 100644 index 00000000000..4c3ef221370 --- /dev/null +++ b/tools/dmi/__init__.py @@ -0,0 +1,247 @@ +# Tools for working with modern DreamMaker icon files (PNGs + metadata) + +import math +from PIL import Image +from PIL.PngImagePlugin import PngInfo + +DEFAULT_SIZE = 32, 32 +LOOP_UNLIMITED = 0 +LOOP_ONCE = 1 + +NORTH = 1 +SOUTH = 2 +EAST = 4 +WEST = 8 +SOUTHEAST = SOUTH | EAST +SOUTHWEST = SOUTH | WEST +NORTHEAST = NORTH | EAST +NORTHWEST = NORTH | WEST + +CARDINALS = [NORTH, SOUTH, EAST, WEST] +DIR_ORDER = [SOUTH, NORTH, EAST, WEST, SOUTHEAST, SOUTHWEST, NORTHEAST, NORTHWEST] +DIR_NAMES = { + 'SOUTH': SOUTH, + 'NORTH': NORTH, + 'EAST': EAST, + 'WEST': WEST, + 'SOUTHEAST': SOUTHEAST, + 'SOUTHWEST': SOUTHWEST, + 'NORTHEAST': NORTHEAST, + 'NORTHWEST': NORTHWEST, + **{str(x): x for x in DIR_ORDER}, + **{x: x for x in DIR_ORDER}, + '0': SOUTH, + None: SOUTH, +} + + +class Dmi: + version = "4.0" + + def __init__(self, width, height): + self.width = width + self.height = height + self.states = [] + + @classmethod + def from_file(cls, fname): + image = Image.open(fname) + if image.mode != 'RGBA': + image = image.convert('RGBA') + + # no metadata = regular image file + if 'Description' not in image.info: + dmi = Dmi(*image.size) + state = dmi.state("") + state.frame(image) + return dmi + + # read metadata + metadata = image.info['Description'] + line_iter = iter(metadata.splitlines()) + assert next(line_iter) == "# BEGIN DMI" + assert next(line_iter) == f"version = {cls.version}" + + dmi = Dmi(*DEFAULT_SIZE) + state = None + + for line in line_iter: + if line == "# END DMI": + break + key, value = line.lstrip().split(" = ") + if key == 'width': + dmi.width = int(value) + elif key == 'height': + dmi.height = int(value) + elif key == 'state': + state = dmi.state(unescape(value)) + elif key == 'dirs': + state.dirs = int(value) + elif key == 'frames': + state._nframes = int(value) + elif key == 'delay': + state.delays = [parse_num(x) for x in value.split(',')] + elif key == 'loop': + state.loop = int(value) + elif key == 'rewind': + state.rewind = parse_bool(value) + elif key == 'hotspot': + x, y, frm = [int(x) for x in value.split(',')] + state.hotspot(frm - 1, x, y) + elif key == 'movement': + state.movement = parse_bool(value) + else: + raise NotImplementedError(key) + + # cut image into frames + width, height = image.size + gridwidth = width // dmi.width + i = 0 + for state in dmi.states: + for frame in range(state._nframes): + for dir in range(state.dirs): + px = dmi.width * (i % gridwidth) + py = dmi.height * (i // gridwidth) + im = image.crop((px, py, px + dmi.width, py + dmi.height)) + assert im.size == (dmi.width, dmi.height) + state.frames.append(im) + i += 1 + state._nframes = None + + return dmi + + def state(self, *args, **kwargs): + s = State(self, *args, **kwargs) + self.states.append(s) + return s + + @property + def default_state(self): + return self.states[0] + + def get_state(self, name): + for state in self.states: + if state.name == name: + return state + raise KeyError(name) + + def _assemble_comment(self): + comment = "# BEGIN DMI\n" + comment += f"version = {self.version}\n" + comment += f"\twidth = {self.width}\n" + comment += f"\theight = {self.height}\n" + for state in self.states: + comment += f"state = {escape(state.name)}\n" + comment += f"\tdirs = {state.dirs}\n" + comment += f"\tframes = {state.framecount}\n" + if state.framecount > 1 and len(state.delays): # any(x != 1 for x in state.delays): + comment += "\tdelay = " + ",".join(map(str, state.delays)) + "\n" + if state.loop != 0: + comment += f"\tloop = {state.loop}\n" + if state.rewind: + comment += "\trewind = 1\n" + if state.movement: + comment += "\tmovement = 1\n" + if state.hotspots and any(state.hotspots): + current = None + for i, value in enumerate(state.hotspots): + if value != current: + x, y = value + comment += f"\thotspot = {x},{y},{i + 1}\n" + current = value + comment += "# END DMI" + return comment + + def to_file(self, filename, *, palette=False): + # assemble comment + comment = self._assemble_comment() + + # assemble spritesheet + W, H = self.width, self.height + num_frames = sum(len(state.frames) for state in self.states) + sqrt = math.ceil(math.sqrt(num_frames)) + output = Image.new('RGBA', (sqrt * W, math.ceil(num_frames / sqrt) * H)) + + i = 0 + for state in self.states: + for frame in state.frames: + output.paste(frame, ((i % sqrt) * W, (i // sqrt) * H)) + i += 1 + + # save + pnginfo = PngInfo() + pnginfo.add_text('Description', comment, zip=True) + if palette: + output = output.convert('P') + output.save(filename, 'png', optimize=True, pnginfo=pnginfo) + + +class State: + def __init__(self, dmi, name, *, loop=LOOP_UNLIMITED, rewind=False, movement=False, dirs=1): + self.dmi = dmi + self.name = name + self.loop = loop + self.rewind = rewind + self.movement = movement + self.dirs = dirs + + self._nframes = None # used during loading only + self.frames = [] + self.delays = [] + self.hotspots = None + + @property + def framecount(self): + if self._nframes is not None: + return self._nframes + else: + return len(self.frames) // self.dirs + + def frame(self, image, *, delay=1): + assert image.size == (self.dmi.width, self.dmi.height) + self.delays.append(delay) + self.frames.append(image) + + def hotspot(self, first_frame, x, y): + if self.hotspots is None: + self.hotspots = [None] * self.framecount + for i in range(first_frame, self.framecount): + self.hotspots[i] = x, y + + def _frame_index(self, frame=0, dir=None): + ofs = DIR_ORDER.index(DIR_NAMES[dir]) + if ofs >= self.dirs: + ofs = 0 + return frame * self.dirs + ofs + + def get_frame(self, *args, **kwargs): + return self.frames[self._frame_index(*args, **kwargs)] + + +def escape(text): + text = text.replace('\\', '\\\\') + text = text.replace('"', '\\"') + return f'"{text}"' + + +def unescape(text, quote='"'): + if text == 'null': + return None + if not (text.startswith(quote) and text.endswith(quote)): + raise ValueError(text) + text = text[1:-1] + text = text.replace('\\"', '"') + text = text.replace('\\\\', '\\') + return text + + +def parse_num(value): + if '.' in value: + return float(value) + return int(value) + + +def parse_bool(value): + if value not in ('0', '1'): + raise ValueError(value) + return value == '1' diff --git a/tools/dmi/merge_driver.py b/tools/dmi/merge_driver.py new file mode 100644 index 00000000000..75c3daacb07 --- /dev/null +++ b/tools/dmi/merge_driver.py @@ -0,0 +1,181 @@ +#!/usr/bin/env python3 +import sys +import dmi +from hooks.merge_frontend import MergeDriver + + +def images_equal(left, right): + if left.size != right.size: + return False + w, h = left.size + left_load, right_load = left.load(), right.load() + for y in range(0, h): + for x in range(0, w): + lpixel, rpixel = left_load[x, y], right_load[x, y] + # quietly ignore changes where both pixels are fully transparent + if lpixel != rpixel and (lpixel[3] != 0 or rpixel[3] != 0): + return False + return True + + +def states_equal(left, right): + result = True + + # basic properties + for attr in ('loop', 'rewind', 'movement', 'dirs', 'delays', 'hotspots', 'framecount'): + lval, rval = getattr(left, attr), getattr(right, attr) + if lval != rval: + result = False + + # frames + for (left_frame, right_frame) in zip(left.frames, right.frames): + if not images_equal(left_frame, right_frame): + result = False + + return result + + +def key_of(state): + return (state.name, state.movement) + + +def dictify(sheet): + result = {} + for state in sheet.states: + k = key_of(state) + if k in result: + print(f" duplicate {k!r}") + result[k] = state + return result + + +def three_way_merge(base, left, right): + base_dims = base.width, base.height + if base_dims != (left.width, left.height) or base_dims != (right.width, right.height): + print("Dimensions have changed:") + print(f" Base: {base.width} x {base.height}") + print(f" Ours: {left.width} x {left.height}") + print(f" Theirs: {right.width} x {right.height}") + return True, None + + base_states, left_states, right_states = dictify(base), dictify(left), dictify(right) + + new_left = {k: v for k, v in left_states.items() if k not in base_states} + new_right = {k: v for k, v in right_states.items() if k not in base_states} + new_both = {} + conflicts = [] + for key, state in list(new_left.items()): + in_right = new_right.get(key, None) + if in_right: + if states_equal(state, in_right): + # allow it + new_both[key] = state + else: + # generate conflict states + print(f" C: {state.name!r}: added differently in both!") + state.name = f"{state.name} !CONFLICT! left" + conflicts.append(state) + in_right.name = f"{state.name} !CONFLICT! right" + conflicts.append(in_right) + # don't add it a second time + del new_left[key] + del new_right[key] + + final_states = [] + # add states that are currently in the base + for state in base.states: + in_left = left_states.get(key_of(state), None) + in_right = right_states.get(key_of(state), None) + left_equals = in_left and states_equal(state, in_left) + right_equals = in_right and states_equal(state, in_right) + + if not in_left and not in_right: + # deleted in both left and right, it's just deleted + print(f" {state.name!r}: deleted in both") + elif not in_left: + # left deletes + print(f" {state.name!r}: deleted in left") + if not right_equals: + print(f" ... but modified in right") + final_states.append(in_right) + elif not in_right: + # right deletes + print(f" {state.name!r}: deleted in right") + if not left_equals: + print(f" ... but modified in left") + final_states.append(in_left) + elif left_equals and right_equals: + # changed in neither + #print(f"Same in both: {state.name!r}") + final_states.append(state) + elif left_equals: + # changed only in right + print(f" {state.name!r}: changed in left") + final_states.append(in_right) + elif right_equals: + # changed only in left + print(f" {state.name!r}: changed in right") + final_states.append(in_left) + elif states_equal(in_left, in_right): + # changed in both, to the same thing + print(f" {state.name!r}: changed same in both") + final_states.append(in_left) # either or + else: + # changed in both + name = state.name + print(f" C: {name!r}: changed differently in both!") + state.name = f"{name} !CONFLICT! base" + conflicts.append(state) + in_left.name = f"{name} !CONFLICT! left" + conflicts.append(in_left) + in_right.name = f"{name} !CONFLICT! right" + conflicts.append(in_right) + + # add states which both left and right added the same + for key, state in new_both.items(): + print(f" {state.name!r}: added same in both") + final_states.append(state) + + # add states that are brand-new in the left + for key, state in new_left.items(): + print(f" {state.name!r}: added in left") + final_states.append(state) + + # add states that are brand-new in the right + for key, state in new_right.items(): + print(f" {state.name!r}: added in right") + final_states.append(state) + + final_states.extend(conflicts) + merged = dmi.Dmi(base.width, base.height) + merged.states = final_states + return len(conflicts), merged + + +class DmiDriver(MergeDriver): + driver_id = 'dmi' + + def merge(self, base, left, right): + icon_base = dmi.Dmi.from_file(base) + icon_left = dmi.Dmi.from_file(left) + icon_right = dmi.Dmi.from_file(right) + trouble, merge_result = three_way_merge(icon_base, icon_left, icon_right) + return not trouble, merge_result + + def to_file(self, outfile, merge_result): + merge_result.to_file(outfile) + + def post_announce(self, success, merge_result): + if not success: + print("!!! Manual merge required!") + if merge_result: + print(" A best-effort merge was performed. You must edit the icon and remove all") + print(" icon states marked with !CONFLICT!, leaving only the desired icon.") + else: + print(" The icon was totally unable to be merged, you must start with one version") + print(" or the other and manually resolve the conflict.") + print(" Information about which states conflicted is listed above.") + + +if __name__ == '__main__': + exit(DmiDriver().main()) diff --git a/tools/dmi/test.py b/tools/dmi/test.py new file mode 100644 index 00000000000..09629927d88 --- /dev/null +++ b/tools/dmi/test.py @@ -0,0 +1,39 @@ +import os +import sys +from dmi import * + + +def _self_test(): + # test: can we load every DMI in the tree + count = 0 + for dirpath, dirnames, filenames in os.walk('.'): + if '.git' in dirnames: + dirnames.remove('.git') + for filename in filenames: + if filename.endswith('.dmi'): + fullpath = os.path.join(dirpath, filename) + try: + Dmi.from_file(fullpath) + except Exception: + print('Failed on:', fullpath) + raise + count += 1 + + print(f"{os.path.relpath(__file__)}: successfully parsed {count} .dmi files") + + +def _usage(): + print(f"Usage:") + print(f" tools{os.sep}bootstrap{os.sep}python -m {__spec__.name}") + exit(1) + + +def _main(): + if len(sys.argv) == 1: + return _self_test() + + return _usage() + + +if __name__ == '__main__': + _main() diff --git a/tools/dmitool/README.txt b/tools/dmitool/README.txt deleted file mode 100644 index 409ab873c3d..00000000000 --- a/tools/dmitool/README.txt +++ /dev/null @@ -1,5 +0,0 @@ -Uses PNGJ: https://code.google.com/p/pngj/. - -For help, use "java -jar dmitool.jar help". - -Requires Java 7. \ No newline at end of file diff --git a/tools/dmitool/build.gradle b/tools/dmitool/build.gradle deleted file mode 100644 index 2e5fbc5b08e..00000000000 --- a/tools/dmitool/build.gradle +++ /dev/null @@ -1,16 +0,0 @@ -apply plugin: 'java' - -repositories { - mavenCentral() -} - -dependencies { - compile group: 'ar.com.hjg', name: 'pngj', version: '2.1.0' -} - -jar { - from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } } - manifest { - attributes 'Main-Class': 'dmitool.Main' - } -} \ No newline at end of file diff --git a/tools/dmitool/dmimerge.sh b/tools/dmitool/dmimerge.sh deleted file mode 100644 index dbe846fc6a9..00000000000 --- a/tools/dmitool/dmimerge.sh +++ /dev/null @@ -1,8 +0,0 @@ -java -jar tools/dmitool/dmitool.jar merge $1 $2 $3 $2 -if [ "$?" -gt 0 ] -then - echo "Unable to automatically resolve all icon_state conflicts, please merge manually." - exit 1 -fi - -exit 0 diff --git a/tools/dmitool/dmisort.sh b/tools/dmitool/dmisort.sh deleted file mode 100644 index 8e595b9292c..00000000000 --- a/tools/dmitool/dmisort.sh +++ /dev/null @@ -1,21 +0,0 @@ -FILE_DIR=0 -echo "This script will loop through a given folder and its sub-folders and sort all icons inside of .dmi files." -echo -n "Enter the path of the directory that needs its .dmi icons sorted (and that's a sub-folder of the folder this file is in): " -read FILE_DIR - -FILES_SORTED=0 -for f in $(find $FILE_DIR/*) - do - if [[ $f == *.dmi ]] - then - ((FILES_SORTED++)) - java -jar dmitool.jar sort $f $f - fi -done - -if [ -d "$FILE_DIR" ] - then - echo "Done. Sorted through" $FILES_SORTED "files." -fi - -read diff --git a/tools/dmitool/dmitool.jar b/tools/dmitool/dmitool.jar deleted file mode 100644 index 4cfa49f36de..00000000000 Binary files a/tools/dmitool/dmitool.jar and /dev/null differ diff --git a/tools/dmitool/dmitool.py b/tools/dmitool/dmitool.py deleted file mode 100644 index 390f0d745ff..00000000000 --- a/tools/dmitool/dmitool.py +++ /dev/null @@ -1,94 +0,0 @@ -""" Python 2.7 wrapper for dmitool. -""" - -import os -from subprocess import Popen, PIPE - -_JAVA_PATH = ["java"] -_DMITOOL_CMD = ["-jar", "dmitool.jar"] - -def _dmitool_call(*dmitool_args, **popen_args): - return Popen(_JAVA_PATH + _DMITOOL_CMD + [str(arg) for arg in dmitool_args], **popen_args) - -def _safe_parse(dict, key, deferred_value): - try: - dict[key] = deferred_value() - except Exception as e: - print "Could not parse property '%s': %s"%(key, e) - return e - return False - -def version(): - """ Returns the version as a string. """ - stdout, stderr = _dmitool_call("version", stdout=PIPE).communicate() - return str(stdout).strip() - -def help(): - """ Returns the help text as a string. """ - stdout, stderr = _dmitool_call("help", stdout=PIPE).communicate() - return str(stdout).strip() - -def info(filepath): - """ Totally not a hack that parses the output from dmitool into a dictionary. - May break at any moment. - """ - subproc = _dmitool_call("info", filepath, stdout=PIPE) - stdout, stderr = subproc.communicate() - - result = {} - data = stdout.split(os.linesep)[1:] - #for s in data: print s - - #parse header line - if len(data) > 0: - header = data.pop(0).split(",") - #don't need to parse states, it's redundant - _safe_parse(result, "images", lambda: int(header[0].split()[0].strip())) - _safe_parse(result, "size", lambda: header[2].split()[1].strip()) - - #parse state information - states = [] - for item in data: - if not len(item): continue - - stateinfo = {} - item = item.split(",", 3) - _safe_parse(stateinfo, "name", lambda: item[0].split()[1].strip(" \"")) - _safe_parse(stateinfo, "dirs", lambda: int(item[1].split()[0].strip())) - _safe_parse(stateinfo, "frames", lambda: int(item[2].split()[0].strip())) - if len(item) > 3: - stateinfo["misc"] = item[3] - - states.append(stateinfo) - - result["states"] = states - return result - -def extract_state(input_path, output_path, icon_state, direction=None, frame=None): - """ Extracts an icon state as a png to a given path. - If provided direction should be a string, one of S, N, E, W, SE, SW, NE, NW. - If provided frame should be a frame number or a string of two frame number separated by a dash. - """ - args = ["extract", input_path, icon_state, output_path] - if direction is not None: args.extend(("direction" , str(direction))) - if frame is not None: args.extend(("frame" , str(frame))) - return _dmitool_call(*args) - -def import_state(target_path, input_path, icon_state, replace=False, delays=None, rewind=False, loop=None, ismovement=False, direction=None, frame=None): - """ Inserts an input png given by the input_path into the target_path. - """ - args = ["import", target_path, icon_state, input_path] - - if replace: args.append("nodup") - if rewind: args.append("rewind") - if ismovement: args.append("movement") - if delays: args.extend(("delays", ",".join(delays))) - if direction is not None: args.extend(("direction", direction)) - if frame is not None: args.extend(("frame", frame)) - - if loop in ("inf", "infinity"): - args.append("loop") - elif loop: - args.extend(("loopn", loop)) - - return _dmitool_call(*args) diff --git a/tools/dmitool/git_merge_installer.bat b/tools/dmitool/git_merge_installer.bat deleted file mode 100644 index c7ba2fa23a1..00000000000 --- a/tools/dmitool/git_merge_installer.bat +++ /dev/null @@ -1,6 +0,0 @@ -@echo off -set tab= -echo. >> ../../.git/config -echo [merge "merge-dmi"] >> ../../.git/config -echo %tab%name = iconfile merge driver >> ../../.git/config -echo %tab%driver = ./tools/dmitool/dmimerge.sh %%O %%A %%B >> ../../.git/config diff --git a/tools/dmitool/git_merge_installer.sh b/tools/dmitool/git_merge_installer.sh deleted file mode 100644 index e0c48823b11..00000000000 --- a/tools/dmitool/git_merge_installer.sh +++ /dev/null @@ -1,6 +0,0 @@ -F="../../.git/config" - -echo '' >> $F -echo '[merge "merge-dmi"]' >> $F -echo ' name = iconfile merge driver' >> $F -echo ' driver = ./tools/dmitool/dmimerge.sh %O %A %B' >> $F diff --git a/tools/dmitool/merging.txt b/tools/dmitool/merging.txt deleted file mode 100644 index 639a634bfb7..00000000000 --- a/tools/dmitool/merging.txt +++ /dev/null @@ -1,14 +0,0 @@ -1. Install java(http://www.java.com/en/download/index.jsp) -2. Make sure java is in your PATH. To test this, open git bash, and type "java". If it says unknown command, you need to add JAVA/bin to your PATH variable (A guide for this can be found at https://www.java.com/en/download/help/path.xml ). - -Merging -The easiest way to do merging is to install the merge driver. For this, open `Baystation12/.git/config` in a text editor, and paste the following lines to the end of it: - -[merge "merge-dmi"] - name = iconfile merge driver - driver = ./tools/dmitool/dmimerge.sh %O %A %B - -You may optionally instead run git_merge_installer.bat or git_merge_installer.sh which should automatically insert these lines for you at the appropriate location. - -After this, merging DMI files should happen automagically unless there are conflicts (an icon_state that both you and someone else changed). -If there are conflicts, you will unfortunately still be stuck with opening both versions in the editor, and manually resolving the issues with those states. diff --git a/tools/dmitool/src/main/java/dmitool/DMI.java b/tools/dmitool/src/main/java/dmitool/DMI.java deleted file mode 100644 index bb560107ae9..00000000000 --- a/tools/dmitool/src/main/java/dmitool/DMI.java +++ /dev/null @@ -1,455 +0,0 @@ -package dmitool; - -import ar.com.hjg.pngj.ImageInfo; -import ar.com.hjg.pngj.ImageLineInt; -import ar.com.hjg.pngj.PngReader; -import ar.com.hjg.pngj.PngWriter; -import ar.com.hjg.pngj.PngjInputException; -import ar.com.hjg.pngj.chunks.PngChunkPLTE; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.ArrayDeque; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.Deque; -import java.util.HashMap; -import java.util.List; - -public class DMI implements Comparator { - int w, h; - List images; - int totalImages = 0; - RGBA[] palette; - boolean isPaletted; - - public DMI(int w, int h) { - this.w = w; - this.h = h; - images = new ArrayList<>(); - isPaletted = false; - palette = null; - } - - public DMI(String f) throws DMIException, FileNotFoundException { - this(new File(f)); - } - - public DMI(File f) throws DMIException, FileNotFoundException { - if(f.length() == 0) { // Empty .dmi is empty file - w = 32; - h = 32; - images = new ArrayList<>(); - isPaletted = false; - palette = null; - return; - } - InputStream in = new FileInputStream(f); - PngReader pngr; - try { - pngr = new PngReader(in); - } catch(PngjInputException pie) { - throw new DMIException("Bad file format!", pie); - } - String descriptor = pngr.getMetadata().getTxtForKey("Description"); - String[] lines = descriptor.split("\n"); - - if(Main.VERBOSITY > 0) System.out.println("Descriptor has " + lines.length + " lines."); - if(Main.VERBOSITY > 3) { - System.out.println("Descriptor:"); - System.out.println(descriptor); - } - - /* length 6 is: - # BEGIN DMI - version = 4.0 - state = "state" - dirs = 1 - frames = 1 - # END DMI - */ - if(lines.length < 6) throw new DMIException(null, 0, "Descriptor too short!"); - - if(!"# BEGIN DMI".equals(lines[0])) throw new DMIException(lines, 0, "Expected '# BEGIN DMI'"); - if(!"# END DMI".equals(lines[lines.length-1])) throw new DMIException(lines, lines.length-1, "Expected '# END DMI'"); - if(!"version = 4.0".equals(lines[1])) throw new DMIException(lines, 1, "Unknown version, expected 'version = 4.0'"); - - this.w = 32; - this.h = 32; - - int i = 2; - - if(lines[i].startsWith("\twidth = ")) { - this.w = Integer.parseInt(lines[2].substring("\twidth = ".length())); - i++; - } - if(lines[i].startsWith("\theight = ")) { - this.h = Integer.parseInt(lines[3].substring("\theight = ".length())); - i++; - } - - List states = new ArrayList<>(); - - while(i < lines.length - 1) { - long imagesInState = 1; - if(!lines[i].startsWith("state = \"") || !lines[i].endsWith("\"")) throw new DMIException(lines, i, "Error reading state string"); - String stateName = lines[i].substring("state = \"".length(), lines[i].length()-1); - i++; - int dirs = 1; - int frames = 1; - float[] delays = null; - boolean rewind = false; - int loop = -1; - String hotspot = null; - boolean movement = false; - while(lines[i].startsWith("\t")) { - if(lines[i].startsWith("\tdirs = ")) { - dirs = Integer.parseInt(lines[i].substring("\tdirs = ".length())); - imagesInState *= dirs; - i++; - } else if(lines[i].startsWith("\tframes = ")) { - frames = Integer.parseInt(lines[i].substring("\tframes = ".length())); - imagesInState *= frames; - i++; - } else if(lines[i].startsWith("\tdelay = ")) { - String delayString = lines[i].substring("\tdelay = ".length()); - String[] delayVals = delayString.split(","); - delays = new float[delayVals.length]; - for(int d=0; d 0) System.out.println(pal.getNentries() + " palette entries"); - - palette = new RGBA[pal.getNentries()]; - int[] rgb = new int[3]; - for(int q=0; q 0) System.out.println("Non-paletted image"); - } - - int iw = pngr.imgInfo.cols; - int ih = pngr.imgInfo.rows; - - if(totalImages > iw * ih) - throw new DMIException(null, 0, "Impossible number of images!"); - - if(Main.VERBOSITY > 0) System.out.println("Image size " + iw+"x"+ih); - int[][] px = new int[ih][]; - - for(int y=0; y statesY) - // this should NEVER happen, we pre-check it - throw new DMIException(null, 0, "CRITICAL: End of image reached with states to go!"); - } - } - if(is.delays != null) { - if((Main.STRICT && is.delays.length*is.dirs != img.length) || is.delays.length*is.dirs < img.length) - throw new DMIException(null, 0, "Delay array size mismatch: " + is.delays.length*is.dirs + " vs " + img.length + "!"); - } - is.images = img; - } - } - - public IconState getIconState(String name) { - for(IconState is: images) { - if(is.name.equals(name)) { - return is; - } - } - return null; - } - - /** - * Makes a copy, unless name is null. - */ - public void addIconState(String name, IconState is) { - if(name == null) { - images.add(is); - totalImages += is.dirs * is.frames; - } else { - IconState newState = (IconState)is.clone(); - newState.name = name; - images.add(newState); - totalImages += is.dirs * is.frames; - } - } - - public boolean removeIconState(String name) { - for(IconState is: images) { - if(is.name.equals(name)) { - images.remove(is); - totalImages -= is.dirs * is.frames; - return true; - } - } - return false; - } - - public boolean setIconState(IconState is) { - for(int i=0; i 0) System.out.println("Fixing PNG chunks..."); - out.writeInt(in.readInt()); - out.writeInt(in.readInt()); - - Deque notZTXT = new ArrayDeque<>(); - - PNGChunk c = null; - - while(c == null || c.type != IEND) { - c = new PNGChunk(in); - if(c.type == zTXt && notZTXT != null) { - PNGChunk cc = null; - while(cc == null || cc.type != IHDR) { - cc = notZTXT.pop(); - cc.write(out); - } - c.write(out); - while(notZTXT.size() != 0) { - PNGChunk pc = notZTXT.pop(); - pc.write(out); - } - notZTXT = null; - } else if(notZTXT != null) { - notZTXT.add(c); - } else { - c.write(out); - } - } - if(Main.VERBOSITY > 0) System.out.println("Chunks fixed."); - } - - @Override public int compare(IconState arg0, IconState arg1) { - return arg0.name.compareTo(arg1.name); - } - - public void writeDMI(OutputStream os) throws IOException { - writeDMI(os, false); - } - public void writeDMI(OutputStream os, boolean sortStates) throws IOException { - if(totalImages == 0) { // Empty .dmis are empty files - os.close(); - return; - } - - // Setup chunk-fix buffer - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - - if(sortStates) { - Collections.sort(images, this); - } - - // Write the dmi into the buffer - int sx = (int)Math.ceil(Math.sqrt(totalImages)); - int sy = totalImages / sx; - if(sx*sy < totalImages) { - sy++; - } - if(Main.VERBOSITY > 0) System.out.println("Image size: " + w + "x" + h + "; number of images " + sx + "x" + sy + " (" + totalImages + ")"); - int ix = sx * w; - int iy = sy * h; - ImageInfo ii = new ImageInfo(ix, iy, 8, true); - PngWriter out = new PngWriter(baos, ii); - out.setCompLevel(9); // Maximum compression - String description = getDescriptor(); - if(Main.VERBOSITY > 0) System.out.println("Descriptor has " + (description.split("\n").length) + " lines."); - out.getMetadata().setText("Description", description, true, true); - - Image[][] img = new Image[sx][sy]; - { - int k = 0; - int r = 0; - for(IconState is: images) { - for(Image i: is.images) { - img[k++][r] = i; - - if(k == sx) { - k = 0; - r++; - } - } - } - } - - for(int irow=0; irow myIS = new HashMap<>(); - HashMap dmiIS = new HashMap<>(); - - for(IconState is: images) { - myIS.put(is.name, is); - } - for(IconState is: dmi.images) { - dmiIS.put(is.name, is); - } - if(!myIS.keySet().equals(dmiIS.keySet())) return false; - for(String s: myIS.keySet()) { - if(!myIS.get(s).equals(dmiIS.get(s))) return false; - } - - return true; - } -} diff --git a/tools/dmitool/src/main/java/dmitool/DMIDiff.java b/tools/dmitool/src/main/java/dmitool/DMIDiff.java deleted file mode 100644 index 431d9c1d15a..00000000000 --- a/tools/dmitool/src/main/java/dmitool/DMIDiff.java +++ /dev/null @@ -1,194 +0,0 @@ -package dmitool; - -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -public class DMIDiff { - Map newIconStates; - Map modifiedIconStates = new HashMap<>(); - Set removedIconStates; - - DMIDiff() { - newIconStates = new HashMap<>(); - removedIconStates = new HashSet<>(); - } - - public DMIDiff(DMI base, DMI mod) { - if(base.h != mod.h || base.w != mod.w) throw new IllegalArgumentException("Cannot compare non-identically-sized DMIs!"); - - HashMap baseIS = new HashMap<>(); - for(IconState is: base.images) { - baseIS.put(is.name, is); - } - - HashMap modIS = new HashMap<>(); - for(IconState is: mod.images) { - modIS.put(is.name, is); - } - - newIconStates = ((HashMap)modIS.clone()); - for(String s: baseIS.keySet()) { - newIconStates.remove(s); - } - - removedIconStates = new HashSet<>(); - removedIconStates.addAll(baseIS.keySet()); - removedIconStates.removeAll(modIS.keySet()); - - Set retainedStates = new HashSet<>(); - retainedStates.addAll(baseIS.keySet()); - retainedStates.retainAll(modIS.keySet()); - - for(String s: retainedStates) { - if(!baseIS.get(s).equals(modIS.get(s))) { - modifiedIconStates.put(s, new IconStateDiff(baseIS.get(s), modIS.get(s))); - } - } - } - /** - * ASSUMES NO MERGE CONFLICTS - MERGE DIFFS FIRST. - */ - public void applyToDMI(DMI dmi) { - for(String s: removedIconStates) { - dmi.removeIconState(s); - } - for(String s: modifiedIconStates.keySet()) { - dmi.setIconState(modifiedIconStates.get(s).newState); - } - for(String s: newIconStates.keySet()) { - dmi.addIconState(null, newIconStates.get(s)); - } - } - - /** - * @param other The diff to merge with - * @param conflictDMI A DMI to add conflicted icon_states to - * @param merged An empty DMIDiff to merge into - * @param aName The log name for this diff - * @param bName The log name for {@code other} - * @return A Set containing all icon_states which conflicted, along with what was done in each diff, in the format "icon_state: here|there"; here and there are one of "added", "modified", and "removed" - */ - public Set mergeDiff(DMIDiff other, DMI conflictDMI, DMIDiff merged, String aName, String bName) { - HashSet myTouched = new HashSet<>(); - myTouched.addAll(removedIconStates); - myTouched.addAll(newIconStates.keySet()); - myTouched.addAll(modifiedIconStates.keySet()); - - HashSet otherTouched = new HashSet<>(); - otherTouched.addAll(other.removedIconStates); - otherTouched.addAll(other.newIconStates.keySet()); - otherTouched.addAll(other.modifiedIconStates.keySet()); - - HashSet bothTouched = (HashSet)myTouched.clone(); - bothTouched.retainAll(otherTouched); // this set now contains the list of icon_states that *both* diffs modified, which we'll put in conflictDMI for manual merge (unless they were deletions - - if(Main.VERBOSITY > 0) { - System.out.println("a: " + Arrays.toString(myTouched.toArray())); - System.out.println("b: " + Arrays.toString(otherTouched.toArray())); - System.out.println("both: " + Arrays.toString(bothTouched.toArray())); - } - - HashSet whatHappened = new HashSet<>(); - - for(String s: bothTouched) { - String here, there; - if(removedIconStates.contains(s)) { - here = "removed"; - } else if(newIconStates.containsKey(s)) { - here = "added"; - } else if(modifiedIconStates.containsKey(s)) { - here = "modified"; - } else { - System.out.println("Unknown error; state="+s); - here = "???"; - } - - if(other.removedIconStates.contains(s)) { - there = "removed"; - } else if(other.newIconStates.containsKey(s)) { - there = "added"; - } else if(other.modifiedIconStates.containsKey(s)) { - there = "modified"; - } else { - System.out.println("Unknown error; state="+s); - there = "???"; - } - - whatHappened.add(s + ": " + here + "|" + there); - } - - // Removals - for(String s: removedIconStates) { - if(!bothTouched.contains(s)) { - merged.removedIconStates.add(s); - } - } - for(String s: other.removedIconStates) { - if(!bothTouched.contains(s)) { - merged.removedIconStates.add(s); - } - } - - // Modifications - for(String s: modifiedIconStates.keySet()) { - if(!bothTouched.contains(s)) { - merged.modifiedIconStates.put(s, modifiedIconStates.get(s)); - } else { - conflictDMI.addIconState(aName + "|" + s, modifiedIconStates.get(s).newState); - } - } - for(String s: other.modifiedIconStates.keySet()) { - if(!bothTouched.contains(s)) { - merged.modifiedIconStates.put(s, other.modifiedIconStates.get(s)); - } else { - conflictDMI.addIconState(bName + "|" + s, other.modifiedIconStates.get(s).newState); - } - } - - // Additions - for(String s: newIconStates.keySet()) { - if(!bothTouched.contains(s)) { - merged.newIconStates.put(s, newIconStates.get(s)); - } else { - conflictDMI.addIconState(aName + s, newIconStates.get(s)); - } - } - for(String s: other.newIconStates.keySet()) { - if(!bothTouched.contains(s)) { - merged.newIconStates.put(s, other.newIconStates.get(s)); - } else { - conflictDMI.addIconState(bName + s, other.newIconStates.get(s)); - } - } - - return whatHappened; - } - - @Override public String toString() { - String s = ""; - String t = "\t"; - String q = "\""; - String n = "\n"; - if(!removedIconStates.isEmpty()) { - s += "Removed:\n"; - for(String state: removedIconStates) - s += t + q + state + q + n; - } - if(!modifiedIconStates.isEmpty()) { - s += "Modified:\n"; - for(String state: modifiedIconStates.keySet()) - s += t + q + state + q + " [" + modifiedIconStates.get(state).toString() + "]\n"; - } - if(!newIconStates.isEmpty()) { - s += "Added:\n"; - for(String state: newIconStates.keySet()) - s += t + q + state + q + " " + newIconStates.get(state).infoStr() + n; - } - if("".equals(s)) - return "No changes"; - return s; - } -} \ No newline at end of file diff --git a/tools/dmitool/src/main/java/dmitool/DMIException.java b/tools/dmitool/src/main/java/dmitool/DMIException.java deleted file mode 100644 index e9293162894..00000000000 --- a/tools/dmitool/src/main/java/dmitool/DMIException.java +++ /dev/null @@ -1,24 +0,0 @@ -package dmitool; - -public class DMIException extends Exception { - String[] desc = null; - int line = 0; - public DMIException(String[] descriptor, int line, String what) { - super(what); - desc = descriptor; - this.line = line; - } - public DMIException(String what) { - super(what); - } - public DMIException(String what, Exception cause) { - super(what, cause); - } - - @Override public String getMessage() { - if(desc != null) - return "\"" + desc[line] + "\" - " + super.getMessage(); - - return super.getMessage(); - } -} \ No newline at end of file diff --git a/tools/dmitool/src/main/java/dmitool/IconState.java b/tools/dmitool/src/main/java/dmitool/IconState.java deleted file mode 100644 index 2a2202c71cd..00000000000 --- a/tools/dmitool/src/main/java/dmitool/IconState.java +++ /dev/null @@ -1,280 +0,0 @@ -package dmitool; - -import java.util.Arrays; -import ar.com.hjg.pngj.ImageInfo; -import ar.com.hjg.pngj.ImageLineInt; -import ar.com.hjg.pngj.PngWriter; -import ar.com.hjg.pngj.PngReader; -import ar.com.hjg.pngj.PngjInputException; -import java.io.InputStream; -import java.io.OutputStream; - -public class IconState { - String name; - int dirs; - int frames; - float[] delays; - Image[] images; // dirs come first - boolean rewind; - int loop; - String hotspot; - 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() { - IconState is = new IconState(name, dirs, frames, images.clone(), delays==null ? null : delays.clone(), rewind, loop, hotspot, movement); - is.delays = delays != null ? delays.clone() : null; - is.rewind = rewind; - - return is; - } - - public IconState(String name, int dirs, int frames, Image[] images, float[] delays, boolean rewind, int loop, String hotspot, boolean movement) { - if(delays != null) { - if(Main.STRICT && delays.length != frames) { - throw new IllegalArgumentException("Delays and frames must be the same length!"); - } - } - this.name = name; - this.dirs = dirs; - this.frames = frames; - this.images = images; - this.rewind = rewind; - this.loop = loop; - this.hotspot = hotspot; - this.delays = delays; - this.movement = movement; - } - void setDelays(float[] delays) { - this.delays = delays; - } - void setRewind(boolean b) { - rewind = b; - } - @Override public boolean equals(Object obj) { - if(obj == this) return true; - if(!(obj instanceof IconState)) return false; - - IconState is = (IconState)obj; - - if(!is.name.equals(name)) return false; - if(is.dirs != dirs) return false; - if(is.frames != frames) return false; - if(!Arrays.equals(images, is.images)) return false; - if(is.rewind != rewind) return false; - if(is.loop != loop) return false; - if(!Arrays.equals(delays, is.delays)) return false; - if(!(is.hotspot == null ? hotspot == null : is.hotspot.equals(hotspot))) return false; - if(is.movement != movement) return false; - - return true; - } - public String infoStr() { - return "[" + frames + " frame(s), " + dirs + " dir(s)]"; - } - public String getDescriptorFragment() { - String s = ""; - String q = "\""; - String n = "\n"; - s += "state = " + q + name + q + n; - s += "\tdirs = " + dirs + n; - s += "\tframes = " + frames + n; - if(delays != null) { - s += "\tdelay = " + delayArrayToString(delays) + n; - } - if(rewind) { - s += "\trewind = 1\n"; - } - if(loop != -1) { - s += "\tloop = " + loop + n; - } - if(hotspot != null) { - s += "\thotspot = " + hotspot + n; - } - if(movement) { - s += "\tmovement = 1\n"; - } - return s; - } - - private static String delayArrayToString(float[] d) { - String s = ""; - for(float f: d) { - s += ","+f; - } - return s.substring(1); - } - - /** - * Dump the state to the given OutputStream in PNG format. Frames will be dumped along the X axis of the image, and directions will be dumped along the Y. - */ - public void dumpToPNG(OutputStream outS, int minDir, int maxDir, int minFrame, int maxFrame) { - int totalDirs = maxDir - minDir + 1; - int totalFrames = maxFrame - minFrame + 1; - - int w = images[minDir + minFrame * this.dirs].w; - int h = images[minDir + minFrame * this.dirs].h; - - if(Main.VERBOSITY > 0) System.out.println("Writing " + totalDirs + " dir(s), " + totalFrames + " frame(s), " + totalDirs*totalFrames + " image(s) total."); - ImageInfo ii = new ImageInfo(totalFrames * w, totalDirs * h, 8, true); - PngWriter out = new PngWriter(outS, ii); - out.setCompLevel(9); - - Image[][] img = new Image[totalFrames][totalDirs]; - { - for(int i=0; i= frames) - throw new IllegalArgumentException("Provided frame is out of range: " + frame); - if(dir < 0 || dir >= dirs) - throw new IllegalArgumentException("Provided dir is out of range: " + dir); - - images[getIndex(dir, frame)] = splice; - } -} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/tools/dmitool/src/main/java/dmitool/IconStateDiff.java b/tools/dmitool/src/main/java/dmitool/IconStateDiff.java deleted file mode 100644 index a5d30db7fe0..00000000000 --- a/tools/dmitool/src/main/java/dmitool/IconStateDiff.java +++ /dev/null @@ -1,126 +0,0 @@ -package dmitool; - -import java.util.HashMap; -import java.util.HashSet; - -public class IconStateDiff { - static class ISAddress { - int dir; - int frame; - - public ISAddress(int dir, int frame) { - this.dir = dir; - this.frame = frame; - } - - public String infoStr(int maxDir, int maxFrame) { - if(maxDir == 1 && maxFrame == 1) { - return ""; - } else if(maxDir == 1) { - return "{" + frame + "}"; - } else if(maxFrame == 1) { - return "{" + Main.dirs[dir] + "}"; - } else { - return "{" + Main.dirs[dir] + " " + frame + "}"; - } - } - } - int oldFrameCount = 0; - int oldDirectionCount = 0; - boolean oldRewind = false; - int oldLoop = -1; - String oldHotspot = null; - - int newFrameCount = 0; - int newDirectionCount = 0; - boolean newRewind = false; - int newLoop = -1; - String newHotspot = null; - - IconState newState; - HashMap modifiedFrames = new HashMap<>(); - HashMap newFrames = new HashMap<>(); - HashSet removedFrames = new HashSet<>(); - - public IconStateDiff(IconState base, IconState mod) { - int maxDir = Math.max(base.dirs, mod.dirs); - int maxFrame = Math.max(base.frames, mod.frames); - - oldFrameCount = base.frames; - oldDirectionCount = base.dirs; - oldRewind = base.rewind; - oldLoop = base.loop; - oldHotspot = base.hotspot; - - newFrameCount = mod.frames; - newDirectionCount = mod.dirs; - newRewind = mod.rewind; - newLoop = mod.loop; - newHotspot = mod.hotspot; - - newState = mod; - - Image baseI, modI; - for(int d=0; d d && base.frames > f) { - baseI = base.images[f * base.dirs + d]; - } else baseI = null; - if(mod.dirs > d && mod.frames > f) { - modI = mod.images[f * mod.dirs + d]; - } else modI = null; - - if(baseI == null && modI == null) continue; - - if(baseI == null) newFrames.put(new ISAddress(d, f), modI); - else if(modI == null) removedFrames.add(new ISAddress(d, f)); - else if(!baseI.equals(modI)) { - modifiedFrames.put(new ISAddress(d, f), modI); - } - } - } - } - - @Override public String toString() { - String s = ""; - String tmp; - - if(newDirectionCount != oldDirectionCount) - s += " | dirs " + oldDirectionCount + "->" + newDirectionCount; - - if(newFrameCount != oldFrameCount) - s += " | frames " + oldFrameCount + "->" + newFrameCount; - - if(newRewind != oldRewind) { - s += " | rewind " + oldRewind + "->" + newRewind; - } - - if(newLoop != oldLoop) { - s += " | loop " + oldLoop + "->" + newLoop; - } - - if(newHotspot == null ? oldHotspot != null : !newHotspot.equals(oldHotspot)) { - s += " | hotspot " + oldHotspot + "->" + newHotspot; - } - - if(!modifiedFrames.isEmpty()) { - int total_frames = Math.min(oldFrameCount, newFrameCount) * Math.min(oldDirectionCount, newDirectionCount); - tmp = ""; - for(ISAddress isa: modifiedFrames.keySet()) { - String str = isa.infoStr(oldDirectionCount, oldFrameCount); - if(!"".equals(str)) { - tmp += ", " + str; - } - } - if(!"".equals(tmp)) { - s += " | modified " + modifiedFrames.size() + " of " + total_frames + ": " + tmp.substring(1); - } else { - s += " | modified " + modifiedFrames.size() + " of " + total_frames; - } - } - - if("".equals(s)) - return "No change"; - return s.substring(3); - } -} \ No newline at end of file diff --git a/tools/dmitool/src/main/java/dmitool/Image.java b/tools/dmitool/src/main/java/dmitool/Image.java deleted file mode 100644 index 2ea7ff41920..00000000000 --- a/tools/dmitool/src/main/java/dmitool/Image.java +++ /dev/null @@ -1,32 +0,0 @@ -package dmitool; - -import java.io.IOException; -import java.io.OutputStream; - -public abstract class Image { - int w, h; - - abstract RGBA getPixel(int x, int y); - - public Image(int w, int h) { - this.w = w; - this.h = h; - } - - @Override public boolean equals(Object obj) { - if(obj == this) return true; - if(!(obj instanceof Image)) return false; - - Image im = (Image) obj; - - if(w != im.w || h != im.h) return false; - - for(int i=0; i argq = new ArrayDeque<>(); - for(String s: args) { - argq.addLast(s); - } - if(argq.size() == 0) { - System.out.println("No command found; use 'help' for help"); - return; - } - String switches = argq.peekFirst(); - if(switches.startsWith("-")) { - for(char c: switches.substring(1).toCharArray()) { - switch(c) { - case 'v': VERBOSITY++; break; - case 'q': VERBOSITY--; break; - case 'S': STRICT = true; break; - } - } - argq.pollFirst(); - } - String op = argq.pollFirst(); - - switch(op) { - case "diff": { - if(argq.size() < 2) { - System.out.println("Insufficient arguments for command!"); - System.out.println(helpStr); - return; - } - String a = argq.pollFirst(); - String b = argq.pollFirst(); - - if(VERBOSITY >= 0) System.out.println("Loading " + a); - DMI dmi = doDMILoad(a); - if(VERBOSITY >= 0) dmi.printInfo(); - - if(VERBOSITY >= 0) System.out.println("Loading " + b); - DMI dmi2 = doDMILoad(b); - if(VERBOSITY >= 0) dmi2.printInfo(); - - DMIDiff dmid = new DMIDiff(dmi, dmi2); - System.out.println(dmid); - break; - } - case "sort": { - if(argq.size() < 1) { - System.out.println("Insufficient arguments for command!"); - System.out.println(helpStr); - return; - } - String f = argq.pollFirst(); - - if(VERBOSITY >= 0) System.out.println("Loading " + f); - DMI dmi = doDMILoad(f); - if(VERBOSITY >= 0) dmi.printInfo(); - - if(VERBOSITY >= 0) System.out.println("Saving " + f); - dmi.writeDMI(new FileOutputStream(f), true); - break; - } - case "merge": { - if(argq.size() < 4) { - System.out.println("Insufficient arguments for command!"); - System.out.println(helpStr); - return; - } - String baseF = argq.pollFirst(), - aF = argq.pollFirst(), - bF = argq.pollFirst(), - mergedF = argq.pollFirst(); - if(VERBOSITY >= 0) System.out.println("Loading " + baseF); - DMI base = doDMILoad(baseF); - if(VERBOSITY >= 0) base.printInfo(); - - if(VERBOSITY >= 0) System.out.println("Loading " + aF); - DMI aDMI = doDMILoad(aF); - if(VERBOSITY >= 0) aDMI.printInfo(); - - if(VERBOSITY >= 0) System.out.println("Loading " + bF); - DMI bDMI = doDMILoad(bF); - if(VERBOSITY >= 0) bDMI.printInfo(); - - DMIDiff aDiff = new DMIDiff(base, aDMI); - DMIDiff bDiff = new DMIDiff(base, bDMI); - DMIDiff mergedDiff = new DMIDiff(); - DMI conflictDMI = new DMI(32, 32); - - Set cf = aDiff.mergeDiff(bDiff, conflictDMI, mergedDiff, aF, bF); - - mergedDiff.applyToDMI(base); - - base.writeDMI(new FileOutputStream(mergedF)); - - if(!cf.isEmpty()) { - if(VERBOSITY >= 0) for(String s: cf) { - System.out.println(s); - } - conflictDMI.writeDMI(new FileOutputStream(mergedF + ".conflict.dmi"), true); - System.out.println("Add/modify conflicts placed in '" + mergedF + ".conflict.dmi'"); - System.exit(1); // Git expects non-zero on merge conflict - } else { - System.out.println("No conflicts"); - System.exit(0); - } - break; - } - case "extract": { - if(argq.size() < 3) { - System.out.println("Insufficient arguments for command!"); - System.out.println(helpStr); - return; - } - String file = argq.pollFirst(), - state = argq.pollFirst(), - outFile = argq.pollFirst(); - - DMI dmi = doDMILoad(file); - if(VERBOSITY >= 0) dmi.printInfo(); - - IconState is = dmi.getIconState(state); - if(is == null) { - System.out.println("icon_state '"+state+"' does not exist!"); - return; - } - // minDir, Maxdir, minFrame, Maxframe - int mDir=0, Mdir=is.dirs-1; - int mFrame=0, Mframe=is.frames-1; - - while(argq.size() > 1) { - String arg = argq.pollFirst(); - - switch(arg) { - case "d": - case "dir": - case "dirs": - case "direction": - case "directions": - String dString = argq.pollFirst(); - if(dString.contains("-")) { - String[] splitD = dString.split("-"); - if(splitD.length == 2) { - mDir = parseDir(splitD[0], is); - Mdir = parseDir(splitD[1], is); - } else { - System.out.println("Illegal dir string: '" + dString + "'!"); - return; - } - } else { - mDir = parseDir(dString, is); - Mdir = mDir; - } - // Invalid value check, warnings are printed in parseDir() - if(mDir == -1 || Mdir == -1) return; - if(Mdir < mDir) { - System.out.println("Maximum dir greater than minimum dir!"); - System.out.println("Textual direction order is S, N, E, W, SE, SW, NE, NW increasing 0 (S) to 7 (NW)"); - return; - } - break; - case "f": - case "frame": - case "frames": - String fString = argq.pollFirst(); - if(fString.contains("-")) { - String[] splitF = fString.split("-"); - if(splitF.length == 2) { - mFrame = parseFrame(splitF[0], is); - Mframe = parseFrame(splitF[1], is); - } else { - System.out.println("Illegal frame string: '" + fString + "'!"); - return; - } - } else { - mFrame = parseFrame(fString, is); - Mframe = mFrame; - } - // Invalid value check, warnings are printed in parseFrame() - if(mFrame == -1 || Mframe == -1) return; - if(Mframe < mFrame) { - System.out.println("Maximum frame greater than minimum frame!"); - return; - } - break; - default: - System.out.println("Unknown argument '" + arg + "' detected, ignoring."); - } - } - if(!argq.isEmpty()) { - System.out.println("Extra argument '" + argq.pollFirst() + "' detected, ignoring."); - } - is.dumpToPNG(new FileOutputStream(outFile), mDir, Mdir, mFrame, Mframe); - break; - } - case "import": { - if(argq.size() < 3) { - System.out.println("Insufficient arguments for command!"); - System.out.println(helpStr); - return; - } - String dmiFile = argq.pollFirst(), - stateName = argq.pollFirst(), - pngFile = argq.pollFirst(); - - boolean noDup = false; - boolean rewind = false; - int loop = 0; - boolean movement = false; - String hotspot = null; - float[] delays = null; - String replaceDir = null; - String replaceFrame = null; - while(!argq.isEmpty()) { - String s = argq.pollFirst(); - switch(s.toLowerCase()) { - case "nodup": - case "nd": - case "n": - noDup = true; - 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= 0) System.out.println("Loading " + dmiFile); - DMI toImportTo = doDMILoad(dmiFile); - if(VERBOSITY >= 0) toImportTo.printInfo(); - IconState is = IconState.importFromPNG(toImportTo, new FileInputStream(pngFile), stateName, delays, rewind, loop, hotspot, movement); - - //image insertion - if(replaceDir != null || replaceFrame != null) { - - IconState targetIs = toImportTo.getIconState(stateName); - if(targetIs == null) { - System.out.println("'direction' or 'frame' specified and no icon state '" + stateName + "' found, aborting!"); - return; - } - if(is.images.length == 0) { - System.out.println("'direction' or 'frame' specified and imported is empty, aborting!"); - return; - } - - if(!noDup) targetIs = targetIs.clone(); - - int dirToReplace, frameToReplace; - if(replaceDir != null && replaceFrame != null) { - frameToReplace = parseFrame(replaceFrame, targetIs); - dirToReplace = parseDir(replaceDir, targetIs); - targetIs.insertImage(dirToReplace, frameToReplace, is.images[0]); - } - else if(replaceDir != null) { - dirToReplace = parseDir(replaceDir, targetIs); - targetIs.insertDir(dirToReplace, is.images); - } - else if(replaceFrame != null) { - frameToReplace = parseFrame(replaceFrame, targetIs); - targetIs.insertFrame(frameToReplace, is.images); - } - - if(!noDup) toImportTo.addIconState(null, targetIs); - } - else { - if(noDup) { - if(!toImportTo.setIconState(is)) { - toImportTo.addIconState(null, is); - } - } else { - toImportTo.addIconState(null, is); - } - } - - if(VERBOSITY >= 0) toImportTo.printInfo(); - - if(VERBOSITY >= 0) System.out.println("Saving " + dmiFile); - toImportTo.writeDMI(new FileOutputStream(dmiFile)); - break; - } - case "verify": { - if(argq.size() < 1) { - System.out.println("Insufficient arguments for command!"); - System.out.println(helpStr); - return; - } - String vF = argq.pollFirst(); - if(VERBOSITY >= 0) System.out.println("Loading " + vF); - DMI v = doDMILoad(vF); - if(VERBOSITY >= 0) v.printInfo(); - 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: - System.out.println("Command '" + op + "' not found!"); - case "help": - System.out.println(helpStr); - break; - } - } - - static int parseDir(String s, IconState is) { - try { - int i = Integer.parseInt(s); - if(0 <= i && i < is.dirs) { - return i; - } else { - System.out.println("Direction not in valid range [0, "+(is.dirs-1)+"]!"); - return -1; - } - } catch(NumberFormatException nfe) { - for(int q=0; q= 0 and index < len(icon_conflicts): - valid_indices.append(index) - elif len(index_range) == 2: - index0 = string_to_num(index_range[0]) - index1 = string_to_num(index_range[1]) - if index0 >= 0 and index0 <= index1 and index1 < len(icon_conflicts): - valid_indices.extend(range(index0, index1 + 1)) - - if not len(valid_indices): - print("No icons selected, exiting.") - sys.exit() - - print("Attempting to fix the following icon files:") - for i in valid_indices: - print(icon_conflicts[i]) - input("Press Enter to start.") - - for i in valid_indices: - path = icon_conflicts[i] - print("{}: {}".format("Merging", path)) - - common_ancestor_hash = run_shell_command("git merge-base MERGE_HEAD HEAD").strip() - - ours_icon = NamedTemporaryFile(delete=False) - theirs_icon = NamedTemporaryFile(delete=False) - base_icon = NamedTemporaryFile(delete=False) - - ours_icon.write(run_shell_command_binary("git show ORIG_HEAD:{}".format(path))) - theirs_icon.write(run_shell_command_binary("git show master:{}".format(path))) - base_icon.write(run_shell_command_binary("git show {}:{}".format(common_ancestor_hash, path))) - - # So it being "open" doesn't prevent other programs from using it - ours_icon.close() - theirs_icon.close() - base_icon.close() - - merge_command = "java -jar {} merge {} {} {} {}".format(relative_root + dmitool_path, base_icon.name, ours_icon.name, theirs_icon.name, relative_root + path + ".fixed") - - print(merge_command) - print(run_shell_command(merge_command)) - os.remove(ours_icon.name) - os.remove(theirs_icon.name) - os.remove(base_icon.name) - print(".") - -main(sys.argv[1])