mirror of
https://github.com/isledecomp/isle-portable.git
synced 2024-11-22 15:37:55 -05:00
generate progress SVGs
This commit is contained in:
parent
24ec7023bd
commit
b080766321
5 changed files with 137 additions and 3 deletions
6
.github/workflows/build.yml
vendored
6
.github/workflows/build.yml
vendored
|
@ -61,8 +61,8 @@ jobs:
|
||||||
C:\msys64\usr\bin\wget.exe https://legoisland.org/download/LEGO1.DLL
|
C:\msys64\usr\bin\wget.exe https://legoisland.org/download/LEGO1.DLL
|
||||||
pip install capstone
|
pip install capstone
|
||||||
pip install colorama
|
pip install colorama
|
||||||
python3 tools/reccmp/reccmp.py -H ISLEPROGRESS.HTML ISLE.EXE Release/ISLE.EXE Release/ISLE.PDB .
|
python3 tools/reccmp/reccmp.py -S ISLEPROGRESS.SVG --svg-icon tools/reccmp/isle.png -H ISLEPROGRESS.HTML ISLE.EXE Release/ISLE.EXE Release/ISLE.PDB .
|
||||||
python3 tools/reccmp/reccmp.py -H LEGO1PROGRESS.HTML LEGO1.DLL Release/LEGO1.DLL Release/LEGO1.PDB .
|
python3 tools/reccmp/reccmp.py -S LEGO1PROGRESS.SVG -T 1929 --svg-icon tools/reccmp/lego1.png -H LEGO1PROGRESS.HTML LEGO1.DLL Release/LEGO1.DLL Release/LEGO1.PDB .
|
||||||
|
|
||||||
- name: Upload Artifact
|
- name: Upload Artifact
|
||||||
uses: actions/upload-artifact@master
|
uses: actions/upload-artifact@master
|
||||||
|
@ -72,3 +72,5 @@ jobs:
|
||||||
Release
|
Release
|
||||||
ISLEPROGRESS.HTML
|
ISLEPROGRESS.HTML
|
||||||
LEGO1PROGRESS.HTML
|
LEGO1PROGRESS.HTML
|
||||||
|
ISLEPROGRESS.SVG
|
||||||
|
LEGO1PROGRESS.SVG
|
||||||
|
|
BIN
tools/reccmp/isle.png
Normal file
BIN
tools/reccmp/isle.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.3 KiB |
BIN
tools/reccmp/lego1.png
Executable file
BIN
tools/reccmp/lego1.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 5.5 KiB |
|
@ -1,6 +1,7 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
|
import base64
|
||||||
from capstone import *
|
from capstone import *
|
||||||
import difflib
|
import difflib
|
||||||
import struct
|
import struct
|
||||||
|
@ -15,9 +16,12 @@
|
||||||
parser.add_argument('recompiled', metavar='recompiled-binary', help='The recompiled binary')
|
parser.add_argument('recompiled', metavar='recompiled-binary', help='The recompiled binary')
|
||||||
parser.add_argument('pdb', metavar='recompiled-pdb', help='The PDB of the recompiled binary')
|
parser.add_argument('pdb', metavar='recompiled-pdb', help='The PDB of the recompiled binary')
|
||||||
parser.add_argument('decomp_dir', metavar='decomp-dir', help='The decompiled source tree')
|
parser.add_argument('decomp_dir', metavar='decomp-dir', help='The decompiled source tree')
|
||||||
|
parser.add_argument('--total', '-T', metavar='total-func-count', help='Total number of expected functions (improves total accuracy statistic)')
|
||||||
parser.add_argument('--verbose', '-v', metavar='offset', help='Print assembly diff for specific function (original file\'s offset)')
|
parser.add_argument('--verbose', '-v', metavar='offset', help='Print assembly diff for specific function (original file\'s offset)')
|
||||||
parser.add_argument('--html', '-H', metavar='output-file', help='Generate searchable HTML summary of status and diffs')
|
parser.add_argument('--html', '-H', metavar='output-file', help='Generate searchable HTML summary of status and diffs')
|
||||||
parser.add_argument('--no-color', '-n', action='store_true', help='Do not color the output')
|
parser.add_argument('--no-color', '-n', action='store_true', help='Do not color the output')
|
||||||
|
parser.add_argument('--svg', '-S', metavar='output-svg', help='Generate SVG graphic of progress')
|
||||||
|
parser.add_argument('--svg-icon', metavar='svg-icon', help='Icon to use in SVG (PNG)')
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
@ -50,6 +54,8 @@
|
||||||
if not os.path.isdir(source):
|
if not os.path.isdir(source):
|
||||||
parser.error('Source directory does not exist')
|
parser.error('Source directory does not exist')
|
||||||
|
|
||||||
|
svg = args.svg
|
||||||
|
|
||||||
# Declare a class that can automatically convert virtual executable addresses
|
# Declare a class that can automatically convert virtual executable addresses
|
||||||
# to file addresses
|
# to file addresses
|
||||||
class Bin:
|
class Bin:
|
||||||
|
@ -365,7 +371,8 @@ def parse_asm(file, addr, size):
|
||||||
|
|
||||||
# If html, record the diffs to an HTML file
|
# If html, record the diffs to an HTML file
|
||||||
if html:
|
if html:
|
||||||
htmlinsert.append('{address: "%s", name: "%s", matching: %s, diff: "%s"}' % (hex(addr), recinfo.name, str(ratio), '\\n'.join(udiff).replace('"', '\\"').replace('\n', '\\n')))
|
escaped = '\\n'.join(udiff).replace('"', '\\"').replace('\n', '\\n').replace('<', '<').replace('>', '>')
|
||||||
|
htmlinsert.append('{address: "%s", name: "%s", matching: %s, diff: "%s"}' % (hex(addr), recinfo.name, str(ratio), escape))
|
||||||
|
|
||||||
except UnicodeDecodeError:
|
except UnicodeDecodeError:
|
||||||
break
|
break
|
||||||
|
@ -389,6 +396,49 @@ def gen_html(html, data):
|
||||||
htmlfile.write(templatedata)
|
htmlfile.write(templatedata)
|
||||||
htmlfile.close()
|
htmlfile.close()
|
||||||
|
|
||||||
|
def gen_svg(svg, name, icon, implemented_funcs, total_funcs, raw_accuracy):
|
||||||
|
templatefile = open(get_file_in_script_dir('template.svg'), 'r')
|
||||||
|
if not templatefile:
|
||||||
|
print('Failed to find SVG template file, can\'t generate SVG summary')
|
||||||
|
return
|
||||||
|
|
||||||
|
templatedata = templatefile.read()
|
||||||
|
templatefile.close()
|
||||||
|
|
||||||
|
# Replace icon
|
||||||
|
if args.svg_icon:
|
||||||
|
iconfile = open(args.svg_icon, 'rb')
|
||||||
|
templatedata = templatedata.replace('{icon}', base64.b64encode(iconfile.read()).decode('utf-8'), 1)
|
||||||
|
iconfile.close()
|
||||||
|
|
||||||
|
# Replace name
|
||||||
|
templatedata = templatedata.replace('{name}', name, 1)
|
||||||
|
|
||||||
|
# Replace implemented statistic
|
||||||
|
templatedata = templatedata.replace('{implemented}', '%.2f%% (%i/%i)' % (implemented_funcs / total_funcs * 100, implemented_funcs, total_funcs), 1)
|
||||||
|
|
||||||
|
# Replace accuracy statistic
|
||||||
|
templatedata = templatedata.replace('{accuracy}', '%.2f%%' % (raw_accuracy / implemented_funcs * 100), 1)
|
||||||
|
|
||||||
|
# Generate progress bar width
|
||||||
|
total_statistic = raw_accuracy / total_funcs
|
||||||
|
percenttemplate = '{progbar'
|
||||||
|
percentstart = templatedata.index(percenttemplate)
|
||||||
|
percentend = templatedata.index('}', percentstart)
|
||||||
|
progwidth = float(templatedata[percentstart + len(percenttemplate) + 1:percentend]) * total_statistic
|
||||||
|
templatedata = templatedata[0:percentstart] + str(progwidth) + templatedata[percentend + 1:]
|
||||||
|
|
||||||
|
# Replace percentage statistic
|
||||||
|
templatedata = templatedata.replace('{percent}', '%.2f%%' % (total_statistic * 100), 1)
|
||||||
|
|
||||||
|
svgfile = open(svg, 'w')
|
||||||
|
if not svgfile:
|
||||||
|
print('Failed to write to SVG file %s' % svg)
|
||||||
|
return
|
||||||
|
|
||||||
|
svgfile.write(templatedata)
|
||||||
|
svgfile.close()
|
||||||
|
|
||||||
if html:
|
if html:
|
||||||
gen_html(html, htmlinsert)
|
gen_html(html, htmlinsert)
|
||||||
|
|
||||||
|
@ -396,5 +446,13 @@ def gen_html(html, data):
|
||||||
if not found_verbose_target:
|
if not found_verbose_target:
|
||||||
print('Failed to find the function with address %s' % hex(verbose))
|
print('Failed to find the function with address %s' % hex(verbose))
|
||||||
else:
|
else:
|
||||||
|
implemented_funcs = function_count
|
||||||
|
|
||||||
|
if args.total:
|
||||||
|
function_count = int(args.total)
|
||||||
|
|
||||||
if function_count > 0:
|
if function_count > 0:
|
||||||
print('\nTotal accuracy %.2f%% across %i functions' % (total_accuracy / function_count * 100, function_count))
|
print('\nTotal accuracy %.2f%% across %i functions' % (total_accuracy / function_count * 100, function_count))
|
||||||
|
|
||||||
|
if svg:
|
||||||
|
gen_svg(svg, os.path.basename(original), args.svg_icon, implemented_funcs, function_count, total_accuracy)
|
||||||
|
|
74
tools/reccmp/template.svg
Normal file
74
tools/reccmp/template.svg
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
width="640"
|
||||||
|
height="640"
|
||||||
|
viewBox="0 0 169.33333 169.33333"
|
||||||
|
version="1.1"
|
||||||
|
id="svg5"
|
||||||
|
xml:space="preserve"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"><defs
|
||||||
|
id="defs2"><rect
|
||||||
|
x="79.651809"
|
||||||
|
y="538.62568"
|
||||||
|
width="480.69626"
|
||||||
|
height="32.696293"
|
||||||
|
id="rect1277" /></defs><g
|
||||||
|
id="g1273"
|
||||||
|
transform="translate(-3.9577451e-7,-0.93505135)"><image
|
||||||
|
width="84.666664"
|
||||||
|
height="84.666664"
|
||||||
|
preserveAspectRatio="none"
|
||||||
|
style="image-rendering:optimizeSpeed"
|
||||||
|
xlink:href="data:image/png;base64,{icon}"
|
||||||
|
id="image1060"
|
||||||
|
x="42.333336"
|
||||||
|
y="20.367643" /><text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:12.7px;font-family:mono;-inkscape-font-specification:mono;text-align:center;text-anchor:middle;fill:#ffffff;stroke:#ffffff;stroke-width:2.64583"
|
||||||
|
x="84.476158"
|
||||||
|
y="116.82081"
|
||||||
|
id="text740"><tspan
|
||||||
|
id="tspan738"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:mono;-inkscape-font-specification:mono;text-align:center;text-anchor:middle;stroke:none;stroke-width:2.64583"
|
||||||
|
x="84.476158"
|
||||||
|
y="116.82081">{name}</tspan></text><g
|
||||||
|
id="g1250"
|
||||||
|
transform="translate(-1.3006529e-5,8.5767994)"><rect
|
||||||
|
style="fill:none;stroke:#ffffff;stroke-width:0.87411;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
id="rect1167"
|
||||||
|
width="127.18422"
|
||||||
|
height="8.6508904"
|
||||||
|
x="21.074554"
|
||||||
|
y="134.86963" /><rect
|
||||||
|
style="display:inline;fill:#ffffff;stroke:none;stroke-width:2.6764"
|
||||||
|
id="rect1169"
|
||||||
|
width="{progbar:127.18422}"
|
||||||
|
height="8.6508904"
|
||||||
|
x="21.074554"
|
||||||
|
y="134.86963" /><text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.23333px;font-family:mono;-inkscape-font-specification:mono;text-align:start;text-anchor:start;fill:#c0c0c0;fill-opacity:1;stroke:none;stroke-width:1.05833;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
x="76.841347"
|
||||||
|
y="140.70638"
|
||||||
|
id="text2152"><tspan
|
||||||
|
style="font-size:4.23333px;fill:#c0c0c0;fill-opacity:1;stroke-width:1.05833"
|
||||||
|
x="76.841347"
|
||||||
|
y="140.70638"
|
||||||
|
id="tspan2150">{percent}</tspan></text></g><text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.23333px;font-family:mono;-inkscape-font-specification:mono;text-align:start;text-anchor:start;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1.05833;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
x="46.947659"
|
||||||
|
y="128.42285"
|
||||||
|
id="text1260"><tspan
|
||||||
|
id="tspan1258"
|
||||||
|
style="font-size:4.23333px;stroke-width:1.05833"
|
||||||
|
x="46.947659"
|
||||||
|
y="128.42285">Implemented: {implemented}</tspan><tspan
|
||||||
|
style="font-size:4.23333px;stroke-width:1.05833"
|
||||||
|
x="46.947659"
|
||||||
|
y="133.71451"
|
||||||
|
id="tspan1262">Accuracy: {accuracy}</tspan></text></g></svg>
|
After Width: | Height: | Size: 3.3 KiB |
Loading…
Reference in a new issue