mirror of
https://github.com/ParadiseSS13/Paradise.git
synced 2026-08-24 12:37:26 +01:00
initial (#18249)
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
@call "%~dp0\..\bootstrap\python.bat" -m dmi.merge_driver --posthoc %*
|
||||
@pause
|
||||
@@ -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'
|
||||
@@ -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())
|
||||
@@ -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()
|
||||
@@ -1,5 +0,0 @@
|
||||
Uses PNGJ: https://code.google.com/p/pngj/.
|
||||
|
||||
For help, use "java -jar dmitool.jar help".
|
||||
|
||||
Requires Java 7.
|
||||
@@ -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'
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
Binary file not shown.
@@ -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)
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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.
|
||||
@@ -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<IconState> {
|
||||
int w, h;
|
||||
List<IconState> 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<IconState> 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<delays.length; d++) {
|
||||
delays[d] = Float.parseFloat(delayVals[d]);
|
||||
}
|
||||
i++;
|
||||
} else if(lines[i].equals("\trewind = 1")) {
|
||||
rewind = true;
|
||||
i++;
|
||||
} else if(lines[i].startsWith("\tloop = ")) {
|
||||
loop = Integer.parseInt(lines[i].substring("\tloop = ".length()));
|
||||
i++;
|
||||
} else if(lines[i].startsWith("\thotspot = ")) {
|
||||
hotspot = lines[i].substring("\thotspot = ".length());
|
||||
i++;
|
||||
} else if(lines[i].equals("\tmovement = 1")) {
|
||||
movement = true;
|
||||
i++;
|
||||
} else {
|
||||
System.out.println("Unknown line '" + lines[i] + "' in state '" + stateName + "'!");
|
||||
i++;
|
||||
}
|
||||
}
|
||||
if(delays != null) {
|
||||
if((Main.STRICT && delays.length != frames) || delays.length < frames) {
|
||||
throw new DMIException(null, 0, "Frames must be equal to delays (" + stateName + "; " + frames + " frames, " + delays.length + " delays)!");
|
||||
}
|
||||
}
|
||||
IconState is = new IconState(stateName, dirs, frames, null, delays, rewind, loop, hotspot, movement);
|
||||
totalImages += imagesInState;
|
||||
states.add(is);
|
||||
}
|
||||
images = states;
|
||||
|
||||
PngChunkPLTE pal = (PngChunkPLTE)pngr.getChunksList().getById1("PLTE");
|
||||
|
||||
isPaletted = pal != null;
|
||||
|
||||
if(isPaletted) {
|
||||
if(Main.VERBOSITY > 0) System.out.println(pal.getNentries() + " palette entries");
|
||||
|
||||
palette = new RGBA[pal.getNentries()];
|
||||
int[] rgb = new int[3];
|
||||
for(int q=0; q<pal.getNentries(); q++) {
|
||||
pal.getEntryRgb(q, rgb);
|
||||
palette[q] = new RGBA(rgb[0], rgb[1], rgb[2], q==0 ? 0 : 255);
|
||||
}
|
||||
} else {
|
||||
if(Main.VERBOSITY > 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<ih; y++) {
|
||||
ImageLineInt ili = (ImageLineInt)pngr.readRow();
|
||||
int[] sl = ili.getScanline();
|
||||
if(sl.length != (isPaletted ? iw : iw*4))
|
||||
throw new DMIException(null, 0, "Error processing image!");
|
||||
px[y] = sl.clone();
|
||||
}
|
||||
|
||||
int statesX = iw / w;
|
||||
int statesY = ih / h;
|
||||
|
||||
int x=0, y=0;
|
||||
for(IconState is: states) {
|
||||
int numImages = is.dirs * is.frames;
|
||||
Image[] img = new Image[numImages];
|
||||
for(int q=0; q<numImages; q++) {
|
||||
if(isPaletted) {
|
||||
int[][] idat = new int[h][w];
|
||||
for(int sy = 0; sy < h; sy++) {
|
||||
for(int sx = 0; sx < w; sx++) {
|
||||
idat[sy][sx] = px[y*h + sy][x*w + sx];
|
||||
}
|
||||
}
|
||||
img[q] = new PalettedImage(w, h, idat, palette);
|
||||
} else {
|
||||
RGBA[][] idat = new RGBA[h][w];
|
||||
for(int sy = 0; sy < h; sy++) {
|
||||
for(int sx = 0; sx < w; sx++) {
|
||||
idat[sy][sx] = new RGBA(px[y*h + sy][x*4*w + 4*sx], px[y*h + sy][x*4*w + 4*sx + 1], px[y*h + sy][x*4*w + 4*sx + 2], px[y*h + sy][x*4*w + 4*sx + 3]);
|
||||
}
|
||||
}
|
||||
img[q] = new NonPalettedImage(w, h, idat);
|
||||
}
|
||||
|
||||
x++;
|
||||
if(x == statesX) {
|
||||
x = 0;
|
||||
y++;
|
||||
if(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<images.size(); i++) {
|
||||
IconState ic = images.get(i);
|
||||
if(ic.name.equals(is.name)) {
|
||||
totalImages -= ic.dirs * ic.frames;
|
||||
totalImages += is.dirs * is.frames;
|
||||
images.set(i, is);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static final int IEND = 0x49454e44;
|
||||
private static final int zTXt = 0x7a545874;
|
||||
private static final int IHDR = 0x49484452;
|
||||
private static void fixChunks(DataInputStream in, DataOutputStream out) throws IOException {
|
||||
if(Main.VERBOSITY > 0) System.out.println("Fixing PNG chunks...");
|
||||
out.writeInt(in.readInt());
|
||||
out.writeInt(in.readInt());
|
||||
|
||||
Deque<PNGChunk> 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<iy; irow++) {
|
||||
ImageLineInt ili = new ImageLineInt(ii);
|
||||
int[] buf = ili.getScanline();
|
||||
for(int icol=0; icol<ix; icol++) {
|
||||
int imageX = icol / w;
|
||||
int pixelX = icol % w;
|
||||
|
||||
int imageY = irow / h;
|
||||
int pixelY = irow % h;
|
||||
|
||||
Image i = img[imageX][imageY];
|
||||
if(i != null) {
|
||||
RGBA c = i.getPixel(pixelX, pixelY);
|
||||
buf[icol*4 ] = c.r;
|
||||
buf[icol*4 + 1] = c.g;
|
||||
buf[icol*4 + 2] = c.b;
|
||||
buf[icol*4 + 3] = c.a;
|
||||
} else {
|
||||
buf[icol*4 ] = 0;
|
||||
buf[icol*4 + 1] = 0;
|
||||
buf[icol*4 + 2] = 0;
|
||||
buf[icol*4 + 3] = 0;
|
||||
}
|
||||
}
|
||||
out.writeRow(ili);
|
||||
}
|
||||
out.end();
|
||||
|
||||
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
|
||||
fixChunks(new DataInputStream(bais), new DataOutputStream(os));
|
||||
}
|
||||
|
||||
private String getDescriptor() {
|
||||
String s = "";
|
||||
String n = "\n";
|
||||
String q = "\"";
|
||||
|
||||
s += "# BEGIN DMI\n";
|
||||
s += "version = 4.0\n";
|
||||
s += " width = " + w + n;
|
||||
s += " height = " + h + n;
|
||||
for(IconState is: images) {
|
||||
s += is.getDescriptorFragment();
|
||||
}
|
||||
s += "# END DMI\n";
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
public void printInfo() {
|
||||
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) {
|
||||
if(obj == this) return true;
|
||||
if(!(obj instanceof DMI)) return false;
|
||||
DMI dmi = (DMI)obj;
|
||||
|
||||
// try to find a simple difference before we dive into icon_state comparisons
|
||||
if(dmi.w != w || dmi.h != h) return false;
|
||||
if(dmi.isPaletted != isPaletted) return false;
|
||||
if(dmi.totalImages != totalImages) return false;
|
||||
if(dmi.images.size() != images.size()) return false;
|
||||
HashMap<String, IconState> myIS = new HashMap<>();
|
||||
HashMap<String, IconState> 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;
|
||||
}
|
||||
}
|
||||
@@ -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<String, IconState> newIconStates;
|
||||
Map<String, IconStateDiff> modifiedIconStates = new HashMap<>();
|
||||
Set<String> 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<String, IconState> baseIS = new HashMap<>();
|
||||
for(IconState is: base.images) {
|
||||
baseIS.put(is.name, is);
|
||||
}
|
||||
|
||||
HashMap<String, IconState> modIS = new HashMap<>();
|
||||
for(IconState is: mod.images) {
|
||||
modIS.put(is.name, is);
|
||||
}
|
||||
|
||||
newIconStates = ((HashMap<String, IconState>)modIS.clone());
|
||||
for(String s: baseIS.keySet()) {
|
||||
newIconStates.remove(s);
|
||||
}
|
||||
|
||||
removedIconStates = new HashSet<>();
|
||||
removedIconStates.addAll(baseIS.keySet());
|
||||
removedIconStates.removeAll(modIS.keySet());
|
||||
|
||||
Set<String> 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<String> 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<String> mergeDiff(DMIDiff other, DMI conflictDMI, DMIDiff merged, String aName, String bName) {
|
||||
HashSet<String> myTouched = new HashSet<>();
|
||||
myTouched.addAll(removedIconStates);
|
||||
myTouched.addAll(newIconStates.keySet());
|
||||
myTouched.addAll(modifiedIconStates.keySet());
|
||||
|
||||
HashSet<String> otherTouched = new HashSet<>();
|
||||
otherTouched.addAll(other.removedIconStates);
|
||||
otherTouched.addAll(other.newIconStates.keySet());
|
||||
otherTouched.addAll(other.modifiedIconStates.keySet());
|
||||
|
||||
HashSet<String> bothTouched = (HashSet<String>)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<String> 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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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<totalFrames; i++) {
|
||||
for(int j=0; j<totalDirs; j++) {
|
||||
img[i][j] = images[(minDir+j) + (minFrame+i) * this.dirs];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(int imY=0; imY<totalDirs; imY++) {
|
||||
for(int pxY=0; pxY<h; pxY++) {
|
||||
ImageLineInt ili = new ImageLineInt(ii);
|
||||
int[] buf = ili.getScanline();
|
||||
for(int imX=0; imX<totalFrames; imX++) {
|
||||
Image i = img[imX][imY];
|
||||
for(int pxX=0; pxX<w; pxX++) {
|
||||
RGBA c = i.getPixel(pxX, pxY);
|
||||
buf[(imX*w + pxX)*4 ] = c.r;
|
||||
buf[(imX*w + pxX)*4 + 1] = c.g;
|
||||
buf[(imX*w + pxX)*4 + 2] = c.b;
|
||||
buf[(imX*w + pxX)*4 + 3] = c.a;
|
||||
}
|
||||
}
|
||||
out.writeRow(ili);
|
||||
}
|
||||
}
|
||||
out.end();
|
||||
}
|
||||
|
||||
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 h = dmi.h;
|
||||
|
||||
PngReader in;
|
||||
try {
|
||||
in = new PngReader(inS);
|
||||
} catch(PngjInputException pie) {
|
||||
throw new DMIException("Bad file format!", pie);
|
||||
}
|
||||
int pxW = in.imgInfo.cols;
|
||||
int pxH = in.imgInfo.rows;
|
||||
int frames = pxW / w; //frames are read along the X axis, dirs along the Y, much like export.
|
||||
int dirs = pxH / h;
|
||||
|
||||
// make sure the size is an integer multiple
|
||||
if(frames * w != pxW || frames==0) throw new DMIException("Illegal image size!");
|
||||
if(dirs * h != pxH || dirs==0) throw new DMIException("Illegal image size!");
|
||||
|
||||
int[][] px = new int[pxH][];
|
||||
for(int i=0; i<pxH; i++) {
|
||||
ImageLineInt ili = (ImageLineInt)in.readRow();
|
||||
int[] sl = ili.getScanline();
|
||||
px[i] = sl.clone();
|
||||
}
|
||||
|
||||
Image[] images = new Image[frames*dirs];
|
||||
for(int imageY=0; imageY<dirs; imageY++) {
|
||||
for(int imageX=0; imageX<frames; imageX++) {
|
||||
RGBA[][] pixels = new RGBA[h][w];
|
||||
for(int pixelY=0; pixelY<h; pixelY++) {
|
||||
for(int pixelX=0; pixelX<w; pixelX++) {
|
||||
int bY = imageY*h + pixelY;
|
||||
int bX = imageX*4*w + 4*pixelX;
|
||||
pixels[pixelY][pixelX] = new RGBA(px[bY][bX ],
|
||||
px[bY][bX + 1],
|
||||
px[bY][bX + 2],
|
||||
px[bY][bX + 3]);
|
||||
}
|
||||
}
|
||||
images[_getIndex(imageY, imageX, dirs)] = new NonPalettedImage(w, h, pixels);
|
||||
}
|
||||
}
|
||||
|
||||
//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, rewind, loop, hotspot, movement);
|
||||
|
||||
}
|
||||
|
||||
//Converts a desired dir and frame to an index into the images array.
|
||||
public int getIndex(int dir, int frame) {
|
||||
return _getIndex(dir, frame, dirs);
|
||||
}
|
||||
|
||||
private static int _getIndex(int dir, int frame, int totalDirs) {
|
||||
return dir + frame*totalDirs;
|
||||
}
|
||||
|
||||
public void insertDir(int dir, Image[] splice) {
|
||||
int maxFrame = frames < splice.length? frames: splice.length;
|
||||
for(int frameIdx = 0; frameIdx < maxFrame; frameIdx++) {
|
||||
insertImage(dir, frameIdx, splice[frameIdx]);
|
||||
}
|
||||
}
|
||||
|
||||
public void insertFrame(int frame, Image[] splice) {
|
||||
int maxDir = dirs < splice.length? dirs: splice.length;
|
||||
for(int dirIdx = 0; dirIdx < maxDir; dirIdx++) {
|
||||
insertImage(dirIdx, frame, splice[dirIdx]);
|
||||
}
|
||||
}
|
||||
|
||||
public void insertImage(int dir, int frame, Image splice) {
|
||||
if(frame < 0 || frame >= 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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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<ISAddress, Image> modifiedFrames = new HashMap<>();
|
||||
HashMap<ISAddress, Image> newFrames = new HashMap<>();
|
||||
HashSet<ISAddress> 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<maxDir; d++) {
|
||||
for(int f=0; f<maxFrame; f++) {
|
||||
if(base.dirs > 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);
|
||||
}
|
||||
}
|
||||
@@ -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<w; i++) {
|
||||
for(int j=0; j<h; j++) {
|
||||
if(!getPixel(i, j).equals(im.getPixel(i, j))) return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,513 +0,0 @@
|
||||
package dmitool;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Deque;
|
||||
import java.util.Set;
|
||||
|
||||
public class Main {
|
||||
public static int VERBOSITY = 0;
|
||||
public static boolean STRICT = false;
|
||||
public static final String VERSION = "v0.6 (7 Jan 2015)";
|
||||
|
||||
public static final String[] dirs = new String[] {
|
||||
"S", "N", "E", "W", "SE", "SW", "NE", "NW"
|
||||
};
|
||||
|
||||
public static final String helpStr =
|
||||
"help\n" +
|
||||
"\tthis text\n" +
|
||||
|
||||
"version\n" +
|
||||
"\tprint version and exit\n" +
|
||||
|
||||
"verify [file]\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" +
|
||||
"\tdiff between [file1] and [file2]\n" +
|
||||
|
||||
"sort [file]\n" +
|
||||
"\tsort the icon_states in [file] into ASCIIbetical order\n" +
|
||||
|
||||
"merge [base] [file1] [file2] [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" +
|
||||
|
||||
"extract [file] [state] [out] {args}\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" +
|
||||
"\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 can be 0-7 or S, N, E, W, SE, SW, NE, NW (non-case-sensitive)\n" +
|
||||
|
||||
"import [file] [state] [in] [options]\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" +
|
||||
"\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" +
|
||||
"\t direction D | dir D : replaces D with the image from [in], instead of the entire state. D can be 0-7 or S, N, E, etc. If the state does not already exist, this is ignored\n" +
|
||||
"";
|
||||
|
||||
public static void main(String[] args) throws FileNotFoundException, IOException, DMIException {
|
||||
Deque<String> 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<String> 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<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;
|
||||
case "dir":
|
||||
case "direction":
|
||||
if(!argq.isEmpty()) {
|
||||
replaceDir = argq.pollFirst();
|
||||
} else {
|
||||
System.out.println("Argument '" + s + "' requires a direction argument following it!");
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case "f":
|
||||
case "frame":
|
||||
if(!argq.isEmpty()) {
|
||||
replaceFrame = argq.pollFirst();
|
||||
} else {
|
||||
System.out.println("Argument '" + s + "' requires a frame argument following it!");
|
||||
return;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
System.out.println("Unknown import argument '" + s + "', ignoring.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(VERBOSITY >= 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<dirs.length && q < is.dirs; q++) {
|
||||
if(dirs[q].equalsIgnoreCase(s)) {
|
||||
return q;
|
||||
}
|
||||
}
|
||||
String dSummary = "";
|
||||
for(int i=0; i<is.dirs; i++) {
|
||||
dSummary += ", " + dirs[i];
|
||||
}
|
||||
dSummary = dSummary.substring(2);
|
||||
System.out.println("Unknown or non-existent direction '" + s + "'!");
|
||||
System.out.println("Valid range: [0, "+(is.dirs-1)+"], or " + dSummary);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
static int parseFrame(String s, IconState is) {
|
||||
try {
|
||||
int i = Integer.parseInt(s);
|
||||
if(0 <= i && i < is.frames) {
|
||||
return i;
|
||||
} else {
|
||||
System.out.println("Frame not in valid range [0, "+(is.frames-1)+"]!");
|
||||
return -1;
|
||||
}
|
||||
} catch(NumberFormatException nfe) {
|
||||
System.out.println("Failed to parse frame number: '" + s + "'!");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
static DMI doDMILoad(String file) {
|
||||
try {
|
||||
DMI dmi = new DMI(file);
|
||||
return dmi;
|
||||
} catch(DMIException dmie) {
|
||||
System.out.println("Failed to load " + file + ": " + dmie.getMessage());
|
||||
} catch(FileNotFoundException fnfe) {
|
||||
System.out.println("File not found: " + file);
|
||||
}
|
||||
System.exit(3);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
package dmitool;
|
||||
|
||||
public class NonPalettedImage extends Image {
|
||||
RGBA[][] pixels;
|
||||
|
||||
public NonPalettedImage(int w, int h, RGBA[][] pixels) {
|
||||
super(w, h);
|
||||
this.pixels = pixels;
|
||||
}
|
||||
|
||||
RGBA getPixel(int x, int y) {
|
||||
return pixels[y][x];
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
package dmitool;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public class PNGChunk {
|
||||
int len;
|
||||
int type;
|
||||
byte[] b;
|
||||
int crc;
|
||||
|
||||
public PNGChunk(DataInputStream in) throws IOException {
|
||||
len = in.readInt();
|
||||
type = in.readInt();
|
||||
b = new byte[len];
|
||||
in.read(b);
|
||||
crc = in.readInt();
|
||||
}
|
||||
|
||||
void write(DataOutputStream out) throws IOException {
|
||||
out.writeInt(len);
|
||||
out.writeInt(type);
|
||||
out.write(b);
|
||||
out.writeInt(crc);
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
package dmitool;
|
||||
|
||||
public class PalettedImage extends Image {
|
||||
int[][] pixels;
|
||||
RGBA[] pal;
|
||||
|
||||
public PalettedImage(int w, int h, int[][] pixels, RGBA[] palette) {
|
||||
super(w, h);
|
||||
this.pixels = pixels;
|
||||
this.pal = palette;
|
||||
}
|
||||
|
||||
RGBA getPixel(int x, int y) {
|
||||
return pal[pixels[y][x]];
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
package dmitool;
|
||||
|
||||
public class RGBA {
|
||||
int r, g, b, a;
|
||||
|
||||
public RGBA(int r, int g, int b, int a) {
|
||||
this.r = r;
|
||||
this.g = g;
|
||||
this.b = b;
|
||||
this.a = a;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String s = Long.toString(toRGBA8888());
|
||||
while(s.length() < 8)
|
||||
s = "0" + s;
|
||||
return "#" + s;
|
||||
}
|
||||
|
||||
@Override public boolean equals(Object obj) {
|
||||
if(obj == this) return true;
|
||||
if(!(obj instanceof RGBA)) return false;
|
||||
|
||||
RGBA o = (RGBA) obj;
|
||||
|
||||
return r==o.r && g==o.g && b==o.b && a==o.a;
|
||||
}
|
||||
|
||||
public long toRGBA8888() {
|
||||
return (r<<24) | (g<<16) | (b<<8) | a;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
@echo off
|
||||
SET RELATIVEROOT="../../"
|
||||
python icon_merge_tool.py %RELATIVEROOT%
|
||||
pause
|
||||
@@ -1,87 +0,0 @@
|
||||
from tempfile import NamedTemporaryFile
|
||||
import sys
|
||||
import os
|
||||
import time
|
||||
import subprocess
|
||||
|
||||
dmitool_path = "tools/dmitool/dmitool.jar"
|
||||
|
||||
def run_shell_command(command):
|
||||
return subprocess.run(command, shell=True, stdout=subprocess.PIPE, universal_newlines=True).stdout
|
||||
|
||||
def run_shell_command_binary(command):
|
||||
return subprocess.run(command, shell=True, stdout=subprocess.PIPE).stdout
|
||||
|
||||
def string_to_num(s):
|
||||
try:
|
||||
return int(s)
|
||||
except ValueError:
|
||||
return -1
|
||||
|
||||
def main(relative_root):
|
||||
git_version = run_shell_command("git version")
|
||||
if not git_version:
|
||||
print("ERROR: Failed to run git. Make sure it is installed and in your PATH.")
|
||||
return False
|
||||
|
||||
file_conflicts = run_shell_command("git diff --name-only --diff-filter=U").split("\n")
|
||||
icon_conflicts = [path for path in file_conflicts if path[len(path)-3::] == "dmi"]
|
||||
|
||||
for i in range(0, len(icon_conflicts)):
|
||||
print("[{}]: {}".format(i, icon_conflicts[i]))
|
||||
selection = input("Choose icon files you want to fix (example: 1,3-5,12):\n")
|
||||
selection = selection.replace(" ", "")
|
||||
selection = selection.split(",")
|
||||
|
||||
#shamelessly copied from mapmerger cli
|
||||
valid_indices = list()
|
||||
for m in selection:
|
||||
index_range = m.split("-")
|
||||
if len(index_range) == 1:
|
||||
index = string_to_num(index_range[0])
|
||||
if index >= 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])
|
||||
Reference in New Issue
Block a user