diff --git a/.github/workflows/deploy-pages.yml b/.github/workflows/deploy-pages.yml
index f843e68..63b5c08 100644
--- a/.github/workflows/deploy-pages.yml
+++ b/.github/workflows/deploy-pages.yml
@@ -1,6 +1,7 @@
name: Deploy Pages
on:
+ workflow_dispatch:
workflow_call:
inputs:
artifact_pattern:
@@ -15,11 +16,17 @@ jobs:
deploy:
runs-on: windows-latest
steps:
+ - uses: actions/checkout@v6
+
- uses: actions/setup-python@v6
with:
python-version: '3.x'
+ - name: Install build dependencies
+ run: python -m pip install -r requirements-core.txt
+
- name: Download emulator results
+ if: ${{ inputs.artifact_pattern != '' }}
uses: actions/download-artifact@v5
with:
pattern: ${{ inputs.artifact_pattern }}
@@ -37,10 +44,12 @@ jobs:
} else {
Write-Host "No emulator JSON artifacts found to publish."
}
+ python main.py --dump-emulators-json --dump-tests-json
+ Copy-Item emulators.json, tests.json -Destination pages -Force
+ python build.py --results-dir pages --emulators pages/emulators.json --tests pages/tests.json --output pages/index.html
+ Remove-Item pages\build.py -ErrorAction SilentlyContinue
Set-Location pages
- python build.py
- git add *.json
- git add index.html
+ git add -A
if (git diff --cached --quiet) {
Write-Host "No gh-pages changes to commit."
exit 0
diff --git a/build.py b/build.py
new file mode 100644
index 0000000..bcdf7c9
--- /dev/null
+++ b/build.py
@@ -0,0 +1,67 @@
+import argparse
+import json
+import os
+from time import gmtime, strftime
+
+
+def main():
+ parser = argparse.ArgumentParser()
+ parser.add_argument('--emulators', default='emulators.json')
+ parser.add_argument('--tests', default='tests.json')
+ parser.add_argument('--results-dir', default='.')
+ parser.add_argument('--output', default='index.html')
+ args = parser.parse_args()
+
+ emulators = json.load(open(args.emulators, 'rt', encoding='utf-8'))
+ tests = json.load(open(args.tests, 'rt', encoding='utf-8'))
+
+ for name in emulators:
+ result_file = os.path.join(args.results_dir, emulators[name]['file'])
+ if os.path.exists(result_file):
+ data = json.load(open(result_file, 'rt', encoding='utf-8'))
+ emulators[name].update(data)
+ emulators[name]['passed'] = len([result for result in data['tests'].values() if result['result'] != 'FAIL'])
+ else:
+ emulators[name].update({'passed': 0, 'tests': {}})
+
+ f = open(args.output, 'wt', encoding='utf-8')
+ f.write("""
+
\n""")
+ f.write("Updated On " + strftime("%a, %d %b %Y %H:%M:%S +0000", gmtime()) + " | \n")
+ for name, emulator in sorted(emulators.items(), key=lambda n: -n[1]['passed']):
+ f.write(" %s (%d/%d) | \n" % (emulator['url'], name, emulator['passed'], len(emulator['tests'])))
+ f.write("
\n")
+ for test in tests:
+ name = test['name'].replace("/", "/")
+ if test['url']:
+ name = "%s" % (test['url'], name)
+ if test['description']:
+ name += "%s" % (test['description'])
+ f.write("| %s | \n" % (name))
+ for name, emulator in sorted(emulators.items(), key=lambda n: -n[1]['passed']):
+ result = emulator['tests'].get(test['name'])
+ if result:
+ f.write(" %s
 | \n" % (result['result'], result['result'], result['screenshot']))
+ else:
+ f.write(" No result | \n")
+ f.write("
\n")
+ f.write("
")
+
+
+if __name__ == '__main__':
+ main()
\ No newline at end of file
diff --git a/main.py b/main.py
index 7ab4ec2..2b06ab7 100644
--- a/main.py
+++ b/main.py
@@ -53,27 +53,119 @@ def _new_instance(module_name, class_name):
return getattr(module, class_name)()
-def load_emulators(filter_data):
- emulator_factories = [
- (lambda: _new_instance("emulators.bdm", "BDM"), ["Beaten Dying Moon", "bdm", "beaten"]),
- (lambda: _new_instance("emulators.mgba", "MGBA"), ["mGBA", "mgba"]),
- (lambda: _new_instance("emulators.kigb", "KiGB"), ["KiGB", "kigb"]),
- (lambda: _new_instance("emulators.sameboy", "SameBoy"), ["SameBoy", "sameboy"]),
- (lambda: _new_instance("emulators.bgb", "BGB"), ["bgb"]),
- (lambda: _new_instance("emulators.vba", "VBA"), ["VisualBoyAdvance", "vba"]),
- (lambda: _new_instance("emulators.vba", "VBAM"), ["VisualBoyAdvance-M", "vba-m", "vbam"]),
- (lambda: _new_instance("emulators.nocash", "NoCash"), ["No$gmb", "nocash", "no$gmb"]),
- (lambda: _new_instance("emulators.gambatte", "GambatteSpeedrun"), ["GambatteSpeedrun", "gambatte"]),
- (lambda: _new_instance("emulators.emulicious", "Emulicious"), ["Emulicious", "emulicious"]),
- (lambda: _new_instance("emulators.goomba", "Goomba"), ["Goomba", "goomba"]),
- (lambda: _new_instance("emulators.binjgb", "Binjgb"), ["binjgb"]),
- (lambda: _new_instance("emulators.pyboy", "PyBoy"), ["PyBoy", "pyboy"]),
- (lambda: _new_instance("emulators.ares", "Ares"), ["ares"]),
- (lambda: _new_instance("emulators.emmy", "Emmy"), ["Emmy", "emmy"]),
- (lambda: _new_instance("emulators.gameroy", "GameRoy"), ["gameroy", "GameRoy"]),
+EMULATOR_SPECS = [
+ {
+ 'factory': lambda: _new_instance("emulators.bdm", "BDM"),
+ 'keywords': ["Beaten Dying Moon", "bdm", "beaten"],
+ 'name': "Beaten Dying Moon",
+ 'url': "https://mattcurrie.com/bdm-demo/",
+ },
+ {
+ 'factory': lambda: _new_instance("emulators.mgba", "MGBA"),
+ 'keywords': ["mGBA", "mgba"],
+ 'name': "mGBA",
+ 'url': "https://mgba.io/",
+ },
+ {
+ 'factory': lambda: _new_instance("emulators.kigb", "KiGB"),
+ 'keywords': ["KiGB", "kigb"],
+ 'name': "KiGB",
+ 'url': "http://kigb.emuunlim.com/",
+ },
+ {
+ 'factory': lambda: _new_instance("emulators.sameboy", "SameBoy"),
+ 'keywords': ["SameBoy", "sameboy"],
+ 'name': "SameBoy",
+ 'url': "https://sameboy.github.io/",
+ },
+ {
+ 'factory': lambda: _new_instance("emulators.bgb", "BGB"),
+ 'keywords': ["bgb"],
+ 'name': "bgb",
+ 'url': "https://bgb.bircd.org/",
+ },
+ {
+ 'factory': lambda: _new_instance("emulators.vba", "VBA"),
+ 'keywords': ["VisualBoyAdvance", "vba"],
+ 'name': "VisualBoyAdvance",
+ 'url': "https://sourceforge.net/projects/vba",
+ },
+ {
+ 'factory': lambda: _new_instance("emulators.vba", "VBAM"),
+ 'keywords': ["VisualBoyAdvance-M", "vba-m", "vbam"],
+ 'name': "VisualBoyAdvance-M",
+ 'url': "https://vba-m.com/",
+ },
+ {
+ 'factory': lambda: _new_instance("emulators.nocash", "NoCash"),
+ 'keywords': ["No$gmb", "nocash", "no$gmb"],
+ 'name': "No$gmb",
+ 'url': "https://problemkaputt.de/gmb.htm",
+ },
+ {
+ 'factory': lambda: _new_instance("emulators.gambatte", "GambatteSpeedrun"),
+ 'keywords': ["GambatteSpeedrun", "gambatte"],
+ 'name': "GambatteSpeedrun",
+ 'url': "https://github.com/pokemon-speedrunning/gambatte-speedrun",
+ },
+ {
+ 'factory': lambda: _new_instance("emulators.emulicious", "Emulicious"),
+ 'keywords': ["Emulicious", "emulicious"],
+ 'name': "Emulicious",
+ 'url': "https://emulicious.net/",
+ },
+ {
+ 'factory': lambda: _new_instance("emulators.goomba", "Goomba"),
+ 'keywords': ["Goomba", "goomba"],
+ 'name': "Goomba",
+ 'url': "https://www.dwedit.org/gba/goombacolor.php",
+ },
+ {
+ 'factory': lambda: _new_instance("emulators.binjgb", "Binjgb"),
+ 'keywords': ["binjgb"],
+ 'name': "binjgb",
+ 'url': "https://github.com/binji/binjgb/releases",
+ },
+ {
+ 'factory': lambda: _new_instance("emulators.pyboy", "PyBoy"),
+ 'keywords': ["PyBoy", "pyboy"],
+ 'name': "PyBoy",
+ 'url': "https://github.com/Baekalfen/PyBoy",
+ },
+ {
+ 'factory': lambda: _new_instance("emulators.ares", "Ares"),
+ 'keywords': ["ares"],
+ 'name': "ares",
+ 'url': "https://ares-emu.net/",
+ },
+ {
+ 'factory': lambda: _new_instance("emulators.emmy", "Emmy"),
+ 'keywords': ["Emmy", "emmy"],
+ 'name': "Emmy",
+ 'url': "https://emmy.n1ark.com/",
+ },
+ {
+ 'factory': lambda: _new_instance("emulators.gameroy", "GameRoy"),
+ 'keywords': ["gameroy", "GameRoy"],
+ 'name': "gameroy",
+ 'url': "https://github.com/Rodrigodd/gameroy",
+ },
+]
+
+
+def get_emulator_specs(filter_data):
+ return [
+ spec for spec in EMULATOR_SPECS
+ if _matches_emulator_filter(filter_data, spec['keywords'])
]
- return [factory() for factory, keywords in emulator_factories if _matches_emulator_filter(filter_data, keywords)]
+
+def get_emulator_json_filename(name):
+ return "%s.json" % (name.replace(" ", "_").lower())
+
+
+def load_emulators(filter_data):
+ return [spec['factory']() for spec in get_emulator_specs(filter_data)]
tests = testroms.acid.all + testroms.blargg.all + testroms.daid.all + testroms.ax6.all + testroms.mooneye.all + testroms.samesuite.all + testroms.hacktix.all + testroms.cpp.all + testroms.mealybug.all
@@ -124,26 +216,14 @@ def checkFilter(input, filter_data):
for test in tests
if checkFilter(test, args.test) and checkFilter(test.model, args.model)
]
- emulators = load_emulators(args.emulator)
-
- print("%d emulators" % (len(emulators)))
- print("%d tests" % (len(tests)))
+ emulator_specs = get_emulator_specs(args.emulator)
- if args.get_runtime:
- for emulator in emulators:
- emulator.setup()
- for test in tests:
- if not checkFilter(test, args.test):
- continue
- print("%s: %s: %g seconds" % (emulator, test, emulator.getRunTimeFor(test)))
- emulator.undoSetup()
- sys.exit()
if args.dump_emulators_json:
json.dump({
- str(emulator): {
- "file": emulator.getJsonFilename(),
- "url": emulator.url,
- } for emulator in emulators
+ spec['name']: {
+ 'file': get_emulator_json_filename(spec['name']),
+ 'url': spec['url'],
+ } for spec in emulator_specs
}, open("emulators.json", "wt"), indent=" ")
if args.dump_tests_json:
json.dump([
@@ -154,6 +234,23 @@ def checkFilter(input, filter_data):
} for test in tests
], open("tests.json", "wt"), indent=" ")
if args.dump_tests_json or args.dump_emulators_json:
+ print("%d emulators" % (len(emulator_specs)))
+ print("%d tests" % (len(tests)))
+ sys.exit()
+
+ emulators = load_emulators(args.emulator)
+
+ print("%d emulators" % (len(emulators)))
+ print("%d tests" % (len(tests)))
+
+ if args.get_runtime:
+ for emulator in emulators:
+ emulator.setup()
+ for test in tests:
+ if not checkFilter(test, args.test):
+ continue
+ print("%s: %s: %g seconds" % (emulator, test, emulator.getRunTimeFor(test)))
+ emulator.undoSetup()
sys.exit()
if args.get_startuptime: