NexusDashboard/app/luclient.py
2022-05-29 01:49:19 -05:00

419 lines
12 KiB
Python

from flask import (
Blueprint,
send_file,
g,
redirect,
url_for,
make_response,
abort
)
from flask_user import login_required
from app.models import CharacterInfo
from app.cdclient import Objects
import glob
import os
from wand import image
from wand.exceptions import BlobError as BE
import pathlib
import json
import sqlite3
import xml.etree.ElementTree as ET
luclient_blueprint = Blueprint('luclient', __name__)
locale = {}
@luclient_blueprint.route('/get_dds_as_png/<filename>')
@login_required
def get_dds_as_png(filename):
if filename.split('.')[-1] != 'dds':
return (404, "NO")
cache = f'cache/{filename.split(".")[0]}.png'
if not os.path.exists(cache):
root = 'app/luclient/res/'
path = glob.glob(
root + f'**/{filename}',
recursive=True
)[0]
with image.Image(filename=path) as img:
img.compression = "no"
img.save(filename='app/cache/' + filename.split('.')[0] + '.png')
return send_file(cache)
@luclient_blueprint.route('/get_dds/<filename>')
@login_required
def get_dds(filename):
if filename.split('.')[-1] != 'dds':
return 404
root = 'app/luclient/res/'
dds = glob.glob(
root + f'**/{filename}',
recursive=True
)[0]
return send_file(dds)
@luclient_blueprint.route('/get_icon_lot/<id>')
@login_required
def get_icon_lot(id):
render_component_id = query_cdclient(
'select component_id from ComponentsRegistry where component_type = 2 and id = ?',
[id],
one=True
)[0]
# find the asset from rendercomponent given the component id
filename = query_cdclient(
'select icon_asset from RenderComponent where id = ?',
[render_component_id],
one=True
)[0]
if filename:
filename = filename.replace("..\\", "").replace("\\", "/")
else:
return redirect(url_for('luclient.unknown'))
cache = f'app/cache/{filename.split(".")[0]}.png'
if not os.path.exists(cache):
root = 'app/luclient/res/'
try:
pathlib.Path(os.path.dirname(cache)).resolve().mkdir(parents=True, exist_ok=True)
with image.Image(filename=f'{root}{filename}'.lower()) as img:
img.compression = "no"
img.save(filename=cache)
except BE:
return redirect(url_for('luclient.unknown'))
return send_file(pathlib.Path(cache).resolve())
@luclient_blueprint.route('/get_icon_iconid/<id>')
@login_required
def get_icon_iconid(id):
filename = query_cdclient(
'select IconPath from Icons where IconID = ?',
[id],
one=True
)[0]
filename = filename.replace("..\\", "").replace("\\", "/")
cache = f'app/cache/{filename.split(".")[0]}.png'
if not os.path.exists(cache):
root = 'app/luclient/res/'
try:
pathlib.Path(os.path.dirname(cache)).resolve().mkdir(parents=True, exist_ok=True)
with image.Image(filename=f'{root}{filename}'.lower()) as img:
img.compression = "no"
img.save(filename=cache)
except BE:
return redirect(url_for('luclient.unknown'))
return send_file(pathlib.Path(cache).resolve())
@luclient_blueprint.route('/ldddb/')
@login_required
def brick_list():
brick_list = []
if len(brick_list) == 0:
suffixes = [".g", ".g1", ".g2", ".g3", ".xml"]
res = pathlib.Path('app/luclient/res/')
# Load g files
for path in res.rglob("*.*"):
if str(path.suffix) in suffixes:
brick_list.append(
{
"type": "file",
"name": str(path.as_posix()).replace("app/luclient/res/", "")
}
)
response = make_response(json.dumps(brick_list))
response.headers.set('Content-Type', 'application/json')
return response
@luclient_blueprint.route('/ldddb/', defaults={'req_path': ''})
@luclient_blueprint.route('/ldddb/<path:req_path>')
def dir_listing(req_path):
# Joining the base and the requested path
rel_path = pathlib.Path(str(pathlib.Path(f'app/luclient/res/{req_path}').resolve()))
# Return 404 if path doesn't exist
if not rel_path.exists():
return abort(404)
# Check if path is a file and serve
if rel_path.is_file():
return send_file(rel_path)
else:
return abort(404)
@luclient_blueprint.route('/unknown')
@login_required
def unknown():
filename = "textures/ui/inventory/unknown.dds"
cache = f'app/cache/{filename.split(".")[0]}.png'
if not os.path.exists(cache):
root = 'app/luclient/res/'
try:
pathlib.Path(os.path.dirname(cache)).resolve().mkdir(parents=True, exist_ok=True)
with image.Image(filename=f'{root}{filename}'.lower()) as img:
img.compression = "no"
img.save(filename=cache)
except BE:
return redirect(url_for('luclient.unknown'))
return send_file(pathlib.Path(cache).resolve())
def get_cdclient():
"""Connect to CDClient from file system Relative Path
Args:
None
"""
cdclient = getattr(g, '_cdclient', None)
if cdclient is None:
cdclient = g._database = sqlite3.connect('app/luclient/res/cdclient.sqlite')
return cdclient
def query_cdclient(query, args=(), one=False):
"""Run sql queries on CDClient
Args:
query (string) : SQL query
args (list) : List of args to place in query
one (bool) : Return only on result or all results
"""
cur = get_cdclient().execute(query, args)
rv = cur.fetchall()
cur.close()
return (rv[0] if rv else None) if one else rv
def translate_from_locale(trans_string):
"""Finds the string translation from locale.xml
Args:
trans_string (string) : ID to find translation
"""
if not trans_string:
return "INVALID STRING"
global locale
locale_data = ""
if not locale:
locale_path = "app/luclient/locale/locale.xml"
with open(locale_path, 'r') as file:
locale_data = file.read()
locale_xml = ET.XML(locale_data)
for item in locale_xml.findall('.//phrase'):
translation = ""
for translation_item in item.findall('.//translation'):
if translation_item.attrib["locale"] == "en_US":
translation = translation_item.text
locale[item.attrib['id']] = translation
if trans_string in locale:
return locale[trans_string]
else:
return trans_string
def get_lot_name(lot_id):
if not lot_id:
return "Missing"
name = translate_from_locale(f'Objects_{lot_id}_name')
if name == f'Objects_{lot_id}_name':
intermed = Objects.query.filter(Objects.id == lot_id).first()
if intermed:
name = intermed.displayName if (intermed.displayName != "None" and intermed.displayName != "" and intermed.displayName is None) else intermed.name
return name
def register_luclient_jinja_helpers(app):
@app.template_filter('get_zone_name')
def get_zone_name(zone_id):
if not zone_id:
return "Missing"
return translate_from_locale(f'ZoneTable_{zone_id}_DisplayDescription')
@app.template_filter('get_skill_desc')
def get_skill_desc(skill_id):
return translate_from_locale(
f'SkillBehavior_{skill_id}_descriptionUI'
).replace(
"%(DamageCombo)", "Damage Combo: "
).replace(
"%(AltCombo)", "<br/>Skeleton Combo: "
).replace(
"%(Description)", "<br/>"
).replace(
"%(ChargeUp)", "<br/>Charge-up: "
)
@app.template_filter('parse_lzid')
def parse_lzid(lzid):
return[
(int(lzid) & ((1 << 16) - 1)),
((int(lzid) >> 16) & ((1 << 16) - 1)),
((int(lzid) >> 32) & ((1 << 30) - 1))
]
@app.template_filter('parse_other_player_id')
def parse_other_player_id(other_player_id):
char_id = (int(other_player_id) & 0xFFFFFFFF)
character = CharacterInfo.query.filter(CharacterInfo.id == char_id).first()
if character:
return[character.id, character.name]
else:
return None
@app.template_filter('get_lot_name')
def jinja_get_lot_name(lot_id):
return get_lot_name(lot_id)
@app.template_filter('get_lot_rarity')
def get_lot_rarity(lot_id):
if not lot_id:
return "Missing"
render_component_id = query_cdclient(
'select component_id from ComponentsRegistry where component_type = 11 and id = ?',
[lot_id],
one=True
)[0]
rarity = query_cdclient(
'select rarity from ItemComponent where id = ?',
[render_component_id],
one=True
)
if rarity:
rarity = rarity[0]
return rarity
@app.template_filter('get_lot_desc')
def get_lot_desc(lot_id):
if not lot_id:
return "Missing"
desc = translate_from_locale(f'Objects_{lot_id}_description')
if desc == f'Objects_{lot_id}_description':
desc = query_cdclient(
'select description from Objects where id = ?',
[lot_id],
one=True
)
if desc in ("", None):
desc = None
else:
desc = desc[0]
if desc in ("", None):
desc = None
if desc:
desc = desc.replace('"', "&#8220;")
return desc
@app.template_filter('get_item_set')
def check_if_in_set(lot_id):
if not lot_id:
return None
item_set = query_cdclient(
'select * from ItemSets where itemIDs like ? or itemIDs like ? or itemIDs like ?',
[f'{lot_id}%', f'%, {lot_id}%', f'%,{lot_id}%'],
one=True
)
if item_set in ("", None):
return None
else:
return item_set
@app.template_filter('get_lot_stats')
def get_lot_stats(lot_id):
if not lot_id:
return None
stats = query_cdclient(
'SELECT imBonusUI, lifeBonusUI, armorBonusUI, skillID, skillIcon FROM SkillBehavior WHERE skillID IN (\
SELECT skillID FROM ObjectSkills WHERE objectTemplate=?\
)',
[lot_id]
)
return consolidate_stats(stats)
@app.template_filter('get_set_stats')
def get_set_stats(lot_id):
if not lot_id:
return "Missing"
stats = query_cdclient(
'SELECT imBonusUI, lifeBonusUI, armorBonusUI, skillID, skillIcon FROM SkillBehavior WHERE skillID IN (\
SELECT skillID FROM ItemSetSkills WHERE SkillSetID=?\
)',
[lot_id]
)
return consolidate_stats(stats)
@app.template_filter('query_cdclient')
def jinja_query_cdclient(query, items):
print(query, items)
return query_cdclient(
query,
items,
one=True
)[0]
@app.template_filter('lu_translate')
def lu_translate(to_translate):
return translate_from_locale(to_translate)
def consolidate_stats(stats):
if len(stats) > 1:
consolidated_stats = {"im": 0, "life": 0, "armor": 0, "skill": []}
for stat in stats:
if stat[0]:
consolidated_stats["im"] += stat[0]
if stat[1]:
consolidated_stats["life"] += stat[1]
if stat[2]:
consolidated_stats["armor"] += stat[2]
if stat[3]:
consolidated_stats["skill"].append([stat[3], stat[4]])
stats = consolidated_stats
elif len(stats) == 1:
stats = {
"im": stats[0][0] if stats[0][0] else 0,
"life": stats[0][1] if stats[0][1] else 0,
"armor": stats[0][2] if stats[0][2] else 0,
"skill": [[stats[0][3], stats[0][4]]] if stats[0][3] else None,
}
else:
stats = None
return stats