Files
pyfa/scripts/icons_update.py
DarkPhoenix 71f3e7b858 Update icons
2024-10-30 18:12:47 +01:00

264 lines
8.4 KiB
Python

#!/usr/bin/env python2.7
"""
This script updates only market/item icons.
"""
import argparse
import json
import os
import sqlite3
from shutil import copyfile
from PIL import Image
def get_full_alias(short_alias):
full_aliases = {
'tq': 'tranquility',
'sisi': 'singularity'}
return full_aliases.get(short_alias, short_alias)
parser = argparse.ArgumentParser(description='This script updates module icons for pyfa')
parser.add_argument('-e', '--eve', required=True, type=str, help='path to eve\'s shared cache folder')
parser.add_argument('-s', '--server', required=False, default='tq', type=str, help='which server to use (defaults to tq)')
parser.add_argument('-i', '--icons', required=True, type=str, help='Path to iconids.json extracted by phobos')
args = parser.parse_args()
script_dir = os.path.dirname(os.path.abspath(__file__))
db_path = os.path.abspath(os.path.join(script_dir, '..', 'eve.db'))
icons_dir = os.path.abspath(os.path.join(script_dir, '..', 'imgs', 'icons'))
render_dir = os.path.abspath(os.path.join(script_dir, '..', 'imgs', 'renders'))
db = sqlite3.connect(db_path)
cursor = db.cursor()
ICON_SIZE = (16, 16)
RENDER_SIZE = (32, 32)
with open(args.icons, 'r') as f:
icon_json = json.load(f)
eve_path = os.path.join(args.eve, 'index_{}.txt'.format(get_full_alias(args.server)))
with open(eve_path, 'r') as f:
lines = f.readlines()
file_index = {x.split(',')[0]: x.split(',') for x in lines}
resfileindex = file_index['app:/resfileindex.txt']
res_cache = os.path.join(args.eve, 'ResFiles')
with open(os.path.join(res_cache, resfileindex[1]), 'r') as f:
lines = f.readlines()
res_index = {x.split(',')[0].lower(): x.split(',') for x in lines}
# Need to copy the file to our cuirrent directory
graphics_loader_file = os.path.join(res_cache, file_index['app:/bin64/graphicIDsLoader.pyd'][1])
to_path = os.path.dirname(os.path.abspath(__file__))
copyfile(graphics_loader_file, os.path.join(os.path.dirname(os.path.abspath(__file__)), 'graphicIDsLoader.pyd'))
# The loader expect it to be the correct filename, so copy trhe file as well
graphics_file = os.path.join(res_cache, res_index['res:/staticdata/graphicIDs.fsdbinary'.lower()][1])
to_path = os.path.dirname(os.path.abspath(__file__))
copyfile(graphics_file, os.path.join(os.path.dirname(os.path.abspath(__file__)), 'graphicIDs.fsdbinary'))
import graphicIDsLoader
graphics = graphicIDsLoader.load(os.path.join(to_path, 'graphicIDs.fsdbinary'))
graphics_py_ob = {}
for x, v in graphics.items():
if hasattr(v, 'iconInfo') and hasattr(v.iconInfo, 'folder'):
graphics_py_ob[x] = v.iconInfo.folder
# Add children to market group list
# {parent: {children}}
mkt_tree = {}
for row in cursor.execute('select marketGroupID, parentGroupID from invmarketgroups'):
parent = row[1]
# We have all the root groups in the set we need anyway
if not parent:
continue
child = row[0]
children = mkt_tree.setdefault(parent, set())
children.add(child)
# Traverse the tree we just composed to add all children for all needed roots
def get_children(parent):
children = set()
for child in mkt_tree.get(parent, ()):
children.add(child)
children.update(get_children(child))
return children
query_items = 'select distinct it.iconID from invtypes as it inner join invgroups as ig on it.groupID = ig.groupID where ig.categoryID != 2118'
query_groups = 'select distinct iconID from invgroups'
query_cats = 'select distinct iconID from invcategories'
query_market = 'select distinct iconID from invmarketgroups'
query_attrib = 'select distinct iconID from dgmattribs'
query_ships = 'select it.graphicID from invtypes as it inner join invgroups as ig on it.groupID = ig.groupID where ig.categoryID in (6, 65)'
needed = set()
existing = set()
export = {}
# Get a list of needed icons based on the items / attributes / etc from the database
for query in (query_items, query_groups, query_cats, query_market, query_attrib):
for row in cursor.execute(query):
fname = row[0]
if fname is None:
continue
needed.add(fname)
# Get a list of all the icons we currently have
for fname in os.listdir(icons_dir):
if not os.path.isfile(os.path.join(icons_dir, fname)):
continue
fname = os.path.splitext(fname)[0]
# Get rid of "icon" prefix as well
#fname = re.sub('^icon', '', fname)
existing.add(fname)
def crop_image(img):
w, h = img.size
if h == w:
return img
normal = min(h, w)
diff_w = w - normal
diff_h = h - normal
crop_top = diff_h // 2
crop_bot = diff_h // 2 + diff_h % 2
crop_left = diff_w // 2
crop_right = diff_w // 2 + diff_w % 2
box = (crop_left, crop_top, w - crop_right, h - crop_bot)
return img.crop(box)
def get_icon_file(res_path, size):
"""
Get the iconFile field value and find proper
icon for it. Return as PIL image object down-
scaled for use in pyfa.
"""
res_path = res_path.lower()
res_path = res_path.replace('\\', '/')
res_path = res_path.replace('//', '/') #1703
if res_path not in res_index:
return None
res_icon = res_index[res_path]
icon_path = res_icon[1]
fullpath = os.path.join(res_cache, icon_path)
if not os.path.isfile(fullpath):
return None
img = Image.open(fullpath)
if size > img.size:
# if we are requesting a size that is bigger than the source, return None. See #1769
return None
img = crop_image(img)
img.thumbnail(size, Image.ANTIALIAS)
# Strip all additional image info (mostly for ICC color
# profiles, see issue #337)
img.info.clear()
return img
toremove = existing.difference(needed)
toupdate = existing.intersection(needed)
toadd = needed.difference(existing)
if toremove:
print('Some icons are not used and will be removed:')
for fname in sorted(toremove):
fullname = '{}.png'.format(fname)
fullpath = os.path.join(icons_dir, fullname)
os.remove(fullpath)
if toupdate:
print(('Updating {} icons...'.format(len(toupdate))))
missing = set()
for fname in sorted(toupdate):
icon = get_icon_file(fname)
if icon is None:
missing.add(fname)
continue
fullname = '{}.png'.format(fname)
fullpath = os.path.join(icons_dir, fullname)
icon.save(fullpath, 'png')
if missing:
print((' {} icons are missing in export:'.format(len(missing))))
for fname in sorted(missing):
print((' {}'.format(fname)))
if toadd:
print(('Adding {} icons...'.format(len(toadd))))
missing = set()
for fname in sorted(toadd):
icon = icon_json.get(str(fname), None)
if icon is None:
print("Can't find iconID {}".format(fname))
continue
key = icon['iconFile'].lower()
for i in range(2):
scale = i+1
icon = get_icon_file(key, tuple([x*scale for x in ICON_SIZE]))
if icon is None:
missing.add(fname)
continue
fullname = '{}@{}x.png'.format(fname, scale)
fullpath = os.path.join(icons_dir, fullname)
icon.save(fullpath, 'png')
if missing:
print((' {} icons are missing in export:'.format(len(missing))))
for fname in sorted(missing):
print((' {}'.format(fname)))
print("Doing renders")
needed.clear()
existing.clear()
toremove.clear()
for row in cursor.execute(query_ships):
needed.add(row[0])
toremove = existing.difference(needed)
toupdate = existing.intersection(needed)
toadd = needed.difference(existing)
if toadd:
print(('Adding {} renders...'.format(len(toadd))))
missing = set()
for fname in sorted(toadd):
try:
key = graphics_py_ob[int(fname)]
except KeyError:
print("Can't find graphicID {}".format(fname))
key = "{}/{}_64.png".format(key, fname)
for i in range(2):
scale = i+1
icon = get_icon_file(key, tuple([x*scale for x in RENDER_SIZE]))
if icon is None:
missing.add(fname)
continue
fullname = '{}@{}x.png'.format(fname, scale)
fullpath = os.path.join(render_dir, fullname)
icon.save(fullpath, 'png')
if missing:
print((' {} renders are missing in export:'.format(len(missing))))
for fname in sorted(missing):
print((' {}'.format(fname)))