refactor: group scripts into walker/ wikigen/ bundle/
Organize the 16 loose scripts by concern:
walker/ -- .wbt save tooling (sand, build_wbt, walker_hashes,
harvest_hashes, recover_key)
wikigen/ -- MediaWiki page generators (make_*_wiki, render_wiki)
bundle/ -- Unity/Odin asset extraction (unitybundle, odin_read,
extract_*, loot_probe, dump_loot_bytes)
The only cross-script imports (build_wbt->walker_hashes,
extract_loot->odin_read) live within the same folder, so each
script's dir on sys.path[0] keeps them resolving with no code
changes. All data paths are absolute, so the moves don't affect
I/O. Named the code dir wikigen/ to avoid colliding with the
generated wiki/ output dir; ignore the regenerable wiki_site/ render.
This commit is contained in:
126
wikigen/render_wiki.py
Normal file
126
wikigen/render_wiki.py
Normal file
@@ -0,0 +1,126 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Render the generated MediaWiki crafting page to a standalone HTML file with
|
||||
MediaWiki (Vector skin) styling, so it can be eyeballed locally. Handles exactly
|
||||
the subset of wikitext our generator emits: headings, bold/italic, [[links]],
|
||||
{| wikitable sortable |}, * lists, <code>, __TOC__, [[Category:]].
|
||||
"""
|
||||
import re, sys, html, pathlib
|
||||
|
||||
SRC = pathlib.Path('/home/downloadpizza/sand_tools/wiki/Crafting.mediawiki')
|
||||
OUT = pathlib.Path.home() / 'sand_tools' / 'wiki_site' / 'index.html'
|
||||
|
||||
def inline(t):
|
||||
# links: [[Target]] or [[Target|Label]]
|
||||
def lk(m):
|
||||
tgt = m.group(1); lab = m.group(2) or tgt
|
||||
return f'<a class="wlink" href="#">{html.escape(lab)}</a>'
|
||||
t = re.sub(r'\[\[([^\]|]+)(?:\|([^\]]+))?\]\]', lk, t)
|
||||
t = re.sub(r"'''(.+?)'''", r'<strong>\1</strong>', t)
|
||||
t = re.sub(r"''(.+?)''", r'<em>\1</em>', t)
|
||||
return t # note: × / <code> already valid HTML, pass through
|
||||
|
||||
def render(wt):
|
||||
out = []
|
||||
cats = []
|
||||
lines = wt.splitlines()
|
||||
i = 0
|
||||
while i < len(lines):
|
||||
ln = lines[i]
|
||||
if ln.strip() == '__TOC__':
|
||||
i += 1; continue
|
||||
m = re.match(r'\[\[Category:([^\]]+)\]\]', ln.strip())
|
||||
if m:
|
||||
cats.append(m.group(1)); i += 1; continue
|
||||
h = re.match(r'^(=+)\s*(.*?)\s*=+\s*$', ln)
|
||||
if h:
|
||||
lvl = len(h.group(1))
|
||||
out.append(f'<h{lvl}>{inline(h.group(2))}</h{lvl}>')
|
||||
i += 1; continue
|
||||
if ln.startswith('{|'): # table
|
||||
cls = 'wikitable'
|
||||
if 'sortable' in ln: cls += ' sortable'
|
||||
out.append(f'<table class="{cls}">')
|
||||
i += 1
|
||||
while i < len(lines) and not lines[i].startswith('|}'):
|
||||
row = lines[i]
|
||||
if row.startswith('!'):
|
||||
cells = row[1:].split('!!')
|
||||
out.append('<tr>' + ''.join(
|
||||
f'<th>{inline(_celltext(c))}</th>' for c in cells) + '</tr>')
|
||||
elif row.startswith('|-'):
|
||||
pass
|
||||
elif row.startswith('|'):
|
||||
cells = row[1:].split('||')
|
||||
out.append('<tr>' + ''.join(
|
||||
f'<td>{inline(_celltext(c))}</td>' for c in cells) + '</tr>')
|
||||
i += 1
|
||||
out.append('</table>')
|
||||
i += 1; continue
|
||||
if ln.startswith('* '):
|
||||
items = []
|
||||
while i < len(lines) and lines[i].startswith('* '):
|
||||
items.append(f'<li>{inline(lines[i][2:])}</li>'); i += 1
|
||||
out.append('<ul>' + ''.join(items) + '</ul>'); continue
|
||||
if ln.strip() == '':
|
||||
i += 1; continue
|
||||
out.append(f'<p>{inline(ln)}</p>')
|
||||
i += 1
|
||||
if cats:
|
||||
out.append('<div class="catlinks"><strong>Categories</strong>: ' +
|
||||
' | '.join(f'<a class="wlink" href="#">{html.escape(c)}</a>' for c in cats) +
|
||||
'</div>')
|
||||
return '\n'.join(out)
|
||||
|
||||
def _celltext(c):
|
||||
# strip optional "attr=... | text" cell prefix
|
||||
if '|' in c and not c.strip().startswith('['):
|
||||
# only split on a | that's an attribute separator (before any [[ )
|
||||
pre, _, post = c.partition('|')
|
||||
if '[[' not in pre:
|
||||
return post.strip()
|
||||
return c.strip()
|
||||
|
||||
PAGE = """<!doctype html><html><head><meta charset="utf-8">
|
||||
<title>{title} - SAND Wiki (local test)</title>
|
||||
<style>
|
||||
body{{font-family:sans-serif;background:#f6f6f6;margin:0;color:#202122}}
|
||||
.mw-body{{max-width:60em;margin:1em auto;background:#fff;border:1px solid #a2a9b1;padding:1.5em 2em}}
|
||||
h1{{font-family:'Linux Libertine',Georgia,serif;font-weight:normal;border-bottom:1px solid #a2a9b1;padding-bottom:.2em;margin-top:0}}
|
||||
h2{{font-family:'Linux Libertine',Georgia,serif;font-weight:normal;border-bottom:1px solid #a2a9b1;padding-bottom:.2em;margin-top:1.2em}}
|
||||
table.wikitable{{background:#f8f9fa;border:1px solid #a2a9b1;border-collapse:collapse;margin:1em 0}}
|
||||
table.wikitable>tr>th,table.wikitable>tr>td,table.wikitable th,table.wikitable td{{border:1px solid #a2a9b1;padding:.3em .6em}}
|
||||
table.wikitable th{{background:#eaecf0;text-align:center}}
|
||||
table.sortable th{{cursor:pointer}}
|
||||
table.sortable th::after{{content:" \\2195";color:#888;font-size:.8em}}
|
||||
a.wlink{{color:#0645ad;text-decoration:none}}
|
||||
a.wlink:hover{{text-decoration:underline}}
|
||||
.catlinks{{border:1px solid #a2a9b1;background:#f8f9fa;padding:.4em .8em;margin-top:1.5em;font-size:.9em}}
|
||||
.note{{background:#fef6e7;border:1px solid #fc3;padding:.4em .8em;font-size:.85em;margin-bottom:1em}}
|
||||
</style></head><body><div class="mw-body">
|
||||
<div class="note">Local render for testing — not a live MediaWiki. Links are inert; sorting is basic JS.</div>
|
||||
<h1>{title}</h1>
|
||||
{body}
|
||||
</div>
|
||||
<script>
|
||||
document.querySelectorAll('table.sortable').forEach(function(t){{
|
||||
t.querySelectorAll('th').forEach(function(th,ci){{
|
||||
th.addEventListener('click',function(){{
|
||||
var rows=Array.from(t.querySelectorAll('tr')).slice(1);
|
||||
var num=th.textContent.match(/Time/);
|
||||
var dir=th._d=!th._d;
|
||||
rows.sort(function(a,b){{
|
||||
var x=a.cells[ci].textContent.trim(),y=b.cells[ci].textContent.trim();
|
||||
if(num){{x=parseFloat(x)||0;y=parseFloat(y)||0;return dir?x-y:y-x;}}
|
||||
return dir?x.localeCompare(y):y.localeCompare(x);
|
||||
}});
|
||||
rows.forEach(function(r){{t.appendChild(r);}});
|
||||
}});
|
||||
}});
|
||||
}});
|
||||
</script></body></html>"""
|
||||
|
||||
if __name__ == '__main__':
|
||||
wt = SRC.read_text()
|
||||
OUT.parent.mkdir(parents=True, exist_ok=True)
|
||||
OUT.write_text(PAGE.format(title='Crafting', body=render(wt)))
|
||||
print(f"wrote {OUT}")
|
||||
Reference in New Issue
Block a user