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
|
#!/usr/bin/env python3
import sys
from collections import defaultdict
END = 0xFFFFFFFF
def icon_hash(s):
h = 0
for c in s:
h = (h << 5) - h + ord(c)
return h
class Hole:
def __init__(self, addr, size):
self.addr = addr
self.size = size
class Cache:
def __init__(self):
self.data = []
def alloc(self, size):
addr = len(self.data)
data = [0x5A] * size
self.data.extend(data)
return Hole(addr, size)
def alloc16(self):
return self.alloc(2)
def alloc32(self):
return self.alloc(4)
def write(self, hole, n):
# Big-endian
for i in range(hole.size):
self.data[hole.addr + hole.size - i - 1] = (n >> (8 * i)) & 0xFF
def push16(self, n):
self.write(self.alloc16(), n)
def push32(self, n):
self.write(self.alloc32(), n)
def push_strings(self, str_dict):
for s, hole in str_dict.items():
self.write(hole, self.curr())
self.data.extend(s.encode() + b"\0")
def curr(self):
return len(self.data)
def main():
dir_map = dict()
icon_map = defaultdict(lambda: defaultdict(set))
icons_path = sys.argv[1]
cache_path = sys.argv[2]
evil_list_loop_index = None
for option in sys.argv[3:]:
k, v = option.split("=")
if k == "evil-list-loop-index":
evil_list_loop_index = int(v)
for line in open(icons_path).readlines():
line = line.strip()
if not line or line.startswith("#"):
continue
s, _, file_name = line.rpartition("/")
name, ext = file_name.split(".")
dir_i = dir_map.get(s, len(dir_map))
dir_map[s] = dir_i
icon_map[name][dir_i].add(ext)
cache = Cache()
# MAJOR_VERSION
cache.push16(1)
# MINOR_VERSION
cache.push16(0)
# HASH_OFFSET
hash_off_hole = cache.alloc32()
# DIRECTORY_LIST_OFFSET
dir_list_off_hole = cache.alloc32()
cache.write(dir_list_off_hole, cache.curr())
# N_DIRECTORIES
cache.push32(len(dir_map))
dir_holes = dict()
for dir in dir_map.keys():
# DIRECTORY_OFFSET
dir_holes[dir] = cache.alloc32()
cache.push_strings(dir_holes)
buckets = [[] for _ in range(31)]
for name in icon_map.keys():
h = icon_hash(name)
b = buckets[h % len(buckets)]
b.append(name)
cache.write(hash_off_hole, cache.curr())
# N_BUCKETS
cache.push32(len(buckets))
bucket_holes = []
for _ in range(len(buckets)):
# ICON_OFFSET
bucket_holes.append(cache.alloc32())
name_holes = dict()
list_holes = dict()
for i, b in enumerate(buckets):
chain_holes = []
hole = bucket_holes[i]
for name in b:
chain_holes.append(cache.curr())
cache.write(hole, cache.curr())
# CHAIN_OFFSET
hole = cache.alloc32()
# NAME_OFFSET
name_holes[name] = cache.alloc32()
# IMAGE_LIST_OFFSET
list_holes[name] = cache.alloc32()
if evil_list_loop_index != None and len(chain_holes) > evil_list_loop_index:
cache.write(hole, chain_holes[evil_list_loop_index])
else:
cache.write(hole, END)
cache.push_strings(name_holes)
for name, dirs in icon_map.items():
hole = list_holes[name]
cache.write(hole, cache.curr())
# N_IMAGES
cache.push32(len(dirs))
for dir_i, exts in dirs.items():
flags = 0
if "xpm" in exts:
flags |= 1
if "svg" in exts:
flags |= 2
if "png" in exts:
flags |= 4
# DIRECTORY_INDEX
cache.push16(dir_i)
# FLAGS
cache.push16(flags)
# IMAGE_DATA_OFFSET
cache.push32(END)
cache_file = open(cache_path, "wb")
cache_file.write(bytes(cache.data))
main()
|