/
color_gen.py
302 lines (251 loc) · 7.7 KB
/
color_gen.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
#! /usr/bin/env python3
import argparse
import re
import sys
COLORS = [
('default', 0x01),
('prefix', 0x02),
('reply2cmd', 0x01),
('showactivity',0x01),
('error', 0x03),
('highlight', 0x02),
('player', 0x02),
('settings', 0x02),
('command', 0x02),
('team 0', 0x08),
('team 1', 0x09),
('team 2', 0x11),
('teamcolor', 0x03),
('red', 0x07),
('lightred', 0x0F),
('darkred', 0x02),
('bluegrey', 0x0A),
('blue', 0x0B),
('darkblue', 0x0C),
('purple', 0x03),
('orchid', 0x0E),
('orange', 0x10),
('yellow', 0x09),
('gold', 0x10),
('lightgreen', 0x05),
('green', 0x04),
('lime', 0x06),
('grey', 0x08),
('grey2', 0x0D),
('engine 1', 0x01),
('engine 2', 0x02),
('engine 3', 0x03),
('engine 4', 0x04),
('engine 5', 0x05),
('engine 6', 0x06),
('engine 7', 0x07),
('engine 8', 0x08),
('engine 9', 0x09),
('engine 10', 0x0A),
('engine 11', 0x0B),
('engine 12', 0x0C),
('engine 13', 0x0D),
('engine 14', 0x0E),
('engine 15', 0x0F),
('engine 16', 0x10)
]
TAB = " "
HEADER = (
"//\n" +
"// This file was generated with color_gen.py and should not be used outside of colorlib.inc\n" +
"//\n" +
"// Do not edit! Regenerate this file with color_gen.py\n" +
"//\n" +
"\n" +
"#if defined _colorlib_map_included\n" +
TAB + "#endinput\n" +
"#endif\n" +
"#define _colorlib_map_included\n" +
"\n"
)
FOOTER = (
"\n"
)
ENUM_DEF = (
"enum CL_Colors\n" +
"{{\n" +
"{}" +
"}};\n"
)
ENUM_ENTRY_DEF = TAB + "{} = {},\n"
COLOR_FUNCTION_DEF = (
"CL_Colors _CL_ColorMap(char color[16])\n" +
"{{\n" +
"{}" +
"\n" +
"{}" +
"}}\n"
)
IF_DEF = [
TAB + "if (color[{}] == {})\n",
TAB + "{{\n",
TAB + "{}",
TAB + "}}\n"
]
ELIF_DEF = [
TAB + "else if (color[{}] == {})\n",
TAB + "{{\n",
TAB + "{}",
TAB + "}}\n"
]
ELSE_DEF = [
TAB + "else\n",
TAB + "{{\n",
TAB + "{};",
TAB + "}}\n"
]
CHAR_DEF = "\'{}\'"
RETURN_DEF = TAB + "return {};\n"
color_enum_names = {}
def get_indent(i : int) -> str:
"""Returns indentation to a given level."""
if i < 1: # no indentation needed
return ""
indent = TAB
for i in range(1, i):
indent = indent + TAB
return indent
def get_hex(i : int) -> str:
"""Returns a hex representation of a char."""
return '0x' + '{:02x}'.format(i).upper()
def get_indented_def(i : int, definition : str, first : bool = True) -> str:
"""Returns an indented definition string."""
# if the first definition in a set do not indent the first line
if first:
indented_def = definition[0]
else:
indented_def = get_indent(i) + definition[0]
# indent the remaining lines
for line in range(1, len(definition)):
indented_def = indented_def + get_indent(i) + definition[line]
return indented_def
def group_till_unique(in_group : list, i : int = 0) -> dict:
"""
Recursively splits a list into a tree,
where each node is a char in the leafs.
"""
if (len(in_group) <= 1):
return in_group
groups = {}
for (key, group) in group_by_char_at(in_group, i).items():
groups[key] = group_till_unique(group, i + 1)
return groups
def group_by_char_at(colors : list, i : int = 0) -> dict:
"""
Returns a dictionary of strings from a list,
grouped by the char at \'i\'.
"""
# construnct a dictionary of strings
# examples:
# colors = ['default', 'darkred', 'red']
# group_by_char_at(colors, 0)
# { 'd': ['default', 'darkred'], 'r': ['red'] }
#
# colors = ['grey', 'grey2']
# group_by_char_at(colors, 4)
# { 0: ['grey'], '2': ['grey2'] }
groups = {}
for color in colors:
if len(color) == i:
# index greater than length of string so use null terminator
groups[0] = [color]
elif color[i] in groups:
groups[color[i]].append(color)
else:
groups[color[i]] = [color]
return groups
def skip_redundant_decisions(group : dict, indent : int, depth : int) -> str:
"""Optimisation step which skips none defining values."""
# example:
# colors = ['default','darkblue', 'darkred']
# group = { d: { a: { r: { k: { b: ['darkblue'], r: ['darkred'] } } } }, e: ['default'] }
# # for the given case we only need to check indexs [0,1] and then [4]
# # to uniquely identify a color
# # ['d', 'a', 'b'], ['d', 'a', 'r'], ['d', 'e']
if len(group) == 1:
for (_, value) in group.items():
body = skip_redundant_decisions(value, indent, depth + 1)
else:
body = create_decisions(group, indent, depth)
return body
def create_enum() -> str:
"""Creates the definition for the enum for the mapping function."""
ev = []
for color in COLORS:
name = 'CL_Color_' + color[0].replace(' ', '_').capitalize()
value = get_hex(color[1])
color_enum_names[color[0]] = name
ev.append(ENUM_ENTRY_DEF.format(name, value))
enums = ""
for enum in ev:
enums = enums + enum
return ENUM_DEF.format(enums)
def create_return(color : str) -> str:
"""Creates a return statement."""
return RETURN_DEF.format(color_enum_names[color])
def create_statement(definition : str,
indent : int,
index : int,
key,
ret : str,
first : bool = True) -> str:
"""Creates a statement (\'if\', \'else if\')."""
if isinstance(key, str):
char = CHAR_DEF.format(key)
else:
char = hex(key)
return get_indented_def(
indent,
definition,
first
).format(index, char, ret)
def create_if(indent : int, index : int, key, ret : str) -> str:
"""Creates an \'if\' statement."""
return create_statement(IF_DEF, indent, index, key, ret, True)
def create_elif(indent : int, index : int, key, ret : str) -> str:
"""Creates an \'else if\' statement."""
return create_statement(ELIF_DEF, indent, index, key, ret, False)
def create_decisions(group : dict, indent : int = 0, depth : int = 0) -> str:
"""Creates the decisions for the mapping function."""
decisions = ""
for i, (key, value) in enumerate(group.items()):
if isinstance(value, dict):
if len(value) == 1:
body = skip_redundant_decisions(value, indent + 1, depth + 1)
else:
body = create_decisions(value, indent + 1, depth + 1)
else:
body = create_return(value[0])
if i == 0:
decisions = create_if(indent, depth, key, body)
else:
decisions = decisions + create_elif(indent, depth, key, body)
return decisions
def create_map() -> str:
"""Creates the mapping function."""
groups = group_till_unique([c[0] for c in COLORS])
return COLOR_FUNCTION_DEF.format(
create_decisions(groups),
create_return('default')
)
def main():
parser = argparse.ArgumentParser(description='ColorLib color map creator.')
parser.add_argument(
'out',
type=argparse.FileType('w', encoding='UTF-8'),
help='output path \'{path to include dir}/colorlib_map.inc\''
)
args = parser.parse_args()
args.out.write(HEADER)
args.out.write(create_enum())
args.out.write('\n')
args.out.write(create_map())
args.out.write(FOOTER)
args.out.close()
if __name__ == '__main__':
main()