Status for Dolphin Central
Recent events
2026-02-03T23:35:02.985655 {'source': 'logging', 'level': 'INFO', 'pathname': '/nix/store/pszlv4r8dapp8a8nw5qjpiz4q8py1bfg-central-env/lib/python3.10/site-packages/central/github/authz.py', 'lineno': 30, 'msg': 'New GH %s: %s', 'args': "('dolphin-emu/trusted-developers', 'marcan,CelestialAmber,aroulin,nullgemm,NanoByte011,mitaclaw,booto,mandar1jn,merryhime,FioraAeterna,Tilka,Sam-Belliveau,endrift,magcius,LPFaint99,moncefmechri,Stevoisiak,rlnilsen,archshift,hdcmeta,mrgreywater,iwubcode,unknownbrackets,RachelBryk,Zopolis4,lioncash,adamdmoss,SirMangler,MayImilae,comex,hthh,yourWaifu,Geotale,phire,RisingFog,AdmiralCurtiss,deReeperJosh,aldelaro5,linkmauve,glennricster,Starsam80,gwicks,smurf3tte,Ebola16,zackhow,Orphis,meffij,Gamer64ytb,JosJuice,Hydr8gon,vladfi1,DacoTaco,cristian64,PatrickFerry,mathieui,ligfx,TryTwo,JMC47,SuperSamus,EmptyChaos,rukai,zopieux,OrN,CasualPokePlayer,sepalani,ColinDTaylor,Pokechu22,neobrain,Techjar,Alcaro,kamiyo,jjdelvalle,delroth,CookiePLMonster,jloehr,Linktothepast,riking,BhaaLseN,OatmealDome,corwin-mcknight,bentley,orbea,TellowKrinkle,Filoppi,jezze,Sintendo,mimimi085181,spxtr,tygyh,galop1n,K0bin,Phatcat,Buddybenj,krnlyng,hrydgard,Parlane,nickbeth,mahdihijazi,ShimmerGlass,jordan-woyak,noahpistilli,JordanTheToaster,Simonx22,Ziek,randomstuff,mmastrac,spycrab,JoshuaVandaele,Tinob,leoetlino,CrossVR,LillyJadeKatrin,degasus,shonumi,dreamsyntax,CrystalGamma,skylersaleh,kayru,mbc07,magumagu,Dentomologist,hackbar,skidau,LAGonauta,malleoz,Helios747,crediar,Lobsterzelda')", 'type': 'internal_log'}
2026-02-03T23:35:01.945872 {'source': 'logging', 'level': 'INFO', 'pathname': '/nix/store/pszlv4r8dapp8a8nw5qjpiz4q8py1bfg-central-env/lib/python3.10/site-packages/central/github/authz.py', 'lineno': 21, 'msg': 'Refreshing list of trusted users (from %s/%s)', 'args': "('dolphin-emu', 'trusted-developers')", 'type': 'internal_log'}
2026-02-03T23:34:37.789140 {'source': 'buildbot', 'repo': 'dolphin-emu/dolphin', 'hash': '201aa659066b41a2cfe89b615213bcb093fcbe79', 'shortrev': '201aa6', 'service': 'lint', 'pr': 14318, 'success': True, 'pending': False, 'url': 'https://dolphin.ci/#/builders/9/builds/8923', 'description': 'Build succeeded on builder lint', 'type': 'build_status'}
2026-02-03T23:34:37.470745 {'source': 'webserver', 'raw': {'buildid': 157756, 'number': 8923, 'builderid': 9, 'buildrequestid': 157882, 'workerid': 10, 'masterid': 1, 'started_at': 1770161567, 'complete_at': 1770161571, 'locks_duration_s': 0, 'complete': True, 'state_string': 'build successful', 'results': 0, 'properties': {'branchname': ['pr-14318', 'Change'], 'shortrev': ['201aa6', 'Change'], 'repo': ['dolphin-emu/dolphin', 'Change'], 'scheduler': ['pr', 'Scheduler'], 'workername': ['ubuntu-lts', 'Worker'], 'project': ['', 'Build'], 'buildnumber': [8923, 'Build'], 'branch': ['refs/pull/14318/head', 'Build'], 'revision': ['201aa659066b41a2cfe89b615213bcb093fcbe79', 'Build'], 'headrev': ['201aa659066b41a2cfe89b615213bcb093fcbe79', 'Change'], 'buildername': ['lint', 'Builder'], 'owners': [['Central (on behalf of: jordan-woyak)'], 'Build'], 'baserev': ['dd2b94cd4a71fb6abda72f58d27434ef2d8b0a5f', 'Change'], 'pr_id': [14318, 'Change'], 'builderid': [9, 'Builder'], 'repository': ['', 'Build'], 'codebase': ['', 'Build'], 'basedir': ['/buildbot', 'Worker'], 'builddir': ['/buildbot/lint', 'Worker'], 'got_revision': ['201aa659066b41a2cfe89b615213bcb093fcbe79', 'GitNoBranch']}, 'buildrequest': {'buildrequestid': 157882, 'buildsetid': 36142, 'builderid': 9, 'priority': 0, 'claimed': True, 'claimed_at': 1770161567, 'claimed_by_masterid': 1, 'complete': True, 'results': 0, 'submitted_at': 1770161566, 'complete_at': 1770161571, 'waited_for': False, 'properties': None}, 'buildset': {'bsid': 36142, 'external_idstring': None, 'reason': "The AnyBranchScheduler scheduler named 'pr' triggered this build", 'submitted_at': 1770161566, 'complete': False, 'complete_at': None, 'results': -1, 'parent_buildid': None, 'parent_relationship': None, 'rebuilt_buildid': None, 'sourcestamps': [{'ssid': 10816, 'branch': 'refs/pull/14318/head', 'revision': '201aa659066b41a2cfe89b615213bcb093fcbe79', 'project': '', 'repository': '', 'codebase': '', 'created_at': 1770161566, 'patch': None}]}, 'parentbuild': None, 'parentbuilder': None, 'builder': {'builderid': 9, 'name': 'lint', 'masterids': [1], 'description': None, 'description_format': None, 'description_html': None, 'projectid': None, 'tags': []}, 'url': 'https://dolphin.ci/#/builders/9/builds/8923'}, 'type': 'raw_bb_hook'}
2026-02-03T23:34:18.255649 {'source': 'buildbot', 'repo': 'dolphin-emu/dolphin', 'hash': '201aa659066b41a2cfe89b615213bcb093fcbe79', 'shortrev': '201aa6', 'service': 'pr-ubu-x64', 'pr': 14318, 'success': False, 'pending': True, 'url': 'https://dolphin.ci/#/builders/27/builds/8923', 'description': 'Auto build in progress on builder pr-ubu-x64', 'type': 'build_status'}
2026-02-03T23:34:17.798758 {'source': 'webserver', 'raw': {'buildid': 157764, 'number': 8923, 'builderid': 27, 'buildrequestid': 157887, 'workerid': 10, 'masterid': 1, 'started_at': 1770161571, 'complete_at': None, 'locks_duration_s': 0, 'complete': False, 'state_string': 'starting', 'results': None, 'properties': {'branchname': ['pr-14318', 'Change'], 'baserev': ['dd2b94cd4a71fb6abda72f58d27434ef2d8b0a5f', 'Change'], 'headrev': ['201aa659066b41a2cfe89b615213bcb093fcbe79', 'Change'], 'shortrev': ['201aa6', 'Change'], 'pr_id': [14318, 'Change'], 'repo': ['dolphin-emu/dolphin', 'Change'], 'scheduler': ['pr', 'Scheduler'], 'buildername': ['pr-ubu-x64', 'Builder'], 'builderid': [27, 'Builder'], 'repository': ['', 'Build'], 'workername': ['ubuntu-lts', 'Worker'], 'buildnumber': [8923, 'Build'], 'branch': ['refs/pull/14318/head', 'Build'], 'revision': ['201aa659066b41a2cfe89b615213bcb093fcbe79', 'Build'], 'codebase': ['', 'Build'], 'project': ['', 'Build'], 'owners': [['Central (on behalf of: jordan-woyak)'], 'Build']}, 'buildrequest': {'buildrequestid': 157887, 'buildsetid': 36142, 'builderid': 27, 'priority': 0, 'claimed': True, 'claimed_at': 1770161571, 'claimed_by_masterid': 1, 'complete': False, 'results': -1, 'submitted_at': 1770161566, 'complete_at': None, 'waited_for': False, 'properties': None}, 'buildset': {'bsid': 36142, 'external_idstring': None, 'reason': "The AnyBranchScheduler scheduler named 'pr' triggered this build", 'submitted_at': 1770161566, 'complete': False, 'complete_at': None, 'results': -1, 'parent_buildid': None, 'parent_relationship': None, 'rebuilt_buildid': None, 'sourcestamps': [{'ssid': 10816, 'branch': 'refs/pull/14318/head', 'revision': '201aa659066b41a2cfe89b615213bcb093fcbe79', 'project': '', 'repository': '', 'codebase': '', 'created_at': 1770161566, 'patch': None}]}, 'parentbuild': None, 'parentbuilder': None, 'builder': {'builderid': 27, 'name': 'pr-ubu-x64', 'masterids': [1], 'description': None, 'description_format': None, 'description_html': None, 'projectid': None, 'tags': []}, 'url': 'https://dolphin.ci/#/builders/27/builds/8923'}, 'type': 'raw_bb_hook'}
2026-02-03T23:34:15.005083 {'source': 'buildbot', 'repo': 'dolphin-emu/dolphin', 'hash': '201aa659066b41a2cfe89b615213bcb093fcbe79', 'shortrev': '201aa6', 'service': 'pr-freebsd-x64', 'pr': 14318, 'success': False, 'pending': True, 'url': 'https://dolphin.ci/#/builders/11/builds/8924', 'description': 'Auto build in progress on builder pr-freebsd-x64', 'type': 'build_status'}
2026-02-03T23:34:14.730916 {'source': 'webserver', 'raw': {'buildid': 157762, 'number': 8924, 'builderid': 11, 'buildrequestid': 157883, 'workerid': 1, 'masterid': 1, 'started_at': 1770161567, 'complete_at': None, 'locks_duration_s': 0, 'complete': False, 'state_string': 'starting', 'results': None, 'properties': {'buildername': ['pr-freebsd-x64', 'Builder'], 'builderid': [11, 'Builder'], 'buildnumber': [8924, 'Build'], 'revision': ['201aa659066b41a2cfe89b615213bcb093fcbe79', 'Build'], 'branch': ['refs/pull/14318/head', 'Build'], 'repository': ['', 'Build'], 'baserev': ['dd2b94cd4a71fb6abda72f58d27434ef2d8b0a5f', 'Change'], 'repo': ['dolphin-emu/dolphin', 'Change'], 'scheduler': ['pr', 'Scheduler'], 'workername': ['freebsd', 'Worker'], 'codebase': ['', 'Build'], 'project': ['', 'Build'], 'owners': [['Central (on behalf of: jordan-woyak)'], 'Build'], 'branchname': ['pr-14318', 'Change'], 'pr_id': [14318, 'Change'], 'headrev': ['201aa659066b41a2cfe89b615213bcb093fcbe79', 'Change'], 'shortrev': ['201aa6', 'Change']}, 'buildrequest': {'buildrequestid': 157883, 'buildsetid': 36142, 'builderid': 11, 'priority': 0, 'claimed': True, 'claimed_at': 1770161567, 'claimed_by_masterid': 1, 'complete': False, 'results': -1, 'submitted_at': 1770161566, 'complete_at': None, 'waited_for': False, 'properties': None}, 'buildset': {'bsid': 36142, 'external_idstring': None, 'reason': "The AnyBranchScheduler scheduler named 'pr' triggered this build", 'submitted_at': 1770161566, 'complete': False, 'complete_at': None, 'results': -1, 'parent_buildid': None, 'parent_relationship': None, 'rebuilt_buildid': None, 'sourcestamps': [{'ssid': 10816, 'branch': 'refs/pull/14318/head', 'revision': '201aa659066b41a2cfe89b615213bcb093fcbe79', 'project': '', 'repository': '', 'codebase': '', 'created_at': 1770161566, 'patch': None}]}, 'parentbuild': None, 'parentbuilder': None, 'builder': {'builderid': 11, 'name': 'pr-freebsd-x64', 'masterids': [1], 'description': None, 'description_format': None, 'description_html': None, 'projectid': None, 'tags': []}, 'url': 'https://dolphin.ci/#/builders/11/builds/8924'}, 'type': 'raw_bb_hook'}
2026-02-03T23:34:14.103886 {'source': 'buildbot', 'repo': 'dolphin-emu/dolphin', 'hash': '201aa659066b41a2cfe89b615213bcb093fcbe79', 'shortrev': '201aa6', 'service': 'pr-win-x64', 'pr': 14318, 'success': False, 'pending': True, 'url': 'https://dolphin.ci/#/builders/2/builds/8926', 'description': 'Auto build in progress on builder pr-win-x64', 'type': 'build_status'}
2026-02-03T23:34:13.813813 {'source': 'webserver', 'raw': {'buildid': 157757, 'number': 8926, 'builderid': 2, 'buildrequestid': 157880, 'workerid': 4, 'masterid': 1, 'started_at': 1770161567, 'complete_at': None, 'locks_duration_s': 0, 'complete': False, 'state_string': 'starting', 'results': None, 'properties': {'headrev': ['201aa659066b41a2cfe89b615213bcb093fcbe79', 'Change'], 'branch': ['refs/pull/14318/head', 'Build'], 'branchname': ['pr-14318', 'Change'], 'baserev': ['dd2b94cd4a71fb6abda72f58d27434ef2d8b0a5f', 'Change'], 'repo': ['dolphin-emu/dolphin', 'Change'], 'builderid': [2, 'Builder'], 'workername': ['windows', 'Worker'], 'buildnumber': [8926, 'Build'], 'shortrev': ['201aa6', 'Change'], 'pr_id': [14318, 'Change'], 'scheduler': ['pr', 'Scheduler'], 'buildername': ['pr-win-x64', 'Builder'], 'revision': ['201aa659066b41a2cfe89b615213bcb093fcbe79', 'Build'], 'repository': ['', 'Build'], 'codebase': ['', 'Build'], 'project': ['', 'Build']}, 'buildrequest': {'buildrequestid': 157880, 'buildsetid': 36142, 'builderid': 2, 'priority': 0, 'claimed': True, 'claimed_at': 1770161567, 'claimed_by_masterid': 1, 'complete': False, 'results': -1, 'submitted_at': 1770161566, 'complete_at': None, 'waited_for': False, 'properties': None}, 'buildset': {'bsid': 36142, 'external_idstring': None, 'reason': "The AnyBranchScheduler scheduler named 'pr' triggered this build", 'submitted_at': 1770161566, 'complete': False, 'complete_at': None, 'results': -1, 'parent_buildid': None, 'parent_relationship': None, 'rebuilt_buildid': None, 'sourcestamps': [{'ssid': 10816, 'branch': 'refs/pull/14318/head', 'revision': '201aa659066b41a2cfe89b615213bcb093fcbe79', 'project': '', 'repository': '', 'codebase': '', 'created_at': 1770161566, 'patch': None}]}, 'parentbuild': None, 'parentbuilder': None, 'builder': {'builderid': 2, 'name': 'pr-win-x64', 'masterids': [1], 'description': None, 'description_format': None, 'description_html': None, 'projectid': None, 'tags': []}, 'url': 'https://dolphin.ci/#/builders/2/builds/8926'}, 'type': 'raw_bb_hook'}
2026-02-03T23:34:12.999848 {'source': 'buildbot', 'repo': 'dolphin-emu/dolphin', 'hash': '201aa659066b41a2cfe89b615213bcb093fcbe79', 'shortrev': '201aa6', 'service': 'pr-osx-universal', 'pr': 14318, 'success': False, 'pending': True, 'url': 'https://dolphin.ci/#/builders/7/builds/8938', 'description': 'Auto build in progress on builder pr-osx-universal', 'type': 'build_status'}
2026-02-03T23:34:12.697269 {'source': 'buildbot', 'repo': 'dolphin-emu/dolphin', 'hash': '201aa659066b41a2cfe89b615213bcb093fcbe79', 'shortrev': '201aa6', 'service': 'pr-deb-x64', 'pr': 14318, 'success': False, 'pending': True, 'url': 'https://dolphin.ci/#/builders/26/builds/8922', 'description': 'Auto build in progress on builder pr-deb-x64', 'type': 'build_status'}
2026-02-03T23:34:12.465710 {'source': 'webserver', 'raw': {'buildid': 157760, 'number': 8938, 'builderid': 7, 'buildrequestid': 157881, 'workerid': 8, 'masterid': 1, 'started_at': 1770161567, 'complete_at': None, 'locks_duration_s': 0, 'complete': False, 'state_string': 'starting', 'results': None, 'properties': {'buildnumber': [8938, 'Build'], 'revision': ['201aa659066b41a2cfe89b615213bcb093fcbe79', 'Build'], 'codebase': ['', 'Build'], 'branch': ['refs/pull/14318/head', 'Build'], 'repository': ['', 'Build'], 'owners': [['Central (on behalf of: jordan-woyak)'], 'Build'], 'branchname': ['pr-14318', 'Change'], 'baserev': ['dd2b94cd4a71fb6abda72f58d27434ef2d8b0a5f', 'Change'], 'headrev': ['201aa659066b41a2cfe89b615213bcb093fcbe79', 'Change'], 'project': ['', 'Build'], 'shortrev': ['201aa6', 'Change'], 'pr_id': [14318, 'Change'], 'builderid': [7, 'Builder'], 'workername': ['osx-m1', 'Worker'], 'repo': ['dolphin-emu/dolphin', 'Change'], 'scheduler': ['pr', 'Scheduler'], 'buildername': ['pr-osx-universal', 'Builder']}, 'buildrequest': {'buildrequestid': 157881, 'buildsetid': 36142, 'builderid': 7, 'priority': 0, 'claimed': True, 'claimed_at': 1770161567, 'claimed_by_masterid': 1, 'complete': False, 'results': -1, 'submitted_at': 1770161566, 'complete_at': None, 'waited_for': False, 'properties': None}, 'buildset': {'bsid': 36142, 'external_idstring': None, 'reason': "The AnyBranchScheduler scheduler named 'pr' triggered this build", 'submitted_at': 1770161566, 'complete': False, 'complete_at': None, 'results': -1, 'parent_buildid': None, 'parent_relationship': None, 'rebuilt_buildid': None, 'sourcestamps': [{'ssid': 10816, 'branch': 'refs/pull/14318/head', 'revision': '201aa659066b41a2cfe89b615213bcb093fcbe79', 'project': '', 'repository': '', 'codebase': '', 'created_at': 1770161566, 'patch': None}]}, 'parentbuild': None, 'parentbuilder': None, 'builder': {'builderid': 7, 'name': 'pr-osx-universal', 'masterids': [1], 'description': None, 'description_format': None, 'description_html': None, 'projectid': None, 'tags': []}, 'url': 'https://dolphin.ci/#/builders/7/builds/8938'}, 'type': 'raw_bb_hook'}
2026-02-03T23:34:12.373303 {'source': 'webserver', 'raw': {'buildid': 157759, 'number': 8922, 'builderid': 26, 'buildrequestid': 157886, 'workerid': 9, 'masterid': 1, 'started_at': 1770161567, 'complete_at': None, 'locks_duration_s': 0, 'complete': False, 'state_string': 'starting', 'results': None, 'properties': {'headrev': ['201aa659066b41a2cfe89b615213bcb093fcbe79', 'Change'], 'shortrev': ['201aa6', 'Change'], 'pr_id': [14318, 'Change'], 'scheduler': ['pr', 'Scheduler'], 'repo': ['dolphin-emu/dolphin', 'Change'], 'branch': ['refs/pull/14318/head', 'Build'], 'branchname': ['pr-14318', 'Change'], 'baserev': ['dd2b94cd4a71fb6abda72f58d27434ef2d8b0a5f', 'Change'], 'buildername': ['pr-deb-x64', 'Builder'], 'builderid': [26, 'Builder'], 'workername': ['debian', 'Worker'], 'buildnumber': [8922, 'Build'], 'codebase': ['', 'Build'], 'project': ['', 'Build'], 'revision': ['201aa659066b41a2cfe89b615213bcb093fcbe79', 'Build'], 'repository': ['', 'Build'], 'owners': [['Central (on behalf of: jordan-woyak)'], 'Build']}, 'buildrequest': {'buildrequestid': 157886, 'buildsetid': 36142, 'builderid': 26, 'priority': 0, 'claimed': True, 'claimed_at': 1770161567, 'claimed_by_masterid': 1, 'complete': False, 'results': -1, 'submitted_at': 1770161566, 'complete_at': None, 'waited_for': False, 'properties': None}, 'buildset': {'bsid': 36142, 'external_idstring': None, 'reason': "The AnyBranchScheduler scheduler named 'pr' triggered this build", 'submitted_at': 1770161566, 'complete': False, 'complete_at': None, 'results': -1, 'parent_buildid': None, 'parent_relationship': None, 'rebuilt_buildid': None, 'sourcestamps': [{'ssid': 10816, 'branch': 'refs/pull/14318/head', 'revision': '201aa659066b41a2cfe89b615213bcb093fcbe79', 'project': '', 'repository': '', 'codebase': '', 'created_at': 1770161566, 'patch': None}]}, 'parentbuild': None, 'parentbuilder': None, 'builder': {'builderid': 26, 'name': 'pr-deb-x64', 'masterids': [1], 'description': None, 'description_format': None, 'description_html': None, 'projectid': None, 'tags': []}, 'url': 'https://dolphin.ci/#/builders/26/builds/8922'}, 'type': 'raw_bb_hook'}
2026-02-03T23:34:12.263249 {'source': 'buildbot', 'repo': 'dolphin-emu/dolphin', 'hash': '201aa659066b41a2cfe89b615213bcb093fcbe79', 'shortrev': '201aa6', 'service': 'pr-android', 'pr': 14318, 'success': False, 'pending': True, 'url': 'https://dolphin.ci/#/builders/22/builds/8926', 'description': 'Auto build in progress on builder pr-android', 'type': 'build_status'}
2026-02-03T23:34:12.003496 {'source': 'webserver', 'raw': {'buildid': 157763, 'number': 8926, 'builderid': 22, 'buildrequestid': 157885, 'workerid': 12, 'masterid': 1, 'started_at': 1770161567, 'complete_at': None, 'locks_duration_s': 0, 'complete': False, 'state_string': 'starting', 'results': None, 'properties': {'headrev': ['201aa659066b41a2cfe89b615213bcb093fcbe79', 'Change'], 'pr_id': [14318, 'Change'], 'buildnumber': [8926, 'Build'], 'buildername': ['pr-android', 'Builder'], 'builderid': [22, 'Builder'], 'repository': ['', 'Build'], 'owners': [['Central (on behalf of: jordan-woyak)'], 'Build'], 'branchname': ['pr-14318', 'Change'], 'branch': ['refs/pull/14318/head', 'Build'], 'codebase': ['', 'Build'], 'project': ['', 'Build'], 'revision': ['201aa659066b41a2cfe89b615213bcb093fcbe79', 'Build'], 'shortrev': ['201aa6', 'Change'], 'scheduler': ['pr', 'Scheduler'], 'baserev': ['dd2b94cd4a71fb6abda72f58d27434ef2d8b0a5f', 'Change'], 'repo': ['dolphin-emu/dolphin', 'Change'], 'workername': ['android', 'Worker']}, 'buildrequest': {'buildrequestid': 157885, 'buildsetid': 36142, 'builderid': 22, 'priority': 0, 'claimed': True, 'claimed_at': 1770161567, 'claimed_by_masterid': 1, 'complete': False, 'results': -1, 'submitted_at': 1770161566, 'complete_at': None, 'waited_for': False, 'properties': None}, 'buildset': {'bsid': 36142, 'external_idstring': None, 'reason': "The AnyBranchScheduler scheduler named 'pr' triggered this build", 'submitted_at': 1770161566, 'complete': False, 'complete_at': None, 'results': -1, 'parent_buildid': None, 'parent_relationship': None, 'rebuilt_buildid': None, 'sourcestamps': [{'ssid': 10816, 'branch': 'refs/pull/14318/head', 'revision': '201aa659066b41a2cfe89b615213bcb093fcbe79', 'project': '', 'repository': '', 'codebase': '', 'created_at': 1770161566, 'patch': None}]}, 'parentbuild': None, 'parentbuilder': None, 'builder': {'builderid': 22, 'name': 'pr-android', 'masterids': [1], 'description': None, 'description_format': None, 'description_html': None, 'projectid': None, 'tags': []}, 'url': 'https://dolphin.ci/#/builders/22/builds/8926'}, 'type': 'raw_bb_hook'}
2026-02-03T23:34:11.463370 {'source': 'buildbot', 'repo': 'dolphin-emu/dolphin', 'hash': '201aa659066b41a2cfe89b615213bcb093fcbe79', 'shortrev': '201aa6', 'service': 'lint', 'pr': 14318, 'success': False, 'pending': True, 'url': 'https://dolphin.ci/#/builders/9/builds/8923', 'description': 'Auto build in progress on builder lint', 'type': 'build_status'}
2026-02-03T23:34:11.174714 {'source': 'webserver', 'raw': {'buildid': 157756, 'number': 8923, 'builderid': 9, 'buildrequestid': 157882, 'workerid': 10, 'masterid': 1, 'started_at': 1770161567, 'complete_at': None, 'locks_duration_s': 0, 'complete': False, 'state_string': 'starting', 'results': None, 'properties': {'branchname': ['pr-14318', 'Change'], 'shortrev': ['201aa6', 'Change'], 'repo': ['dolphin-emu/dolphin', 'Change'], 'scheduler': ['pr', 'Scheduler'], 'workername': ['ubuntu-lts', 'Worker'], 'project': ['', 'Build'], 'buildnumber': [8923, 'Build'], 'branch': ['refs/pull/14318/head', 'Build'], 'revision': ['201aa659066b41a2cfe89b615213bcb093fcbe79', 'Build'], 'headrev': ['201aa659066b41a2cfe89b615213bcb093fcbe79', 'Change'], 'buildername': ['lint', 'Builder'], 'baserev': ['dd2b94cd4a71fb6abda72f58d27434ef2d8b0a5f', 'Change'], 'pr_id': [14318, 'Change'], 'builderid': [9, 'Builder'], 'repository': ['', 'Build'], 'codebase': ['', 'Build']}, 'buildrequest': {'buildrequestid': 157882, 'buildsetid': 36142, 'builderid': 9, 'priority': 0, 'claimed': True, 'claimed_at': 1770161567, 'claimed_by_masterid': 1, 'complete': False, 'results': -1, 'submitted_at': 1770161566, 'complete_at': None, 'waited_for': False, 'properties': None}, 'buildset': {'bsid': 36142, 'external_idstring': None, 'reason': "The AnyBranchScheduler scheduler named 'pr' triggered this build", 'submitted_at': 1770161566, 'complete': False, 'complete_at': None, 'results': -1, 'parent_buildid': None, 'parent_relationship': None, 'rebuilt_buildid': None, 'sourcestamps': [{'ssid': 10816, 'branch': 'refs/pull/14318/head', 'revision': '201aa659066b41a2cfe89b615213bcb093fcbe79', 'project': '', 'repository': '', 'codebase': '', 'created_at': 1770161566, 'patch': None}]}, 'parentbuild': None, 'parentbuilder': None, 'builder': {'builderid': 9, 'name': 'lint', 'masterids': [1], 'description': None, 'description_format': None, 'description_html': None, 'projectid': None, 'tags': []}, 'url': 'https://dolphin.ci/#/builders/9/builds/8923'}, 'type': 'raw_bb_hook'}
2026-02-03T23:33:22.579414 {'source': 'buildbot', 'repo': 'dolphin-emu/dolphin', 'hash': '201aa659066b41a2cfe89b615213bcb093fcbe79', 'shortrev': '201aa6', 'service': 'pr-flatpak-x64', 'pr': 14318, 'success': False, 'pending': True, 'url': 'https://dolphin.ci/#/builders/43/builds/4069', 'description': 'Auto build in progress on builder pr-flatpak-x64', 'type': 'build_status'}
2026-02-03T23:33:22.248903 {'source': 'webserver', 'raw': {'buildid': 157761, 'number': 4069, 'builderid': 43, 'buildrequestid': 157889, 'workerid': 13, 'masterid': 1, 'started_at': 1770161567, 'complete_at': None, 'locks_duration_s': 0, 'complete': False, 'state_string': 'starting', 'results': None, 'properties': {'branchname': ['pr-14318', 'Change'], 'headrev': ['201aa659066b41a2cfe89b615213bcb093fcbe79', 'Change'], 'pr_id': [14318, 'Change'], 'buildername': ['pr-flatpak-x64', 'Builder'], 'baserev': ['dd2b94cd4a71fb6abda72f58d27434ef2d8b0a5f', 'Change'], 'branch': ['refs/pull/14318/head', 'Build'], 'owners': [['Central (on behalf of: jordan-woyak)'], 'Build'], 'shortrev': ['201aa6', 'Change'], 'buildnumber': [4069, 'Build'], 'codebase': ['', 'Build'], 'repo': ['dolphin-emu/dolphin', 'Change'], 'scheduler': ['pr', 'Scheduler'], 'builderid': [43, 'Builder'], 'workername': ['altair-flatpak', 'Worker'], 'revision': ['201aa659066b41a2cfe89b615213bcb093fcbe79', 'Build'], 'repository': ['', 'Build'], 'project': ['', 'Build']}, 'buildrequest': {'buildrequestid': 157889, 'buildsetid': 36142, 'builderid': 43, 'priority': 0, 'claimed': True, 'claimed_at': 1770161567, 'claimed_by_masterid': 1, 'complete': False, 'results': -1, 'submitted_at': 1770161566, 'complete_at': None, 'waited_for': False, 'properties': None}, 'buildset': {'bsid': 36142, 'external_idstring': None, 'reason': "The AnyBranchScheduler scheduler named 'pr' triggered this build", 'submitted_at': 1770161566, 'complete': False, 'complete_at': None, 'results': -1, 'parent_buildid': None, 'parent_relationship': None, 'rebuilt_buildid': None, 'sourcestamps': [{'ssid': 10816, 'branch': 'refs/pull/14318/head', 'revision': '201aa659066b41a2cfe89b615213bcb093fcbe79', 'project': '', 'repository': '', 'codebase': '', 'created_at': 1770161566, 'patch': None}]}, 'parentbuild': None, 'parentbuilder': None, 'builder': {'builderid': 43, 'name': 'pr-flatpak-x64', 'masterids': [1], 'description': None, 'description_format': None, 'description_html': None, 'projectid': None, 'tags': []}, 'url': 'https://dolphin.ci/#/builders/43/builds/4069'}, 'type': 'raw_bb_hook'}
2026-02-03T23:33:21.847936 {'source': 'buildbot', 'repo': 'dolphin-emu/dolphin', 'hash': '201aa659066b41a2cfe89b615213bcb093fcbe79', 'shortrev': '201aa6', 'service': 'pr-flatpak-arm64', 'pr': 14318, 'success': False, 'pending': True, 'url': 'https://dolphin.ci/#/builders/44/builds/4014', 'description': 'Auto build in progress on builder pr-flatpak-arm64', 'type': 'build_status'}
2026-02-03T23:33:21.536353 {'source': 'webserver', 'raw': {'buildid': 157758, 'number': 4014, 'builderid': 44, 'buildrequestid': 157890, 'workerid': 14, 'masterid': 1, 'started_at': 1770161567, 'complete_at': None, 'locks_duration_s': 0, 'complete': False, 'state_string': 'starting', 'results': None, 'properties': {'baserev': ['dd2b94cd4a71fb6abda72f58d27434ef2d8b0a5f', 'Change'], 'headrev': ['201aa659066b41a2cfe89b615213bcb093fcbe79', 'Change'], 'branchname': ['pr-14318', 'Change'], 'revision': ['201aa659066b41a2cfe89b615213bcb093fcbe79', 'Build'], 'buildername': ['pr-flatpak-arm64', 'Builder'], 'builderid': [44, 'Builder'], 'scheduler': ['pr', 'Scheduler'], 'shortrev': ['201aa6', 'Change'], 'pr_id': [14318, 'Change'], 'repo': ['dolphin-emu/dolphin', 'Change'], 'workername': ['deneb-flatpak', 'Worker'], 'buildnumber': [4014, 'Build'], 'branch': ['refs/pull/14318/head', 'Build'], 'owners': [['Central (on behalf of: jordan-woyak)'], 'Build'], 'project': ['', 'Build'], 'repository': ['', 'Build'], 'codebase': ['', 'Build']}, 'buildrequest': {'buildrequestid': 157890, 'buildsetid': 36142, 'builderid': 44, 'priority': 0, 'claimed': True, 'claimed_at': 1770161567, 'claimed_by_masterid': 1, 'complete': False, 'results': -1, 'submitted_at': 1770161566, 'complete_at': None, 'waited_for': False, 'properties': None}, 'buildset': {'bsid': 36142, 'external_idstring': None, 'reason': "The AnyBranchScheduler scheduler named 'pr' triggered this build", 'submitted_at': 1770161566, 'complete': False, 'complete_at': None, 'results': -1, 'parent_buildid': None, 'parent_relationship': None, 'rebuilt_buildid': None, 'sourcestamps': [{'ssid': 10816, 'branch': 'refs/pull/14318/head', 'revision': '201aa659066b41a2cfe89b615213bcb093fcbe79', 'project': '', 'repository': '', 'codebase': '', 'created_at': 1770161566, 'patch': None}]}, 'parentbuild': None, 'parentbuilder': None, 'builder': {'builderid': 44, 'name': 'pr-flatpak-arm64', 'masterids': [1], 'description': None, 'description_format': None, 'description_html': None, 'projectid': None, 'tags': []}, 'url': 'https://dolphin.ci/#/builders/44/builds/4014'}, 'type': 'raw_bb_hook'}
2026-02-03T23:32:46.871760 {'source': 'prbuilder', 'repo': 'dolphin-emu/dolphin', 'hash': '201aa659066b41a2cfe89b615213bcb093fcbe79', 'shortrev': '201aa6', 'service': 'lint', 'pr': 14318, 'success': False, 'pending': True, 'url': 'https://dolphin.ci/', 'description': 'Auto build pending', 'type': 'build_status'}
2026-02-03T23:32:46.584472 {'source': 'prbuilder', 'repo': 'dolphin-emu/dolphin', 'hash': '201aa659066b41a2cfe89b615213bcb093fcbe79', 'shortrev': '201aa6', 'service': 'pr-flatpak-arm64', 'pr': 14318, 'success': False, 'pending': True, 'url': 'https://dolphin.ci/', 'description': 'Auto build pending', 'type': 'build_status'}
2026-02-03T23:32:46.272392 {'source': 'prbuilder', 'repo': 'dolphin-emu/dolphin', 'hash': '201aa659066b41a2cfe89b615213bcb093fcbe79', 'shortrev': '201aa6', 'service': 'pr-flatpak-x64', 'pr': 14318, 'success': False, 'pending': True, 'url': 'https://dolphin.ci/', 'description': 'Auto build pending', 'type': 'build_status'}
Recent 'build_status' events
2026-02-03T23:34:37.789140 {'source': 'buildbot', 'repo': 'dolphin-emu/dolphin', 'hash': '201aa659066b41a2cfe89b615213bcb093fcbe79', 'shortrev': '201aa6', 'service': 'lint', 'pr': 14318, 'success': True, 'pending': False, 'url': 'https://dolphin.ci/#/builders/9/builds/8923', 'description': 'Build succeeded on builder lint', 'type': 'build_status'}
2026-02-03T23:34:18.255649 {'source': 'buildbot', 'repo': 'dolphin-emu/dolphin', 'hash': '201aa659066b41a2cfe89b615213bcb093fcbe79', 'shortrev': '201aa6', 'service': 'pr-ubu-x64', 'pr': 14318, 'success': False, 'pending': True, 'url': 'https://dolphin.ci/#/builders/27/builds/8923', 'description': 'Auto build in progress on builder pr-ubu-x64', 'type': 'build_status'}
2026-02-03T23:34:15.005083 {'source': 'buildbot', 'repo': 'dolphin-emu/dolphin', 'hash': '201aa659066b41a2cfe89b615213bcb093fcbe79', 'shortrev': '201aa6', 'service': 'pr-freebsd-x64', 'pr': 14318, 'success': False, 'pending': True, 'url': 'https://dolphin.ci/#/builders/11/builds/8924', 'description': 'Auto build in progress on builder pr-freebsd-x64', 'type': 'build_status'}
2026-02-03T23:34:14.103886 {'source': 'buildbot', 'repo': 'dolphin-emu/dolphin', 'hash': '201aa659066b41a2cfe89b615213bcb093fcbe79', 'shortrev': '201aa6', 'service': 'pr-win-x64', 'pr': 14318, 'success': False, 'pending': True, 'url': 'https://dolphin.ci/#/builders/2/builds/8926', 'description': 'Auto build in progress on builder pr-win-x64', 'type': 'build_status'}
2026-02-03T23:34:12.999848 {'source': 'buildbot', 'repo': 'dolphin-emu/dolphin', 'hash': '201aa659066b41a2cfe89b615213bcb093fcbe79', 'shortrev': '201aa6', 'service': 'pr-osx-universal', 'pr': 14318, 'success': False, 'pending': True, 'url': 'https://dolphin.ci/#/builders/7/builds/8938', 'description': 'Auto build in progress on builder pr-osx-universal', 'type': 'build_status'}
2026-02-03T23:34:12.697269 {'source': 'buildbot', 'repo': 'dolphin-emu/dolphin', 'hash': '201aa659066b41a2cfe89b615213bcb093fcbe79', 'shortrev': '201aa6', 'service': 'pr-deb-x64', 'pr': 14318, 'success': False, 'pending': True, 'url': 'https://dolphin.ci/#/builders/26/builds/8922', 'description': 'Auto build in progress on builder pr-deb-x64', 'type': 'build_status'}
2026-02-03T23:34:12.263249 {'source': 'buildbot', 'repo': 'dolphin-emu/dolphin', 'hash': '201aa659066b41a2cfe89b615213bcb093fcbe79', 'shortrev': '201aa6', 'service': 'pr-android', 'pr': 14318, 'success': False, 'pending': True, 'url': 'https://dolphin.ci/#/builders/22/builds/8926', 'description': 'Auto build in progress on builder pr-android', 'type': 'build_status'}
2026-02-03T23:34:11.463370 {'source': 'buildbot', 'repo': 'dolphin-emu/dolphin', 'hash': '201aa659066b41a2cfe89b615213bcb093fcbe79', 'shortrev': '201aa6', 'service': 'lint', 'pr': 14318, 'success': False, 'pending': True, 'url': 'https://dolphin.ci/#/builders/9/builds/8923', 'description': 'Auto build in progress on builder lint', 'type': 'build_status'}
2026-02-03T23:33:22.579414 {'source': 'buildbot', 'repo': 'dolphin-emu/dolphin', 'hash': '201aa659066b41a2cfe89b615213bcb093fcbe79', 'shortrev': '201aa6', 'service': 'pr-flatpak-x64', 'pr': 14318, 'success': False, 'pending': True, 'url': 'https://dolphin.ci/#/builders/43/builds/4069', 'description': 'Auto build in progress on builder pr-flatpak-x64', 'type': 'build_status'}
2026-02-03T23:33:21.847936 {'source': 'buildbot', 'repo': 'dolphin-emu/dolphin', 'hash': '201aa659066b41a2cfe89b615213bcb093fcbe79', 'shortrev': '201aa6', 'service': 'pr-flatpak-arm64', 'pr': 14318, 'success': False, 'pending': True, 'url': 'https://dolphin.ci/#/builders/44/builds/4014', 'description': 'Auto build in progress on builder pr-flatpak-arm64', 'type': 'build_status'}
2026-02-03T23:32:46.871760 {'source': 'prbuilder', 'repo': 'dolphin-emu/dolphin', 'hash': '201aa659066b41a2cfe89b615213bcb093fcbe79', 'shortrev': '201aa6', 'service': 'lint', 'pr': 14318, 'success': False, 'pending': True, 'url': 'https://dolphin.ci/', 'description': 'Auto build pending', 'type': 'build_status'}
2026-02-03T23:32:46.584472 {'source': 'prbuilder', 'repo': 'dolphin-emu/dolphin', 'hash': '201aa659066b41a2cfe89b615213bcb093fcbe79', 'shortrev': '201aa6', 'service': 'pr-flatpak-arm64', 'pr': 14318, 'success': False, 'pending': True, 'url': 'https://dolphin.ci/', 'description': 'Auto build pending', 'type': 'build_status'}
2026-02-03T23:32:46.272392 {'source': 'prbuilder', 'repo': 'dolphin-emu/dolphin', 'hash': '201aa659066b41a2cfe89b615213bcb093fcbe79', 'shortrev': '201aa6', 'service': 'pr-flatpak-x64', 'pr': 14318, 'success': False, 'pending': True, 'url': 'https://dolphin.ci/', 'description': 'Auto build pending', 'type': 'build_status'}
2026-02-03T23:32:45.948778 {'source': 'prbuilder', 'repo': 'dolphin-emu/dolphin', 'hash': '201aa659066b41a2cfe89b615213bcb093fcbe79', 'shortrev': '201aa6', 'service': 'pr-freebsd-x64', 'pr': 14318, 'success': False, 'pending': True, 'url': 'https://dolphin.ci/', 'description': 'Auto build pending', 'type': 'build_status'}
2026-02-03T23:32:45.655160 {'source': 'prbuilder', 'repo': 'dolphin-emu/dolphin', 'hash': '201aa659066b41a2cfe89b615213bcb093fcbe79', 'shortrev': '201aa6', 'service': 'pr-win-dbg-x64', 'pr': 14318, 'success': False, 'pending': True, 'url': 'https://dolphin.ci/', 'description': 'Auto build pending', 'type': 'build_status'}
2026-02-03T23:32:45.350306 {'source': 'prbuilder', 'repo': 'dolphin-emu/dolphin', 'hash': '201aa659066b41a2cfe89b615213bcb093fcbe79', 'shortrev': '201aa6', 'service': 'pr-win-arm64', 'pr': 14318, 'success': False, 'pending': True, 'url': 'https://dolphin.ci/', 'description': 'Auto build pending', 'type': 'build_status'}
2026-02-03T23:32:44.729907 {'source': 'prbuilder', 'repo': 'dolphin-emu/dolphin', 'hash': '201aa659066b41a2cfe89b615213bcb093fcbe79', 'shortrev': '201aa6', 'service': 'pr-win-x64', 'pr': 14318, 'success': False, 'pending': True, 'url': 'https://dolphin.ci/', 'description': 'Auto build pending', 'type': 'build_status'}
2026-02-03T23:32:44.450300 {'source': 'prbuilder', 'repo': 'dolphin-emu/dolphin', 'hash': '201aa659066b41a2cfe89b615213bcb093fcbe79', 'shortrev': '201aa6', 'service': 'pr-ubu-x64', 'pr': 14318, 'success': False, 'pending': True, 'url': 'https://dolphin.ci/', 'description': 'Auto build pending', 'type': 'build_status'}
2026-02-03T23:32:44.093114 {'source': 'prbuilder', 'repo': 'dolphin-emu/dolphin', 'hash': '201aa659066b41a2cfe89b615213bcb093fcbe79', 'shortrev': '201aa6', 'service': 'pr-osx-universal', 'pr': 14318, 'success': False, 'pending': True, 'url': 'https://dolphin.ci/', 'description': 'Auto build pending', 'type': 'build_status'}
2026-02-03T23:32:43.789337 {'source': 'prbuilder', 'repo': 'dolphin-emu/dolphin', 'hash': '201aa659066b41a2cfe89b615213bcb093fcbe79', 'shortrev': '201aa6', 'service': 'pr-deb-dbg-x64', 'pr': 14318, 'success': False, 'pending': True, 'url': 'https://dolphin.ci/', 'description': 'Auto build pending', 'type': 'build_status'}
2026-02-03T23:32:43.494387 {'source': 'prbuilder', 'repo': 'dolphin-emu/dolphin', 'hash': '201aa659066b41a2cfe89b615213bcb093fcbe79', 'shortrev': '201aa6', 'service': 'pr-deb-x64', 'pr': 14318, 'success': False, 'pending': True, 'url': 'https://dolphin.ci/', 'description': 'Auto build pending', 'type': 'build_status'}
2026-02-03T23:32:43.178418 {'source': 'prbuilder', 'repo': 'dolphin-emu/dolphin', 'hash': '201aa659066b41a2cfe89b615213bcb093fcbe79', 'shortrev': '201aa6', 'service': 'pr-android', 'pr': 14318, 'success': False, 'pending': True, 'url': 'https://dolphin.ci/', 'description': 'Auto build pending', 'type': 'build_status'}
2026-02-03T23:32:42.860903 {'source': 'prbuilder', 'repo': 'dolphin-emu/dolphin', 'hash': '201aa659066b41a2cfe89b615213bcb093fcbe79', 'shortrev': '201aa6', 'service': 'default', 'pr': 14318, 'success': True, 'pending': False, 'url': '', 'description': 'Very basic checks passed, handed off to Buildbot.', 'type': 'build_status'}
2026-02-03T23:03:56.278794 {'source': 'buildbot', 'repo': 'dolphin-emu/dolphin', 'hash': '72a6ad6fac50dccf5f37921497b92e75e84bbf40', 'shortrev': '72a6ad', 'service': 'pr-android', 'pr': 14218, 'success': True, 'pending': False, 'url': 'https://dolphin.ci/#/builders/22/builds/8925', 'description': 'Build succeeded on builder pr-android', 'type': 'build_status'}
2026-02-03T22:57:43.019054 {'source': 'buildbot', 'repo': 'dolphin-emu/dolphin', 'hash': '72a6ad6fac50dccf5f37921497b92e75e84bbf40', 'shortrev': '72a6ad', 'service': 'pr-flatpak-x64', 'pr': 14218, 'success': True, 'pending': False, 'url': 'https://dolphin.ci/#/builders/43/builds/4068', 'description': 'Build succeeded on builder pr-flatpak-x64', 'type': 'build_status'}
Recent 'command_message' events
2026-02-03T21:39:22.468786 {'source': 'ircclient', 'who': 'JosJuice', 'what': 'irrawaddy: please rebuild 14317', 'type': 'command_message'}
2026-02-03T21:39:01.984953 {'source': 'ircclient', 'who': 'JosJuice', 'what': 'irrawaddy: please rebuild 13997', 'type': 'command_message'}
2026-02-02T21:19:07.268593 {'source': 'ircclient', 'who': 'JosJuice', 'what': 'irrawaddy: please rebuild 14317', 'type': 'command_message'}
Recent 'gh_commit_comment' events
2026-02-03T16:08:06.874133 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'pizzzza19', 'commit': '128a6454b6247c0c2a2cafb2d91d03f71fc112e0', 'url': 'https://github.com/dolphin-emu/dolphin/commit/128a6454b6247c0c2a2cafb2d91d03f71fc112e0#commitcomment-176299891', 'type': 'gh_commit_comment'}
Recent 'gh_issue_comment' events
2026-02-03T21:10:07.303427 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'jordan-woyak', 'action': 'created', 'id': 13970, 'title': 'WiimoteReal: Windows improvements.', 'url': 'https://github.com/dolphin-emu/dolphin/pull/13970#issuecomment-3843701295', 'safe_author': True, 'body': "> Do you know why some bt dongles rejects the reconnection? Probably some security feature on authentication that wiimotes doesn't have.\r\n\r\nI don't know. I haven't experienced that.", 'raw': {'action': 'created', 'issue': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13970', 'repository_url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'labels_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13970/labels{/name}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13970/comments', 'events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13970/events', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/13970', 'id': 3455745903, 'node_id': 'PR_kwDOALCn2M6qnqKV', 'number': 13970, 'title': 'WiimoteReal: Windows improvements.', 'user': {'login': 'jordan-woyak', 'id': 1768214, 'node_id': 'MDQ6VXNlcjE3NjgyMTQ=', 'avatar_url': 'https://avatars.githubusercontent.com/u/1768214?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/jordan-woyak', 'html_url': 'https://github.com/jordan-woyak', 'followers_url': 'https://api.github.com/users/jordan-woyak/followers', 'following_url': 'https://api.github.com/users/jordan-woyak/following{/other_user}', 'gists_url': 'https://api.github.com/users/jordan-woyak/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/jordan-woyak/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/jordan-woyak/subscriptions', 'organizations_url': 'https://api.github.com/users/jordan-woyak/orgs', 'repos_url': 'https://api.github.com/users/jordan-woyak/repos', 'events_url': 'https://api.github.com/users/jordan-woyak/events{/privacy}', 'received_events_url': 'https://api.github.com/users/jordan-woyak/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'labels': [], 'state': 'closed', 'locked': False, 'assignee': None, 'assignees': [], 'milestone': None, 'comments': 27, 'created_at': '2025-09-26T04:25:44Z', 'updated_at': '2026-02-03T21:10:04Z', 'closed_at': '2025-10-13T06:54:54Z', 'author_association': 'MEMBER', 'type': None, 'active_lock_reason': None, 'draft': False, 'pull_request': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13970', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/13970', 'diff_url': 'https://github.com/dolphin-emu/dolphin/pull/13970.diff', 'patch_url': 'https://github.com/dolphin-emu/dolphin/pull/13970.patch', 'merged_at': '2025-10-13T06:54:54Z'}, 'body': 'Majorly overhauled the Windows Wii remote code.\r\n\r\nThis PR should be ready now.\r\n\r\nStatically link with "Bthprops.lib"\r\nDynamic linking was introduced in https://github.com/dolphin-emu/dolphin/commit/3acf0eb3af98009cc4e00bd9bcf43873dc26c4da to make Dolphin run under Wine.\r\nMy search results imply that Wine has long since added stubs for these functions so I\'m hoping it works fine now?\r\nedit: Wine seems to work fine.\r\n\r\nA 10 year old comment describes a workaround needed for the Toshiba Bluetooth stack.\r\nI\'ve removed the workaround.\r\n\r\nRemoved `HidP_SetOutputReport` fallback (Windows 7 workaround).\r\n\r\nFixed buggy overlapped Read/Write event usage (probably my fault from long ago).\r\n\r\nAdded checks for a Wii remote device name on the HID interface to only attempt connecting to actual Wii remotes devices.\r\nThe existing code just throws Wii remote data at every interface and checks for sane replies.\r\n\r\nAdded a periodic write to test for disconnected remotes in a timely manner.\r\n\r\nChanged the `Refresh` button to not remove authenticated remotes.\r\nSome users found it annoying.\r\nIt now only removes unauthenticated disconnected remotes.\r\n\r\nAdded `Sync` and `Reset` actions to establish and remove pairings.\r\nThe `Refresh` button is now a `QToolButton` with additional actions (only on Windows).\r\n<img width="120" height="177" alt="image" src="https://github.com/user-attachments/assets/16e57431-1eab-4465-a4f8-856188b0f942" />\r\n\r\nPairing a remote with the `Sync` button will enable it to reestablish a connection on any button press.\r\nIt currently shows an ugly animation while the task runs async.\r\n<img width="189" height="72" alt="image" src="https://github.com/user-attachments/assets/076191ce-8312-4cdc-8223-14a4bb9eca2d" />\r\nIt\'s better than nothing and can be visually improved later.\r\n\r\n`Refresh` or `Continuous Scanning` are no longer required to connect paired remotes.\r\nPairing with Windows or the DolphinBar "just works" by assigning a slot to `Real Wii Remote`.\r\n\r\nAdditionally, I am constantly bombarded with ControllerInterface / WiimotePool related deadlocks.\r\nThis seems like an existing issue that fixing properly is probably going to take another PR.\r\nIt seems like it\'s SDL?\r\nedit: SDL\'s DirectInput handling has been disabled in #13982 \r\n\r\nTODO\r\n - [x] Make the `Sync` button not require also clicking `Refresh`.\r\n - [x] Make paired + reconnected Wii remotes work without using `Refresh`\r\n - [x] Test Dolphin with Wine (just in general, not Wii remote functionality)\r\n - [x] Clean up log spam from continuous opening of HID interfaces.\r\n - [x] Make the Sync / Reset buttons display some kind kind of feedback.', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13970/reactions', 'total_count': 4, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 1, 'confused': 0, 'heart': 3, 'rocket': 0, 'eyes': 0}, 'timeline_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13970/timeline', 'performed_via_github_app': None, 'state_reason': None}, 'comment': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments/3843701295', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/13970#issuecomment-3843701295', 'issue_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13970', 'id': 3843701295, 'node_id': 'IC_kwDOALCn2M7lGjov', 'user': {'login': 'jordan-woyak', 'id': 1768214, 'node_id': 'MDQ6VXNlcjE3NjgyMTQ=', 'avatar_url': 'https://avatars.githubusercontent.com/u/1768214?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/jordan-woyak', 'html_url': 'https://github.com/jordan-woyak', 'followers_url': 'https://api.github.com/users/jordan-woyak/followers', 'following_url': 'https://api.github.com/users/jordan-woyak/following{/other_user}', 'gists_url': 'https://api.github.com/users/jordan-woyak/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/jordan-woyak/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/jordan-woyak/subscriptions', 'organizations_url': 'https://api.github.com/users/jordan-woyak/orgs', 'repos_url': 'https://api.github.com/users/jordan-woyak/repos', 'events_url': 'https://api.github.com/users/jordan-woyak/events{/privacy}', 'received_events_url': 'https://api.github.com/users/jordan-woyak/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'created_at': '2026-02-03T21:10:04Z', 'updated_at': '2026-02-03T21:10:04Z', 'body': "> Do you know why some bt dongles rejects the reconnection? Probably some security feature on authentication that wiimotes doesn't have.\r\n\r\nI don't know. I haven't experienced that.", 'author_association': 'MEMBER', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments/3843701295/reactions', 'total_count': 0, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 0, 'confused': 0, 'heart': 0, 'rocket': 0, 'eyes': 0}, 'performed_via_github_app': None}, 'repository': {'id': 11577304, 'node_id': 'MDEwOlJlcG9zaXRvcnkxMTU3NzMwNA==', 'name': 'dolphin', 'full_name': 'dolphin-emu/dolphin', 'private': False, 'owner': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/dolphin-emu', 'html_url': 'https://github.com/dolphin-emu', 'followers_url': 'https://api.github.com/users/dolphin-emu/followers', 'following_url': 'https://api.github.com/users/dolphin-emu/following{/other_user}', 'gists_url': 'https://api.github.com/users/dolphin-emu/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/dolphin-emu/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/dolphin-emu/subscriptions', 'organizations_url': 'https://api.github.com/users/dolphin-emu/orgs', 'repos_url': 'https://api.github.com/users/dolphin-emu/repos', 'events_url': 'https://api.github.com/users/dolphin-emu/events{/privacy}', 'received_events_url': 'https://api.github.com/users/dolphin-emu/received_events', 'type': 'Organization', 'user_view_type': 'public', 'site_admin': False}, 'html_url': 'https://github.com/dolphin-emu/dolphin', 'description': 'Dolphin is a GameCube / Wii emulator, allowing you to play games for these two platforms on PC with improvements.', 'fork': False, 'url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'forks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/forks', 'keys_url': 'https://api.github.com/repos/dolphin-emu/dolphin/keys{/key_id}', 'collaborators_url': 'https://api.github.com/repos/dolphin-emu/dolphin/collaborators{/collaborator}', 'teams_url': 'https://api.github.com/repos/dolphin-emu/dolphin/teams', 'hooks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/hooks', 'issue_events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/events{/number}', 'events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/events', 'assignees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/assignees{/user}', 'branches_url': 'https://api.github.com/repos/dolphin-emu/dolphin/branches{/branch}', 'tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/tags', 'blobs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/blobs{/sha}', 'git_tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/tags{/sha}', 'git_refs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/refs{/sha}', 'trees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/trees{/sha}', 'statuses_url': 'https://api.github.com/repos/dolphin-emu/dolphin/statuses/{sha}', 'languages_url': 'https://api.github.com/repos/dolphin-emu/dolphin/languages', 'stargazers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/stargazers', 'contributors_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contributors', 'subscribers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscribers', 'subscription_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscription', 'commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/commits{/sha}', 'git_commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/commits{/sha}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/comments{/number}', 'issue_comment_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments{/number}', 'contents_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contents/{+path}', 'compare_url': 'https://api.github.com/repos/dolphin-emu/dolphin/compare/{base}...{head}', 'merges_url': 'https://api.github.com/repos/dolphin-emu/dolphin/merges', 'archive_url': 'https://api.github.com/repos/dolphin-emu/dolphin/{archive_format}{/ref}', 'downloads_url': 'https://api.github.com/repos/dolphin-emu/dolphin/downloads', 'issues_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues{/number}', 'pulls_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls{/number}', 'milestones_url': 'https://api.github.com/repos/dolphin-emu/dolphin/milestones{/number}', 'notifications_url': 'https://api.github.com/repos/dolphin-emu/dolphin/notifications{?since,all,participating}', 'labels_url': 'https://api.github.com/repos/dolphin-emu/dolphin/labels{/name}', 'releases_url': 'https://api.github.com/repos/dolphin-emu/dolphin/releases{/id}', 'deployments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/deployments', 'created_at': '2013-07-22T09:07:57Z', 'updated_at': '2026-02-03T18:03:13Z', 'pushed_at': '2026-02-03T04:26:13Z', 'git_url': 'git://github.com/dolphin-emu/dolphin.git', 'ssh_url': 'git@github.com:dolphin-emu/dolphin.git', 'clone_url': 'https://github.com/dolphin-emu/dolphin.git', 'svn_url': 'https://github.com/dolphin-emu/dolphin', 'homepage': 'https://dolphin-emu.org', 'size': 544014, 'stargazers_count': 14582, 'watchers_count': 14582, 'language': 'C++', 'has_issues': False, 'has_projects': False, 'has_downloads': True, 'has_wiki': True, 'has_pages': False, 'has_discussions': False, 'forks_count': 2966, 'mirror_url': None, 'archived': False, 'disabled': False, 'open_issues_count': 360, 'license': {'key': 'other', 'name': 'Other', 'spdx_id': 'NOASSERTION', 'url': None, 'node_id': 'MDc6TGljZW5zZTA='}, 'allow_forking': True, 'is_template': False, 'web_commit_signoff_required': False, 'topics': ['cpp', 'emulation', 'emulator', 'gamecube', 'wii'], 'visibility': 'public', 'forks': 2966, 'open_issues': 360, 'watchers': 14582, 'default_branch': 'master', 'custom_properties': {}}, 'organization': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'url': 'https://api.github.com/orgs/dolphin-emu', 'repos_url': 'https://api.github.com/orgs/dolphin-emu/repos', 'events_url': 'https://api.github.com/orgs/dolphin-emu/events', 'hooks_url': 'https://api.github.com/orgs/dolphin-emu/hooks', 'issues_url': 'https://api.github.com/orgs/dolphin-emu/issues', 'members_url': 'https://api.github.com/orgs/dolphin-emu/members{/member}', 'public_members_url': 'https://api.github.com/orgs/dolphin-emu/public_members{/member}', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'description': ''}, 'sender': {'login': 'jordan-woyak', 'id': 1768214, 'node_id': 'MDQ6VXNlcjE3NjgyMTQ=', 'avatar_url': 'https://avatars.githubusercontent.com/u/1768214?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/jordan-woyak', 'html_url': 'https://github.com/jordan-woyak', 'followers_url': 'https://api.github.com/users/jordan-woyak/followers', 'following_url': 'https://api.github.com/users/jordan-woyak/following{/other_user}', 'gists_url': 'https://api.github.com/users/jordan-woyak/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/jordan-woyak/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/jordan-woyak/subscriptions', 'organizations_url': 'https://api.github.com/users/jordan-woyak/orgs', 'repos_url': 'https://api.github.com/users/jordan-woyak/repos', 'events_url': 'https://api.github.com/users/jordan-woyak/events{/privacy}', 'received_events_url': 'https://api.github.com/users/jordan-woyak/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'installation': {'id': 35437981, 'node_id': 'MDIzOkludGVncmF0aW9uSW5zdGFsbGF0aW9uMzU0Mzc5ODE='}}, 'type': 'gh_issue_comment'}
2026-02-03T21:01:13.944059 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'Trihy', 'action': 'edited', 'id': 13970, 'title': 'WiimoteReal: Windows improvements.', 'url': 'https://github.com/dolphin-emu/dolphin/pull/13970#issuecomment-3843649310', 'safe_author': False, 'body': "Do you know why some bt dongles rejects the reconnection? Probably some security feature on authentication that wiimotes doesn't have.\r\n\r\nI been testing many built in BT and found the mediatek to have this problem. There are others. Some can fe fixed using another driver\r\n\r\nSo seems to be BT driver related. But maybe the BT dongle expect another response and we can change that. The wiimote blink 2 or 3 times and turn off. That usually means rejected.", 'raw': {'action': 'edited', 'changes': {'body': {'from': "Do you know why some bt dongles rejects the reconnection? Probably some security feature on authentication that wiimotes doesn't have.\r\n\r\nI been testing many built in BT and found the mediatek to have this problem. There are others. Some can fe fixed using another driver\r\n\r\nSo seems to be BT driver related. But maybe the BT dongle expect another response and we can change that. "}}, 'issue': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13970', 'repository_url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'labels_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13970/labels{/name}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13970/comments', 'events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13970/events', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/13970', 'id': 3455745903, 'node_id': 'PR_kwDOALCn2M6qnqKV', 'number': 13970, 'title': 'WiimoteReal: Windows improvements.', 'user': {'login': 'jordan-woyak', 'id': 1768214, 'node_id': 'MDQ6VXNlcjE3NjgyMTQ=', 'avatar_url': 'https://avatars.githubusercontent.com/u/1768214?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/jordan-woyak', 'html_url': 'https://github.com/jordan-woyak', 'followers_url': 'https://api.github.com/users/jordan-woyak/followers', 'following_url': 'https://api.github.com/users/jordan-woyak/following{/other_user}', 'gists_url': 'https://api.github.com/users/jordan-woyak/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/jordan-woyak/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/jordan-woyak/subscriptions', 'organizations_url': 'https://api.github.com/users/jordan-woyak/orgs', 'repos_url': 'https://api.github.com/users/jordan-woyak/repos', 'events_url': 'https://api.github.com/users/jordan-woyak/events{/privacy}', 'received_events_url': 'https://api.github.com/users/jordan-woyak/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'labels': [], 'state': 'closed', 'locked': False, 'assignee': None, 'assignees': [], 'milestone': None, 'comments': 26, 'created_at': '2025-09-26T04:25:44Z', 'updated_at': '2026-02-03T20:58:41Z', 'closed_at': '2025-10-13T06:54:54Z', 'author_association': 'MEMBER', 'type': None, 'active_lock_reason': None, 'draft': False, 'pull_request': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13970', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/13970', 'diff_url': 'https://github.com/dolphin-emu/dolphin/pull/13970.diff', 'patch_url': 'https://github.com/dolphin-emu/dolphin/pull/13970.patch', 'merged_at': '2025-10-13T06:54:54Z'}, 'body': 'Majorly overhauled the Windows Wii remote code.\r\n\r\nThis PR should be ready now.\r\n\r\nStatically link with "Bthprops.lib"\r\nDynamic linking was introduced in https://github.com/dolphin-emu/dolphin/commit/3acf0eb3af98009cc4e00bd9bcf43873dc26c4da to make Dolphin run under Wine.\r\nMy search results imply that Wine has long since added stubs for these functions so I\'m hoping it works fine now?\r\nedit: Wine seems to work fine.\r\n\r\nA 10 year old comment describes a workaround needed for the Toshiba Bluetooth stack.\r\nI\'ve removed the workaround.\r\n\r\nRemoved `HidP_SetOutputReport` fallback (Windows 7 workaround).\r\n\r\nFixed buggy overlapped Read/Write event usage (probably my fault from long ago).\r\n\r\nAdded checks for a Wii remote device name on the HID interface to only attempt connecting to actual Wii remotes devices.\r\nThe existing code just throws Wii remote data at every interface and checks for sane replies.\r\n\r\nAdded a periodic write to test for disconnected remotes in a timely manner.\r\n\r\nChanged the `Refresh` button to not remove authenticated remotes.\r\nSome users found it annoying.\r\nIt now only removes unauthenticated disconnected remotes.\r\n\r\nAdded `Sync` and `Reset` actions to establish and remove pairings.\r\nThe `Refresh` button is now a `QToolButton` with additional actions (only on Windows).\r\n<img width="120" height="177" alt="image" src="https://github.com/user-attachments/assets/16e57431-1eab-4465-a4f8-856188b0f942" />\r\n\r\nPairing a remote with the `Sync` button will enable it to reestablish a connection on any button press.\r\nIt currently shows an ugly animation while the task runs async.\r\n<img width="189" height="72" alt="image" src="https://github.com/user-attachments/assets/076191ce-8312-4cdc-8223-14a4bb9eca2d" />\r\nIt\'s better than nothing and can be visually improved later.\r\n\r\n`Refresh` or `Continuous Scanning` are no longer required to connect paired remotes.\r\nPairing with Windows or the DolphinBar "just works" by assigning a slot to `Real Wii Remote`.\r\n\r\nAdditionally, I am constantly bombarded with ControllerInterface / WiimotePool related deadlocks.\r\nThis seems like an existing issue that fixing properly is probably going to take another PR.\r\nIt seems like it\'s SDL?\r\nedit: SDL\'s DirectInput handling has been disabled in #13982 \r\n\r\nTODO\r\n - [x] Make the `Sync` button not require also clicking `Refresh`.\r\n - [x] Make paired + reconnected Wii remotes work without using `Refresh`\r\n - [x] Test Dolphin with Wine (just in general, not Wii remote functionality)\r\n - [x] Clean up log spam from continuous opening of HID interfaces.\r\n - [x] Make the Sync / Reset buttons display some kind kind of feedback.', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13970/reactions', 'total_count': 4, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 1, 'confused': 0, 'heart': 3, 'rocket': 0, 'eyes': 0}, 'timeline_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13970/timeline', 'performed_via_github_app': None, 'state_reason': None}, 'comment': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments/3843649310', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/13970#issuecomment-3843649310', 'issue_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13970', 'id': 3843649310, 'node_id': 'IC_kwDOALCn2M7lGW8e', 'user': {'login': 'Trihy', 'id': 107453916, 'node_id': 'U_kgDOBmed3A', 'avatar_url': 'https://avatars.githubusercontent.com/u/107453916?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/Trihy', 'html_url': 'https://github.com/Trihy', 'followers_url': 'https://api.github.com/users/Trihy/followers', 'following_url': 'https://api.github.com/users/Trihy/following{/other_user}', 'gists_url': 'https://api.github.com/users/Trihy/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/Trihy/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/Trihy/subscriptions', 'organizations_url': 'https://api.github.com/users/Trihy/orgs', 'repos_url': 'https://api.github.com/users/Trihy/repos', 'events_url': 'https://api.github.com/users/Trihy/events{/privacy}', 'received_events_url': 'https://api.github.com/users/Trihy/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'created_at': '2026-02-03T20:58:41Z', 'updated_at': '2026-02-03T21:01:12Z', 'body': "Do you know why some bt dongles rejects the reconnection? Probably some security feature on authentication that wiimotes doesn't have.\r\n\r\nI been testing many built in BT and found the mediatek to have this problem. There are others. Some can fe fixed using another driver\r\n\r\nSo seems to be BT driver related. But maybe the BT dongle expect another response and we can change that. The wiimote blink 2 or 3 times and turn off. That usually means rejected.", 'author_association': 'NONE', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments/3843649310/reactions', 'total_count': 0, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 0, 'confused': 0, 'heart': 0, 'rocket': 0, 'eyes': 0}, 'performed_via_github_app': None}, 'repository': {'id': 11577304, 'node_id': 'MDEwOlJlcG9zaXRvcnkxMTU3NzMwNA==', 'name': 'dolphin', 'full_name': 'dolphin-emu/dolphin', 'private': False, 'owner': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/dolphin-emu', 'html_url': 'https://github.com/dolphin-emu', 'followers_url': 'https://api.github.com/users/dolphin-emu/followers', 'following_url': 'https://api.github.com/users/dolphin-emu/following{/other_user}', 'gists_url': 'https://api.github.com/users/dolphin-emu/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/dolphin-emu/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/dolphin-emu/subscriptions', 'organizations_url': 'https://api.github.com/users/dolphin-emu/orgs', 'repos_url': 'https://api.github.com/users/dolphin-emu/repos', 'events_url': 'https://api.github.com/users/dolphin-emu/events{/privacy}', 'received_events_url': 'https://api.github.com/users/dolphin-emu/received_events', 'type': 'Organization', 'user_view_type': 'public', 'site_admin': False}, 'html_url': 'https://github.com/dolphin-emu/dolphin', 'description': 'Dolphin is a GameCube / Wii emulator, allowing you to play games for these two platforms on PC with improvements.', 'fork': False, 'url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'forks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/forks', 'keys_url': 'https://api.github.com/repos/dolphin-emu/dolphin/keys{/key_id}', 'collaborators_url': 'https://api.github.com/repos/dolphin-emu/dolphin/collaborators{/collaborator}', 'teams_url': 'https://api.github.com/repos/dolphin-emu/dolphin/teams', 'hooks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/hooks', 'issue_events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/events{/number}', 'events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/events', 'assignees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/assignees{/user}', 'branches_url': 'https://api.github.com/repos/dolphin-emu/dolphin/branches{/branch}', 'tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/tags', 'blobs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/blobs{/sha}', 'git_tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/tags{/sha}', 'git_refs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/refs{/sha}', 'trees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/trees{/sha}', 'statuses_url': 'https://api.github.com/repos/dolphin-emu/dolphin/statuses/{sha}', 'languages_url': 'https://api.github.com/repos/dolphin-emu/dolphin/languages', 'stargazers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/stargazers', 'contributors_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contributors', 'subscribers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscribers', 'subscription_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscription', 'commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/commits{/sha}', 'git_commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/commits{/sha}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/comments{/number}', 'issue_comment_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments{/number}', 'contents_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contents/{+path}', 'compare_url': 'https://api.github.com/repos/dolphin-emu/dolphin/compare/{base}...{head}', 'merges_url': 'https://api.github.com/repos/dolphin-emu/dolphin/merges', 'archive_url': 'https://api.github.com/repos/dolphin-emu/dolphin/{archive_format}{/ref}', 'downloads_url': 'https://api.github.com/repos/dolphin-emu/dolphin/downloads', 'issues_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues{/number}', 'pulls_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls{/number}', 'milestones_url': 'https://api.github.com/repos/dolphin-emu/dolphin/milestones{/number}', 'notifications_url': 'https://api.github.com/repos/dolphin-emu/dolphin/notifications{?since,all,participating}', 'labels_url': 'https://api.github.com/repos/dolphin-emu/dolphin/labels{/name}', 'releases_url': 'https://api.github.com/repos/dolphin-emu/dolphin/releases{/id}', 'deployments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/deployments', 'created_at': '2013-07-22T09:07:57Z', 'updated_at': '2026-02-03T18:03:13Z', 'pushed_at': '2026-02-03T04:26:13Z', 'git_url': 'git://github.com/dolphin-emu/dolphin.git', 'ssh_url': 'git@github.com:dolphin-emu/dolphin.git', 'clone_url': 'https://github.com/dolphin-emu/dolphin.git', 'svn_url': 'https://github.com/dolphin-emu/dolphin', 'homepage': 'https://dolphin-emu.org', 'size': 544014, 'stargazers_count': 14582, 'watchers_count': 14582, 'language': 'C++', 'has_issues': False, 'has_projects': False, 'has_downloads': True, 'has_wiki': True, 'has_pages': False, 'has_discussions': False, 'forks_count': 2966, 'mirror_url': None, 'archived': False, 'disabled': False, 'open_issues_count': 360, 'license': {'key': 'other', 'name': 'Other', 'spdx_id': 'NOASSERTION', 'url': None, 'node_id': 'MDc6TGljZW5zZTA='}, 'allow_forking': True, 'is_template': False, 'web_commit_signoff_required': False, 'topics': ['cpp', 'emulation', 'emulator', 'gamecube', 'wii'], 'visibility': 'public', 'forks': 2966, 'open_issues': 360, 'watchers': 14582, 'default_branch': 'master', 'custom_properties': {}}, 'organization': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'url': 'https://api.github.com/orgs/dolphin-emu', 'repos_url': 'https://api.github.com/orgs/dolphin-emu/repos', 'events_url': 'https://api.github.com/orgs/dolphin-emu/events', 'hooks_url': 'https://api.github.com/orgs/dolphin-emu/hooks', 'issues_url': 'https://api.github.com/orgs/dolphin-emu/issues', 'members_url': 'https://api.github.com/orgs/dolphin-emu/members{/member}', 'public_members_url': 'https://api.github.com/orgs/dolphin-emu/public_members{/member}', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'description': ''}, 'sender': {'login': 'Trihy', 'id': 107453916, 'node_id': 'U_kgDOBmed3A', 'avatar_url': 'https://avatars.githubusercontent.com/u/107453916?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/Trihy', 'html_url': 'https://github.com/Trihy', 'followers_url': 'https://api.github.com/users/Trihy/followers', 'following_url': 'https://api.github.com/users/Trihy/following{/other_user}', 'gists_url': 'https://api.github.com/users/Trihy/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/Trihy/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/Trihy/subscriptions', 'organizations_url': 'https://api.github.com/users/Trihy/orgs', 'repos_url': 'https://api.github.com/users/Trihy/repos', 'events_url': 'https://api.github.com/users/Trihy/events{/privacy}', 'received_events_url': 'https://api.github.com/users/Trihy/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'installation': {'id': 35437981, 'node_id': 'MDIzOkludGVncmF0aW9uSW5zdGFsbGF0aW9uMzU0Mzc5ODE='}}, 'type': 'gh_issue_comment'}
2026-02-03T20:58:44.456467 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'Trihy', 'action': 'created', 'id': 13970, 'title': 'WiimoteReal: Windows improvements.', 'url': 'https://github.com/dolphin-emu/dolphin/pull/13970#issuecomment-3843649310', 'safe_author': False, 'body': "Do you know why some bt dongles rejects the reconnection? Probably some security feature on authentication that wiimotes doesn't have.\r\n\r\nI been testing many built in BT and found the mediatek to have this problem. There are others. Some can fe fixed using another driver\r\n\r\nSo seems to be BT driver related. But maybe the BT dongle expect another response and we can change that. ", 'raw': {'action': 'created', 'issue': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13970', 'repository_url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'labels_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13970/labels{/name}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13970/comments', 'events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13970/events', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/13970', 'id': 3455745903, 'node_id': 'PR_kwDOALCn2M6qnqKV', 'number': 13970, 'title': 'WiimoteReal: Windows improvements.', 'user': {'login': 'jordan-woyak', 'id': 1768214, 'node_id': 'MDQ6VXNlcjE3NjgyMTQ=', 'avatar_url': 'https://avatars.githubusercontent.com/u/1768214?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/jordan-woyak', 'html_url': 'https://github.com/jordan-woyak', 'followers_url': 'https://api.github.com/users/jordan-woyak/followers', 'following_url': 'https://api.github.com/users/jordan-woyak/following{/other_user}', 'gists_url': 'https://api.github.com/users/jordan-woyak/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/jordan-woyak/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/jordan-woyak/subscriptions', 'organizations_url': 'https://api.github.com/users/jordan-woyak/orgs', 'repos_url': 'https://api.github.com/users/jordan-woyak/repos', 'events_url': 'https://api.github.com/users/jordan-woyak/events{/privacy}', 'received_events_url': 'https://api.github.com/users/jordan-woyak/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'labels': [], 'state': 'closed', 'locked': False, 'assignee': None, 'assignees': [], 'milestone': None, 'comments': 26, 'created_at': '2025-09-26T04:25:44Z', 'updated_at': '2026-02-03T20:58:41Z', 'closed_at': '2025-10-13T06:54:54Z', 'author_association': 'MEMBER', 'type': None, 'active_lock_reason': None, 'draft': False, 'pull_request': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13970', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/13970', 'diff_url': 'https://github.com/dolphin-emu/dolphin/pull/13970.diff', 'patch_url': 'https://github.com/dolphin-emu/dolphin/pull/13970.patch', 'merged_at': '2025-10-13T06:54:54Z'}, 'body': 'Majorly overhauled the Windows Wii remote code.\r\n\r\nThis PR should be ready now.\r\n\r\nStatically link with "Bthprops.lib"\r\nDynamic linking was introduced in https://github.com/dolphin-emu/dolphin/commit/3acf0eb3af98009cc4e00bd9bcf43873dc26c4da to make Dolphin run under Wine.\r\nMy search results imply that Wine has long since added stubs for these functions so I\'m hoping it works fine now?\r\nedit: Wine seems to work fine.\r\n\r\nA 10 year old comment describes a workaround needed for the Toshiba Bluetooth stack.\r\nI\'ve removed the workaround.\r\n\r\nRemoved `HidP_SetOutputReport` fallback (Windows 7 workaround).\r\n\r\nFixed buggy overlapped Read/Write event usage (probably my fault from long ago).\r\n\r\nAdded checks for a Wii remote device name on the HID interface to only attempt connecting to actual Wii remotes devices.\r\nThe existing code just throws Wii remote data at every interface and checks for sane replies.\r\n\r\nAdded a periodic write to test for disconnected remotes in a timely manner.\r\n\r\nChanged the `Refresh` button to not remove authenticated remotes.\r\nSome users found it annoying.\r\nIt now only removes unauthenticated disconnected remotes.\r\n\r\nAdded `Sync` and `Reset` actions to establish and remove pairings.\r\nThe `Refresh` button is now a `QToolButton` with additional actions (only on Windows).\r\n<img width="120" height="177" alt="image" src="https://github.com/user-attachments/assets/16e57431-1eab-4465-a4f8-856188b0f942" />\r\n\r\nPairing a remote with the `Sync` button will enable it to reestablish a connection on any button press.\r\nIt currently shows an ugly animation while the task runs async.\r\n<img width="189" height="72" alt="image" src="https://github.com/user-attachments/assets/076191ce-8312-4cdc-8223-14a4bb9eca2d" />\r\nIt\'s better than nothing and can be visually improved later.\r\n\r\n`Refresh` or `Continuous Scanning` are no longer required to connect paired remotes.\r\nPairing with Windows or the DolphinBar "just works" by assigning a slot to `Real Wii Remote`.\r\n\r\nAdditionally, I am constantly bombarded with ControllerInterface / WiimotePool related deadlocks.\r\nThis seems like an existing issue that fixing properly is probably going to take another PR.\r\nIt seems like it\'s SDL?\r\nedit: SDL\'s DirectInput handling has been disabled in #13982 \r\n\r\nTODO\r\n - [x] Make the `Sync` button not require also clicking `Refresh`.\r\n - [x] Make paired + reconnected Wii remotes work without using `Refresh`\r\n - [x] Test Dolphin with Wine (just in general, not Wii remote functionality)\r\n - [x] Clean up log spam from continuous opening of HID interfaces.\r\n - [x] Make the Sync / Reset buttons display some kind kind of feedback.', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13970/reactions', 'total_count': 4, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 1, 'confused': 0, 'heart': 3, 'rocket': 0, 'eyes': 0}, 'timeline_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13970/timeline', 'performed_via_github_app': None, 'state_reason': None}, 'comment': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments/3843649310', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/13970#issuecomment-3843649310', 'issue_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13970', 'id': 3843649310, 'node_id': 'IC_kwDOALCn2M7lGW8e', 'user': {'login': 'Trihy', 'id': 107453916, 'node_id': 'U_kgDOBmed3A', 'avatar_url': 'https://avatars.githubusercontent.com/u/107453916?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/Trihy', 'html_url': 'https://github.com/Trihy', 'followers_url': 'https://api.github.com/users/Trihy/followers', 'following_url': 'https://api.github.com/users/Trihy/following{/other_user}', 'gists_url': 'https://api.github.com/users/Trihy/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/Trihy/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/Trihy/subscriptions', 'organizations_url': 'https://api.github.com/users/Trihy/orgs', 'repos_url': 'https://api.github.com/users/Trihy/repos', 'events_url': 'https://api.github.com/users/Trihy/events{/privacy}', 'received_events_url': 'https://api.github.com/users/Trihy/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'created_at': '2026-02-03T20:58:41Z', 'updated_at': '2026-02-03T20:58:41Z', 'body': "Do you know why some bt dongles rejects the reconnection? Probably some security feature on authentication that wiimotes doesn't have.\r\n\r\nI been testing many built in BT and found the mediatek to have this problem. There are others. Some can fe fixed using another driver\r\n\r\nSo seems to be BT driver related. But maybe the BT dongle expect another response and we can change that. ", 'author_association': 'NONE', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments/3843649310/reactions', 'total_count': 0, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 0, 'confused': 0, 'heart': 0, 'rocket': 0, 'eyes': 0}, 'performed_via_github_app': None}, 'repository': {'id': 11577304, 'node_id': 'MDEwOlJlcG9zaXRvcnkxMTU3NzMwNA==', 'name': 'dolphin', 'full_name': 'dolphin-emu/dolphin', 'private': False, 'owner': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/dolphin-emu', 'html_url': 'https://github.com/dolphin-emu', 'followers_url': 'https://api.github.com/users/dolphin-emu/followers', 'following_url': 'https://api.github.com/users/dolphin-emu/following{/other_user}', 'gists_url': 'https://api.github.com/users/dolphin-emu/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/dolphin-emu/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/dolphin-emu/subscriptions', 'organizations_url': 'https://api.github.com/users/dolphin-emu/orgs', 'repos_url': 'https://api.github.com/users/dolphin-emu/repos', 'events_url': 'https://api.github.com/users/dolphin-emu/events{/privacy}', 'received_events_url': 'https://api.github.com/users/dolphin-emu/received_events', 'type': 'Organization', 'user_view_type': 'public', 'site_admin': False}, 'html_url': 'https://github.com/dolphin-emu/dolphin', 'description': 'Dolphin is a GameCube / Wii emulator, allowing you to play games for these two platforms on PC with improvements.', 'fork': False, 'url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'forks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/forks', 'keys_url': 'https://api.github.com/repos/dolphin-emu/dolphin/keys{/key_id}', 'collaborators_url': 'https://api.github.com/repos/dolphin-emu/dolphin/collaborators{/collaborator}', 'teams_url': 'https://api.github.com/repos/dolphin-emu/dolphin/teams', 'hooks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/hooks', 'issue_events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/events{/number}', 'events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/events', 'assignees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/assignees{/user}', 'branches_url': 'https://api.github.com/repos/dolphin-emu/dolphin/branches{/branch}', 'tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/tags', 'blobs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/blobs{/sha}', 'git_tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/tags{/sha}', 'git_refs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/refs{/sha}', 'trees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/trees{/sha}', 'statuses_url': 'https://api.github.com/repos/dolphin-emu/dolphin/statuses/{sha}', 'languages_url': 'https://api.github.com/repos/dolphin-emu/dolphin/languages', 'stargazers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/stargazers', 'contributors_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contributors', 'subscribers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscribers', 'subscription_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscription', 'commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/commits{/sha}', 'git_commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/commits{/sha}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/comments{/number}', 'issue_comment_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments{/number}', 'contents_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contents/{+path}', 'compare_url': 'https://api.github.com/repos/dolphin-emu/dolphin/compare/{base}...{head}', 'merges_url': 'https://api.github.com/repos/dolphin-emu/dolphin/merges', 'archive_url': 'https://api.github.com/repos/dolphin-emu/dolphin/{archive_format}{/ref}', 'downloads_url': 'https://api.github.com/repos/dolphin-emu/dolphin/downloads', 'issues_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues{/number}', 'pulls_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls{/number}', 'milestones_url': 'https://api.github.com/repos/dolphin-emu/dolphin/milestones{/number}', 'notifications_url': 'https://api.github.com/repos/dolphin-emu/dolphin/notifications{?since,all,participating}', 'labels_url': 'https://api.github.com/repos/dolphin-emu/dolphin/labels{/name}', 'releases_url': 'https://api.github.com/repos/dolphin-emu/dolphin/releases{/id}', 'deployments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/deployments', 'created_at': '2013-07-22T09:07:57Z', 'updated_at': '2026-02-03T18:03:13Z', 'pushed_at': '2026-02-03T04:26:13Z', 'git_url': 'git://github.com/dolphin-emu/dolphin.git', 'ssh_url': 'git@github.com:dolphin-emu/dolphin.git', 'clone_url': 'https://github.com/dolphin-emu/dolphin.git', 'svn_url': 'https://github.com/dolphin-emu/dolphin', 'homepage': 'https://dolphin-emu.org', 'size': 544014, 'stargazers_count': 14582, 'watchers_count': 14582, 'language': 'C++', 'has_issues': False, 'has_projects': False, 'has_downloads': True, 'has_wiki': True, 'has_pages': False, 'has_discussions': False, 'forks_count': 2966, 'mirror_url': None, 'archived': False, 'disabled': False, 'open_issues_count': 360, 'license': {'key': 'other', 'name': 'Other', 'spdx_id': 'NOASSERTION', 'url': None, 'node_id': 'MDc6TGljZW5zZTA='}, 'allow_forking': True, 'is_template': False, 'web_commit_signoff_required': False, 'topics': ['cpp', 'emulation', 'emulator', 'gamecube', 'wii'], 'visibility': 'public', 'forks': 2966, 'open_issues': 360, 'watchers': 14582, 'default_branch': 'master', 'custom_properties': {}}, 'organization': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'url': 'https://api.github.com/orgs/dolphin-emu', 'repos_url': 'https://api.github.com/orgs/dolphin-emu/repos', 'events_url': 'https://api.github.com/orgs/dolphin-emu/events', 'hooks_url': 'https://api.github.com/orgs/dolphin-emu/hooks', 'issues_url': 'https://api.github.com/orgs/dolphin-emu/issues', 'members_url': 'https://api.github.com/orgs/dolphin-emu/members{/member}', 'public_members_url': 'https://api.github.com/orgs/dolphin-emu/public_members{/member}', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'description': ''}, 'sender': {'login': 'Trihy', 'id': 107453916, 'node_id': 'U_kgDOBmed3A', 'avatar_url': 'https://avatars.githubusercontent.com/u/107453916?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/Trihy', 'html_url': 'https://github.com/Trihy', 'followers_url': 'https://api.github.com/users/Trihy/followers', 'following_url': 'https://api.github.com/users/Trihy/following{/other_user}', 'gists_url': 'https://api.github.com/users/Trihy/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/Trihy/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/Trihy/subscriptions', 'organizations_url': 'https://api.github.com/users/Trihy/orgs', 'repos_url': 'https://api.github.com/users/Trihy/repos', 'events_url': 'https://api.github.com/users/Trihy/events{/privacy}', 'received_events_url': 'https://api.github.com/users/Trihy/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'installation': {'id': 35437981, 'node_id': 'MDIzOkludGVncmF0aW9uSW5zdGFsbGF0aW9uMzU0Mzc5ODE='}}, 'type': 'gh_issue_comment'}
2026-02-03T17:22:19.677153 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'JMC47', 'action': 'edited', 'id': 13844, 'title': 'Core: Triforce support', 'url': 'https://github.com/dolphin-emu/dolphin/pull/13844#issuecomment-3842626451', 'safe_author': True, 'body': "All revisions of all games may not be supported initially. We'll continue to work on it as we go, but support should be considered a work in progress.", 'raw': {'action': 'edited', 'changes': {'body': {'from': "All revisions of all games may not be supported initially. We'll continue to work on it as we go, but support should a work in progress."}}, 'issue': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13844', 'repository_url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'labels_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13844/labels{/name}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13844/comments', 'events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13844/events', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/13844', 'id': 3286480944, 'node_id': 'PR_kwDOALCn2M6h2r1s', 'number': 13844, 'title': 'Core: Triforce support', 'user': {'login': 'crediar', 'id': 145270593, 'node_id': 'U_kgDOCKinQQ', 'avatar_url': 'https://avatars.githubusercontent.com/u/145270593?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/crediar', 'html_url': 'https://github.com/crediar', 'followers_url': 'https://api.github.com/users/crediar/followers', 'following_url': 'https://api.github.com/users/crediar/following{/other_user}', 'gists_url': 'https://api.github.com/users/crediar/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/crediar/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/crediar/subscriptions', 'organizations_url': 'https://api.github.com/users/crediar/orgs', 'repos_url': 'https://api.github.com/users/crediar/repos', 'events_url': 'https://api.github.com/users/crediar/events{/privacy}', 'received_events_url': 'https://api.github.com/users/crediar/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'labels': [], 'state': 'open', 'locked': False, 'assignee': None, 'assignees': [], 'milestone': None, 'comments': 102, 'created_at': '2025-08-02T23:06:09Z', 'updated_at': '2026-02-03T17:22:06Z', 'closed_at': None, 'author_association': 'NONE', 'type': None, 'active_lock_reason': None, 'draft': False, 'pull_request': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13844', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/13844', 'diff_url': 'https://github.com/dolphin-emu/dolphin/pull/13844.diff', 'patch_url': 'https://github.com/dolphin-emu/dolphin/pull/13844.patch', 'merged_at': None}, 'body': 'All the required changes to make Triforce games bootable.', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13844/reactions', 'total_count': 35, '+1': 4, '-1': 0, 'laugh': 0, 'hooray': 24, 'confused': 0, 'heart': 2, 'rocket': 5, 'eyes': 0}, 'timeline_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13844/timeline', 'performed_via_github_app': None, 'state_reason': None}, 'comment': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments/3842626451', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/13844#issuecomment-3842626451', 'issue_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13844', 'id': 3842626451, 'node_id': 'IC_kwDOALCn2M7lCdOT', 'user': {'login': 'JMC47', 'id': 6598209, 'node_id': 'MDQ6VXNlcjY1OTgyMDk=', 'avatar_url': 'https://avatars.githubusercontent.com/u/6598209?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/JMC47', 'html_url': 'https://github.com/JMC47', 'followers_url': 'https://api.github.com/users/JMC47/followers', 'following_url': 'https://api.github.com/users/JMC47/following{/other_user}', 'gists_url': 'https://api.github.com/users/JMC47/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/JMC47/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/JMC47/subscriptions', 'organizations_url': 'https://api.github.com/users/JMC47/orgs', 'repos_url': 'https://api.github.com/users/JMC47/repos', 'events_url': 'https://api.github.com/users/JMC47/events{/privacy}', 'received_events_url': 'https://api.github.com/users/JMC47/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'created_at': '2026-02-03T17:22:06Z', 'updated_at': '2026-02-03T17:22:17Z', 'body': "All revisions of all games may not be supported initially. We'll continue to work on it as we go, but support should be considered a work in progress.", 'author_association': 'MEMBER', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments/3842626451/reactions', 'total_count': 0, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 0, 'confused': 0, 'heart': 0, 'rocket': 0, 'eyes': 0}, 'performed_via_github_app': None}, 'repository': {'id': 11577304, 'node_id': 'MDEwOlJlcG9zaXRvcnkxMTU3NzMwNA==', 'name': 'dolphin', 'full_name': 'dolphin-emu/dolphin', 'private': False, 'owner': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/dolphin-emu', 'html_url': 'https://github.com/dolphin-emu', 'followers_url': 'https://api.github.com/users/dolphin-emu/followers', 'following_url': 'https://api.github.com/users/dolphin-emu/following{/other_user}', 'gists_url': 'https://api.github.com/users/dolphin-emu/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/dolphin-emu/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/dolphin-emu/subscriptions', 'organizations_url': 'https://api.github.com/users/dolphin-emu/orgs', 'repos_url': 'https://api.github.com/users/dolphin-emu/repos', 'events_url': 'https://api.github.com/users/dolphin-emu/events{/privacy}', 'received_events_url': 'https://api.github.com/users/dolphin-emu/received_events', 'type': 'Organization', 'user_view_type': 'public', 'site_admin': False}, 'html_url': 'https://github.com/dolphin-emu/dolphin', 'description': 'Dolphin is a GameCube / Wii emulator, allowing you to play games for these two platforms on PC with improvements.', 'fork': False, 'url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'forks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/forks', 'keys_url': 'https://api.github.com/repos/dolphin-emu/dolphin/keys{/key_id}', 'collaborators_url': 'https://api.github.com/repos/dolphin-emu/dolphin/collaborators{/collaborator}', 'teams_url': 'https://api.github.com/repos/dolphin-emu/dolphin/teams', 'hooks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/hooks', 'issue_events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/events{/number}', 'events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/events', 'assignees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/assignees{/user}', 'branches_url': 'https://api.github.com/repos/dolphin-emu/dolphin/branches{/branch}', 'tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/tags', 'blobs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/blobs{/sha}', 'git_tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/tags{/sha}', 'git_refs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/refs{/sha}', 'trees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/trees{/sha}', 'statuses_url': 'https://api.github.com/repos/dolphin-emu/dolphin/statuses/{sha}', 'languages_url': 'https://api.github.com/repos/dolphin-emu/dolphin/languages', 'stargazers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/stargazers', 'contributors_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contributors', 'subscribers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscribers', 'subscription_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscription', 'commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/commits{/sha}', 'git_commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/commits{/sha}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/comments{/number}', 'issue_comment_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments{/number}', 'contents_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contents/{+path}', 'compare_url': 'https://api.github.com/repos/dolphin-emu/dolphin/compare/{base}...{head}', 'merges_url': 'https://api.github.com/repos/dolphin-emu/dolphin/merges', 'archive_url': 'https://api.github.com/repos/dolphin-emu/dolphin/{archive_format}{/ref}', 'downloads_url': 'https://api.github.com/repos/dolphin-emu/dolphin/downloads', 'issues_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues{/number}', 'pulls_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls{/number}', 'milestones_url': 'https://api.github.com/repos/dolphin-emu/dolphin/milestones{/number}', 'notifications_url': 'https://api.github.com/repos/dolphin-emu/dolphin/notifications{?since,all,participating}', 'labels_url': 'https://api.github.com/repos/dolphin-emu/dolphin/labels{/name}', 'releases_url': 'https://api.github.com/repos/dolphin-emu/dolphin/releases{/id}', 'deployments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/deployments', 'created_at': '2013-07-22T09:07:57Z', 'updated_at': '2026-02-03T16:16:52Z', 'pushed_at': '2026-02-03T04:26:13Z', 'git_url': 'git://github.com/dolphin-emu/dolphin.git', 'ssh_url': 'git@github.com:dolphin-emu/dolphin.git', 'clone_url': 'https://github.com/dolphin-emu/dolphin.git', 'svn_url': 'https://github.com/dolphin-emu/dolphin', 'homepage': 'https://dolphin-emu.org', 'size': 544014, 'stargazers_count': 14581, 'watchers_count': 14581, 'language': 'C++', 'has_issues': False, 'has_projects': False, 'has_downloads': True, 'has_wiki': True, 'has_pages': False, 'has_discussions': False, 'forks_count': 2965, 'mirror_url': None, 'archived': False, 'disabled': False, 'open_issues_count': 360, 'license': {'key': 'other', 'name': 'Other', 'spdx_id': 'NOASSERTION', 'url': None, 'node_id': 'MDc6TGljZW5zZTA='}, 'allow_forking': True, 'is_template': False, 'web_commit_signoff_required': False, 'topics': ['cpp', 'emulation', 'emulator', 'gamecube', 'wii'], 'visibility': 'public', 'forks': 2965, 'open_issues': 360, 'watchers': 14581, 'default_branch': 'master', 'custom_properties': {}}, 'organization': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'url': 'https://api.github.com/orgs/dolphin-emu', 'repos_url': 'https://api.github.com/orgs/dolphin-emu/repos', 'events_url': 'https://api.github.com/orgs/dolphin-emu/events', 'hooks_url': 'https://api.github.com/orgs/dolphin-emu/hooks', 'issues_url': 'https://api.github.com/orgs/dolphin-emu/issues', 'members_url': 'https://api.github.com/orgs/dolphin-emu/members{/member}', 'public_members_url': 'https://api.github.com/orgs/dolphin-emu/public_members{/member}', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'description': ''}, 'sender': {'login': 'JMC47', 'id': 6598209, 'node_id': 'MDQ6VXNlcjY1OTgyMDk=', 'avatar_url': 'https://avatars.githubusercontent.com/u/6598209?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/JMC47', 'html_url': 'https://github.com/JMC47', 'followers_url': 'https://api.github.com/users/JMC47/followers', 'following_url': 'https://api.github.com/users/JMC47/following{/other_user}', 'gists_url': 'https://api.github.com/users/JMC47/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/JMC47/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/JMC47/subscriptions', 'organizations_url': 'https://api.github.com/users/JMC47/orgs', 'repos_url': 'https://api.github.com/users/JMC47/repos', 'events_url': 'https://api.github.com/users/JMC47/events{/privacy}', 'received_events_url': 'https://api.github.com/users/JMC47/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'installation': {'id': 35437981, 'node_id': 'MDIzOkludGVncmF0aW9uSW5zdGFsbGF0aW9uMzU0Mzc5ODE='}}, 'type': 'gh_issue_comment'}
2026-02-03T17:22:09.068259 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'JMC47', 'action': 'created', 'id': 13844, 'title': 'Core: Triforce support', 'url': 'https://github.com/dolphin-emu/dolphin/pull/13844#issuecomment-3842626451', 'safe_author': True, 'body': "All revisions of all games may not be supported initially. We'll continue to work on it as we go, but support should a work in progress.", 'raw': {'action': 'created', 'issue': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13844', 'repository_url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'labels_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13844/labels{/name}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13844/comments', 'events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13844/events', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/13844', 'id': 3286480944, 'node_id': 'PR_kwDOALCn2M6h2r1s', 'number': 13844, 'title': 'Core: Triforce support', 'user': {'login': 'crediar', 'id': 145270593, 'node_id': 'U_kgDOCKinQQ', 'avatar_url': 'https://avatars.githubusercontent.com/u/145270593?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/crediar', 'html_url': 'https://github.com/crediar', 'followers_url': 'https://api.github.com/users/crediar/followers', 'following_url': 'https://api.github.com/users/crediar/following{/other_user}', 'gists_url': 'https://api.github.com/users/crediar/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/crediar/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/crediar/subscriptions', 'organizations_url': 'https://api.github.com/users/crediar/orgs', 'repos_url': 'https://api.github.com/users/crediar/repos', 'events_url': 'https://api.github.com/users/crediar/events{/privacy}', 'received_events_url': 'https://api.github.com/users/crediar/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'labels': [], 'state': 'open', 'locked': False, 'assignee': None, 'assignees': [], 'milestone': None, 'comments': 102, 'created_at': '2025-08-02T23:06:09Z', 'updated_at': '2026-02-03T17:22:06Z', 'closed_at': None, 'author_association': 'NONE', 'type': None, 'active_lock_reason': None, 'draft': False, 'pull_request': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13844', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/13844', 'diff_url': 'https://github.com/dolphin-emu/dolphin/pull/13844.diff', 'patch_url': 'https://github.com/dolphin-emu/dolphin/pull/13844.patch', 'merged_at': None}, 'body': 'All the required changes to make Triforce games bootable.', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13844/reactions', 'total_count': 35, '+1': 4, '-1': 0, 'laugh': 0, 'hooray': 24, 'confused': 0, 'heart': 2, 'rocket': 5, 'eyes': 0}, 'timeline_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13844/timeline', 'performed_via_github_app': None, 'state_reason': None}, 'comment': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments/3842626451', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/13844#issuecomment-3842626451', 'issue_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13844', 'id': 3842626451, 'node_id': 'IC_kwDOALCn2M7lCdOT', 'user': {'login': 'JMC47', 'id': 6598209, 'node_id': 'MDQ6VXNlcjY1OTgyMDk=', 'avatar_url': 'https://avatars.githubusercontent.com/u/6598209?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/JMC47', 'html_url': 'https://github.com/JMC47', 'followers_url': 'https://api.github.com/users/JMC47/followers', 'following_url': 'https://api.github.com/users/JMC47/following{/other_user}', 'gists_url': 'https://api.github.com/users/JMC47/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/JMC47/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/JMC47/subscriptions', 'organizations_url': 'https://api.github.com/users/JMC47/orgs', 'repos_url': 'https://api.github.com/users/JMC47/repos', 'events_url': 'https://api.github.com/users/JMC47/events{/privacy}', 'received_events_url': 'https://api.github.com/users/JMC47/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'created_at': '2026-02-03T17:22:06Z', 'updated_at': '2026-02-03T17:22:06Z', 'body': "All revisions of all games may not be supported initially. We'll continue to work on it as we go, but support should a work in progress.", 'author_association': 'MEMBER', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments/3842626451/reactions', 'total_count': 0, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 0, 'confused': 0, 'heart': 0, 'rocket': 0, 'eyes': 0}, 'performed_via_github_app': None}, 'repository': {'id': 11577304, 'node_id': 'MDEwOlJlcG9zaXRvcnkxMTU3NzMwNA==', 'name': 'dolphin', 'full_name': 'dolphin-emu/dolphin', 'private': False, 'owner': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/dolphin-emu', 'html_url': 'https://github.com/dolphin-emu', 'followers_url': 'https://api.github.com/users/dolphin-emu/followers', 'following_url': 'https://api.github.com/users/dolphin-emu/following{/other_user}', 'gists_url': 'https://api.github.com/users/dolphin-emu/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/dolphin-emu/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/dolphin-emu/subscriptions', 'organizations_url': 'https://api.github.com/users/dolphin-emu/orgs', 'repos_url': 'https://api.github.com/users/dolphin-emu/repos', 'events_url': 'https://api.github.com/users/dolphin-emu/events{/privacy}', 'received_events_url': 'https://api.github.com/users/dolphin-emu/received_events', 'type': 'Organization', 'user_view_type': 'public', 'site_admin': False}, 'html_url': 'https://github.com/dolphin-emu/dolphin', 'description': 'Dolphin is a GameCube / Wii emulator, allowing you to play games for these two platforms on PC with improvements.', 'fork': False, 'url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'forks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/forks', 'keys_url': 'https://api.github.com/repos/dolphin-emu/dolphin/keys{/key_id}', 'collaborators_url': 'https://api.github.com/repos/dolphin-emu/dolphin/collaborators{/collaborator}', 'teams_url': 'https://api.github.com/repos/dolphin-emu/dolphin/teams', 'hooks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/hooks', 'issue_events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/events{/number}', 'events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/events', 'assignees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/assignees{/user}', 'branches_url': 'https://api.github.com/repos/dolphin-emu/dolphin/branches{/branch}', 'tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/tags', 'blobs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/blobs{/sha}', 'git_tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/tags{/sha}', 'git_refs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/refs{/sha}', 'trees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/trees{/sha}', 'statuses_url': 'https://api.github.com/repos/dolphin-emu/dolphin/statuses/{sha}', 'languages_url': 'https://api.github.com/repos/dolphin-emu/dolphin/languages', 'stargazers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/stargazers', 'contributors_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contributors', 'subscribers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscribers', 'subscription_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscription', 'commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/commits{/sha}', 'git_commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/commits{/sha}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/comments{/number}', 'issue_comment_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments{/number}', 'contents_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contents/{+path}', 'compare_url': 'https://api.github.com/repos/dolphin-emu/dolphin/compare/{base}...{head}', 'merges_url': 'https://api.github.com/repos/dolphin-emu/dolphin/merges', 'archive_url': 'https://api.github.com/repos/dolphin-emu/dolphin/{archive_format}{/ref}', 'downloads_url': 'https://api.github.com/repos/dolphin-emu/dolphin/downloads', 'issues_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues{/number}', 'pulls_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls{/number}', 'milestones_url': 'https://api.github.com/repos/dolphin-emu/dolphin/milestones{/number}', 'notifications_url': 'https://api.github.com/repos/dolphin-emu/dolphin/notifications{?since,all,participating}', 'labels_url': 'https://api.github.com/repos/dolphin-emu/dolphin/labels{/name}', 'releases_url': 'https://api.github.com/repos/dolphin-emu/dolphin/releases{/id}', 'deployments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/deployments', 'created_at': '2013-07-22T09:07:57Z', 'updated_at': '2026-02-03T16:16:52Z', 'pushed_at': '2026-02-03T04:26:13Z', 'git_url': 'git://github.com/dolphin-emu/dolphin.git', 'ssh_url': 'git@github.com:dolphin-emu/dolphin.git', 'clone_url': 'https://github.com/dolphin-emu/dolphin.git', 'svn_url': 'https://github.com/dolphin-emu/dolphin', 'homepage': 'https://dolphin-emu.org', 'size': 544014, 'stargazers_count': 14581, 'watchers_count': 14581, 'language': 'C++', 'has_issues': False, 'has_projects': False, 'has_downloads': True, 'has_wiki': True, 'has_pages': False, 'has_discussions': False, 'forks_count': 2965, 'mirror_url': None, 'archived': False, 'disabled': False, 'open_issues_count': 360, 'license': {'key': 'other', 'name': 'Other', 'spdx_id': 'NOASSERTION', 'url': None, 'node_id': 'MDc6TGljZW5zZTA='}, 'allow_forking': True, 'is_template': False, 'web_commit_signoff_required': False, 'topics': ['cpp', 'emulation', 'emulator', 'gamecube', 'wii'], 'visibility': 'public', 'forks': 2965, 'open_issues': 360, 'watchers': 14581, 'default_branch': 'master', 'custom_properties': {}}, 'organization': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'url': 'https://api.github.com/orgs/dolphin-emu', 'repos_url': 'https://api.github.com/orgs/dolphin-emu/repos', 'events_url': 'https://api.github.com/orgs/dolphin-emu/events', 'hooks_url': 'https://api.github.com/orgs/dolphin-emu/hooks', 'issues_url': 'https://api.github.com/orgs/dolphin-emu/issues', 'members_url': 'https://api.github.com/orgs/dolphin-emu/members{/member}', 'public_members_url': 'https://api.github.com/orgs/dolphin-emu/public_members{/member}', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'description': ''}, 'sender': {'login': 'JMC47', 'id': 6598209, 'node_id': 'MDQ6VXNlcjY1OTgyMDk=', 'avatar_url': 'https://avatars.githubusercontent.com/u/6598209?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/JMC47', 'html_url': 'https://github.com/JMC47', 'followers_url': 'https://api.github.com/users/JMC47/followers', 'following_url': 'https://api.github.com/users/JMC47/following{/other_user}', 'gists_url': 'https://api.github.com/users/JMC47/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/JMC47/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/JMC47/subscriptions', 'organizations_url': 'https://api.github.com/users/JMC47/orgs', 'repos_url': 'https://api.github.com/users/JMC47/repos', 'events_url': 'https://api.github.com/users/JMC47/events{/privacy}', 'received_events_url': 'https://api.github.com/users/JMC47/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'installation': {'id': 35437981, 'node_id': 'MDIzOkludGVncmF0aW9uSW5zdGFsbGF0aW9uMzU0Mzc5ODE='}}, 'type': 'gh_issue_comment'}
2026-02-03T17:08:19.684924 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'pizzzza19', 'action': 'created', 'id': 13844, 'title': 'Core: Triforce support', 'url': 'https://github.com/dolphin-emu/dolphin/pull/13844#issuecomment-3842552882', 'safe_author': False, 'body': 'Tested with manually built exe as guided by official dolphin readme.\nPrevious PR also had a same error. It is a known issue and may have discussed by other people outside this PR.', 'raw': {'action': 'created', 'issue': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13844', 'repository_url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'labels_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13844/labels{/name}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13844/comments', 'events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13844/events', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/13844', 'id': 3286480944, 'node_id': 'PR_kwDOALCn2M6h2r1s', 'number': 13844, 'title': 'Core: Triforce support', 'user': {'login': 'crediar', 'id': 145270593, 'node_id': 'U_kgDOCKinQQ', 'avatar_url': 'https://avatars.githubusercontent.com/u/145270593?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/crediar', 'html_url': 'https://github.com/crediar', 'followers_url': 'https://api.github.com/users/crediar/followers', 'following_url': 'https://api.github.com/users/crediar/following{/other_user}', 'gists_url': 'https://api.github.com/users/crediar/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/crediar/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/crediar/subscriptions', 'organizations_url': 'https://api.github.com/users/crediar/orgs', 'repos_url': 'https://api.github.com/users/crediar/repos', 'events_url': 'https://api.github.com/users/crediar/events{/privacy}', 'received_events_url': 'https://api.github.com/users/crediar/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'labels': [], 'state': 'open', 'locked': False, 'assignee': None, 'assignees': [], 'milestone': None, 'comments': 101, 'created_at': '2025-08-02T23:06:09Z', 'updated_at': '2026-02-03T17:08:17Z', 'closed_at': None, 'author_association': 'NONE', 'type': None, 'active_lock_reason': None, 'draft': False, 'pull_request': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13844', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/13844', 'diff_url': 'https://github.com/dolphin-emu/dolphin/pull/13844.diff', 'patch_url': 'https://github.com/dolphin-emu/dolphin/pull/13844.patch', 'merged_at': None}, 'body': 'All the required changes to make Triforce games bootable.', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13844/reactions', 'total_count': 35, '+1': 4, '-1': 0, 'laugh': 0, 'hooray': 24, 'confused': 0, 'heart': 2, 'rocket': 5, 'eyes': 0}, 'timeline_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13844/timeline', 'performed_via_github_app': None, 'state_reason': None}, 'comment': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments/3842552882', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/13844#issuecomment-3842552882', 'issue_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13844', 'id': 3842552882, 'node_id': 'IC_kwDOALCn2M7lCLQy', 'user': {'login': 'pizzzza19', 'id': 183198342, 'node_id': 'U_kgDOCutihg', 'avatar_url': 'https://avatars.githubusercontent.com/u/183198342?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/pizzzza19', 'html_url': 'https://github.com/pizzzza19', 'followers_url': 'https://api.github.com/users/pizzzza19/followers', 'following_url': 'https://api.github.com/users/pizzzza19/following{/other_user}', 'gists_url': 'https://api.github.com/users/pizzzza19/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/pizzzza19/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/pizzzza19/subscriptions', 'organizations_url': 'https://api.github.com/users/pizzzza19/orgs', 'repos_url': 'https://api.github.com/users/pizzzza19/repos', 'events_url': 'https://api.github.com/users/pizzzza19/events{/privacy}', 'received_events_url': 'https://api.github.com/users/pizzzza19/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'created_at': '2026-02-03T17:08:17Z', 'updated_at': '2026-02-03T17:08:17Z', 'body': 'Tested with manually built exe as guided by official dolphin readme.\nPrevious PR also had a same error. It is a known issue and may have discussed by other people outside this PR.', 'author_association': 'NONE', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments/3842552882/reactions', 'total_count': 0, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 0, 'confused': 0, 'heart': 0, 'rocket': 0, 'eyes': 0}, 'performed_via_github_app': None}, 'repository': {'id': 11577304, 'node_id': 'MDEwOlJlcG9zaXRvcnkxMTU3NzMwNA==', 'name': 'dolphin', 'full_name': 'dolphin-emu/dolphin', 'private': False, 'owner': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/dolphin-emu', 'html_url': 'https://github.com/dolphin-emu', 'followers_url': 'https://api.github.com/users/dolphin-emu/followers', 'following_url': 'https://api.github.com/users/dolphin-emu/following{/other_user}', 'gists_url': 'https://api.github.com/users/dolphin-emu/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/dolphin-emu/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/dolphin-emu/subscriptions', 'organizations_url': 'https://api.github.com/users/dolphin-emu/orgs', 'repos_url': 'https://api.github.com/users/dolphin-emu/repos', 'events_url': 'https://api.github.com/users/dolphin-emu/events{/privacy}', 'received_events_url': 'https://api.github.com/users/dolphin-emu/received_events', 'type': 'Organization', 'user_view_type': 'public', 'site_admin': False}, 'html_url': 'https://github.com/dolphin-emu/dolphin', 'description': 'Dolphin is a GameCube / Wii emulator, allowing you to play games for these two platforms on PC with improvements.', 'fork': False, 'url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'forks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/forks', 'keys_url': 'https://api.github.com/repos/dolphin-emu/dolphin/keys{/key_id}', 'collaborators_url': 'https://api.github.com/repos/dolphin-emu/dolphin/collaborators{/collaborator}', 'teams_url': 'https://api.github.com/repos/dolphin-emu/dolphin/teams', 'hooks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/hooks', 'issue_events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/events{/number}', 'events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/events', 'assignees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/assignees{/user}', 'branches_url': 'https://api.github.com/repos/dolphin-emu/dolphin/branches{/branch}', 'tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/tags', 'blobs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/blobs{/sha}', 'git_tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/tags{/sha}', 'git_refs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/refs{/sha}', 'trees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/trees{/sha}', 'statuses_url': 'https://api.github.com/repos/dolphin-emu/dolphin/statuses/{sha}', 'languages_url': 'https://api.github.com/repos/dolphin-emu/dolphin/languages', 'stargazers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/stargazers', 'contributors_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contributors', 'subscribers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscribers', 'subscription_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscription', 'commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/commits{/sha}', 'git_commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/commits{/sha}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/comments{/number}', 'issue_comment_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments{/number}', 'contents_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contents/{+path}', 'compare_url': 'https://api.github.com/repos/dolphin-emu/dolphin/compare/{base}...{head}', 'merges_url': 'https://api.github.com/repos/dolphin-emu/dolphin/merges', 'archive_url': 'https://api.github.com/repos/dolphin-emu/dolphin/{archive_format}{/ref}', 'downloads_url': 'https://api.github.com/repos/dolphin-emu/dolphin/downloads', 'issues_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues{/number}', 'pulls_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls{/number}', 'milestones_url': 'https://api.github.com/repos/dolphin-emu/dolphin/milestones{/number}', 'notifications_url': 'https://api.github.com/repos/dolphin-emu/dolphin/notifications{?since,all,participating}', 'labels_url': 'https://api.github.com/repos/dolphin-emu/dolphin/labels{/name}', 'releases_url': 'https://api.github.com/repos/dolphin-emu/dolphin/releases{/id}', 'deployments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/deployments', 'created_at': '2013-07-22T09:07:57Z', 'updated_at': '2026-02-03T16:16:52Z', 'pushed_at': '2026-02-03T04:26:13Z', 'git_url': 'git://github.com/dolphin-emu/dolphin.git', 'ssh_url': 'git@github.com:dolphin-emu/dolphin.git', 'clone_url': 'https://github.com/dolphin-emu/dolphin.git', 'svn_url': 'https://github.com/dolphin-emu/dolphin', 'homepage': 'https://dolphin-emu.org', 'size': 544014, 'stargazers_count': 14581, 'watchers_count': 14581, 'language': 'C++', 'has_issues': False, 'has_projects': False, 'has_downloads': True, 'has_wiki': True, 'has_pages': False, 'has_discussions': False, 'forks_count': 2965, 'mirror_url': None, 'archived': False, 'disabled': False, 'open_issues_count': 360, 'license': {'key': 'other', 'name': 'Other', 'spdx_id': 'NOASSERTION', 'url': None, 'node_id': 'MDc6TGljZW5zZTA='}, 'allow_forking': True, 'is_template': False, 'web_commit_signoff_required': False, 'topics': ['cpp', 'emulation', 'emulator', 'gamecube', 'wii'], 'visibility': 'public', 'forks': 2965, 'open_issues': 360, 'watchers': 14581, 'default_branch': 'master', 'custom_properties': {}}, 'organization': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'url': 'https://api.github.com/orgs/dolphin-emu', 'repos_url': 'https://api.github.com/orgs/dolphin-emu/repos', 'events_url': 'https://api.github.com/orgs/dolphin-emu/events', 'hooks_url': 'https://api.github.com/orgs/dolphin-emu/hooks', 'issues_url': 'https://api.github.com/orgs/dolphin-emu/issues', 'members_url': 'https://api.github.com/orgs/dolphin-emu/members{/member}', 'public_members_url': 'https://api.github.com/orgs/dolphin-emu/public_members{/member}', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'description': ''}, 'sender': {'login': 'pizzzza19', 'id': 183198342, 'node_id': 'U_kgDOCutihg', 'avatar_url': 'https://avatars.githubusercontent.com/u/183198342?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/pizzzza19', 'html_url': 'https://github.com/pizzzza19', 'followers_url': 'https://api.github.com/users/pizzzza19/followers', 'following_url': 'https://api.github.com/users/pizzzza19/following{/other_user}', 'gists_url': 'https://api.github.com/users/pizzzza19/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/pizzzza19/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/pizzzza19/subscriptions', 'organizations_url': 'https://api.github.com/users/pizzzza19/orgs', 'repos_url': 'https://api.github.com/users/pizzzza19/repos', 'events_url': 'https://api.github.com/users/pizzzza19/events{/privacy}', 'received_events_url': 'https://api.github.com/users/pizzzza19/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'installation': {'id': 35437981, 'node_id': 'MDIzOkludGVncmF0aW9uSW5zdGFsbGF0aW9uMzU0Mzc5ODE='}}, 'type': 'gh_issue_comment'}
2026-02-03T16:31:29.616382 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'sepalani', 'action': 'created', 'id': 13844, 'title': 'Core: Triforce support', 'url': 'https://github.com/dolphin-emu/dolphin/pull/13844#issuecomment-3842350109', 'safe_author': True, 'body': '> Hello I built .exe with your latest code. it is a long journey to add triforce support but there are errors with some ROMs. \r\n[GEYJ6E] VS2002: Game ID duplicated with JPN and EXP region. JPN rom will be overwritten as EXP mode. (removed Japan team and license logos) \r\n[GHNJ6E] VS4 VerA (JPN) : Game frozen at demo screen or entering team select screen. This will only happen with JPN rom. It is well known that specific gekko code can skip boot error, but above cannot pass without fixing the main dolphin code.\r\n\r\n@pizzzza19 \r\nDid these games worked at some point with this PR? Do these games display in Dolphin\'s log window (with the highest verbosity enabled) error messages that can hint us regarding which pieces are missing? Does the issue occur with the build provided by [Dolphin\'s buildbot](https://dolphin.ci/#/builders/2/builds/8918)?\r\n\r\n<a href="https://dolphin.ci/#/builders/2/builds/8918"><img width="1427" height="486" alt="Image" src="https://github.com/user-attachments/assets/480e061d-edd0-4836-bdab-e87b46c5d60a" /></a>\r\n\r\n@JMC47 \r\nI don\'t have any Triforce games so you\'re probably more knowledgeable than me regarding the above games.', 'raw': {'action': 'created', 'issue': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13844', 'repository_url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'labels_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13844/labels{/name}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13844/comments', 'events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13844/events', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/13844', 'id': 3286480944, 'node_id': 'PR_kwDOALCn2M6h2r1s', 'number': 13844, 'title': 'Core: Triforce support', 'user': {'login': 'crediar', 'id': 145270593, 'node_id': 'U_kgDOCKinQQ', 'avatar_url': 'https://avatars.githubusercontent.com/u/145270593?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/crediar', 'html_url': 'https://github.com/crediar', 'followers_url': 'https://api.github.com/users/crediar/followers', 'following_url': 'https://api.github.com/users/crediar/following{/other_user}', 'gists_url': 'https://api.github.com/users/crediar/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/crediar/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/crediar/subscriptions', 'organizations_url': 'https://api.github.com/users/crediar/orgs', 'repos_url': 'https://api.github.com/users/crediar/repos', 'events_url': 'https://api.github.com/users/crediar/events{/privacy}', 'received_events_url': 'https://api.github.com/users/crediar/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'labels': [], 'state': 'open', 'locked': False, 'assignee': None, 'assignees': [], 'milestone': None, 'comments': 100, 'created_at': '2025-08-02T23:06:09Z', 'updated_at': '2026-02-03T16:31:27Z', 'closed_at': None, 'author_association': 'NONE', 'type': None, 'active_lock_reason': None, 'draft': False, 'pull_request': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13844', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/13844', 'diff_url': 'https://github.com/dolphin-emu/dolphin/pull/13844.diff', 'patch_url': 'https://github.com/dolphin-emu/dolphin/pull/13844.patch', 'merged_at': None}, 'body': 'All the required changes to make Triforce games bootable.', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13844/reactions', 'total_count': 35, '+1': 4, '-1': 0, 'laugh': 0, 'hooray': 24, 'confused': 0, 'heart': 2, 'rocket': 5, 'eyes': 0}, 'timeline_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13844/timeline', 'performed_via_github_app': None, 'state_reason': None}, 'comment': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments/3842350109', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/13844#issuecomment-3842350109', 'issue_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13844', 'id': 3842350109, 'node_id': 'IC_kwDOALCn2M7lBZwd', 'user': {'login': 'sepalani', 'id': 7890055, 'node_id': 'MDQ6VXNlcjc4OTAwNTU=', 'avatar_url': 'https://avatars.githubusercontent.com/u/7890055?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/sepalani', 'html_url': 'https://github.com/sepalani', 'followers_url': 'https://api.github.com/users/sepalani/followers', 'following_url': 'https://api.github.com/users/sepalani/following{/other_user}', 'gists_url': 'https://api.github.com/users/sepalani/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/sepalani/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/sepalani/subscriptions', 'organizations_url': 'https://api.github.com/users/sepalani/orgs', 'repos_url': 'https://api.github.com/users/sepalani/repos', 'events_url': 'https://api.github.com/users/sepalani/events{/privacy}', 'received_events_url': 'https://api.github.com/users/sepalani/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'created_at': '2026-02-03T16:31:27Z', 'updated_at': '2026-02-03T16:31:27Z', 'body': '> Hello I built .exe with your latest code. it is a long journey to add triforce support but there are errors with some ROMs. \r\n[GEYJ6E] VS2002: Game ID duplicated with JPN and EXP region. JPN rom will be overwritten as EXP mode. (removed Japan team and license logos) \r\n[GHNJ6E] VS4 VerA (JPN) : Game frozen at demo screen or entering team select screen. This will only happen with JPN rom. It is well known that specific gekko code can skip boot error, but above cannot pass without fixing the main dolphin code.\r\n\r\n@pizzzza19 \r\nDid these games worked at some point with this PR? Do these games display in Dolphin\'s log window (with the highest verbosity enabled) error messages that can hint us regarding which pieces are missing? Does the issue occur with the build provided by [Dolphin\'s buildbot](https://dolphin.ci/#/builders/2/builds/8918)?\r\n\r\n<a href="https://dolphin.ci/#/builders/2/builds/8918"><img width="1427" height="486" alt="Image" src="https://github.com/user-attachments/assets/480e061d-edd0-4836-bdab-e87b46c5d60a" /></a>\r\n\r\n@JMC47 \r\nI don\'t have any Triforce games so you\'re probably more knowledgeable than me regarding the above games.', 'author_association': 'MEMBER', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments/3842350109/reactions', 'total_count': 0, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 0, 'confused': 0, 'heart': 0, 'rocket': 0, 'eyes': 0}, 'performed_via_github_app': None}, 'repository': {'id': 11577304, 'node_id': 'MDEwOlJlcG9zaXRvcnkxMTU3NzMwNA==', 'name': 'dolphin', 'full_name': 'dolphin-emu/dolphin', 'private': False, 'owner': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/dolphin-emu', 'html_url': 'https://github.com/dolphin-emu', 'followers_url': 'https://api.github.com/users/dolphin-emu/followers', 'following_url': 'https://api.github.com/users/dolphin-emu/following{/other_user}', 'gists_url': 'https://api.github.com/users/dolphin-emu/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/dolphin-emu/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/dolphin-emu/subscriptions', 'organizations_url': 'https://api.github.com/users/dolphin-emu/orgs', 'repos_url': 'https://api.github.com/users/dolphin-emu/repos', 'events_url': 'https://api.github.com/users/dolphin-emu/events{/privacy}', 'received_events_url': 'https://api.github.com/users/dolphin-emu/received_events', 'type': 'Organization', 'user_view_type': 'public', 'site_admin': False}, 'html_url': 'https://github.com/dolphin-emu/dolphin', 'description': 'Dolphin is a GameCube / Wii emulator, allowing you to play games for these two platforms on PC with improvements.', 'fork': False, 'url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'forks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/forks', 'keys_url': 'https://api.github.com/repos/dolphin-emu/dolphin/keys{/key_id}', 'collaborators_url': 'https://api.github.com/repos/dolphin-emu/dolphin/collaborators{/collaborator}', 'teams_url': 'https://api.github.com/repos/dolphin-emu/dolphin/teams', 'hooks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/hooks', 'issue_events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/events{/number}', 'events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/events', 'assignees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/assignees{/user}', 'branches_url': 'https://api.github.com/repos/dolphin-emu/dolphin/branches{/branch}', 'tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/tags', 'blobs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/blobs{/sha}', 'git_tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/tags{/sha}', 'git_refs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/refs{/sha}', 'trees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/trees{/sha}', 'statuses_url': 'https://api.github.com/repos/dolphin-emu/dolphin/statuses/{sha}', 'languages_url': 'https://api.github.com/repos/dolphin-emu/dolphin/languages', 'stargazers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/stargazers', 'contributors_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contributors', 'subscribers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscribers', 'subscription_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscription', 'commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/commits{/sha}', 'git_commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/commits{/sha}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/comments{/number}', 'issue_comment_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments{/number}', 'contents_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contents/{+path}', 'compare_url': 'https://api.github.com/repos/dolphin-emu/dolphin/compare/{base}...{head}', 'merges_url': 'https://api.github.com/repos/dolphin-emu/dolphin/merges', 'archive_url': 'https://api.github.com/repos/dolphin-emu/dolphin/{archive_format}{/ref}', 'downloads_url': 'https://api.github.com/repos/dolphin-emu/dolphin/downloads', 'issues_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues{/number}', 'pulls_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls{/number}', 'milestones_url': 'https://api.github.com/repos/dolphin-emu/dolphin/milestones{/number}', 'notifications_url': 'https://api.github.com/repos/dolphin-emu/dolphin/notifications{?since,all,participating}', 'labels_url': 'https://api.github.com/repos/dolphin-emu/dolphin/labels{/name}', 'releases_url': 'https://api.github.com/repos/dolphin-emu/dolphin/releases{/id}', 'deployments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/deployments', 'created_at': '2013-07-22T09:07:57Z', 'updated_at': '2026-02-03T16:16:52Z', 'pushed_at': '2026-02-03T04:26:13Z', 'git_url': 'git://github.com/dolphin-emu/dolphin.git', 'ssh_url': 'git@github.com:dolphin-emu/dolphin.git', 'clone_url': 'https://github.com/dolphin-emu/dolphin.git', 'svn_url': 'https://github.com/dolphin-emu/dolphin', 'homepage': 'https://dolphin-emu.org', 'size': 544014, 'stargazers_count': 14581, 'watchers_count': 14581, 'language': 'C++', 'has_issues': False, 'has_projects': False, 'has_downloads': True, 'has_wiki': True, 'has_pages': False, 'has_discussions': False, 'forks_count': 2965, 'mirror_url': None, 'archived': False, 'disabled': False, 'open_issues_count': 360, 'license': {'key': 'other', 'name': 'Other', 'spdx_id': 'NOASSERTION', 'url': None, 'node_id': 'MDc6TGljZW5zZTA='}, 'allow_forking': True, 'is_template': False, 'web_commit_signoff_required': False, 'topics': ['cpp', 'emulation', 'emulator', 'gamecube', 'wii'], 'visibility': 'public', 'forks': 2965, 'open_issues': 360, 'watchers': 14581, 'default_branch': 'master', 'custom_properties': {}}, 'organization': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'url': 'https://api.github.com/orgs/dolphin-emu', 'repos_url': 'https://api.github.com/orgs/dolphin-emu/repos', 'events_url': 'https://api.github.com/orgs/dolphin-emu/events', 'hooks_url': 'https://api.github.com/orgs/dolphin-emu/hooks', 'issues_url': 'https://api.github.com/orgs/dolphin-emu/issues', 'members_url': 'https://api.github.com/orgs/dolphin-emu/members{/member}', 'public_members_url': 'https://api.github.com/orgs/dolphin-emu/public_members{/member}', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'description': ''}, 'sender': {'login': 'sepalani', 'id': 7890055, 'node_id': 'MDQ6VXNlcjc4OTAwNTU=', 'avatar_url': 'https://avatars.githubusercontent.com/u/7890055?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/sepalani', 'html_url': 'https://github.com/sepalani', 'followers_url': 'https://api.github.com/users/sepalani/followers', 'following_url': 'https://api.github.com/users/sepalani/following{/other_user}', 'gists_url': 'https://api.github.com/users/sepalani/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/sepalani/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/sepalani/subscriptions', 'organizations_url': 'https://api.github.com/users/sepalani/orgs', 'repos_url': 'https://api.github.com/users/sepalani/repos', 'events_url': 'https://api.github.com/users/sepalani/events{/privacy}', 'received_events_url': 'https://api.github.com/users/sepalani/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'installation': {'id': 35437981, 'node_id': 'MDIzOkludGVncmF0aW9uSW5zdGFsbGF0aW9uMzU0Mzc5ODE='}}, 'type': 'gh_issue_comment'}
2026-02-02T21:17:44.679741 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'Hibyehello', 'action': 'created', 'id': 13991, 'title': "Jit: Emit Branch Watch code only if it's enabled", 'url': 'https://github.com/dolphin-emu/dolphin/pull/13991#issuecomment-3837468646', 'safe_author': False, 'body': 'Tested on an M4 MacBook with no problems', 'raw': {'action': 'created', 'issue': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13991', 'repository_url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'labels_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13991/labels{/name}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13991/comments', 'events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13991/events', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/13991', 'id': 3485022552, 'node_id': 'PR_kwDOALCn2M6sJ1qq', 'number': 13991, 'title': "Jit: Emit Branch Watch code only if it's enabled", 'user': {'login': 'SuperSamus', 'id': 40663462, 'node_id': 'MDQ6VXNlcjQwNjYzNDYy', 'avatar_url': 'https://avatars.githubusercontent.com/u/40663462?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/SuperSamus', 'html_url': 'https://github.com/SuperSamus', 'followers_url': 'https://api.github.com/users/SuperSamus/followers', 'following_url': 'https://api.github.com/users/SuperSamus/following{/other_user}', 'gists_url': 'https://api.github.com/users/SuperSamus/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/SuperSamus/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/SuperSamus/subscriptions', 'organizations_url': 'https://api.github.com/users/SuperSamus/orgs', 'repos_url': 'https://api.github.com/users/SuperSamus/repos', 'events_url': 'https://api.github.com/users/SuperSamus/events{/privacy}', 'received_events_url': 'https://api.github.com/users/SuperSamus/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'labels': [], 'state': 'open', 'locked': False, 'assignee': None, 'assignees': [], 'milestone': None, 'comments': 5, 'created_at': '2025-10-05T17:58:27Z', 'updated_at': '2026-02-02T21:17:42Z', 'closed_at': None, 'author_association': 'CONTRIBUTOR', 'type': None, 'active_lock_reason': None, 'draft': False, 'pull_request': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13991', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/13991', 'diff_url': 'https://github.com/dolphin-emu/dolphin/pull/13991.diff', 'patch_url': 'https://github.com/dolphin-emu/dolphin/pull/13991.patch', 'merged_at': None}, 'body': 'JIT code related to Branch Watch was emitted if the debugging UI was active: the emitted code would dynamically check whether Branch Watch is active.\r\nHowever, this causes two problems:\r\n1. It decreases performance by just having the debugging UI enabled*\r\n2. It clutters the host assembly in the JIT tab, making it harder to read (unaware readers will wonder what these instructions are for)\r\n\r\nWith this PR, code related to Branch Watch is emitted only if Branch Watch itself is active, fixing the issues above.\r\nThe JIT cache will now be wiped whenever the feature is toggled, causing a slight stutter. However, this isn\'t the kind of feature that is toggled over and over, so IMO it is an acceptable trade-off.\r\n\r\nARM64 is untested.\r\n\r\n#### A note about the debugging UI\r\n\r\nSo, you might wonder what the asterisk is about. It\'s not that the PR doesn\'t improve performance with the debug UI on, because it does.\r\nThe problem is that the performance is still worse than with the debugging UI disabled. I\'m not sure why.[^1]\r\nEven though this performance decrease is intended, *this downside isn\'t communicated anywhere in Dolphin*, and it\'s very possible for someone to unawarely leave the option enabled. (Hi.)\r\nSo... that should be communicated, but where? Only the tooltip of the option, or even in the option title itself `(disables optimizations)`? (Plus the help text in the command line for the `-d` argument.)\r\n\r\nI\'ll leave the discussion for this and on whether to merge the PR (IMO, still worthy due to point 2) to the jury.\r\n\r\n[^1]: I already tested that "reverting" [the disabled BLR optimization](https://github.com/dolphin-emu/dolphin/blob/70bd0943a7a271606367ac129eb39cbdcd659e42/Source/Core/Core/PowerPC/JitCommon/JitBase.cpp#L153-L154) or [the extra checks in the main loop meant for the memory breakpoints](https://github.com/dolphin-emu/dolphin/blob/70bd0943a7a271606367ac129eb39cbdcd659e42/Source/Core/Core/PowerPC/Jit64/JitAsm.cpp#L99-L104) changes basically nothing.', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13991/reactions', 'total_count': 0, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 0, 'confused': 0, 'heart': 0, 'rocket': 0, 'eyes': 0}, 'timeline_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13991/timeline', 'performed_via_github_app': None, 'state_reason': None}, 'comment': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments/3837468646', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/13991#issuecomment-3837468646', 'issue_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13991', 'id': 3837468646, 'node_id': 'IC_kwDOALCn2M7kux_m', 'user': {'login': 'Hibyehello', 'id': 36666883, 'node_id': 'MDQ6VXNlcjM2NjY2ODgz', 'avatar_url': 'https://avatars.githubusercontent.com/u/36666883?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/Hibyehello', 'html_url': 'https://github.com/Hibyehello', 'followers_url': 'https://api.github.com/users/Hibyehello/followers', 'following_url': 'https://api.github.com/users/Hibyehello/following{/other_user}', 'gists_url': 'https://api.github.com/users/Hibyehello/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/Hibyehello/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/Hibyehello/subscriptions', 'organizations_url': 'https://api.github.com/users/Hibyehello/orgs', 'repos_url': 'https://api.github.com/users/Hibyehello/repos', 'events_url': 'https://api.github.com/users/Hibyehello/events{/privacy}', 'received_events_url': 'https://api.github.com/users/Hibyehello/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'created_at': '2026-02-02T21:17:41Z', 'updated_at': '2026-02-02T21:17:41Z', 'body': 'Tested on an M4 MacBook with no problems', 'author_association': 'CONTRIBUTOR', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments/3837468646/reactions', 'total_count': 0, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 0, 'confused': 0, 'heart': 0, 'rocket': 0, 'eyes': 0}, 'performed_via_github_app': None}, 'repository': {'id': 11577304, 'node_id': 'MDEwOlJlcG9zaXRvcnkxMTU3NzMwNA==', 'name': 'dolphin', 'full_name': 'dolphin-emu/dolphin', 'private': False, 'owner': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/dolphin-emu', 'html_url': 'https://github.com/dolphin-emu', 'followers_url': 'https://api.github.com/users/dolphin-emu/followers', 'following_url': 'https://api.github.com/users/dolphin-emu/following{/other_user}', 'gists_url': 'https://api.github.com/users/dolphin-emu/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/dolphin-emu/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/dolphin-emu/subscriptions', 'organizations_url': 'https://api.github.com/users/dolphin-emu/orgs', 'repos_url': 'https://api.github.com/users/dolphin-emu/repos', 'events_url': 'https://api.github.com/users/dolphin-emu/events{/privacy}', 'received_events_url': 'https://api.github.com/users/dolphin-emu/received_events', 'type': 'Organization', 'user_view_type': 'public', 'site_admin': False}, 'html_url': 'https://github.com/dolphin-emu/dolphin', 'description': 'Dolphin is a GameCube / Wii emulator, allowing you to play games for these two platforms on PC with improvements.', 'fork': False, 'url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'forks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/forks', 'keys_url': 'https://api.github.com/repos/dolphin-emu/dolphin/keys{/key_id}', 'collaborators_url': 'https://api.github.com/repos/dolphin-emu/dolphin/collaborators{/collaborator}', 'teams_url': 'https://api.github.com/repos/dolphin-emu/dolphin/teams', 'hooks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/hooks', 'issue_events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/events{/number}', 'events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/events', 'assignees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/assignees{/user}', 'branches_url': 'https://api.github.com/repos/dolphin-emu/dolphin/branches{/branch}', 'tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/tags', 'blobs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/blobs{/sha}', 'git_tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/tags{/sha}', 'git_refs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/refs{/sha}', 'trees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/trees{/sha}', 'statuses_url': 'https://api.github.com/repos/dolphin-emu/dolphin/statuses/{sha}', 'languages_url': 'https://api.github.com/repos/dolphin-emu/dolphin/languages', 'stargazers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/stargazers', 'contributors_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contributors', 'subscribers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscribers', 'subscription_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscription', 'commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/commits{/sha}', 'git_commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/commits{/sha}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/comments{/number}', 'issue_comment_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments{/number}', 'contents_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contents/{+path}', 'compare_url': 'https://api.github.com/repos/dolphin-emu/dolphin/compare/{base}...{head}', 'merges_url': 'https://api.github.com/repos/dolphin-emu/dolphin/merges', 'archive_url': 'https://api.github.com/repos/dolphin-emu/dolphin/{archive_format}{/ref}', 'downloads_url': 'https://api.github.com/repos/dolphin-emu/dolphin/downloads', 'issues_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues{/number}', 'pulls_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls{/number}', 'milestones_url': 'https://api.github.com/repos/dolphin-emu/dolphin/milestones{/number}', 'notifications_url': 'https://api.github.com/repos/dolphin-emu/dolphin/notifications{?since,all,participating}', 'labels_url': 'https://api.github.com/repos/dolphin-emu/dolphin/labels{/name}', 'releases_url': 'https://api.github.com/repos/dolphin-emu/dolphin/releases{/id}', 'deployments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/deployments', 'created_at': '2013-07-22T09:07:57Z', 'updated_at': '2026-02-02T20:05:56Z', 'pushed_at': '2026-02-02T20:05:51Z', 'git_url': 'git://github.com/dolphin-emu/dolphin.git', 'ssh_url': 'git@github.com:dolphin-emu/dolphin.git', 'clone_url': 'https://github.com/dolphin-emu/dolphin.git', 'svn_url': 'https://github.com/dolphin-emu/dolphin', 'homepage': 'https://dolphin-emu.org', 'size': 544012, 'stargazers_count': 14576, 'watchers_count': 14576, 'language': 'C++', 'has_issues': False, 'has_projects': False, 'has_downloads': True, 'has_wiki': True, 'has_pages': False, 'has_discussions': False, 'forks_count': 2966, 'mirror_url': None, 'archived': False, 'disabled': False, 'open_issues_count': 361, 'license': {'key': 'other', 'name': 'Other', 'spdx_id': 'NOASSERTION', 'url': None, 'node_id': 'MDc6TGljZW5zZTA='}, 'allow_forking': True, 'is_template': False, 'web_commit_signoff_required': False, 'topics': ['cpp', 'emulation', 'emulator', 'gamecube', 'wii'], 'visibility': 'public', 'forks': 2966, 'open_issues': 361, 'watchers': 14576, 'default_branch': 'master', 'custom_properties': {}}, 'organization': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'url': 'https://api.github.com/orgs/dolphin-emu', 'repos_url': 'https://api.github.com/orgs/dolphin-emu/repos', 'events_url': 'https://api.github.com/orgs/dolphin-emu/events', 'hooks_url': 'https://api.github.com/orgs/dolphin-emu/hooks', 'issues_url': 'https://api.github.com/orgs/dolphin-emu/issues', 'members_url': 'https://api.github.com/orgs/dolphin-emu/members{/member}', 'public_members_url': 'https://api.github.com/orgs/dolphin-emu/public_members{/member}', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'description': ''}, 'sender': {'login': 'Hibyehello', 'id': 36666883, 'node_id': 'MDQ6VXNlcjM2NjY2ODgz', 'avatar_url': 'https://avatars.githubusercontent.com/u/36666883?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/Hibyehello', 'html_url': 'https://github.com/Hibyehello', 'followers_url': 'https://api.github.com/users/Hibyehello/followers', 'following_url': 'https://api.github.com/users/Hibyehello/following{/other_user}', 'gists_url': 'https://api.github.com/users/Hibyehello/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/Hibyehello/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/Hibyehello/subscriptions', 'organizations_url': 'https://api.github.com/users/Hibyehello/orgs', 'repos_url': 'https://api.github.com/users/Hibyehello/repos', 'events_url': 'https://api.github.com/users/Hibyehello/events{/privacy}', 'received_events_url': 'https://api.github.com/users/Hibyehello/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'installation': {'id': 35437981, 'node_id': 'MDIzOkludGVncmF0aW9uSW5zdGFsbGF0aW9uMzU0Mzc5ODE='}}, 'type': 'gh_issue_comment'}
2026-02-02T14:13:26.355641 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'dolphin-ci[bot]', 'action': 'deleted', 'id': 14260, 'title': 'Externals: Update cpp-ipc to latest for mingw compatibility', 'url': 'https://github.com/dolphin-emu/dolphin/pull/14260#issuecomment-3833918915', 'safe_author': False, 'body': '[FifoCI](https://fifo.ci/about/) detected that this change impacts graphical rendering. Here are the [behavior differences](https://fifo.ci/version/512972c1cd4a10111412d619e464fc72a964101e/) detected by the system:\n\n<details>\n<summary>Detected differences</summary>\n\n||ogl-lin-mesa|vk-lin-mesa|\n|-|-|-|\n|DKCR-Char|[🔍 diff](https://fifo.ci/compare/15468920-15426640/)|[🔍 diff](https://fifo.ci/compare/15469060-15426780/)|\n|DKCR-fast-depth|[🔍 diff](https://fifo.ci/compare/15468941-15426661/)|[🔍 diff](https://fifo.ci/compare/15469081-15426801/)|\n|MaS-LOG-wiimote|[🔍 diff](https://fifo.ci/compare/15468958-15426678/)|[🔍 diff](https://fifo.ci/compare/15469098-15426818/)|\n|aeon-charge-attack|-|[🔍 diff](https://fifo.ci/compare/15469066-15426786/)|\n|burnout2-vehicletextures|[🔍 diff](https://fifo.ci/compare/15468963-15426683/)|[🔍 diff](https://fifo.ci/compare/15469103-15426823/)|\n|chibi-robo-fastdepth|-|[🔍 diff](https://fifo.ci/compare/15469042-15426762/)|\n|chibi-robo-zfighting|-|[🔍 diff](https://fifo.ci/compare/15469008-15426728/)|\n|custom-brawl-char|-|[🔍 diff](https://fifo.ci/compare/15469068-15426788/)|\n|ea-pink|[🔍 diff](https://fifo.ci/compare/15468980-15426700/)|[🔍 diff](https://fifo.ci/compare/15469120-15426840/)|\n|f-zero-rain|[🔍 diff](https://fifo.ci/compare/15468934-15426654/)|[🔍 diff](https://fifo.ci/compare/15469074-15426794/)|\n|fortune-street|-|[🔍 diff](https://fifo.ci/compare/15469027-15426747/)|\n|fortune-street-white-box|-|[🔍 diff](https://fifo.ci/compare/15469029-15426749/)|\n|hotwheels-shadows|-|[🔍 diff](https://fifo.ci/compare/15469142-15426862/)|\n|jd2-fmv|-|[🔍 diff](https://fifo.ci/compare/15469111-15426831/)|\n|jj-awae-mirrored|-|[🔍 diff](https://fifo.ci/compare/15469102-15426822/)|\n|kirby-logicop|[🔍 diff](https://fifo.ci/compare/15468989-15426709/)|[🔍 diff](https://fifo.ci/compare/15469129-15426849/)|\n|kirby-shadows|[🔍 diff](https://fifo.ci/compare/15468876-15426596/)|[🔍 diff](https://fifo.ci/compare/15469016-15426736/)|\n|last-story-shadows|[🔍 diff](https://fifo.ci/compare/15468960-15426680/)|[🔍 diff](https://fifo.ci/compare/15469100-15426820/)|\n|mario-baseball-shadows|-|[🔍 diff](https://fifo.ci/compare/15469095-15426815/)|\n|mario-golf-oob|-|[🔍 diff](https://fifo.ci/compare/15469126-15426846/)|\n|mario-golf-vertex-expansion|[🔍 diff](https://fifo.ci/compare/15469001-15426721/)|[🔍 diff](https://fifo.ci/compare/15469141-15426861/)|\n|mario-sluggers-bar|-|[🔍 diff](https://fifo.ci/compare/15469032-15426752/)|\n|mario-tennis-menu|-|[🔍 diff](https://fifo.ci/compare/15469031-15426751/)|\n|megaman-heat|[🔍 diff](https://fifo.ci/compare/15468896-15426616/)|[🔍 diff](https://fifo.ci/compare/15469036-15426756/)|\n|metroid-visor|[🔍 diff](https://fifo.ci/compare/15468942-15426662/)|[🔍 diff](https://fifo.ci/compare/15469082-15426802/)|\n|milotic-texture|[🔍 diff](https://fifo.ci/compare/15468911-15426631/)|[🔍 diff](https://fifo.ci/compare/15469051-15426771/)|\n|mkdd-efb|-|[🔍 diff](https://fifo.ci/compare/15469019-15426739/)|\n|mkw-bridge|-|[🔍 diff](https://fifo.ci/compare/15469110-15426830/)|\n|mp2-scanner|[🔍 diff](https://fifo.ci/compare/15468965-15426685/)|[🔍 diff](https://fifo.ci/compare/15469105-15426825/)|\n|mp3-bloom|[🔍 diff](https://fifo.ci/compare/15468957-15426677/)|[🔍 diff](https://fifo.ci/compare/15469097-15426817/)|\n|mtennis-zfreeze|[🔍 diff](https://fifo.ci/compare/15468894-15426614/)|[🔍 diff](https://fifo.ci/compare/15469034-15426754/)|\n|nddemo-bumpmapping|[🔍 diff](https://fifo.ci/compare/15468917-15426637/)|[🔍 diff](https://fifo.ci/compare/15469057-15426777/)|\n|nddemo-lighting|[🔍 diff](https://fifo.ci/compare/15468938-15426658/)|[🔍 diff](https://fifo.ci/compare/15469078-15426798/)|\n|nfsu-purplerect|-|[🔍 diff](https://fifo.ci/compare/15469021-15426741/)|\n|nfsu-reflections|-|[🔍 diff](https://fifo.ci/compare/15469011-15426731/)|\n|nhl-slap|[🔍 diff](https://fifo.ci/compare/15468966-15426686/)|[🔍 diff](https://fifo.ci/compare/15469106-15426826/)|\n|nsmbw-coins|-|[🔍 diff](https://fifo.ci/compare/15469064-15426784/)|\n|nsmbw-intro|-|[🔍 diff](https://fifo.ci/compare/15469013-15426733/)|\n|pbr-sfx|[🔍 diff](https://fifo.ci/compare/15468984-15426704/)|[🔍 diff](https://fifo.ci/compare/15469124-15426844/)|\n|pm-hc-jp|-|[🔍 diff](https://fifo.ci/compare/15469059-15426779/)|\n|quake-gx|-|[🔍 diff](https://fifo.ci/compare/15469130-15426850/)|\n|rs2-glass|[🔍 diff](https://fifo.ci/compare/15468899-15426619/)|[🔍 diff](https://fifo.ci/compare/15469039-15426759/)|\n|rs2-zfreeze|-|[🔍 diff](https://fifo.ci/compare/15469022-15426742/)|\n|rs3-bumpmapping|-|[🔍 diff](https://fifo.ci/compare/15469089-15426809/)|\n|sadx-ui|-|[🔍 diff](https://fifo.ci/compare/15469041-15426761/)|\n|sfa-shadows|-|[🔍 diff](https://fifo.ci/compare/15469085-15426805/)|\n|shadow-eyes|-|[🔍 diff](https://fifo.ci/compare/15469116-15426836/)|\n|smb-mirror|-|[🔍 diff](https://fifo.ci/compare/15469109-15426829/)|\n|smg-marioeyes|-|[🔍 diff](https://fifo.ci/compare/15469023-15426743/)|\n|smg-mmg|-|[🔍 diff](https://fifo.ci/compare/15469119-15426839/)|\n|sonic-riders-blur|-|[🔍 diff](https://fifo.ci/compare/15469094-15426814/)|\n|sonic-riders-zg-4p|-|[🔍 diff](https://fifo.ci/compare/15469121-15426841/)|\n|soniccolors-mm|[🔍 diff](https://fifo.ci/compare/15468923-15426643/)|[🔍 diff](https://fifo.ci/compare/15469063-15426783/)|\n|ssbb-mod-lloyd|-|[🔍 diff](https://fifo.ci/compare/15469080-15426800/)|\n|ssbm-pointsize|-|[🔍 diff](https://fifo.ci/compare/15469028-15426748/)|\n|sw3-dt|[🔍 diff](https://fifo.ci/compare/15468944-15426664/)|[🔍 diff](https://fifo.ci/compare/15469084-15426804/)|\n|thps4-shadow|[🔍 diff](https://fifo.ci/compare/15468903-15426623/)|[🔍 diff](https://fifo.ci/compare/15469043-15426763/)|\n|tsp3-pinkgrass|[🔍 diff](https://fifo.ci/compare/15468884-15426604/)|[🔍 diff](https://fifo.ci/compare/15469024-15426744/)|\n|vegas-party-depth|[🔍 diff](https://fifo.ci/compare/15468929-15426649/)|[🔍 diff](https://fifo.ci/compare/15469069-15426789/)|\n|ztp-grass|-|[🔍 diff](https://fifo.ci/compare/15469025-15426745/)|\n|zww-waves|-|[🔍 diff](https://fifo.ci/compare/15469045-15426765/)|\n</details>\n<sub><sup>automated-fifoci-reporter</sup></sub>', 'raw': {'action': 'deleted', 'issue': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14260', 'repository_url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'labels_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14260/labels{/name}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14260/comments', 'events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14260/events', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/14260', 'id': 3789545274, 'node_id': 'PR_kwDOALCn2M678v8s', 'number': 14260, 'title': 'Externals: Update cpp-ipc to latest for mingw compatibility', 'user': {'login': 'cscd98', 'id': 1188869, 'node_id': 'MDQ6VXNlcjExODg4Njk=', 'avatar_url': 'https://avatars.githubusercontent.com/u/1188869?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/cscd98', 'html_url': 'https://github.com/cscd98', 'followers_url': 'https://api.github.com/users/cscd98/followers', 'following_url': 'https://api.github.com/users/cscd98/following{/other_user}', 'gists_url': 'https://api.github.com/users/cscd98/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/cscd98/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/cscd98/subscriptions', 'organizations_url': 'https://api.github.com/users/cscd98/orgs', 'repos_url': 'https://api.github.com/users/cscd98/repos', 'events_url': 'https://api.github.com/users/cscd98/events{/privacy}', 'received_events_url': 'https://api.github.com/users/cscd98/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'labels': [], 'state': 'open', 'locked': False, 'assignee': None, 'assignees': [], 'milestone': None, 'comments': 7, 'created_at': '2026-01-07T16:57:40Z', 'updated_at': '2026-02-02T13:47:45Z', 'closed_at': None, 'author_association': 'CONTRIBUTOR', 'type': None, 'active_lock_reason': None, 'draft': False, 'pull_request': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/14260', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/14260', 'diff_url': 'https://github.com/dolphin-emu/dolphin/pull/14260.diff', 'patch_url': 'https://github.com/dolphin-emu/dolphin/pull/14260.patch', 'merged_at': None}, 'body': 'This has this https://github.com/mutouyun/cpp-ipc/pull/169 merged in as some new Windows.h includes made it in breaking mingw compatibility, so this fixes that.\r\n\r\nNeeds testing.', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14260/reactions', 'total_count': 0, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 0, 'confused': 0, 'heart': 0, 'rocket': 0, 'eyes': 0}, 'timeline_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14260/timeline', 'performed_via_github_app': None, 'state_reason': None}, 'comment': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments/3833918915', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/14260#issuecomment-3833918915', 'issue_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14260', 'id': 3833918915, 'node_id': 'IC_kwDOALCn2M7khPXD', 'user': {'login': 'dolphin-ci[bot]', 'id': 59266906, 'node_id': 'MDM6Qm90NTkyNjY5MDY=', 'avatar_url': 'https://avatars.githubusercontent.com/in/49947?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D', 'html_url': 'https://github.com/apps/dolphin-ci', 'followers_url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D/followers', 'following_url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D/following{/other_user}', 'gists_url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D/subscriptions', 'organizations_url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D/orgs', 'repos_url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D/repos', 'events_url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D/events{/privacy}', 'received_events_url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D/received_events', 'type': 'Bot', 'user_view_type': 'public', 'site_admin': False}, 'created_at': '2026-02-02T09:18:14Z', 'updated_at': '2026-02-02T09:18:14Z', 'body': '[FifoCI](https://fifo.ci/about/) detected that this change impacts graphical rendering. Here are the [behavior differences](https://fifo.ci/version/512972c1cd4a10111412d619e464fc72a964101e/) detected by the system:\n\n<details>\n<summary>Detected differences</summary>\n\n||ogl-lin-mesa|vk-lin-mesa|\n|-|-|-|\n|DKCR-Char|[🔍 diff](https://fifo.ci/compare/15468920-15426640/)|[🔍 diff](https://fifo.ci/compare/15469060-15426780/)|\n|DKCR-fast-depth|[🔍 diff](https://fifo.ci/compare/15468941-15426661/)|[🔍 diff](https://fifo.ci/compare/15469081-15426801/)|\n|MaS-LOG-wiimote|[🔍 diff](https://fifo.ci/compare/15468958-15426678/)|[🔍 diff](https://fifo.ci/compare/15469098-15426818/)|\n|aeon-charge-attack|-|[🔍 diff](https://fifo.ci/compare/15469066-15426786/)|\n|burnout2-vehicletextures|[🔍 diff](https://fifo.ci/compare/15468963-15426683/)|[🔍 diff](https://fifo.ci/compare/15469103-15426823/)|\n|chibi-robo-fastdepth|-|[🔍 diff](https://fifo.ci/compare/15469042-15426762/)|\n|chibi-robo-zfighting|-|[🔍 diff](https://fifo.ci/compare/15469008-15426728/)|\n|custom-brawl-char|-|[🔍 diff](https://fifo.ci/compare/15469068-15426788/)|\n|ea-pink|[🔍 diff](https://fifo.ci/compare/15468980-15426700/)|[🔍 diff](https://fifo.ci/compare/15469120-15426840/)|\n|f-zero-rain|[🔍 diff](https://fifo.ci/compare/15468934-15426654/)|[🔍 diff](https://fifo.ci/compare/15469074-15426794/)|\n|fortune-street|-|[🔍 diff](https://fifo.ci/compare/15469027-15426747/)|\n|fortune-street-white-box|-|[🔍 diff](https://fifo.ci/compare/15469029-15426749/)|\n|hotwheels-shadows|-|[🔍 diff](https://fifo.ci/compare/15469142-15426862/)|\n|jd2-fmv|-|[🔍 diff](https://fifo.ci/compare/15469111-15426831/)|\n|jj-awae-mirrored|-|[🔍 diff](https://fifo.ci/compare/15469102-15426822/)|\n|kirby-logicop|[🔍 diff](https://fifo.ci/compare/15468989-15426709/)|[🔍 diff](https://fifo.ci/compare/15469129-15426849/)|\n|kirby-shadows|[🔍 diff](https://fifo.ci/compare/15468876-15426596/)|[🔍 diff](https://fifo.ci/compare/15469016-15426736/)|\n|last-story-shadows|[🔍 diff](https://fifo.ci/compare/15468960-15426680/)|[🔍 diff](https://fifo.ci/compare/15469100-15426820/)|\n|mario-baseball-shadows|-|[🔍 diff](https://fifo.ci/compare/15469095-15426815/)|\n|mario-golf-oob|-|[🔍 diff](https://fifo.ci/compare/15469126-15426846/)|\n|mario-golf-vertex-expansion|[🔍 diff](https://fifo.ci/compare/15469001-15426721/)|[🔍 diff](https://fifo.ci/compare/15469141-15426861/)|\n|mario-sluggers-bar|-|[🔍 diff](https://fifo.ci/compare/15469032-15426752/)|\n|mario-tennis-menu|-|[🔍 diff](https://fifo.ci/compare/15469031-15426751/)|\n|megaman-heat|[🔍 diff](https://fifo.ci/compare/15468896-15426616/)|[🔍 diff](https://fifo.ci/compare/15469036-15426756/)|\n|metroid-visor|[🔍 diff](https://fifo.ci/compare/15468942-15426662/)|[🔍 diff](https://fifo.ci/compare/15469082-15426802/)|\n|milotic-texture|[🔍 diff](https://fifo.ci/compare/15468911-15426631/)|[🔍 diff](https://fifo.ci/compare/15469051-15426771/)|\n|mkdd-efb|-|[🔍 diff](https://fifo.ci/compare/15469019-15426739/)|\n|mkw-bridge|-|[🔍 diff](https://fifo.ci/compare/15469110-15426830/)|\n|mp2-scanner|[🔍 diff](https://fifo.ci/compare/15468965-15426685/)|[🔍 diff](https://fifo.ci/compare/15469105-15426825/)|\n|mp3-bloom|[🔍 diff](https://fifo.ci/compare/15468957-15426677/)|[🔍 diff](https://fifo.ci/compare/15469097-15426817/)|\n|mtennis-zfreeze|[🔍 diff](https://fifo.ci/compare/15468894-15426614/)|[🔍 diff](https://fifo.ci/compare/15469034-15426754/)|\n|nddemo-bumpmapping|[🔍 diff](https://fifo.ci/compare/15468917-15426637/)|[🔍 diff](https://fifo.ci/compare/15469057-15426777/)|\n|nddemo-lighting|[🔍 diff](https://fifo.ci/compare/15468938-15426658/)|[🔍 diff](https://fifo.ci/compare/15469078-15426798/)|\n|nfsu-purplerect|-|[🔍 diff](https://fifo.ci/compare/15469021-15426741/)|\n|nfsu-reflections|-|[🔍 diff](https://fifo.ci/compare/15469011-15426731/)|\n|nhl-slap|[🔍 diff](https://fifo.ci/compare/15468966-15426686/)|[🔍 diff](https://fifo.ci/compare/15469106-15426826/)|\n|nsmbw-coins|-|[🔍 diff](https://fifo.ci/compare/15469064-15426784/)|\n|nsmbw-intro|-|[🔍 diff](https://fifo.ci/compare/15469013-15426733/)|\n|pbr-sfx|[🔍 diff](https://fifo.ci/compare/15468984-15426704/)|[🔍 diff](https://fifo.ci/compare/15469124-15426844/)|\n|pm-hc-jp|-|[🔍 diff](https://fifo.ci/compare/15469059-15426779/)|\n|quake-gx|-|[🔍 diff](https://fifo.ci/compare/15469130-15426850/)|\n|rs2-glass|[🔍 diff](https://fifo.ci/compare/15468899-15426619/)|[🔍 diff](https://fifo.ci/compare/15469039-15426759/)|\n|rs2-zfreeze|-|[🔍 diff](https://fifo.ci/compare/15469022-15426742/)|\n|rs3-bumpmapping|-|[🔍 diff](https://fifo.ci/compare/15469089-15426809/)|\n|sadx-ui|-|[🔍 diff](https://fifo.ci/compare/15469041-15426761/)|\n|sfa-shadows|-|[🔍 diff](https://fifo.ci/compare/15469085-15426805/)|\n|shadow-eyes|-|[🔍 diff](https://fifo.ci/compare/15469116-15426836/)|\n|smb-mirror|-|[🔍 diff](https://fifo.ci/compare/15469109-15426829/)|\n|smg-marioeyes|-|[🔍 diff](https://fifo.ci/compare/15469023-15426743/)|\n|smg-mmg|-|[🔍 diff](https://fifo.ci/compare/15469119-15426839/)|\n|sonic-riders-blur|-|[🔍 diff](https://fifo.ci/compare/15469094-15426814/)|\n|sonic-riders-zg-4p|-|[🔍 diff](https://fifo.ci/compare/15469121-15426841/)|\n|soniccolors-mm|[🔍 diff](https://fifo.ci/compare/15468923-15426643/)|[🔍 diff](https://fifo.ci/compare/15469063-15426783/)|\n|ssbb-mod-lloyd|-|[🔍 diff](https://fifo.ci/compare/15469080-15426800/)|\n|ssbm-pointsize|-|[🔍 diff](https://fifo.ci/compare/15469028-15426748/)|\n|sw3-dt|[🔍 diff](https://fifo.ci/compare/15468944-15426664/)|[🔍 diff](https://fifo.ci/compare/15469084-15426804/)|\n|thps4-shadow|[🔍 diff](https://fifo.ci/compare/15468903-15426623/)|[🔍 diff](https://fifo.ci/compare/15469043-15426763/)|\n|tsp3-pinkgrass|[🔍 diff](https://fifo.ci/compare/15468884-15426604/)|[🔍 diff](https://fifo.ci/compare/15469024-15426744/)|\n|vegas-party-depth|[🔍 diff](https://fifo.ci/compare/15468929-15426649/)|[🔍 diff](https://fifo.ci/compare/15469069-15426789/)|\n|ztp-grass|-|[🔍 diff](https://fifo.ci/compare/15469025-15426745/)|\n|zww-waves|-|[🔍 diff](https://fifo.ci/compare/15469045-15426765/)|\n</details>\n<sub><sup>automated-fifoci-reporter</sup></sub>', 'author_association': 'NONE', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments/3833918915/reactions', 'total_count': 0, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 0, 'confused': 0, 'heart': 0, 'rocket': 0, 'eyes': 0}, 'performed_via_github_app': {'id': 49947, 'client_id': 'Iv1.1fcaea7644d8b727', 'slug': 'dolphin-ci', 'node_id': 'MDM6QXBwNDk5NDc=', 'owner': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/dolphin-emu', 'html_url': 'https://github.com/dolphin-emu', 'followers_url': 'https://api.github.com/users/dolphin-emu/followers', 'following_url': 'https://api.github.com/users/dolphin-emu/following{/other_user}', 'gists_url': 'https://api.github.com/users/dolphin-emu/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/dolphin-emu/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/dolphin-emu/subscriptions', 'organizations_url': 'https://api.github.com/users/dolphin-emu/orgs', 'repos_url': 'https://api.github.com/users/dolphin-emu/repos', 'events_url': 'https://api.github.com/users/dolphin-emu/events{/privacy}', 'received_events_url': 'https://api.github.com/users/dolphin-emu/received_events', 'type': 'Organization', 'user_view_type': 'public', 'site_admin': False}, 'name': 'Dolphin CI', 'description': 'Continuous Integration setup for [dolphin-emu.org](https://dolphin-emu.org/).', 'external_url': 'https://github.com/dolphin-emu', 'html_url': 'https://github.com/apps/dolphin-ci', 'created_at': '2019-12-26T22:26:07Z', 'updated_at': '2019-12-26T22:33:19Z', 'permissions': {'checks': 'write', 'contents': 'read', 'issues': 'write', 'members': 'read', 'metadata': 'read', 'pull_requests': 'write', 'statuses': 'write'}, 'events': ['check_run', 'commit_comment', 'issue_comment', 'pull_request', 'pull_request_review', 'pull_request_review_comment', 'push']}}, 'repository': {'id': 11577304, 'node_id': 'MDEwOlJlcG9zaXRvcnkxMTU3NzMwNA==', 'name': 'dolphin', 'full_name': 'dolphin-emu/dolphin', 'private': False, 'owner': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/dolphin-emu', 'html_url': 'https://github.com/dolphin-emu', 'followers_url': 'https://api.github.com/users/dolphin-emu/followers', 'following_url': 'https://api.github.com/users/dolphin-emu/following{/other_user}', 'gists_url': 'https://api.github.com/users/dolphin-emu/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/dolphin-emu/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/dolphin-emu/subscriptions', 'organizations_url': 'https://api.github.com/users/dolphin-emu/orgs', 'repos_url': 'https://api.github.com/users/dolphin-emu/repos', 'events_url': 'https://api.github.com/users/dolphin-emu/events{/privacy}', 'received_events_url': 'https://api.github.com/users/dolphin-emu/received_events', 'type': 'Organization', 'user_view_type': 'public', 'site_admin': False}, 'html_url': 'https://github.com/dolphin-emu/dolphin', 'description': 'Dolphin is a GameCube / Wii emulator, allowing you to play games for these two platforms on PC with improvements.', 'fork': False, 'url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'forks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/forks', 'keys_url': 'https://api.github.com/repos/dolphin-emu/dolphin/keys{/key_id}', 'collaborators_url': 'https://api.github.com/repos/dolphin-emu/dolphin/collaborators{/collaborator}', 'teams_url': 'https://api.github.com/repos/dolphin-emu/dolphin/teams', 'hooks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/hooks', 'issue_events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/events{/number}', 'events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/events', 'assignees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/assignees{/user}', 'branches_url': 'https://api.github.com/repos/dolphin-emu/dolphin/branches{/branch}', 'tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/tags', 'blobs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/blobs{/sha}', 'git_tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/tags{/sha}', 'git_refs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/refs{/sha}', 'trees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/trees{/sha}', 'statuses_url': 'https://api.github.com/repos/dolphin-emu/dolphin/statuses/{sha}', 'languages_url': 'https://api.github.com/repos/dolphin-emu/dolphin/languages', 'stargazers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/stargazers', 'contributors_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contributors', 'subscribers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscribers', 'subscription_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscription', 'commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/commits{/sha}', 'git_commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/commits{/sha}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/comments{/number}', 'issue_comment_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments{/number}', 'contents_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contents/{+path}', 'compare_url': 'https://api.github.com/repos/dolphin-emu/dolphin/compare/{base}...{head}', 'merges_url': 'https://api.github.com/repos/dolphin-emu/dolphin/merges', 'archive_url': 'https://api.github.com/repos/dolphin-emu/dolphin/{archive_format}{/ref}', 'downloads_url': 'https://api.github.com/repos/dolphin-emu/dolphin/downloads', 'issues_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues{/number}', 'pulls_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls{/number}', 'milestones_url': 'https://api.github.com/repos/dolphin-emu/dolphin/milestones{/number}', 'notifications_url': 'https://api.github.com/repos/dolphin-emu/dolphin/notifications{?since,all,participating}', 'labels_url': 'https://api.github.com/repos/dolphin-emu/dolphin/labels{/name}', 'releases_url': 'https://api.github.com/repos/dolphin-emu/dolphin/releases{/id}', 'deployments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/deployments', 'created_at': '2013-07-22T09:07:57Z', 'updated_at': '2026-02-02T13:00:07Z', 'pushed_at': '2026-01-31T23:02:09Z', 'git_url': 'git://github.com/dolphin-emu/dolphin.git', 'ssh_url': 'git@github.com:dolphin-emu/dolphin.git', 'clone_url': 'https://github.com/dolphin-emu/dolphin.git', 'svn_url': 'https://github.com/dolphin-emu/dolphin', 'homepage': 'https://dolphin-emu.org', 'size': 544012, 'stargazers_count': 14575, 'watchers_count': 14575, 'language': 'C++', 'has_issues': False, 'has_projects': False, 'has_downloads': True, 'has_wiki': True, 'has_pages': False, 'has_discussions': False, 'forks_count': 2966, 'mirror_url': None, 'archived': False, 'disabled': False, 'open_issues_count': 360, 'license': {'key': 'other', 'name': 'Other', 'spdx_id': 'NOASSERTION', 'url': None, 'node_id': 'MDc6TGljZW5zZTA='}, 'allow_forking': True, 'is_template': False, 'web_commit_signoff_required': False, 'topics': ['cpp', 'emulation', 'emulator', 'gamecube', 'wii'], 'visibility': 'public', 'forks': 2966, 'open_issues': 360, 'watchers': 14575, 'default_branch': 'master', 'custom_properties': {}}, 'organization': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'url': 'https://api.github.com/orgs/dolphin-emu', 'repos_url': 'https://api.github.com/orgs/dolphin-emu/repos', 'events_url': 'https://api.github.com/orgs/dolphin-emu/events', 'hooks_url': 'https://api.github.com/orgs/dolphin-emu/hooks', 'issues_url': 'https://api.github.com/orgs/dolphin-emu/issues', 'members_url': 'https://api.github.com/orgs/dolphin-emu/members{/member}', 'public_members_url': 'https://api.github.com/orgs/dolphin-emu/public_members{/member}', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'description': ''}, 'sender': {'login': 'dolphin-ci[bot]', 'id': 59266906, 'node_id': 'MDM6Qm90NTkyNjY5MDY=', 'avatar_url': 'https://avatars.githubusercontent.com/in/49947?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D', 'html_url': 'https://github.com/apps/dolphin-ci', 'followers_url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D/followers', 'following_url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D/following{/other_user}', 'gists_url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D/subscriptions', 'organizations_url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D/orgs', 'repos_url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D/repos', 'events_url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D/events{/privacy}', 'received_events_url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D/received_events', 'type': 'Bot', 'user_view_type': 'public', 'site_admin': False}, 'installation': {'id': 35437981, 'node_id': 'MDIzOkludGVncmF0aW9uSW5zdGFsbGF0aW9uMzU0Mzc5ODE='}}, 'type': 'gh_issue_comment'}
2026-02-02T13:47:45.794718 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'sepalani', 'action': 'deleted', 'id': 14260, 'title': 'Externals: Update cpp-ipc to latest for mingw compatibility', 'url': 'https://github.com/dolphin-emu/dolphin/pull/14260#issuecomment-3835225593', 'safe_author': True, 'body': '@dolphin-emu-bot rebuild', 'raw': {'action': 'deleted', 'issue': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14260', 'repository_url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'labels_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14260/labels{/name}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14260/comments', 'events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14260/events', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/14260', 'id': 3789545274, 'node_id': 'PR_kwDOALCn2M678v8s', 'number': 14260, 'title': 'Externals: Update cpp-ipc to latest for mingw compatibility', 'user': {'login': 'cscd98', 'id': 1188869, 'node_id': 'MDQ6VXNlcjExODg4Njk=', 'avatar_url': 'https://avatars.githubusercontent.com/u/1188869?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/cscd98', 'html_url': 'https://github.com/cscd98', 'followers_url': 'https://api.github.com/users/cscd98/followers', 'following_url': 'https://api.github.com/users/cscd98/following{/other_user}', 'gists_url': 'https://api.github.com/users/cscd98/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/cscd98/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/cscd98/subscriptions', 'organizations_url': 'https://api.github.com/users/cscd98/orgs', 'repos_url': 'https://api.github.com/users/cscd98/repos', 'events_url': 'https://api.github.com/users/cscd98/events{/privacy}', 'received_events_url': 'https://api.github.com/users/cscd98/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'labels': [], 'state': 'open', 'locked': False, 'assignee': None, 'assignees': [], 'milestone': None, 'comments': 8, 'created_at': '2026-01-07T16:57:40Z', 'updated_at': '2026-02-02T13:47:33Z', 'closed_at': None, 'author_association': 'CONTRIBUTOR', 'type': None, 'active_lock_reason': None, 'draft': False, 'pull_request': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/14260', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/14260', 'diff_url': 'https://github.com/dolphin-emu/dolphin/pull/14260.diff', 'patch_url': 'https://github.com/dolphin-emu/dolphin/pull/14260.patch', 'merged_at': None}, 'body': 'This has this https://github.com/mutouyun/cpp-ipc/pull/169 merged in as some new Windows.h includes made it in breaking mingw compatibility, so this fixes that.\r\n\r\nNeeds testing.', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14260/reactions', 'total_count': 0, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 0, 'confused': 0, 'heart': 0, 'rocket': 0, 'eyes': 0}, 'timeline_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14260/timeline', 'performed_via_github_app': None, 'state_reason': None}, 'comment': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments/3835225593', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/14260#issuecomment-3835225593', 'issue_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14260', 'id': 3835225593, 'node_id': 'IC_kwDOALCn2M7kmOX5', 'user': {'login': 'sepalani', 'id': 7890055, 'node_id': 'MDQ6VXNlcjc4OTAwNTU=', 'avatar_url': 'https://avatars.githubusercontent.com/u/7890055?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/sepalani', 'html_url': 'https://github.com/sepalani', 'followers_url': 'https://api.github.com/users/sepalani/followers', 'following_url': 'https://api.github.com/users/sepalani/following{/other_user}', 'gists_url': 'https://api.github.com/users/sepalani/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/sepalani/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/sepalani/subscriptions', 'organizations_url': 'https://api.github.com/users/sepalani/orgs', 'repos_url': 'https://api.github.com/users/sepalani/repos', 'events_url': 'https://api.github.com/users/sepalani/events{/privacy}', 'received_events_url': 'https://api.github.com/users/sepalani/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'created_at': '2026-02-02T13:47:32Z', 'updated_at': '2026-02-02T13:47:32Z', 'body': '@dolphin-emu-bot rebuild', 'author_association': 'MEMBER', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments/3835225593/reactions', 'total_count': 0, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 0, 'confused': 0, 'heart': 0, 'rocket': 0, 'eyes': 0}, 'performed_via_github_app': None}, 'repository': {'id': 11577304, 'node_id': 'MDEwOlJlcG9zaXRvcnkxMTU3NzMwNA==', 'name': 'dolphin', 'full_name': 'dolphin-emu/dolphin', 'private': False, 'owner': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/dolphin-emu', 'html_url': 'https://github.com/dolphin-emu', 'followers_url': 'https://api.github.com/users/dolphin-emu/followers', 'following_url': 'https://api.github.com/users/dolphin-emu/following{/other_user}', 'gists_url': 'https://api.github.com/users/dolphin-emu/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/dolphin-emu/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/dolphin-emu/subscriptions', 'organizations_url': 'https://api.github.com/users/dolphin-emu/orgs', 'repos_url': 'https://api.github.com/users/dolphin-emu/repos', 'events_url': 'https://api.github.com/users/dolphin-emu/events{/privacy}', 'received_events_url': 'https://api.github.com/users/dolphin-emu/received_events', 'type': 'Organization', 'user_view_type': 'public', 'site_admin': False}, 'html_url': 'https://github.com/dolphin-emu/dolphin', 'description': 'Dolphin is a GameCube / Wii emulator, allowing you to play games for these two platforms on PC with improvements.', 'fork': False, 'url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'forks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/forks', 'keys_url': 'https://api.github.com/repos/dolphin-emu/dolphin/keys{/key_id}', 'collaborators_url': 'https://api.github.com/repos/dolphin-emu/dolphin/collaborators{/collaborator}', 'teams_url': 'https://api.github.com/repos/dolphin-emu/dolphin/teams', 'hooks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/hooks', 'issue_events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/events{/number}', 'events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/events', 'assignees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/assignees{/user}', 'branches_url': 'https://api.github.com/repos/dolphin-emu/dolphin/branches{/branch}', 'tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/tags', 'blobs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/blobs{/sha}', 'git_tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/tags{/sha}', 'git_refs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/refs{/sha}', 'trees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/trees{/sha}', 'statuses_url': 'https://api.github.com/repos/dolphin-emu/dolphin/statuses/{sha}', 'languages_url': 'https://api.github.com/repos/dolphin-emu/dolphin/languages', 'stargazers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/stargazers', 'contributors_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contributors', 'subscribers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscribers', 'subscription_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscription', 'commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/commits{/sha}', 'git_commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/commits{/sha}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/comments{/number}', 'issue_comment_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments{/number}', 'contents_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contents/{+path}', 'compare_url': 'https://api.github.com/repos/dolphin-emu/dolphin/compare/{base}...{head}', 'merges_url': 'https://api.github.com/repos/dolphin-emu/dolphin/merges', 'archive_url': 'https://api.github.com/repos/dolphin-emu/dolphin/{archive_format}{/ref}', 'downloads_url': 'https://api.github.com/repos/dolphin-emu/dolphin/downloads', 'issues_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues{/number}', 'pulls_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls{/number}', 'milestones_url': 'https://api.github.com/repos/dolphin-emu/dolphin/milestones{/number}', 'notifications_url': 'https://api.github.com/repos/dolphin-emu/dolphin/notifications{?since,all,participating}', 'labels_url': 'https://api.github.com/repos/dolphin-emu/dolphin/labels{/name}', 'releases_url': 'https://api.github.com/repos/dolphin-emu/dolphin/releases{/id}', 'deployments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/deployments', 'created_at': '2013-07-22T09:07:57Z', 'updated_at': '2026-02-02T13:00:07Z', 'pushed_at': '2026-01-31T23:02:09Z', 'git_url': 'git://github.com/dolphin-emu/dolphin.git', 'ssh_url': 'git@github.com:dolphin-emu/dolphin.git', 'clone_url': 'https://github.com/dolphin-emu/dolphin.git', 'svn_url': 'https://github.com/dolphin-emu/dolphin', 'homepage': 'https://dolphin-emu.org', 'size': 544012, 'stargazers_count': 14575, 'watchers_count': 14575, 'language': 'C++', 'has_issues': False, 'has_projects': False, 'has_downloads': True, 'has_wiki': True, 'has_pages': False, 'has_discussions': False, 'forks_count': 2967, 'mirror_url': None, 'archived': False, 'disabled': False, 'open_issues_count': 360, 'license': {'key': 'other', 'name': 'Other', 'spdx_id': 'NOASSERTION', 'url': None, 'node_id': 'MDc6TGljZW5zZTA='}, 'allow_forking': True, 'is_template': False, 'web_commit_signoff_required': False, 'topics': ['cpp', 'emulation', 'emulator', 'gamecube', 'wii'], 'visibility': 'public', 'forks': 2967, 'open_issues': 360, 'watchers': 14575, 'default_branch': 'master', 'custom_properties': {}}, 'organization': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'url': 'https://api.github.com/orgs/dolphin-emu', 'repos_url': 'https://api.github.com/orgs/dolphin-emu/repos', 'events_url': 'https://api.github.com/orgs/dolphin-emu/events', 'hooks_url': 'https://api.github.com/orgs/dolphin-emu/hooks', 'issues_url': 'https://api.github.com/orgs/dolphin-emu/issues', 'members_url': 'https://api.github.com/orgs/dolphin-emu/members{/member}', 'public_members_url': 'https://api.github.com/orgs/dolphin-emu/public_members{/member}', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'description': ''}, 'sender': {'login': 'sepalani', 'id': 7890055, 'node_id': 'MDQ6VXNlcjc4OTAwNTU=', 'avatar_url': 'https://avatars.githubusercontent.com/u/7890055?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/sepalani', 'html_url': 'https://github.com/sepalani', 'followers_url': 'https://api.github.com/users/sepalani/followers', 'following_url': 'https://api.github.com/users/sepalani/following{/other_user}', 'gists_url': 'https://api.github.com/users/sepalani/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/sepalani/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/sepalani/subscriptions', 'organizations_url': 'https://api.github.com/users/sepalani/orgs', 'repos_url': 'https://api.github.com/users/sepalani/repos', 'events_url': 'https://api.github.com/users/sepalani/events{/privacy}', 'received_events_url': 'https://api.github.com/users/sepalani/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'installation': {'id': 35437981, 'node_id': 'MDIzOkludGVncmF0aW9uSW5zdGFsbGF0aW9uMzU0Mzc5ODE='}}, 'type': 'gh_issue_comment'}
2026-02-02T13:47:35.017369 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'sepalani', 'action': 'created', 'id': 14260, 'title': 'Externals: Update cpp-ipc to latest for mingw compatibility', 'url': 'https://github.com/dolphin-emu/dolphin/pull/14260#issuecomment-3835225593', 'safe_author': True, 'body': '@dolphin-emu-bot rebuild', 'raw': {'action': 'created', 'issue': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14260', 'repository_url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'labels_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14260/labels{/name}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14260/comments', 'events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14260/events', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/14260', 'id': 3789545274, 'node_id': 'PR_kwDOALCn2M678v8s', 'number': 14260, 'title': 'Externals: Update cpp-ipc to latest for mingw compatibility', 'user': {'login': 'cscd98', 'id': 1188869, 'node_id': 'MDQ6VXNlcjExODg4Njk=', 'avatar_url': 'https://avatars.githubusercontent.com/u/1188869?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/cscd98', 'html_url': 'https://github.com/cscd98', 'followers_url': 'https://api.github.com/users/cscd98/followers', 'following_url': 'https://api.github.com/users/cscd98/following{/other_user}', 'gists_url': 'https://api.github.com/users/cscd98/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/cscd98/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/cscd98/subscriptions', 'organizations_url': 'https://api.github.com/users/cscd98/orgs', 'repos_url': 'https://api.github.com/users/cscd98/repos', 'events_url': 'https://api.github.com/users/cscd98/events{/privacy}', 'received_events_url': 'https://api.github.com/users/cscd98/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'labels': [], 'state': 'open', 'locked': False, 'assignee': None, 'assignees': [], 'milestone': None, 'comments': 8, 'created_at': '2026-01-07T16:57:40Z', 'updated_at': '2026-02-02T13:47:33Z', 'closed_at': None, 'author_association': 'CONTRIBUTOR', 'type': None, 'active_lock_reason': None, 'draft': False, 'pull_request': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/14260', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/14260', 'diff_url': 'https://github.com/dolphin-emu/dolphin/pull/14260.diff', 'patch_url': 'https://github.com/dolphin-emu/dolphin/pull/14260.patch', 'merged_at': None}, 'body': 'This has this https://github.com/mutouyun/cpp-ipc/pull/169 merged in as some new Windows.h includes made it in breaking mingw compatibility, so this fixes that.\r\n\r\nNeeds testing.', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14260/reactions', 'total_count': 0, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 0, 'confused': 0, 'heart': 0, 'rocket': 0, 'eyes': 0}, 'timeline_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14260/timeline', 'performed_via_github_app': None, 'state_reason': None}, 'comment': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments/3835225593', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/14260#issuecomment-3835225593', 'issue_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14260', 'id': 3835225593, 'node_id': 'IC_kwDOALCn2M7kmOX5', 'user': {'login': 'sepalani', 'id': 7890055, 'node_id': 'MDQ6VXNlcjc4OTAwNTU=', 'avatar_url': 'https://avatars.githubusercontent.com/u/7890055?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/sepalani', 'html_url': 'https://github.com/sepalani', 'followers_url': 'https://api.github.com/users/sepalani/followers', 'following_url': 'https://api.github.com/users/sepalani/following{/other_user}', 'gists_url': 'https://api.github.com/users/sepalani/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/sepalani/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/sepalani/subscriptions', 'organizations_url': 'https://api.github.com/users/sepalani/orgs', 'repos_url': 'https://api.github.com/users/sepalani/repos', 'events_url': 'https://api.github.com/users/sepalani/events{/privacy}', 'received_events_url': 'https://api.github.com/users/sepalani/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'created_at': '2026-02-02T13:47:32Z', 'updated_at': '2026-02-02T13:47:32Z', 'body': '@dolphin-emu-bot rebuild', 'author_association': 'MEMBER', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments/3835225593/reactions', 'total_count': 0, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 0, 'confused': 0, 'heart': 0, 'rocket': 0, 'eyes': 0}, 'performed_via_github_app': None}, 'repository': {'id': 11577304, 'node_id': 'MDEwOlJlcG9zaXRvcnkxMTU3NzMwNA==', 'name': 'dolphin', 'full_name': 'dolphin-emu/dolphin', 'private': False, 'owner': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/dolphin-emu', 'html_url': 'https://github.com/dolphin-emu', 'followers_url': 'https://api.github.com/users/dolphin-emu/followers', 'following_url': 'https://api.github.com/users/dolphin-emu/following{/other_user}', 'gists_url': 'https://api.github.com/users/dolphin-emu/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/dolphin-emu/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/dolphin-emu/subscriptions', 'organizations_url': 'https://api.github.com/users/dolphin-emu/orgs', 'repos_url': 'https://api.github.com/users/dolphin-emu/repos', 'events_url': 'https://api.github.com/users/dolphin-emu/events{/privacy}', 'received_events_url': 'https://api.github.com/users/dolphin-emu/received_events', 'type': 'Organization', 'user_view_type': 'public', 'site_admin': False}, 'html_url': 'https://github.com/dolphin-emu/dolphin', 'description': 'Dolphin is a GameCube / Wii emulator, allowing you to play games for these two platforms on PC with improvements.', 'fork': False, 'url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'forks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/forks', 'keys_url': 'https://api.github.com/repos/dolphin-emu/dolphin/keys{/key_id}', 'collaborators_url': 'https://api.github.com/repos/dolphin-emu/dolphin/collaborators{/collaborator}', 'teams_url': 'https://api.github.com/repos/dolphin-emu/dolphin/teams', 'hooks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/hooks', 'issue_events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/events{/number}', 'events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/events', 'assignees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/assignees{/user}', 'branches_url': 'https://api.github.com/repos/dolphin-emu/dolphin/branches{/branch}', 'tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/tags', 'blobs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/blobs{/sha}', 'git_tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/tags{/sha}', 'git_refs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/refs{/sha}', 'trees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/trees{/sha}', 'statuses_url': 'https://api.github.com/repos/dolphin-emu/dolphin/statuses/{sha}', 'languages_url': 'https://api.github.com/repos/dolphin-emu/dolphin/languages', 'stargazers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/stargazers', 'contributors_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contributors', 'subscribers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscribers', 'subscription_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscription', 'commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/commits{/sha}', 'git_commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/commits{/sha}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/comments{/number}', 'issue_comment_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments{/number}', 'contents_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contents/{+path}', 'compare_url': 'https://api.github.com/repos/dolphin-emu/dolphin/compare/{base}...{head}', 'merges_url': 'https://api.github.com/repos/dolphin-emu/dolphin/merges', 'archive_url': 'https://api.github.com/repos/dolphin-emu/dolphin/{archive_format}{/ref}', 'downloads_url': 'https://api.github.com/repos/dolphin-emu/dolphin/downloads', 'issues_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues{/number}', 'pulls_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls{/number}', 'milestones_url': 'https://api.github.com/repos/dolphin-emu/dolphin/milestones{/number}', 'notifications_url': 'https://api.github.com/repos/dolphin-emu/dolphin/notifications{?since,all,participating}', 'labels_url': 'https://api.github.com/repos/dolphin-emu/dolphin/labels{/name}', 'releases_url': 'https://api.github.com/repos/dolphin-emu/dolphin/releases{/id}', 'deployments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/deployments', 'created_at': '2013-07-22T09:07:57Z', 'updated_at': '2026-02-02T13:00:07Z', 'pushed_at': '2026-01-31T23:02:09Z', 'git_url': 'git://github.com/dolphin-emu/dolphin.git', 'ssh_url': 'git@github.com:dolphin-emu/dolphin.git', 'clone_url': 'https://github.com/dolphin-emu/dolphin.git', 'svn_url': 'https://github.com/dolphin-emu/dolphin', 'homepage': 'https://dolphin-emu.org', 'size': 544012, 'stargazers_count': 14575, 'watchers_count': 14575, 'language': 'C++', 'has_issues': False, 'has_projects': False, 'has_downloads': True, 'has_wiki': True, 'has_pages': False, 'has_discussions': False, 'forks_count': 2967, 'mirror_url': None, 'archived': False, 'disabled': False, 'open_issues_count': 360, 'license': {'key': 'other', 'name': 'Other', 'spdx_id': 'NOASSERTION', 'url': None, 'node_id': 'MDc6TGljZW5zZTA='}, 'allow_forking': True, 'is_template': False, 'web_commit_signoff_required': False, 'topics': ['cpp', 'emulation', 'emulator', 'gamecube', 'wii'], 'visibility': 'public', 'forks': 2967, 'open_issues': 360, 'watchers': 14575, 'default_branch': 'master', 'custom_properties': {}}, 'organization': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'url': 'https://api.github.com/orgs/dolphin-emu', 'repos_url': 'https://api.github.com/orgs/dolphin-emu/repos', 'events_url': 'https://api.github.com/orgs/dolphin-emu/events', 'hooks_url': 'https://api.github.com/orgs/dolphin-emu/hooks', 'issues_url': 'https://api.github.com/orgs/dolphin-emu/issues', 'members_url': 'https://api.github.com/orgs/dolphin-emu/members{/member}', 'public_members_url': 'https://api.github.com/orgs/dolphin-emu/public_members{/member}', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'description': ''}, 'sender': {'login': 'sepalani', 'id': 7890055, 'node_id': 'MDQ6VXNlcjc4OTAwNTU=', 'avatar_url': 'https://avatars.githubusercontent.com/u/7890055?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/sepalani', 'html_url': 'https://github.com/sepalani', 'followers_url': 'https://api.github.com/users/sepalani/followers', 'following_url': 'https://api.github.com/users/sepalani/following{/other_user}', 'gists_url': 'https://api.github.com/users/sepalani/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/sepalani/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/sepalani/subscriptions', 'organizations_url': 'https://api.github.com/users/sepalani/orgs', 'repos_url': 'https://api.github.com/users/sepalani/repos', 'events_url': 'https://api.github.com/users/sepalani/events{/privacy}', 'received_events_url': 'https://api.github.com/users/sepalani/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'installation': {'id': 35437981, 'node_id': 'MDIzOkludGVncmF0aW9uSW5zdGFsbGF0aW9uMzU0Mzc5ODE='}}, 'type': 'gh_issue_comment'}
2026-02-02T09:18:16.840373 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'dolphin-ci[bot]', 'action': 'created', 'id': 14260, 'title': 'Externals: Update cpp-ipc to latest for mingw compatibility', 'url': 'https://github.com/dolphin-emu/dolphin/pull/14260#issuecomment-3833918915', 'safe_author': False, 'body': '[FifoCI](https://fifo.ci/about/) detected that this change impacts graphical rendering. Here are the [behavior differences](https://fifo.ci/version/512972c1cd4a10111412d619e464fc72a964101e/) detected by the system:\n\n<details>\n<summary>Detected differences</summary>\n\n||ogl-lin-mesa|vk-lin-mesa|\n|-|-|-|\n|DKCR-Char|[🔍 diff](https://fifo.ci/compare/15468920-15426640/)|[🔍 diff](https://fifo.ci/compare/15469060-15426780/)|\n|DKCR-fast-depth|[🔍 diff](https://fifo.ci/compare/15468941-15426661/)|[🔍 diff](https://fifo.ci/compare/15469081-15426801/)|\n|MaS-LOG-wiimote|[🔍 diff](https://fifo.ci/compare/15468958-15426678/)|[🔍 diff](https://fifo.ci/compare/15469098-15426818/)|\n|aeon-charge-attack|-|[🔍 diff](https://fifo.ci/compare/15469066-15426786/)|\n|burnout2-vehicletextures|[🔍 diff](https://fifo.ci/compare/15468963-15426683/)|[🔍 diff](https://fifo.ci/compare/15469103-15426823/)|\n|chibi-robo-fastdepth|-|[🔍 diff](https://fifo.ci/compare/15469042-15426762/)|\n|chibi-robo-zfighting|-|[🔍 diff](https://fifo.ci/compare/15469008-15426728/)|\n|custom-brawl-char|-|[🔍 diff](https://fifo.ci/compare/15469068-15426788/)|\n|ea-pink|[🔍 diff](https://fifo.ci/compare/15468980-15426700/)|[🔍 diff](https://fifo.ci/compare/15469120-15426840/)|\n|f-zero-rain|[🔍 diff](https://fifo.ci/compare/15468934-15426654/)|[🔍 diff](https://fifo.ci/compare/15469074-15426794/)|\n|fortune-street|-|[🔍 diff](https://fifo.ci/compare/15469027-15426747/)|\n|fortune-street-white-box|-|[🔍 diff](https://fifo.ci/compare/15469029-15426749/)|\n|hotwheels-shadows|-|[🔍 diff](https://fifo.ci/compare/15469142-15426862/)|\n|jd2-fmv|-|[🔍 diff](https://fifo.ci/compare/15469111-15426831/)|\n|jj-awae-mirrored|-|[🔍 diff](https://fifo.ci/compare/15469102-15426822/)|\n|kirby-logicop|[🔍 diff](https://fifo.ci/compare/15468989-15426709/)|[🔍 diff](https://fifo.ci/compare/15469129-15426849/)|\n|kirby-shadows|[🔍 diff](https://fifo.ci/compare/15468876-15426596/)|[🔍 diff](https://fifo.ci/compare/15469016-15426736/)|\n|last-story-shadows|[🔍 diff](https://fifo.ci/compare/15468960-15426680/)|[🔍 diff](https://fifo.ci/compare/15469100-15426820/)|\n|mario-baseball-shadows|-|[🔍 diff](https://fifo.ci/compare/15469095-15426815/)|\n|mario-golf-oob|-|[🔍 diff](https://fifo.ci/compare/15469126-15426846/)|\n|mario-golf-vertex-expansion|[🔍 diff](https://fifo.ci/compare/15469001-15426721/)|[🔍 diff](https://fifo.ci/compare/15469141-15426861/)|\n|mario-sluggers-bar|-|[🔍 diff](https://fifo.ci/compare/15469032-15426752/)|\n|mario-tennis-menu|-|[🔍 diff](https://fifo.ci/compare/15469031-15426751/)|\n|megaman-heat|[🔍 diff](https://fifo.ci/compare/15468896-15426616/)|[🔍 diff](https://fifo.ci/compare/15469036-15426756/)|\n|metroid-visor|[🔍 diff](https://fifo.ci/compare/15468942-15426662/)|[🔍 diff](https://fifo.ci/compare/15469082-15426802/)|\n|milotic-texture|[🔍 diff](https://fifo.ci/compare/15468911-15426631/)|[🔍 diff](https://fifo.ci/compare/15469051-15426771/)|\n|mkdd-efb|-|[🔍 diff](https://fifo.ci/compare/15469019-15426739/)|\n|mkw-bridge|-|[🔍 diff](https://fifo.ci/compare/15469110-15426830/)|\n|mp2-scanner|[🔍 diff](https://fifo.ci/compare/15468965-15426685/)|[🔍 diff](https://fifo.ci/compare/15469105-15426825/)|\n|mp3-bloom|[🔍 diff](https://fifo.ci/compare/15468957-15426677/)|[🔍 diff](https://fifo.ci/compare/15469097-15426817/)|\n|mtennis-zfreeze|[🔍 diff](https://fifo.ci/compare/15468894-15426614/)|[🔍 diff](https://fifo.ci/compare/15469034-15426754/)|\n|nddemo-bumpmapping|[🔍 diff](https://fifo.ci/compare/15468917-15426637/)|[🔍 diff](https://fifo.ci/compare/15469057-15426777/)|\n|nddemo-lighting|[🔍 diff](https://fifo.ci/compare/15468938-15426658/)|[🔍 diff](https://fifo.ci/compare/15469078-15426798/)|\n|nfsu-purplerect|-|[🔍 diff](https://fifo.ci/compare/15469021-15426741/)|\n|nfsu-reflections|-|[🔍 diff](https://fifo.ci/compare/15469011-15426731/)|\n|nhl-slap|[🔍 diff](https://fifo.ci/compare/15468966-15426686/)|[🔍 diff](https://fifo.ci/compare/15469106-15426826/)|\n|nsmbw-coins|-|[🔍 diff](https://fifo.ci/compare/15469064-15426784/)|\n|nsmbw-intro|-|[🔍 diff](https://fifo.ci/compare/15469013-15426733/)|\n|pbr-sfx|[🔍 diff](https://fifo.ci/compare/15468984-15426704/)|[🔍 diff](https://fifo.ci/compare/15469124-15426844/)|\n|pm-hc-jp|-|[🔍 diff](https://fifo.ci/compare/15469059-15426779/)|\n|quake-gx|-|[🔍 diff](https://fifo.ci/compare/15469130-15426850/)|\n|rs2-glass|[🔍 diff](https://fifo.ci/compare/15468899-15426619/)|[🔍 diff](https://fifo.ci/compare/15469039-15426759/)|\n|rs2-zfreeze|-|[🔍 diff](https://fifo.ci/compare/15469022-15426742/)|\n|rs3-bumpmapping|-|[🔍 diff](https://fifo.ci/compare/15469089-15426809/)|\n|sadx-ui|-|[🔍 diff](https://fifo.ci/compare/15469041-15426761/)|\n|sfa-shadows|-|[🔍 diff](https://fifo.ci/compare/15469085-15426805/)|\n|shadow-eyes|-|[🔍 diff](https://fifo.ci/compare/15469116-15426836/)|\n|smb-mirror|-|[🔍 diff](https://fifo.ci/compare/15469109-15426829/)|\n|smg-marioeyes|-|[🔍 diff](https://fifo.ci/compare/15469023-15426743/)|\n|smg-mmg|-|[🔍 diff](https://fifo.ci/compare/15469119-15426839/)|\n|sonic-riders-blur|-|[🔍 diff](https://fifo.ci/compare/15469094-15426814/)|\n|sonic-riders-zg-4p|-|[🔍 diff](https://fifo.ci/compare/15469121-15426841/)|\n|soniccolors-mm|[🔍 diff](https://fifo.ci/compare/15468923-15426643/)|[🔍 diff](https://fifo.ci/compare/15469063-15426783/)|\n|ssbb-mod-lloyd|-|[🔍 diff](https://fifo.ci/compare/15469080-15426800/)|\n|ssbm-pointsize|-|[🔍 diff](https://fifo.ci/compare/15469028-15426748/)|\n|sw3-dt|[🔍 diff](https://fifo.ci/compare/15468944-15426664/)|[🔍 diff](https://fifo.ci/compare/15469084-15426804/)|\n|thps4-shadow|[🔍 diff](https://fifo.ci/compare/15468903-15426623/)|[🔍 diff](https://fifo.ci/compare/15469043-15426763/)|\n|tsp3-pinkgrass|[🔍 diff](https://fifo.ci/compare/15468884-15426604/)|[🔍 diff](https://fifo.ci/compare/15469024-15426744/)|\n|vegas-party-depth|[🔍 diff](https://fifo.ci/compare/15468929-15426649/)|[🔍 diff](https://fifo.ci/compare/15469069-15426789/)|\n|ztp-grass|-|[🔍 diff](https://fifo.ci/compare/15469025-15426745/)|\n|zww-waves|-|[🔍 diff](https://fifo.ci/compare/15469045-15426765/)|\n</details>\n<sub><sup>automated-fifoci-reporter</sup></sub>', 'raw': {'action': 'created', 'issue': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14260', 'repository_url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'labels_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14260/labels{/name}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14260/comments', 'events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14260/events', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/14260', 'id': 3789545274, 'node_id': 'PR_kwDOALCn2M678v8s', 'number': 14260, 'title': 'Externals: Update cpp-ipc to latest for mingw compatibility', 'user': {'login': 'cscd98', 'id': 1188869, 'node_id': 'MDQ6VXNlcjExODg4Njk=', 'avatar_url': 'https://avatars.githubusercontent.com/u/1188869?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/cscd98', 'html_url': 'https://github.com/cscd98', 'followers_url': 'https://api.github.com/users/cscd98/followers', 'following_url': 'https://api.github.com/users/cscd98/following{/other_user}', 'gists_url': 'https://api.github.com/users/cscd98/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/cscd98/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/cscd98/subscriptions', 'organizations_url': 'https://api.github.com/users/cscd98/orgs', 'repos_url': 'https://api.github.com/users/cscd98/repos', 'events_url': 'https://api.github.com/users/cscd98/events{/privacy}', 'received_events_url': 'https://api.github.com/users/cscd98/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'labels': [], 'state': 'open', 'locked': False, 'assignee': None, 'assignees': [], 'milestone': None, 'comments': 7, 'created_at': '2026-01-07T16:57:40Z', 'updated_at': '2026-02-02T09:18:14Z', 'closed_at': None, 'author_association': 'CONTRIBUTOR', 'type': None, 'active_lock_reason': None, 'draft': False, 'pull_request': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/14260', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/14260', 'diff_url': 'https://github.com/dolphin-emu/dolphin/pull/14260.diff', 'patch_url': 'https://github.com/dolphin-emu/dolphin/pull/14260.patch', 'merged_at': None}, 'body': 'This has this https://github.com/mutouyun/cpp-ipc/pull/169 merged in as some new Windows.h includes made it in breaking mingw compatibility, so this fixes that.\r\n\r\nNeeds testing.', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14260/reactions', 'total_count': 0, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 0, 'confused': 0, 'heart': 0, 'rocket': 0, 'eyes': 0}, 'timeline_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14260/timeline', 'performed_via_github_app': None, 'state_reason': None}, 'comment': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments/3833918915', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/14260#issuecomment-3833918915', 'issue_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14260', 'id': 3833918915, 'node_id': 'IC_kwDOALCn2M7khPXD', 'user': {'login': 'dolphin-ci[bot]', 'id': 59266906, 'node_id': 'MDM6Qm90NTkyNjY5MDY=', 'avatar_url': 'https://avatars.githubusercontent.com/in/49947?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D', 'html_url': 'https://github.com/apps/dolphin-ci', 'followers_url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D/followers', 'following_url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D/following{/other_user}', 'gists_url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D/subscriptions', 'organizations_url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D/orgs', 'repos_url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D/repos', 'events_url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D/events{/privacy}', 'received_events_url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D/received_events', 'type': 'Bot', 'user_view_type': 'public', 'site_admin': False}, 'created_at': '2026-02-02T09:18:14Z', 'updated_at': '2026-02-02T09:18:14Z', 'body': '[FifoCI](https://fifo.ci/about/) detected that this change impacts graphical rendering. Here are the [behavior differences](https://fifo.ci/version/512972c1cd4a10111412d619e464fc72a964101e/) detected by the system:\n\n<details>\n<summary>Detected differences</summary>\n\n||ogl-lin-mesa|vk-lin-mesa|\n|-|-|-|\n|DKCR-Char|[🔍 diff](https://fifo.ci/compare/15468920-15426640/)|[🔍 diff](https://fifo.ci/compare/15469060-15426780/)|\n|DKCR-fast-depth|[🔍 diff](https://fifo.ci/compare/15468941-15426661/)|[🔍 diff](https://fifo.ci/compare/15469081-15426801/)|\n|MaS-LOG-wiimote|[🔍 diff](https://fifo.ci/compare/15468958-15426678/)|[🔍 diff](https://fifo.ci/compare/15469098-15426818/)|\n|aeon-charge-attack|-|[🔍 diff](https://fifo.ci/compare/15469066-15426786/)|\n|burnout2-vehicletextures|[🔍 diff](https://fifo.ci/compare/15468963-15426683/)|[🔍 diff](https://fifo.ci/compare/15469103-15426823/)|\n|chibi-robo-fastdepth|-|[🔍 diff](https://fifo.ci/compare/15469042-15426762/)|\n|chibi-robo-zfighting|-|[🔍 diff](https://fifo.ci/compare/15469008-15426728/)|\n|custom-brawl-char|-|[🔍 diff](https://fifo.ci/compare/15469068-15426788/)|\n|ea-pink|[🔍 diff](https://fifo.ci/compare/15468980-15426700/)|[🔍 diff](https://fifo.ci/compare/15469120-15426840/)|\n|f-zero-rain|[🔍 diff](https://fifo.ci/compare/15468934-15426654/)|[🔍 diff](https://fifo.ci/compare/15469074-15426794/)|\n|fortune-street|-|[🔍 diff](https://fifo.ci/compare/15469027-15426747/)|\n|fortune-street-white-box|-|[🔍 diff](https://fifo.ci/compare/15469029-15426749/)|\n|hotwheels-shadows|-|[🔍 diff](https://fifo.ci/compare/15469142-15426862/)|\n|jd2-fmv|-|[🔍 diff](https://fifo.ci/compare/15469111-15426831/)|\n|jj-awae-mirrored|-|[🔍 diff](https://fifo.ci/compare/15469102-15426822/)|\n|kirby-logicop|[🔍 diff](https://fifo.ci/compare/15468989-15426709/)|[🔍 diff](https://fifo.ci/compare/15469129-15426849/)|\n|kirby-shadows|[🔍 diff](https://fifo.ci/compare/15468876-15426596/)|[🔍 diff](https://fifo.ci/compare/15469016-15426736/)|\n|last-story-shadows|[🔍 diff](https://fifo.ci/compare/15468960-15426680/)|[🔍 diff](https://fifo.ci/compare/15469100-15426820/)|\n|mario-baseball-shadows|-|[🔍 diff](https://fifo.ci/compare/15469095-15426815/)|\n|mario-golf-oob|-|[🔍 diff](https://fifo.ci/compare/15469126-15426846/)|\n|mario-golf-vertex-expansion|[🔍 diff](https://fifo.ci/compare/15469001-15426721/)|[🔍 diff](https://fifo.ci/compare/15469141-15426861/)|\n|mario-sluggers-bar|-|[🔍 diff](https://fifo.ci/compare/15469032-15426752/)|\n|mario-tennis-menu|-|[🔍 diff](https://fifo.ci/compare/15469031-15426751/)|\n|megaman-heat|[🔍 diff](https://fifo.ci/compare/15468896-15426616/)|[🔍 diff](https://fifo.ci/compare/15469036-15426756/)|\n|metroid-visor|[🔍 diff](https://fifo.ci/compare/15468942-15426662/)|[🔍 diff](https://fifo.ci/compare/15469082-15426802/)|\n|milotic-texture|[🔍 diff](https://fifo.ci/compare/15468911-15426631/)|[🔍 diff](https://fifo.ci/compare/15469051-15426771/)|\n|mkdd-efb|-|[🔍 diff](https://fifo.ci/compare/15469019-15426739/)|\n|mkw-bridge|-|[🔍 diff](https://fifo.ci/compare/15469110-15426830/)|\n|mp2-scanner|[🔍 diff](https://fifo.ci/compare/15468965-15426685/)|[🔍 diff](https://fifo.ci/compare/15469105-15426825/)|\n|mp3-bloom|[🔍 diff](https://fifo.ci/compare/15468957-15426677/)|[🔍 diff](https://fifo.ci/compare/15469097-15426817/)|\n|mtennis-zfreeze|[🔍 diff](https://fifo.ci/compare/15468894-15426614/)|[🔍 diff](https://fifo.ci/compare/15469034-15426754/)|\n|nddemo-bumpmapping|[🔍 diff](https://fifo.ci/compare/15468917-15426637/)|[🔍 diff](https://fifo.ci/compare/15469057-15426777/)|\n|nddemo-lighting|[🔍 diff](https://fifo.ci/compare/15468938-15426658/)|[🔍 diff](https://fifo.ci/compare/15469078-15426798/)|\n|nfsu-purplerect|-|[🔍 diff](https://fifo.ci/compare/15469021-15426741/)|\n|nfsu-reflections|-|[🔍 diff](https://fifo.ci/compare/15469011-15426731/)|\n|nhl-slap|[🔍 diff](https://fifo.ci/compare/15468966-15426686/)|[🔍 diff](https://fifo.ci/compare/15469106-15426826/)|\n|nsmbw-coins|-|[🔍 diff](https://fifo.ci/compare/15469064-15426784/)|\n|nsmbw-intro|-|[🔍 diff](https://fifo.ci/compare/15469013-15426733/)|\n|pbr-sfx|[🔍 diff](https://fifo.ci/compare/15468984-15426704/)|[🔍 diff](https://fifo.ci/compare/15469124-15426844/)|\n|pm-hc-jp|-|[🔍 diff](https://fifo.ci/compare/15469059-15426779/)|\n|quake-gx|-|[🔍 diff](https://fifo.ci/compare/15469130-15426850/)|\n|rs2-glass|[🔍 diff](https://fifo.ci/compare/15468899-15426619/)|[🔍 diff](https://fifo.ci/compare/15469039-15426759/)|\n|rs2-zfreeze|-|[🔍 diff](https://fifo.ci/compare/15469022-15426742/)|\n|rs3-bumpmapping|-|[🔍 diff](https://fifo.ci/compare/15469089-15426809/)|\n|sadx-ui|-|[🔍 diff](https://fifo.ci/compare/15469041-15426761/)|\n|sfa-shadows|-|[🔍 diff](https://fifo.ci/compare/15469085-15426805/)|\n|shadow-eyes|-|[🔍 diff](https://fifo.ci/compare/15469116-15426836/)|\n|smb-mirror|-|[🔍 diff](https://fifo.ci/compare/15469109-15426829/)|\n|smg-marioeyes|-|[🔍 diff](https://fifo.ci/compare/15469023-15426743/)|\n|smg-mmg|-|[🔍 diff](https://fifo.ci/compare/15469119-15426839/)|\n|sonic-riders-blur|-|[🔍 diff](https://fifo.ci/compare/15469094-15426814/)|\n|sonic-riders-zg-4p|-|[🔍 diff](https://fifo.ci/compare/15469121-15426841/)|\n|soniccolors-mm|[🔍 diff](https://fifo.ci/compare/15468923-15426643/)|[🔍 diff](https://fifo.ci/compare/15469063-15426783/)|\n|ssbb-mod-lloyd|-|[🔍 diff](https://fifo.ci/compare/15469080-15426800/)|\n|ssbm-pointsize|-|[🔍 diff](https://fifo.ci/compare/15469028-15426748/)|\n|sw3-dt|[🔍 diff](https://fifo.ci/compare/15468944-15426664/)|[🔍 diff](https://fifo.ci/compare/15469084-15426804/)|\n|thps4-shadow|[🔍 diff](https://fifo.ci/compare/15468903-15426623/)|[🔍 diff](https://fifo.ci/compare/15469043-15426763/)|\n|tsp3-pinkgrass|[🔍 diff](https://fifo.ci/compare/15468884-15426604/)|[🔍 diff](https://fifo.ci/compare/15469024-15426744/)|\n|vegas-party-depth|[🔍 diff](https://fifo.ci/compare/15468929-15426649/)|[🔍 diff](https://fifo.ci/compare/15469069-15426789/)|\n|ztp-grass|-|[🔍 diff](https://fifo.ci/compare/15469025-15426745/)|\n|zww-waves|-|[🔍 diff](https://fifo.ci/compare/15469045-15426765/)|\n</details>\n<sub><sup>automated-fifoci-reporter</sup></sub>', 'author_association': 'NONE', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments/3833918915/reactions', 'total_count': 0, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 0, 'confused': 0, 'heart': 0, 'rocket': 0, 'eyes': 0}, 'performed_via_github_app': {'id': 49947, 'client_id': 'Iv1.1fcaea7644d8b727', 'slug': 'dolphin-ci', 'node_id': 'MDM6QXBwNDk5NDc=', 'owner': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/dolphin-emu', 'html_url': 'https://github.com/dolphin-emu', 'followers_url': 'https://api.github.com/users/dolphin-emu/followers', 'following_url': 'https://api.github.com/users/dolphin-emu/following{/other_user}', 'gists_url': 'https://api.github.com/users/dolphin-emu/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/dolphin-emu/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/dolphin-emu/subscriptions', 'organizations_url': 'https://api.github.com/users/dolphin-emu/orgs', 'repos_url': 'https://api.github.com/users/dolphin-emu/repos', 'events_url': 'https://api.github.com/users/dolphin-emu/events{/privacy}', 'received_events_url': 'https://api.github.com/users/dolphin-emu/received_events', 'type': 'Organization', 'user_view_type': 'public', 'site_admin': False}, 'name': 'Dolphin CI', 'description': 'Continuous Integration setup for [dolphin-emu.org](https://dolphin-emu.org/).', 'external_url': 'https://github.com/dolphin-emu', 'html_url': 'https://github.com/apps/dolphin-ci', 'created_at': '2019-12-26T22:26:07Z', 'updated_at': '2019-12-26T22:33:19Z', 'permissions': {'checks': 'write', 'contents': 'read', 'issues': 'write', 'members': 'read', 'metadata': 'read', 'pull_requests': 'write', 'statuses': 'write'}, 'events': ['check_run', 'commit_comment', 'issue_comment', 'pull_request', 'pull_request_review', 'pull_request_review_comment', 'push']}}, 'repository': {'id': 11577304, 'node_id': 'MDEwOlJlcG9zaXRvcnkxMTU3NzMwNA==', 'name': 'dolphin', 'full_name': 'dolphin-emu/dolphin', 'private': False, 'owner': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/dolphin-emu', 'html_url': 'https://github.com/dolphin-emu', 'followers_url': 'https://api.github.com/users/dolphin-emu/followers', 'following_url': 'https://api.github.com/users/dolphin-emu/following{/other_user}', 'gists_url': 'https://api.github.com/users/dolphin-emu/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/dolphin-emu/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/dolphin-emu/subscriptions', 'organizations_url': 'https://api.github.com/users/dolphin-emu/orgs', 'repos_url': 'https://api.github.com/users/dolphin-emu/repos', 'events_url': 'https://api.github.com/users/dolphin-emu/events{/privacy}', 'received_events_url': 'https://api.github.com/users/dolphin-emu/received_events', 'type': 'Organization', 'user_view_type': 'public', 'site_admin': False}, 'html_url': 'https://github.com/dolphin-emu/dolphin', 'description': 'Dolphin is a GameCube / Wii emulator, allowing you to play games for these two platforms on PC with improvements.', 'fork': False, 'url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'forks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/forks', 'keys_url': 'https://api.github.com/repos/dolphin-emu/dolphin/keys{/key_id}', 'collaborators_url': 'https://api.github.com/repos/dolphin-emu/dolphin/collaborators{/collaborator}', 'teams_url': 'https://api.github.com/repos/dolphin-emu/dolphin/teams', 'hooks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/hooks', 'issue_events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/events{/number}', 'events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/events', 'assignees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/assignees{/user}', 'branches_url': 'https://api.github.com/repos/dolphin-emu/dolphin/branches{/branch}', 'tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/tags', 'blobs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/blobs{/sha}', 'git_tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/tags{/sha}', 'git_refs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/refs{/sha}', 'trees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/trees{/sha}', 'statuses_url': 'https://api.github.com/repos/dolphin-emu/dolphin/statuses/{sha}', 'languages_url': 'https://api.github.com/repos/dolphin-emu/dolphin/languages', 'stargazers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/stargazers', 'contributors_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contributors', 'subscribers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscribers', 'subscription_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscription', 'commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/commits{/sha}', 'git_commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/commits{/sha}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/comments{/number}', 'issue_comment_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments{/number}', 'contents_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contents/{+path}', 'compare_url': 'https://api.github.com/repos/dolphin-emu/dolphin/compare/{base}...{head}', 'merges_url': 'https://api.github.com/repos/dolphin-emu/dolphin/merges', 'archive_url': 'https://api.github.com/repos/dolphin-emu/dolphin/{archive_format}{/ref}', 'downloads_url': 'https://api.github.com/repos/dolphin-emu/dolphin/downloads', 'issues_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues{/number}', 'pulls_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls{/number}', 'milestones_url': 'https://api.github.com/repos/dolphin-emu/dolphin/milestones{/number}', 'notifications_url': 'https://api.github.com/repos/dolphin-emu/dolphin/notifications{?since,all,participating}', 'labels_url': 'https://api.github.com/repos/dolphin-emu/dolphin/labels{/name}', 'releases_url': 'https://api.github.com/repos/dolphin-emu/dolphin/releases{/id}', 'deployments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/deployments', 'created_at': '2013-07-22T09:07:57Z', 'updated_at': '2026-02-02T08:38:08Z', 'pushed_at': '2026-01-31T23:02:09Z', 'git_url': 'git://github.com/dolphin-emu/dolphin.git', 'ssh_url': 'git@github.com:dolphin-emu/dolphin.git', 'clone_url': 'https://github.com/dolphin-emu/dolphin.git', 'svn_url': 'https://github.com/dolphin-emu/dolphin', 'homepage': 'https://dolphin-emu.org', 'size': 544012, 'stargazers_count': 14574, 'watchers_count': 14574, 'language': 'C++', 'has_issues': False, 'has_projects': False, 'has_downloads': True, 'has_wiki': True, 'has_pages': False, 'has_discussions': False, 'forks_count': 2967, 'mirror_url': None, 'archived': False, 'disabled': False, 'open_issues_count': 360, 'license': {'key': 'other', 'name': 'Other', 'spdx_id': 'NOASSERTION', 'url': None, 'node_id': 'MDc6TGljZW5zZTA='}, 'allow_forking': True, 'is_template': False, 'web_commit_signoff_required': False, 'topics': ['cpp', 'emulation', 'emulator', 'gamecube', 'wii'], 'visibility': 'public', 'forks': 2967, 'open_issues': 360, 'watchers': 14574, 'default_branch': 'master', 'custom_properties': {}}, 'organization': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'url': 'https://api.github.com/orgs/dolphin-emu', 'repos_url': 'https://api.github.com/orgs/dolphin-emu/repos', 'events_url': 'https://api.github.com/orgs/dolphin-emu/events', 'hooks_url': 'https://api.github.com/orgs/dolphin-emu/hooks', 'issues_url': 'https://api.github.com/orgs/dolphin-emu/issues', 'members_url': 'https://api.github.com/orgs/dolphin-emu/members{/member}', 'public_members_url': 'https://api.github.com/orgs/dolphin-emu/public_members{/member}', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'description': ''}, 'sender': {'login': 'dolphin-ci[bot]', 'id': 59266906, 'node_id': 'MDM6Qm90NTkyNjY5MDY=', 'avatar_url': 'https://avatars.githubusercontent.com/in/49947?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D', 'html_url': 'https://github.com/apps/dolphin-ci', 'followers_url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D/followers', 'following_url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D/following{/other_user}', 'gists_url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D/subscriptions', 'organizations_url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D/orgs', 'repos_url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D/repos', 'events_url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D/events{/privacy}', 'received_events_url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D/received_events', 'type': 'Bot', 'user_view_type': 'public', 'site_admin': False}, 'installation': {'id': 35437981, 'node_id': 'MDIzOkludGVncmF0aW9uSW5zdGFsbGF0aW9uMzU0Mzc5ODE='}}, 'type': 'gh_issue_comment'}
2026-02-02T09:18:14.496051 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'dolphin-ci[bot]', 'action': 'deleted', 'id': 14260, 'title': 'Externals: Update cpp-ipc to latest for mingw compatibility', 'url': 'https://github.com/dolphin-emu/dolphin/pull/14260#issuecomment-3833874415', 'safe_author': False, 'body': '[FifoCI](https://fifo.ci/about/) detected that this change impacts graphical rendering. Here are the [behavior differences](https://fifo.ci/version/512972c1cd4a10111412d619e464fc72a964101e/) detected by the system:\n\n<details>\n<summary>Detected differences</summary>\n\n||ogl-lin-mesa|\n|-|-|\n|DKCR-Char|[🔍 diff](https://fifo.ci/compare/15468920-15426640/)|\n|DKCR-fast-depth|[🔍 diff](https://fifo.ci/compare/15468941-15426661/)|\n|MaS-LOG-wiimote|[🔍 diff](https://fifo.ci/compare/15468958-15426678/)|\n|burnout2-vehicletextures|[🔍 diff](https://fifo.ci/compare/15468963-15426683/)|\n|ea-pink|[🔍 diff](https://fifo.ci/compare/15468980-15426700/)|\n|f-zero-rain|[🔍 diff](https://fifo.ci/compare/15468934-15426654/)|\n|kirby-logicop|[🔍 diff](https://fifo.ci/compare/15468989-15426709/)|\n|kirby-shadows|[🔍 diff](https://fifo.ci/compare/15468876-15426596/)|\n|last-story-shadows|[🔍 diff](https://fifo.ci/compare/15468960-15426680/)|\n|mario-golf-vertex-expansion|[🔍 diff](https://fifo.ci/compare/15469001-15426721/)|\n|megaman-heat|[🔍 diff](https://fifo.ci/compare/15468896-15426616/)|\n|metroid-visor|[🔍 diff](https://fifo.ci/compare/15468942-15426662/)|\n|milotic-texture|[🔍 diff](https://fifo.ci/compare/15468911-15426631/)|\n|mp2-scanner|[🔍 diff](https://fifo.ci/compare/15468965-15426685/)|\n|mp3-bloom|[🔍 diff](https://fifo.ci/compare/15468957-15426677/)|\n|mtennis-zfreeze|[🔍 diff](https://fifo.ci/compare/15468894-15426614/)|\n|nddemo-bumpmapping|[🔍 diff](https://fifo.ci/compare/15468917-15426637/)|\n|nddemo-lighting|[🔍 diff](https://fifo.ci/compare/15468938-15426658/)|\n|nhl-slap|[🔍 diff](https://fifo.ci/compare/15468966-15426686/)|\n|pbr-sfx|[🔍 diff](https://fifo.ci/compare/15468984-15426704/)|\n|rs2-glass|[🔍 diff](https://fifo.ci/compare/15468899-15426619/)|\n|soniccolors-mm|[🔍 diff](https://fifo.ci/compare/15468923-15426643/)|\n|sw3-dt|[🔍 diff](https://fifo.ci/compare/15468944-15426664/)|\n|thps4-shadow|[🔍 diff](https://fifo.ci/compare/15468903-15426623/)|\n|tsp3-pinkgrass|[🔍 diff](https://fifo.ci/compare/15468884-15426604/)|\n|vegas-party-depth|[🔍 diff](https://fifo.ci/compare/15468929-15426649/)|\n</details>\n<sub><sup>automated-fifoci-reporter</sup></sub>', 'raw': {'action': 'deleted', 'issue': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14260', 'repository_url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'labels_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14260/labels{/name}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14260/comments', 'events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14260/events', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/14260', 'id': 3789545274, 'node_id': 'PR_kwDOALCn2M678v8s', 'number': 14260, 'title': 'Externals: Update cpp-ipc to latest for mingw compatibility', 'user': {'login': 'cscd98', 'id': 1188869, 'node_id': 'MDQ6VXNlcjExODg4Njk=', 'avatar_url': 'https://avatars.githubusercontent.com/u/1188869?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/cscd98', 'html_url': 'https://github.com/cscd98', 'followers_url': 'https://api.github.com/users/cscd98/followers', 'following_url': 'https://api.github.com/users/cscd98/following{/other_user}', 'gists_url': 'https://api.github.com/users/cscd98/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/cscd98/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/cscd98/subscriptions', 'organizations_url': 'https://api.github.com/users/cscd98/orgs', 'repos_url': 'https://api.github.com/users/cscd98/repos', 'events_url': 'https://api.github.com/users/cscd98/events{/privacy}', 'received_events_url': 'https://api.github.com/users/cscd98/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'labels': [], 'state': 'open', 'locked': False, 'assignee': None, 'assignees': [], 'milestone': None, 'comments': 7, 'created_at': '2026-01-07T16:57:40Z', 'updated_at': '2026-02-02T09:07:59Z', 'closed_at': None, 'author_association': 'CONTRIBUTOR', 'type': None, 'active_lock_reason': None, 'draft': False, 'pull_request': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/14260', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/14260', 'diff_url': 'https://github.com/dolphin-emu/dolphin/pull/14260.diff', 'patch_url': 'https://github.com/dolphin-emu/dolphin/pull/14260.patch', 'merged_at': None}, 'body': 'This has this https://github.com/mutouyun/cpp-ipc/pull/169 merged in as some new Windows.h includes made it in breaking mingw compatibility, so this fixes that.\r\n\r\nNeeds testing.', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14260/reactions', 'total_count': 0, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 0, 'confused': 0, 'heart': 0, 'rocket': 0, 'eyes': 0}, 'timeline_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14260/timeline', 'performed_via_github_app': None, 'state_reason': None}, 'comment': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments/3833874415', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/14260#issuecomment-3833874415', 'issue_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14260', 'id': 3833874415, 'node_id': 'IC_kwDOALCn2M7khEfv', 'user': {'login': 'dolphin-ci[bot]', 'id': 59266906, 'node_id': 'MDM6Qm90NTkyNjY5MDY=', 'avatar_url': 'https://avatars.githubusercontent.com/in/49947?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D', 'html_url': 'https://github.com/apps/dolphin-ci', 'followers_url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D/followers', 'following_url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D/following{/other_user}', 'gists_url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D/subscriptions', 'organizations_url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D/orgs', 'repos_url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D/repos', 'events_url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D/events{/privacy}', 'received_events_url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D/received_events', 'type': 'Bot', 'user_view_type': 'public', 'site_admin': False}, 'created_at': '2026-02-02T09:07:59Z', 'updated_at': '2026-02-02T09:07:59Z', 'body': '[FifoCI](https://fifo.ci/about/) detected that this change impacts graphical rendering. Here are the [behavior differences](https://fifo.ci/version/512972c1cd4a10111412d619e464fc72a964101e/) detected by the system:\n\n<details>\n<summary>Detected differences</summary>\n\n||ogl-lin-mesa|\n|-|-|\n|DKCR-Char|[🔍 diff](https://fifo.ci/compare/15468920-15426640/)|\n|DKCR-fast-depth|[🔍 diff](https://fifo.ci/compare/15468941-15426661/)|\n|MaS-LOG-wiimote|[🔍 diff](https://fifo.ci/compare/15468958-15426678/)|\n|burnout2-vehicletextures|[🔍 diff](https://fifo.ci/compare/15468963-15426683/)|\n|ea-pink|[🔍 diff](https://fifo.ci/compare/15468980-15426700/)|\n|f-zero-rain|[🔍 diff](https://fifo.ci/compare/15468934-15426654/)|\n|kirby-logicop|[🔍 diff](https://fifo.ci/compare/15468989-15426709/)|\n|kirby-shadows|[🔍 diff](https://fifo.ci/compare/15468876-15426596/)|\n|last-story-shadows|[🔍 diff](https://fifo.ci/compare/15468960-15426680/)|\n|mario-golf-vertex-expansion|[🔍 diff](https://fifo.ci/compare/15469001-15426721/)|\n|megaman-heat|[🔍 diff](https://fifo.ci/compare/15468896-15426616/)|\n|metroid-visor|[🔍 diff](https://fifo.ci/compare/15468942-15426662/)|\n|milotic-texture|[🔍 diff](https://fifo.ci/compare/15468911-15426631/)|\n|mp2-scanner|[🔍 diff](https://fifo.ci/compare/15468965-15426685/)|\n|mp3-bloom|[🔍 diff](https://fifo.ci/compare/15468957-15426677/)|\n|mtennis-zfreeze|[🔍 diff](https://fifo.ci/compare/15468894-15426614/)|\n|nddemo-bumpmapping|[🔍 diff](https://fifo.ci/compare/15468917-15426637/)|\n|nddemo-lighting|[🔍 diff](https://fifo.ci/compare/15468938-15426658/)|\n|nhl-slap|[🔍 diff](https://fifo.ci/compare/15468966-15426686/)|\n|pbr-sfx|[🔍 diff](https://fifo.ci/compare/15468984-15426704/)|\n|rs2-glass|[🔍 diff](https://fifo.ci/compare/15468899-15426619/)|\n|soniccolors-mm|[🔍 diff](https://fifo.ci/compare/15468923-15426643/)|\n|sw3-dt|[🔍 diff](https://fifo.ci/compare/15468944-15426664/)|\n|thps4-shadow|[🔍 diff](https://fifo.ci/compare/15468903-15426623/)|\n|tsp3-pinkgrass|[🔍 diff](https://fifo.ci/compare/15468884-15426604/)|\n|vegas-party-depth|[🔍 diff](https://fifo.ci/compare/15468929-15426649/)|\n</details>\n<sub><sup>automated-fifoci-reporter</sup></sub>', 'author_association': 'NONE', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments/3833874415/reactions', 'total_count': 0, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 0, 'confused': 0, 'heart': 0, 'rocket': 0, 'eyes': 0}, 'performed_via_github_app': {'id': 49947, 'client_id': 'Iv1.1fcaea7644d8b727', 'slug': 'dolphin-ci', 'node_id': 'MDM6QXBwNDk5NDc=', 'owner': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/dolphin-emu', 'html_url': 'https://github.com/dolphin-emu', 'followers_url': 'https://api.github.com/users/dolphin-emu/followers', 'following_url': 'https://api.github.com/users/dolphin-emu/following{/other_user}', 'gists_url': 'https://api.github.com/users/dolphin-emu/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/dolphin-emu/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/dolphin-emu/subscriptions', 'organizations_url': 'https://api.github.com/users/dolphin-emu/orgs', 'repos_url': 'https://api.github.com/users/dolphin-emu/repos', 'events_url': 'https://api.github.com/users/dolphin-emu/events{/privacy}', 'received_events_url': 'https://api.github.com/users/dolphin-emu/received_events', 'type': 'Organization', 'user_view_type': 'public', 'site_admin': False}, 'name': 'Dolphin CI', 'description': 'Continuous Integration setup for [dolphin-emu.org](https://dolphin-emu.org/).', 'external_url': 'https://github.com/dolphin-emu', 'html_url': 'https://github.com/apps/dolphin-ci', 'created_at': '2019-12-26T22:26:07Z', 'updated_at': '2019-12-26T22:33:19Z', 'permissions': {'checks': 'write', 'contents': 'read', 'issues': 'write', 'members': 'read', 'metadata': 'read', 'pull_requests': 'write', 'statuses': 'write'}, 'events': ['check_run', 'commit_comment', 'issue_comment', 'pull_request', 'pull_request_review', 'pull_request_review_comment', 'push']}}, 'repository': {'id': 11577304, 'node_id': 'MDEwOlJlcG9zaXRvcnkxMTU3NzMwNA==', 'name': 'dolphin', 'full_name': 'dolphin-emu/dolphin', 'private': False, 'owner': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/dolphin-emu', 'html_url': 'https://github.com/dolphin-emu', 'followers_url': 'https://api.github.com/users/dolphin-emu/followers', 'following_url': 'https://api.github.com/users/dolphin-emu/following{/other_user}', 'gists_url': 'https://api.github.com/users/dolphin-emu/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/dolphin-emu/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/dolphin-emu/subscriptions', 'organizations_url': 'https://api.github.com/users/dolphin-emu/orgs', 'repos_url': 'https://api.github.com/users/dolphin-emu/repos', 'events_url': 'https://api.github.com/users/dolphin-emu/events{/privacy}', 'received_events_url': 'https://api.github.com/users/dolphin-emu/received_events', 'type': 'Organization', 'user_view_type': 'public', 'site_admin': False}, 'html_url': 'https://github.com/dolphin-emu/dolphin', 'description': 'Dolphin is a GameCube / Wii emulator, allowing you to play games for these two platforms on PC with improvements.', 'fork': False, 'url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'forks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/forks', 'keys_url': 'https://api.github.com/repos/dolphin-emu/dolphin/keys{/key_id}', 'collaborators_url': 'https://api.github.com/repos/dolphin-emu/dolphin/collaborators{/collaborator}', 'teams_url': 'https://api.github.com/repos/dolphin-emu/dolphin/teams', 'hooks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/hooks', 'issue_events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/events{/number}', 'events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/events', 'assignees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/assignees{/user}', 'branches_url': 'https://api.github.com/repos/dolphin-emu/dolphin/branches{/branch}', 'tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/tags', 'blobs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/blobs{/sha}', 'git_tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/tags{/sha}', 'git_refs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/refs{/sha}', 'trees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/trees{/sha}', 'statuses_url': 'https://api.github.com/repos/dolphin-emu/dolphin/statuses/{sha}', 'languages_url': 'https://api.github.com/repos/dolphin-emu/dolphin/languages', 'stargazers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/stargazers', 'contributors_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contributors', 'subscribers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscribers', 'subscription_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscription', 'commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/commits{/sha}', 'git_commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/commits{/sha}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/comments{/number}', 'issue_comment_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments{/number}', 'contents_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contents/{+path}', 'compare_url': 'https://api.github.com/repos/dolphin-emu/dolphin/compare/{base}...{head}', 'merges_url': 'https://api.github.com/repos/dolphin-emu/dolphin/merges', 'archive_url': 'https://api.github.com/repos/dolphin-emu/dolphin/{archive_format}{/ref}', 'downloads_url': 'https://api.github.com/repos/dolphin-emu/dolphin/downloads', 'issues_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues{/number}', 'pulls_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls{/number}', 'milestones_url': 'https://api.github.com/repos/dolphin-emu/dolphin/milestones{/number}', 'notifications_url': 'https://api.github.com/repos/dolphin-emu/dolphin/notifications{?since,all,participating}', 'labels_url': 'https://api.github.com/repos/dolphin-emu/dolphin/labels{/name}', 'releases_url': 'https://api.github.com/repos/dolphin-emu/dolphin/releases{/id}', 'deployments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/deployments', 'created_at': '2013-07-22T09:07:57Z', 'updated_at': '2026-02-02T08:38:08Z', 'pushed_at': '2026-01-31T23:02:09Z', 'git_url': 'git://github.com/dolphin-emu/dolphin.git', 'ssh_url': 'git@github.com:dolphin-emu/dolphin.git', 'clone_url': 'https://github.com/dolphin-emu/dolphin.git', 'svn_url': 'https://github.com/dolphin-emu/dolphin', 'homepage': 'https://dolphin-emu.org', 'size': 544012, 'stargazers_count': 14574, 'watchers_count': 14574, 'language': 'C++', 'has_issues': False, 'has_projects': False, 'has_downloads': True, 'has_wiki': True, 'has_pages': False, 'has_discussions': False, 'forks_count': 2967, 'mirror_url': None, 'archived': False, 'disabled': False, 'open_issues_count': 360, 'license': {'key': 'other', 'name': 'Other', 'spdx_id': 'NOASSERTION', 'url': None, 'node_id': 'MDc6TGljZW5zZTA='}, 'allow_forking': True, 'is_template': False, 'web_commit_signoff_required': False, 'topics': ['cpp', 'emulation', 'emulator', 'gamecube', 'wii'], 'visibility': 'public', 'forks': 2967, 'open_issues': 360, 'watchers': 14574, 'default_branch': 'master', 'custom_properties': {}}, 'organization': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'url': 'https://api.github.com/orgs/dolphin-emu', 'repos_url': 'https://api.github.com/orgs/dolphin-emu/repos', 'events_url': 'https://api.github.com/orgs/dolphin-emu/events', 'hooks_url': 'https://api.github.com/orgs/dolphin-emu/hooks', 'issues_url': 'https://api.github.com/orgs/dolphin-emu/issues', 'members_url': 'https://api.github.com/orgs/dolphin-emu/members{/member}', 'public_members_url': 'https://api.github.com/orgs/dolphin-emu/public_members{/member}', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'description': ''}, 'sender': {'login': 'dolphin-ci[bot]', 'id': 59266906, 'node_id': 'MDM6Qm90NTkyNjY5MDY=', 'avatar_url': 'https://avatars.githubusercontent.com/in/49947?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D', 'html_url': 'https://github.com/apps/dolphin-ci', 'followers_url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D/followers', 'following_url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D/following{/other_user}', 'gists_url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D/subscriptions', 'organizations_url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D/orgs', 'repos_url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D/repos', 'events_url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D/events{/privacy}', 'received_events_url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D/received_events', 'type': 'Bot', 'user_view_type': 'public', 'site_admin': False}, 'installation': {'id': 35437981, 'node_id': 'MDIzOkludGVncmF0aW9uSW5zdGFsbGF0aW9uMzU0Mzc5ODE='}}, 'type': 'gh_issue_comment'}
2026-02-02T09:08:02.093505 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'dolphin-ci[bot]', 'action': 'created', 'id': 14260, 'title': 'Externals: Update cpp-ipc to latest for mingw compatibility', 'url': 'https://github.com/dolphin-emu/dolphin/pull/14260#issuecomment-3833874415', 'safe_author': False, 'body': '[FifoCI](https://fifo.ci/about/) detected that this change impacts graphical rendering. Here are the [behavior differences](https://fifo.ci/version/512972c1cd4a10111412d619e464fc72a964101e/) detected by the system:\n\n<details>\n<summary>Detected differences</summary>\n\n||ogl-lin-mesa|\n|-|-|\n|DKCR-Char|[🔍 diff](https://fifo.ci/compare/15468920-15426640/)|\n|DKCR-fast-depth|[🔍 diff](https://fifo.ci/compare/15468941-15426661/)|\n|MaS-LOG-wiimote|[🔍 diff](https://fifo.ci/compare/15468958-15426678/)|\n|burnout2-vehicletextures|[🔍 diff](https://fifo.ci/compare/15468963-15426683/)|\n|ea-pink|[🔍 diff](https://fifo.ci/compare/15468980-15426700/)|\n|f-zero-rain|[🔍 diff](https://fifo.ci/compare/15468934-15426654/)|\n|kirby-logicop|[🔍 diff](https://fifo.ci/compare/15468989-15426709/)|\n|kirby-shadows|[🔍 diff](https://fifo.ci/compare/15468876-15426596/)|\n|last-story-shadows|[🔍 diff](https://fifo.ci/compare/15468960-15426680/)|\n|mario-golf-vertex-expansion|[🔍 diff](https://fifo.ci/compare/15469001-15426721/)|\n|megaman-heat|[🔍 diff](https://fifo.ci/compare/15468896-15426616/)|\n|metroid-visor|[🔍 diff](https://fifo.ci/compare/15468942-15426662/)|\n|milotic-texture|[🔍 diff](https://fifo.ci/compare/15468911-15426631/)|\n|mp2-scanner|[🔍 diff](https://fifo.ci/compare/15468965-15426685/)|\n|mp3-bloom|[🔍 diff](https://fifo.ci/compare/15468957-15426677/)|\n|mtennis-zfreeze|[🔍 diff](https://fifo.ci/compare/15468894-15426614/)|\n|nddemo-bumpmapping|[🔍 diff](https://fifo.ci/compare/15468917-15426637/)|\n|nddemo-lighting|[🔍 diff](https://fifo.ci/compare/15468938-15426658/)|\n|nhl-slap|[🔍 diff](https://fifo.ci/compare/15468966-15426686/)|\n|pbr-sfx|[🔍 diff](https://fifo.ci/compare/15468984-15426704/)|\n|rs2-glass|[🔍 diff](https://fifo.ci/compare/15468899-15426619/)|\n|soniccolors-mm|[🔍 diff](https://fifo.ci/compare/15468923-15426643/)|\n|sw3-dt|[🔍 diff](https://fifo.ci/compare/15468944-15426664/)|\n|thps4-shadow|[🔍 diff](https://fifo.ci/compare/15468903-15426623/)|\n|tsp3-pinkgrass|[🔍 diff](https://fifo.ci/compare/15468884-15426604/)|\n|vegas-party-depth|[🔍 diff](https://fifo.ci/compare/15468929-15426649/)|\n</details>\n<sub><sup>automated-fifoci-reporter</sup></sub>', 'raw': {'action': 'created', 'issue': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14260', 'repository_url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'labels_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14260/labels{/name}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14260/comments', 'events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14260/events', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/14260', 'id': 3789545274, 'node_id': 'PR_kwDOALCn2M678v8s', 'number': 14260, 'title': 'Externals: Update cpp-ipc to latest for mingw compatibility', 'user': {'login': 'cscd98', 'id': 1188869, 'node_id': 'MDQ6VXNlcjExODg4Njk=', 'avatar_url': 'https://avatars.githubusercontent.com/u/1188869?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/cscd98', 'html_url': 'https://github.com/cscd98', 'followers_url': 'https://api.github.com/users/cscd98/followers', 'following_url': 'https://api.github.com/users/cscd98/following{/other_user}', 'gists_url': 'https://api.github.com/users/cscd98/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/cscd98/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/cscd98/subscriptions', 'organizations_url': 'https://api.github.com/users/cscd98/orgs', 'repos_url': 'https://api.github.com/users/cscd98/repos', 'events_url': 'https://api.github.com/users/cscd98/events{/privacy}', 'received_events_url': 'https://api.github.com/users/cscd98/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'labels': [], 'state': 'open', 'locked': False, 'assignee': None, 'assignees': [], 'milestone': None, 'comments': 7, 'created_at': '2026-01-07T16:57:40Z', 'updated_at': '2026-02-02T09:07:59Z', 'closed_at': None, 'author_association': 'CONTRIBUTOR', 'type': None, 'active_lock_reason': None, 'draft': False, 'pull_request': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/14260', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/14260', 'diff_url': 'https://github.com/dolphin-emu/dolphin/pull/14260.diff', 'patch_url': 'https://github.com/dolphin-emu/dolphin/pull/14260.patch', 'merged_at': None}, 'body': 'This has this https://github.com/mutouyun/cpp-ipc/pull/169 merged in as some new Windows.h includes made it in breaking mingw compatibility, so this fixes that.\r\n\r\nNeeds testing.', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14260/reactions', 'total_count': 0, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 0, 'confused': 0, 'heart': 0, 'rocket': 0, 'eyes': 0}, 'timeline_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14260/timeline', 'performed_via_github_app': None, 'state_reason': None}, 'comment': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments/3833874415', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/14260#issuecomment-3833874415', 'issue_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14260', 'id': 3833874415, 'node_id': 'IC_kwDOALCn2M7khEfv', 'user': {'login': 'dolphin-ci[bot]', 'id': 59266906, 'node_id': 'MDM6Qm90NTkyNjY5MDY=', 'avatar_url': 'https://avatars.githubusercontent.com/in/49947?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D', 'html_url': 'https://github.com/apps/dolphin-ci', 'followers_url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D/followers', 'following_url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D/following{/other_user}', 'gists_url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D/subscriptions', 'organizations_url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D/orgs', 'repos_url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D/repos', 'events_url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D/events{/privacy}', 'received_events_url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D/received_events', 'type': 'Bot', 'user_view_type': 'public', 'site_admin': False}, 'created_at': '2026-02-02T09:07:59Z', 'updated_at': '2026-02-02T09:07:59Z', 'body': '[FifoCI](https://fifo.ci/about/) detected that this change impacts graphical rendering. Here are the [behavior differences](https://fifo.ci/version/512972c1cd4a10111412d619e464fc72a964101e/) detected by the system:\n\n<details>\n<summary>Detected differences</summary>\n\n||ogl-lin-mesa|\n|-|-|\n|DKCR-Char|[🔍 diff](https://fifo.ci/compare/15468920-15426640/)|\n|DKCR-fast-depth|[🔍 diff](https://fifo.ci/compare/15468941-15426661/)|\n|MaS-LOG-wiimote|[🔍 diff](https://fifo.ci/compare/15468958-15426678/)|\n|burnout2-vehicletextures|[🔍 diff](https://fifo.ci/compare/15468963-15426683/)|\n|ea-pink|[🔍 diff](https://fifo.ci/compare/15468980-15426700/)|\n|f-zero-rain|[🔍 diff](https://fifo.ci/compare/15468934-15426654/)|\n|kirby-logicop|[🔍 diff](https://fifo.ci/compare/15468989-15426709/)|\n|kirby-shadows|[🔍 diff](https://fifo.ci/compare/15468876-15426596/)|\n|last-story-shadows|[🔍 diff](https://fifo.ci/compare/15468960-15426680/)|\n|mario-golf-vertex-expansion|[🔍 diff](https://fifo.ci/compare/15469001-15426721/)|\n|megaman-heat|[🔍 diff](https://fifo.ci/compare/15468896-15426616/)|\n|metroid-visor|[🔍 diff](https://fifo.ci/compare/15468942-15426662/)|\n|milotic-texture|[🔍 diff](https://fifo.ci/compare/15468911-15426631/)|\n|mp2-scanner|[🔍 diff](https://fifo.ci/compare/15468965-15426685/)|\n|mp3-bloom|[🔍 diff](https://fifo.ci/compare/15468957-15426677/)|\n|mtennis-zfreeze|[🔍 diff](https://fifo.ci/compare/15468894-15426614/)|\n|nddemo-bumpmapping|[🔍 diff](https://fifo.ci/compare/15468917-15426637/)|\n|nddemo-lighting|[🔍 diff](https://fifo.ci/compare/15468938-15426658/)|\n|nhl-slap|[🔍 diff](https://fifo.ci/compare/15468966-15426686/)|\n|pbr-sfx|[🔍 diff](https://fifo.ci/compare/15468984-15426704/)|\n|rs2-glass|[🔍 diff](https://fifo.ci/compare/15468899-15426619/)|\n|soniccolors-mm|[🔍 diff](https://fifo.ci/compare/15468923-15426643/)|\n|sw3-dt|[🔍 diff](https://fifo.ci/compare/15468944-15426664/)|\n|thps4-shadow|[🔍 diff](https://fifo.ci/compare/15468903-15426623/)|\n|tsp3-pinkgrass|[🔍 diff](https://fifo.ci/compare/15468884-15426604/)|\n|vegas-party-depth|[🔍 diff](https://fifo.ci/compare/15468929-15426649/)|\n</details>\n<sub><sup>automated-fifoci-reporter</sup></sub>', 'author_association': 'NONE', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments/3833874415/reactions', 'total_count': 0, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 0, 'confused': 0, 'heart': 0, 'rocket': 0, 'eyes': 0}, 'performed_via_github_app': {'id': 49947, 'client_id': 'Iv1.1fcaea7644d8b727', 'slug': 'dolphin-ci', 'node_id': 'MDM6QXBwNDk5NDc=', 'owner': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/dolphin-emu', 'html_url': 'https://github.com/dolphin-emu', 'followers_url': 'https://api.github.com/users/dolphin-emu/followers', 'following_url': 'https://api.github.com/users/dolphin-emu/following{/other_user}', 'gists_url': 'https://api.github.com/users/dolphin-emu/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/dolphin-emu/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/dolphin-emu/subscriptions', 'organizations_url': 'https://api.github.com/users/dolphin-emu/orgs', 'repos_url': 'https://api.github.com/users/dolphin-emu/repos', 'events_url': 'https://api.github.com/users/dolphin-emu/events{/privacy}', 'received_events_url': 'https://api.github.com/users/dolphin-emu/received_events', 'type': 'Organization', 'user_view_type': 'public', 'site_admin': False}, 'name': 'Dolphin CI', 'description': 'Continuous Integration setup for [dolphin-emu.org](https://dolphin-emu.org/).', 'external_url': 'https://github.com/dolphin-emu', 'html_url': 'https://github.com/apps/dolphin-ci', 'created_at': '2019-12-26T22:26:07Z', 'updated_at': '2019-12-26T22:33:19Z', 'permissions': {'checks': 'write', 'contents': 'read', 'issues': 'write', 'members': 'read', 'metadata': 'read', 'pull_requests': 'write', 'statuses': 'write'}, 'events': ['check_run', 'commit_comment', 'issue_comment', 'pull_request', 'pull_request_review', 'pull_request_review_comment', 'push']}}, 'repository': {'id': 11577304, 'node_id': 'MDEwOlJlcG9zaXRvcnkxMTU3NzMwNA==', 'name': 'dolphin', 'full_name': 'dolphin-emu/dolphin', 'private': False, 'owner': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/dolphin-emu', 'html_url': 'https://github.com/dolphin-emu', 'followers_url': 'https://api.github.com/users/dolphin-emu/followers', 'following_url': 'https://api.github.com/users/dolphin-emu/following{/other_user}', 'gists_url': 'https://api.github.com/users/dolphin-emu/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/dolphin-emu/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/dolphin-emu/subscriptions', 'organizations_url': 'https://api.github.com/users/dolphin-emu/orgs', 'repos_url': 'https://api.github.com/users/dolphin-emu/repos', 'events_url': 'https://api.github.com/users/dolphin-emu/events{/privacy}', 'received_events_url': 'https://api.github.com/users/dolphin-emu/received_events', 'type': 'Organization', 'user_view_type': 'public', 'site_admin': False}, 'html_url': 'https://github.com/dolphin-emu/dolphin', 'description': 'Dolphin is a GameCube / Wii emulator, allowing you to play games for these two platforms on PC with improvements.', 'fork': False, 'url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'forks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/forks', 'keys_url': 'https://api.github.com/repos/dolphin-emu/dolphin/keys{/key_id}', 'collaborators_url': 'https://api.github.com/repos/dolphin-emu/dolphin/collaborators{/collaborator}', 'teams_url': 'https://api.github.com/repos/dolphin-emu/dolphin/teams', 'hooks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/hooks', 'issue_events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/events{/number}', 'events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/events', 'assignees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/assignees{/user}', 'branches_url': 'https://api.github.com/repos/dolphin-emu/dolphin/branches{/branch}', 'tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/tags', 'blobs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/blobs{/sha}', 'git_tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/tags{/sha}', 'git_refs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/refs{/sha}', 'trees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/trees{/sha}', 'statuses_url': 'https://api.github.com/repos/dolphin-emu/dolphin/statuses/{sha}', 'languages_url': 'https://api.github.com/repos/dolphin-emu/dolphin/languages', 'stargazers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/stargazers', 'contributors_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contributors', 'subscribers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscribers', 'subscription_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscription', 'commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/commits{/sha}', 'git_commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/commits{/sha}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/comments{/number}', 'issue_comment_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments{/number}', 'contents_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contents/{+path}', 'compare_url': 'https://api.github.com/repos/dolphin-emu/dolphin/compare/{base}...{head}', 'merges_url': 'https://api.github.com/repos/dolphin-emu/dolphin/merges', 'archive_url': 'https://api.github.com/repos/dolphin-emu/dolphin/{archive_format}{/ref}', 'downloads_url': 'https://api.github.com/repos/dolphin-emu/dolphin/downloads', 'issues_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues{/number}', 'pulls_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls{/number}', 'milestones_url': 'https://api.github.com/repos/dolphin-emu/dolphin/milestones{/number}', 'notifications_url': 'https://api.github.com/repos/dolphin-emu/dolphin/notifications{?since,all,participating}', 'labels_url': 'https://api.github.com/repos/dolphin-emu/dolphin/labels{/name}', 'releases_url': 'https://api.github.com/repos/dolphin-emu/dolphin/releases{/id}', 'deployments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/deployments', 'created_at': '2013-07-22T09:07:57Z', 'updated_at': '2026-02-02T08:38:08Z', 'pushed_at': '2026-01-31T23:02:09Z', 'git_url': 'git://github.com/dolphin-emu/dolphin.git', 'ssh_url': 'git@github.com:dolphin-emu/dolphin.git', 'clone_url': 'https://github.com/dolphin-emu/dolphin.git', 'svn_url': 'https://github.com/dolphin-emu/dolphin', 'homepage': 'https://dolphin-emu.org', 'size': 544012, 'stargazers_count': 14574, 'watchers_count': 14574, 'language': 'C++', 'has_issues': False, 'has_projects': False, 'has_downloads': True, 'has_wiki': True, 'has_pages': False, 'has_discussions': False, 'forks_count': 2967, 'mirror_url': None, 'archived': False, 'disabled': False, 'open_issues_count': 360, 'license': {'key': 'other', 'name': 'Other', 'spdx_id': 'NOASSERTION', 'url': None, 'node_id': 'MDc6TGljZW5zZTA='}, 'allow_forking': True, 'is_template': False, 'web_commit_signoff_required': False, 'topics': ['cpp', 'emulation', 'emulator', 'gamecube', 'wii'], 'visibility': 'public', 'forks': 2967, 'open_issues': 360, 'watchers': 14574, 'default_branch': 'master', 'custom_properties': {}}, 'organization': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'url': 'https://api.github.com/orgs/dolphin-emu', 'repos_url': 'https://api.github.com/orgs/dolphin-emu/repos', 'events_url': 'https://api.github.com/orgs/dolphin-emu/events', 'hooks_url': 'https://api.github.com/orgs/dolphin-emu/hooks', 'issues_url': 'https://api.github.com/orgs/dolphin-emu/issues', 'members_url': 'https://api.github.com/orgs/dolphin-emu/members{/member}', 'public_members_url': 'https://api.github.com/orgs/dolphin-emu/public_members{/member}', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'description': ''}, 'sender': {'login': 'dolphin-ci[bot]', 'id': 59266906, 'node_id': 'MDM6Qm90NTkyNjY5MDY=', 'avatar_url': 'https://avatars.githubusercontent.com/in/49947?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D', 'html_url': 'https://github.com/apps/dolphin-ci', 'followers_url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D/followers', 'following_url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D/following{/other_user}', 'gists_url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D/subscriptions', 'organizations_url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D/orgs', 'repos_url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D/repos', 'events_url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D/events{/privacy}', 'received_events_url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D/received_events', 'type': 'Bot', 'user_view_type': 'public', 'site_admin': False}, 'installation': {'id': 35437981, 'node_id': 'MDIzOkludGVncmF0aW9uSW5zdGFsbGF0aW9uMzU0Mzc5ODE='}}, 'type': 'gh_issue_comment'}
2026-02-02T08:55:22.738308 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'dolphin-ci[bot]', 'action': 'deleted', 'id': 14260, 'title': 'Externals: Update cpp-ipc to latest for mingw compatibility', 'url': 'https://github.com/dolphin-emu/dolphin/pull/14260#issuecomment-3816809833', 'safe_author': False, 'body': '[FifoCI](https://fifo.ci/about/) detected that this change impacts graphical rendering. Here are the [behavior differences](https://fifo.ci/version/c79193a2cb21af931e79f9cca7ab3dd592f9fd34/) detected by the system:\n\n<details>\n<summary>Detected differences</summary>\n\n||ogl-lin-mesa|vk-lin-mesa|\n|-|-|-|\n|DKCR-Char|[🔍 diff](https://fifo.ci/compare/15456460-15426640/)|[🔍 diff](https://fifo.ci/compare/15456600-15426780/)|\n|DKCR-fast-depth|[🔍 diff](https://fifo.ci/compare/15456481-15426661/)|[🔍 diff](https://fifo.ci/compare/15456621-15426801/)|\n|MaS-LOG-wiimote|[🔍 diff](https://fifo.ci/compare/15456498-15426678/)|[🔍 diff](https://fifo.ci/compare/15456638-15426818/)|\n|aeon-charge-attack|-|[🔍 diff](https://fifo.ci/compare/15456606-15426786/)|\n|burnout2-vehicletextures|[🔍 diff](https://fifo.ci/compare/15456503-15426683/)|[🔍 diff](https://fifo.ci/compare/15456643-15426823/)|\n|chibi-robo-fastdepth|-|[🔍 diff](https://fifo.ci/compare/15456582-15426762/)|\n|chibi-robo-zfighting|-|[🔍 diff](https://fifo.ci/compare/15456548-15426728/)|\n|custom-brawl-char|-|[🔍 diff](https://fifo.ci/compare/15456608-15426788/)|\n|ea-pink|[🔍 diff](https://fifo.ci/compare/15456520-15426700/)|[🔍 diff](https://fifo.ci/compare/15456660-15426840/)|\n|f-zero-rain|[🔍 diff](https://fifo.ci/compare/15456474-15426654/)|[🔍 diff](https://fifo.ci/compare/15456614-15426794/)|\n|fortune-street|-|[🔍 diff](https://fifo.ci/compare/15456567-15426747/)|\n|fortune-street-white-box|-|[🔍 diff](https://fifo.ci/compare/15456569-15426749/)|\n|hotwheels-shadows|-|[🔍 diff](https://fifo.ci/compare/15456682-15426862/)|\n|jd2-fmv|-|[🔍 diff](https://fifo.ci/compare/15456651-15426831/)|\n|jj-awae-mirrored|-|[🔍 diff](https://fifo.ci/compare/15456642-15426822/)|\n|kirby-logicop|[🔍 diff](https://fifo.ci/compare/15456529-15426709/)|[🔍 diff](https://fifo.ci/compare/15456669-15426849/)|\n|kirby-shadows|[🔍 diff](https://fifo.ci/compare/15456416-15426596/)|[🔍 diff](https://fifo.ci/compare/15456556-15426736/)|\n|last-story-shadows|[🔍 diff](https://fifo.ci/compare/15456500-15426680/)|[🔍 diff](https://fifo.ci/compare/15456640-15426820/)|\n|mario-baseball-shadows|-|[🔍 diff](https://fifo.ci/compare/15456635-15426815/)|\n|mario-golf-oob|-|[🔍 diff](https://fifo.ci/compare/15456666-15426846/)|\n|mario-golf-vertex-expansion|[🔍 diff](https://fifo.ci/compare/15456541-15426721/)|[🔍 diff](https://fifo.ci/compare/15456681-15426861/)|\n|mario-sluggers-bar|-|[🔍 diff](https://fifo.ci/compare/15456572-15426752/)|\n|mario-tennis-menu|-|[🔍 diff](https://fifo.ci/compare/15456571-15426751/)|\n|megaman-heat|[🔍 diff](https://fifo.ci/compare/15456436-15426616/)|[🔍 diff](https://fifo.ci/compare/15456576-15426756/)|\n|metroid-visor|[🔍 diff](https://fifo.ci/compare/15456482-15426662/)|[🔍 diff](https://fifo.ci/compare/15456622-15426802/)|\n|milotic-texture|[🔍 diff](https://fifo.ci/compare/15456451-15426631/)|[🔍 diff](https://fifo.ci/compare/15456591-15426771/)|\n|mkdd-efb|-|[🔍 diff](https://fifo.ci/compare/15456559-15426739/)|\n|mkw-bridge|-|[🔍 diff](https://fifo.ci/compare/15456650-15426830/)|\n|mp2-scanner|[🔍 diff](https://fifo.ci/compare/15456505-15426685/)|[🔍 diff](https://fifo.ci/compare/15456645-15426825/)|\n|mp3-bloom|[🔍 diff](https://fifo.ci/compare/15456497-15426677/)|[🔍 diff](https://fifo.ci/compare/15456637-15426817/)|\n|mtennis-zfreeze|[🔍 diff](https://fifo.ci/compare/15456434-15426614/)|[🔍 diff](https://fifo.ci/compare/15456574-15426754/)|\n|nddemo-bumpmapping|[🔍 diff](https://fifo.ci/compare/15456457-15426637/)|[🔍 diff](https://fifo.ci/compare/15456597-15426777/)|\n|nddemo-lighting|[🔍 diff](https://fifo.ci/compare/15456478-15426658/)|[🔍 diff](https://fifo.ci/compare/15456618-15426798/)|\n|nfsu-purplerect|-|[🔍 diff](https://fifo.ci/compare/15456561-15426741/)|\n|nfsu-reflections|-|[🔍 diff](https://fifo.ci/compare/15456551-15426731/)|\n|nhl-slap|[🔍 diff](https://fifo.ci/compare/15456506-15426686/)|[🔍 diff](https://fifo.ci/compare/15456646-15426826/)|\n|nsmbw-coins|-|[🔍 diff](https://fifo.ci/compare/15456604-15426784/)|\n|nsmbw-intro|-|[🔍 diff](https://fifo.ci/compare/15456553-15426733/)|\n|pbr-sfx|[🔍 diff](https://fifo.ci/compare/15456524-15426704/)|[🔍 diff](https://fifo.ci/compare/15456664-15426844/)|\n|pm-hc-jp|-|[🔍 diff](https://fifo.ci/compare/15456599-15426779/)|\n|quake-gx|-|[🔍 diff](https://fifo.ci/compare/15456670-15426850/)|\n|rs2-glass|[🔍 diff](https://fifo.ci/compare/15456439-15426619/)|[🔍 diff](https://fifo.ci/compare/15456579-15426759/)|\n|rs2-zfreeze|-|[🔍 diff](https://fifo.ci/compare/15456562-15426742/)|\n|rs3-bumpmapping|-|[🔍 diff](https://fifo.ci/compare/15456629-15426809/)|\n|sadx-ui|-|[🔍 diff](https://fifo.ci/compare/15456581-15426761/)|\n|sfa-shadows|-|[🔍 diff](https://fifo.ci/compare/15456625-15426805/)|\n|shadow-eyes|-|[🔍 diff](https://fifo.ci/compare/15456656-15426836/)|\n|smb-mirror|-|[🔍 diff](https://fifo.ci/compare/15456649-15426829/)|\n|smg-marioeyes|-|[🔍 diff](https://fifo.ci/compare/15456563-15426743/)|\n|smg-mmg|-|[🔍 diff](https://fifo.ci/compare/15456659-15426839/)|\n|sonic-riders-blur|-|[🔍 diff](https://fifo.ci/compare/15456634-15426814/)|\n|sonic-riders-zg-4p|-|[🔍 diff](https://fifo.ci/compare/15456661-15426841/)|\n|soniccolors-mm|[🔍 diff](https://fifo.ci/compare/15456463-15426643/)|[🔍 diff](https://fifo.ci/compare/15456603-15426783/)|\n|ssbb-mod-lloyd|-|[🔍 diff](https://fifo.ci/compare/15456620-15426800/)|\n|ssbm-pointsize|-|[🔍 diff](https://fifo.ci/compare/15456568-15426748/)|\n|sw3-dt|[🔍 diff](https://fifo.ci/compare/15456484-15426664/)|[🔍 diff](https://fifo.ci/compare/15456624-15426804/)|\n|thps4-shadow|[🔍 diff](https://fifo.ci/compare/15456443-15426623/)|[🔍 diff](https://fifo.ci/compare/15456583-15426763/)|\n|tsp3-pinkgrass|[🔍 diff](https://fifo.ci/compare/15456424-15426604/)|[🔍 diff](https://fifo.ci/compare/15456564-15426744/)|\n|vegas-party-depth|[🔍 diff](https://fifo.ci/compare/15456469-15426649/)|[🔍 diff](https://fifo.ci/compare/15456609-15426789/)|\n|ztp-grass|-|[🔍 diff](https://fifo.ci/compare/15456565-15426745/)|\n|zww-waves|-|[🔍 diff](https://fifo.ci/compare/15456585-15426765/)|\n</details>\n<sub><sup>automated-fifoci-reporter</sup></sub>', 'raw': {'action': 'deleted', 'issue': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14260', 'repository_url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'labels_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14260/labels{/name}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14260/comments', 'events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14260/events', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/14260', 'id': 3789545274, 'node_id': 'PR_kwDOALCn2M678v8s', 'number': 14260, 'title': 'Externals: Update cpp-ipc to latest for mingw compatibility', 'user': {'login': 'cscd98', 'id': 1188869, 'node_id': 'MDQ6VXNlcjExODg4Njk=', 'avatar_url': 'https://avatars.githubusercontent.com/u/1188869?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/cscd98', 'html_url': 'https://github.com/cscd98', 'followers_url': 'https://api.github.com/users/cscd98/followers', 'following_url': 'https://api.github.com/users/cscd98/following{/other_user}', 'gists_url': 'https://api.github.com/users/cscd98/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/cscd98/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/cscd98/subscriptions', 'organizations_url': 'https://api.github.com/users/cscd98/orgs', 'repos_url': 'https://api.github.com/users/cscd98/repos', 'events_url': 'https://api.github.com/users/cscd98/events{/privacy}', 'received_events_url': 'https://api.github.com/users/cscd98/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'labels': [], 'state': 'open', 'locked': False, 'assignee': None, 'assignees': [], 'milestone': None, 'comments': 7, 'created_at': '2026-01-07T16:57:40Z', 'updated_at': '2026-02-02T08:27:57Z', 'closed_at': None, 'author_association': 'CONTRIBUTOR', 'type': None, 'active_lock_reason': None, 'draft': False, 'pull_request': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/14260', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/14260', 'diff_url': 'https://github.com/dolphin-emu/dolphin/pull/14260.diff', 'patch_url': 'https://github.com/dolphin-emu/dolphin/pull/14260.patch', 'merged_at': None}, 'body': 'This has this https://github.com/mutouyun/cpp-ipc/pull/169 merged in as some new Windows.h includes made it in breaking mingw compatibility, so this fixes that.\r\n\r\nNeeds testing.', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14260/reactions', 'total_count': 0, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 0, 'confused': 0, 'heart': 0, 'rocket': 0, 'eyes': 0}, 'timeline_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14260/timeline', 'performed_via_github_app': None, 'state_reason': None}, 'comment': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments/3816809833', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/14260#issuecomment-3816809833', 'issue_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14260', 'id': 3816809833, 'node_id': 'IC_kwDOALCn2M7jf-Vp', 'user': {'login': 'dolphin-ci[bot]', 'id': 59266906, 'node_id': 'MDM6Qm90NTkyNjY5MDY=', 'avatar_url': 'https://avatars.githubusercontent.com/in/49947?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D', 'html_url': 'https://github.com/apps/dolphin-ci', 'followers_url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D/followers', 'following_url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D/following{/other_user}', 'gists_url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D/subscriptions', 'organizations_url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D/orgs', 'repos_url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D/repos', 'events_url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D/events{/privacy}', 'received_events_url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D/received_events', 'type': 'Bot', 'user_view_type': 'public', 'site_admin': False}, 'created_at': '2026-01-29T10:32:14Z', 'updated_at': '2026-01-29T10:32:14Z', 'body': '[FifoCI](https://fifo.ci/about/) detected that this change impacts graphical rendering. Here are the [behavior differences](https://fifo.ci/version/c79193a2cb21af931e79f9cca7ab3dd592f9fd34/) detected by the system:\n\n<details>\n<summary>Detected differences</summary>\n\n||ogl-lin-mesa|vk-lin-mesa|\n|-|-|-|\n|DKCR-Char|[🔍 diff](https://fifo.ci/compare/15456460-15426640/)|[🔍 diff](https://fifo.ci/compare/15456600-15426780/)|\n|DKCR-fast-depth|[🔍 diff](https://fifo.ci/compare/15456481-15426661/)|[🔍 diff](https://fifo.ci/compare/15456621-15426801/)|\n|MaS-LOG-wiimote|[🔍 diff](https://fifo.ci/compare/15456498-15426678/)|[🔍 diff](https://fifo.ci/compare/15456638-15426818/)|\n|aeon-charge-attack|-|[🔍 diff](https://fifo.ci/compare/15456606-15426786/)|\n|burnout2-vehicletextures|[🔍 diff](https://fifo.ci/compare/15456503-15426683/)|[🔍 diff](https://fifo.ci/compare/15456643-15426823/)|\n|chibi-robo-fastdepth|-|[🔍 diff](https://fifo.ci/compare/15456582-15426762/)|\n|chibi-robo-zfighting|-|[🔍 diff](https://fifo.ci/compare/15456548-15426728/)|\n|custom-brawl-char|-|[🔍 diff](https://fifo.ci/compare/15456608-15426788/)|\n|ea-pink|[🔍 diff](https://fifo.ci/compare/15456520-15426700/)|[🔍 diff](https://fifo.ci/compare/15456660-15426840/)|\n|f-zero-rain|[🔍 diff](https://fifo.ci/compare/15456474-15426654/)|[🔍 diff](https://fifo.ci/compare/15456614-15426794/)|\n|fortune-street|-|[🔍 diff](https://fifo.ci/compare/15456567-15426747/)|\n|fortune-street-white-box|-|[🔍 diff](https://fifo.ci/compare/15456569-15426749/)|\n|hotwheels-shadows|-|[🔍 diff](https://fifo.ci/compare/15456682-15426862/)|\n|jd2-fmv|-|[🔍 diff](https://fifo.ci/compare/15456651-15426831/)|\n|jj-awae-mirrored|-|[🔍 diff](https://fifo.ci/compare/15456642-15426822/)|\n|kirby-logicop|[🔍 diff](https://fifo.ci/compare/15456529-15426709/)|[🔍 diff](https://fifo.ci/compare/15456669-15426849/)|\n|kirby-shadows|[🔍 diff](https://fifo.ci/compare/15456416-15426596/)|[🔍 diff](https://fifo.ci/compare/15456556-15426736/)|\n|last-story-shadows|[🔍 diff](https://fifo.ci/compare/15456500-15426680/)|[🔍 diff](https://fifo.ci/compare/15456640-15426820/)|\n|mario-baseball-shadows|-|[🔍 diff](https://fifo.ci/compare/15456635-15426815/)|\n|mario-golf-oob|-|[🔍 diff](https://fifo.ci/compare/15456666-15426846/)|\n|mario-golf-vertex-expansion|[🔍 diff](https://fifo.ci/compare/15456541-15426721/)|[🔍 diff](https://fifo.ci/compare/15456681-15426861/)|\n|mario-sluggers-bar|-|[🔍 diff](https://fifo.ci/compare/15456572-15426752/)|\n|mario-tennis-menu|-|[🔍 diff](https://fifo.ci/compare/15456571-15426751/)|\n|megaman-heat|[🔍 diff](https://fifo.ci/compare/15456436-15426616/)|[🔍 diff](https://fifo.ci/compare/15456576-15426756/)|\n|metroid-visor|[🔍 diff](https://fifo.ci/compare/15456482-15426662/)|[🔍 diff](https://fifo.ci/compare/15456622-15426802/)|\n|milotic-texture|[🔍 diff](https://fifo.ci/compare/15456451-15426631/)|[🔍 diff](https://fifo.ci/compare/15456591-15426771/)|\n|mkdd-efb|-|[🔍 diff](https://fifo.ci/compare/15456559-15426739/)|\n|mkw-bridge|-|[🔍 diff](https://fifo.ci/compare/15456650-15426830/)|\n|mp2-scanner|[🔍 diff](https://fifo.ci/compare/15456505-15426685/)|[🔍 diff](https://fifo.ci/compare/15456645-15426825/)|\n|mp3-bloom|[🔍 diff](https://fifo.ci/compare/15456497-15426677/)|[🔍 diff](https://fifo.ci/compare/15456637-15426817/)|\n|mtennis-zfreeze|[🔍 diff](https://fifo.ci/compare/15456434-15426614/)|[🔍 diff](https://fifo.ci/compare/15456574-15426754/)|\n|nddemo-bumpmapping|[🔍 diff](https://fifo.ci/compare/15456457-15426637/)|[🔍 diff](https://fifo.ci/compare/15456597-15426777/)|\n|nddemo-lighting|[🔍 diff](https://fifo.ci/compare/15456478-15426658/)|[🔍 diff](https://fifo.ci/compare/15456618-15426798/)|\n|nfsu-purplerect|-|[🔍 diff](https://fifo.ci/compare/15456561-15426741/)|\n|nfsu-reflections|-|[🔍 diff](https://fifo.ci/compare/15456551-15426731/)|\n|nhl-slap|[🔍 diff](https://fifo.ci/compare/15456506-15426686/)|[🔍 diff](https://fifo.ci/compare/15456646-15426826/)|\n|nsmbw-coins|-|[🔍 diff](https://fifo.ci/compare/15456604-15426784/)|\n|nsmbw-intro|-|[🔍 diff](https://fifo.ci/compare/15456553-15426733/)|\n|pbr-sfx|[🔍 diff](https://fifo.ci/compare/15456524-15426704/)|[🔍 diff](https://fifo.ci/compare/15456664-15426844/)|\n|pm-hc-jp|-|[🔍 diff](https://fifo.ci/compare/15456599-15426779/)|\n|quake-gx|-|[🔍 diff](https://fifo.ci/compare/15456670-15426850/)|\n|rs2-glass|[🔍 diff](https://fifo.ci/compare/15456439-15426619/)|[🔍 diff](https://fifo.ci/compare/15456579-15426759/)|\n|rs2-zfreeze|-|[🔍 diff](https://fifo.ci/compare/15456562-15426742/)|\n|rs3-bumpmapping|-|[🔍 diff](https://fifo.ci/compare/15456629-15426809/)|\n|sadx-ui|-|[🔍 diff](https://fifo.ci/compare/15456581-15426761/)|\n|sfa-shadows|-|[🔍 diff](https://fifo.ci/compare/15456625-15426805/)|\n|shadow-eyes|-|[🔍 diff](https://fifo.ci/compare/15456656-15426836/)|\n|smb-mirror|-|[🔍 diff](https://fifo.ci/compare/15456649-15426829/)|\n|smg-marioeyes|-|[🔍 diff](https://fifo.ci/compare/15456563-15426743/)|\n|smg-mmg|-|[🔍 diff](https://fifo.ci/compare/15456659-15426839/)|\n|sonic-riders-blur|-|[🔍 diff](https://fifo.ci/compare/15456634-15426814/)|\n|sonic-riders-zg-4p|-|[🔍 diff](https://fifo.ci/compare/15456661-15426841/)|\n|soniccolors-mm|[🔍 diff](https://fifo.ci/compare/15456463-15426643/)|[🔍 diff](https://fifo.ci/compare/15456603-15426783/)|\n|ssbb-mod-lloyd|-|[🔍 diff](https://fifo.ci/compare/15456620-15426800/)|\n|ssbm-pointsize|-|[🔍 diff](https://fifo.ci/compare/15456568-15426748/)|\n|sw3-dt|[🔍 diff](https://fifo.ci/compare/15456484-15426664/)|[🔍 diff](https://fifo.ci/compare/15456624-15426804/)|\n|thps4-shadow|[🔍 diff](https://fifo.ci/compare/15456443-15426623/)|[🔍 diff](https://fifo.ci/compare/15456583-15426763/)|\n|tsp3-pinkgrass|[🔍 diff](https://fifo.ci/compare/15456424-15426604/)|[🔍 diff](https://fifo.ci/compare/15456564-15426744/)|\n|vegas-party-depth|[🔍 diff](https://fifo.ci/compare/15456469-15426649/)|[🔍 diff](https://fifo.ci/compare/15456609-15426789/)|\n|ztp-grass|-|[🔍 diff](https://fifo.ci/compare/15456565-15426745/)|\n|zww-waves|-|[🔍 diff](https://fifo.ci/compare/15456585-15426765/)|\n</details>\n<sub><sup>automated-fifoci-reporter</sup></sub>', 'author_association': 'NONE', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments/3816809833/reactions', 'total_count': 0, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 0, 'confused': 0, 'heart': 0, 'rocket': 0, 'eyes': 0}, 'performed_via_github_app': {'id': 49947, 'client_id': 'Iv1.1fcaea7644d8b727', 'slug': 'dolphin-ci', 'node_id': 'MDM6QXBwNDk5NDc=', 'owner': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/dolphin-emu', 'html_url': 'https://github.com/dolphin-emu', 'followers_url': 'https://api.github.com/users/dolphin-emu/followers', 'following_url': 'https://api.github.com/users/dolphin-emu/following{/other_user}', 'gists_url': 'https://api.github.com/users/dolphin-emu/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/dolphin-emu/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/dolphin-emu/subscriptions', 'organizations_url': 'https://api.github.com/users/dolphin-emu/orgs', 'repos_url': 'https://api.github.com/users/dolphin-emu/repos', 'events_url': 'https://api.github.com/users/dolphin-emu/events{/privacy}', 'received_events_url': 'https://api.github.com/users/dolphin-emu/received_events', 'type': 'Organization', 'user_view_type': 'public', 'site_admin': False}, 'name': 'Dolphin CI', 'description': 'Continuous Integration setup for [dolphin-emu.org](https://dolphin-emu.org/).', 'external_url': 'https://github.com/dolphin-emu', 'html_url': 'https://github.com/apps/dolphin-ci', 'created_at': '2019-12-26T22:26:07Z', 'updated_at': '2019-12-26T22:33:19Z', 'permissions': {'checks': 'write', 'contents': 'read', 'issues': 'write', 'members': 'read', 'metadata': 'read', 'pull_requests': 'write', 'statuses': 'write'}, 'events': ['check_run', 'commit_comment', 'issue_comment', 'pull_request', 'pull_request_review', 'pull_request_review_comment', 'push']}}, 'repository': {'id': 11577304, 'node_id': 'MDEwOlJlcG9zaXRvcnkxMTU3NzMwNA==', 'name': 'dolphin', 'full_name': 'dolphin-emu/dolphin', 'private': False, 'owner': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/dolphin-emu', 'html_url': 'https://github.com/dolphin-emu', 'followers_url': 'https://api.github.com/users/dolphin-emu/followers', 'following_url': 'https://api.github.com/users/dolphin-emu/following{/other_user}', 'gists_url': 'https://api.github.com/users/dolphin-emu/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/dolphin-emu/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/dolphin-emu/subscriptions', 'organizations_url': 'https://api.github.com/users/dolphin-emu/orgs', 'repos_url': 'https://api.github.com/users/dolphin-emu/repos', 'events_url': 'https://api.github.com/users/dolphin-emu/events{/privacy}', 'received_events_url': 'https://api.github.com/users/dolphin-emu/received_events', 'type': 'Organization', 'user_view_type': 'public', 'site_admin': False}, 'html_url': 'https://github.com/dolphin-emu/dolphin', 'description': 'Dolphin is a GameCube / Wii emulator, allowing you to play games for these two platforms on PC with improvements.', 'fork': False, 'url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'forks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/forks', 'keys_url': 'https://api.github.com/repos/dolphin-emu/dolphin/keys{/key_id}', 'collaborators_url': 'https://api.github.com/repos/dolphin-emu/dolphin/collaborators{/collaborator}', 'teams_url': 'https://api.github.com/repos/dolphin-emu/dolphin/teams', 'hooks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/hooks', 'issue_events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/events{/number}', 'events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/events', 'assignees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/assignees{/user}', 'branches_url': 'https://api.github.com/repos/dolphin-emu/dolphin/branches{/branch}', 'tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/tags', 'blobs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/blobs{/sha}', 'git_tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/tags{/sha}', 'git_refs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/refs{/sha}', 'trees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/trees{/sha}', 'statuses_url': 'https://api.github.com/repos/dolphin-emu/dolphin/statuses/{sha}', 'languages_url': 'https://api.github.com/repos/dolphin-emu/dolphin/languages', 'stargazers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/stargazers', 'contributors_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contributors', 'subscribers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscribers', 'subscription_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscription', 'commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/commits{/sha}', 'git_commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/commits{/sha}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/comments{/number}', 'issue_comment_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments{/number}', 'contents_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contents/{+path}', 'compare_url': 'https://api.github.com/repos/dolphin-emu/dolphin/compare/{base}...{head}', 'merges_url': 'https://api.github.com/repos/dolphin-emu/dolphin/merges', 'archive_url': 'https://api.github.com/repos/dolphin-emu/dolphin/{archive_format}{/ref}', 'downloads_url': 'https://api.github.com/repos/dolphin-emu/dolphin/downloads', 'issues_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues{/number}', 'pulls_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls{/number}', 'milestones_url': 'https://api.github.com/repos/dolphin-emu/dolphin/milestones{/number}', 'notifications_url': 'https://api.github.com/repos/dolphin-emu/dolphin/notifications{?since,all,participating}', 'labels_url': 'https://api.github.com/repos/dolphin-emu/dolphin/labels{/name}', 'releases_url': 'https://api.github.com/repos/dolphin-emu/dolphin/releases{/id}', 'deployments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/deployments', 'created_at': '2013-07-22T09:07:57Z', 'updated_at': '2026-02-02T08:38:08Z', 'pushed_at': '2026-01-31T23:02:09Z', 'git_url': 'git://github.com/dolphin-emu/dolphin.git', 'ssh_url': 'git@github.com:dolphin-emu/dolphin.git', 'clone_url': 'https://github.com/dolphin-emu/dolphin.git', 'svn_url': 'https://github.com/dolphin-emu/dolphin', 'homepage': 'https://dolphin-emu.org', 'size': 544012, 'stargazers_count': 14574, 'watchers_count': 14574, 'language': 'C++', 'has_issues': False, 'has_projects': False, 'has_downloads': True, 'has_wiki': True, 'has_pages': False, 'has_discussions': False, 'forks_count': 2967, 'mirror_url': None, 'archived': False, 'disabled': False, 'open_issues_count': 360, 'license': {'key': 'other', 'name': 'Other', 'spdx_id': 'NOASSERTION', 'url': None, 'node_id': 'MDc6TGljZW5zZTA='}, 'allow_forking': True, 'is_template': False, 'web_commit_signoff_required': False, 'topics': ['cpp', 'emulation', 'emulator', 'gamecube', 'wii'], 'visibility': 'public', 'forks': 2967, 'open_issues': 360, 'watchers': 14574, 'default_branch': 'master', 'custom_properties': {}}, 'organization': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'url': 'https://api.github.com/orgs/dolphin-emu', 'repos_url': 'https://api.github.com/orgs/dolphin-emu/repos', 'events_url': 'https://api.github.com/orgs/dolphin-emu/events', 'hooks_url': 'https://api.github.com/orgs/dolphin-emu/hooks', 'issues_url': 'https://api.github.com/orgs/dolphin-emu/issues', 'members_url': 'https://api.github.com/orgs/dolphin-emu/members{/member}', 'public_members_url': 'https://api.github.com/orgs/dolphin-emu/public_members{/member}', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'description': ''}, 'sender': {'login': 'dolphin-ci[bot]', 'id': 59266906, 'node_id': 'MDM6Qm90NTkyNjY5MDY=', 'avatar_url': 'https://avatars.githubusercontent.com/in/49947?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D', 'html_url': 'https://github.com/apps/dolphin-ci', 'followers_url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D/followers', 'following_url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D/following{/other_user}', 'gists_url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D/subscriptions', 'organizations_url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D/orgs', 'repos_url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D/repos', 'events_url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D/events{/privacy}', 'received_events_url': 'https://api.github.com/users/dolphin-ci%5Bbot%5D/received_events', 'type': 'Bot', 'user_view_type': 'public', 'site_admin': False}, 'installation': {'id': 35437981, 'node_id': 'MDIzOkludGVncmF0aW9uSW5zdGFsbGF0aW9uMzU0Mzc5ODE='}}, 'type': 'gh_issue_comment'}
2026-02-02T08:27:57.825812 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'sepalani', 'action': 'deleted', 'id': 14260, 'title': 'Externals: Update cpp-ipc to latest for mingw compatibility', 'url': 'https://github.com/dolphin-emu/dolphin/pull/14260#issuecomment-3833690255', 'safe_author': True, 'body': '@dolphin-emu-bot rebuild', 'raw': {'action': 'deleted', 'issue': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14260', 'repository_url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'labels_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14260/labels{/name}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14260/comments', 'events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14260/events', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/14260', 'id': 3789545274, 'node_id': 'PR_kwDOALCn2M678v8s', 'number': 14260, 'title': 'Externals: Update cpp-ipc to latest for mingw compatibility', 'user': {'login': 'cscd98', 'id': 1188869, 'node_id': 'MDQ6VXNlcjExODg4Njk=', 'avatar_url': 'https://avatars.githubusercontent.com/u/1188869?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/cscd98', 'html_url': 'https://github.com/cscd98', 'followers_url': 'https://api.github.com/users/cscd98/followers', 'following_url': 'https://api.github.com/users/cscd98/following{/other_user}', 'gists_url': 'https://api.github.com/users/cscd98/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/cscd98/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/cscd98/subscriptions', 'organizations_url': 'https://api.github.com/users/cscd98/orgs', 'repos_url': 'https://api.github.com/users/cscd98/repos', 'events_url': 'https://api.github.com/users/cscd98/events{/privacy}', 'received_events_url': 'https://api.github.com/users/cscd98/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'labels': [], 'state': 'open', 'locked': False, 'assignee': None, 'assignees': [], 'milestone': None, 'comments': 8, 'created_at': '2026-01-07T16:57:40Z', 'updated_at': '2026-02-02T08:27:47Z', 'closed_at': None, 'author_association': 'CONTRIBUTOR', 'type': None, 'active_lock_reason': None, 'draft': False, 'pull_request': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/14260', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/14260', 'diff_url': 'https://github.com/dolphin-emu/dolphin/pull/14260.diff', 'patch_url': 'https://github.com/dolphin-emu/dolphin/pull/14260.patch', 'merged_at': None}, 'body': 'This has this https://github.com/mutouyun/cpp-ipc/pull/169 merged in as some new Windows.h includes made it in breaking mingw compatibility, so this fixes that.\r\n\r\nNeeds testing.', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14260/reactions', 'total_count': 0, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 0, 'confused': 0, 'heart': 0, 'rocket': 0, 'eyes': 0}, 'timeline_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14260/timeline', 'performed_via_github_app': None, 'state_reason': None}, 'comment': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments/3833690255', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/14260#issuecomment-3833690255', 'issue_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14260', 'id': 3833690255, 'node_id': 'IC_kwDOALCn2M7kgXiP', 'user': {'login': 'sepalani', 'id': 7890055, 'node_id': 'MDQ6VXNlcjc4OTAwNTU=', 'avatar_url': 'https://avatars.githubusercontent.com/u/7890055?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/sepalani', 'html_url': 'https://github.com/sepalani', 'followers_url': 'https://api.github.com/users/sepalani/followers', 'following_url': 'https://api.github.com/users/sepalani/following{/other_user}', 'gists_url': 'https://api.github.com/users/sepalani/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/sepalani/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/sepalani/subscriptions', 'organizations_url': 'https://api.github.com/users/sepalani/orgs', 'repos_url': 'https://api.github.com/users/sepalani/repos', 'events_url': 'https://api.github.com/users/sepalani/events{/privacy}', 'received_events_url': 'https://api.github.com/users/sepalani/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'created_at': '2026-02-02T08:27:47Z', 'updated_at': '2026-02-02T08:27:47Z', 'body': '@dolphin-emu-bot rebuild', 'author_association': 'MEMBER', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments/3833690255/reactions', 'total_count': 0, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 0, 'confused': 0, 'heart': 0, 'rocket': 0, 'eyes': 0}, 'performed_via_github_app': None}, 'repository': {'id': 11577304, 'node_id': 'MDEwOlJlcG9zaXRvcnkxMTU3NzMwNA==', 'name': 'dolphin', 'full_name': 'dolphin-emu/dolphin', 'private': False, 'owner': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/dolphin-emu', 'html_url': 'https://github.com/dolphin-emu', 'followers_url': 'https://api.github.com/users/dolphin-emu/followers', 'following_url': 'https://api.github.com/users/dolphin-emu/following{/other_user}', 'gists_url': 'https://api.github.com/users/dolphin-emu/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/dolphin-emu/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/dolphin-emu/subscriptions', 'organizations_url': 'https://api.github.com/users/dolphin-emu/orgs', 'repos_url': 'https://api.github.com/users/dolphin-emu/repos', 'events_url': 'https://api.github.com/users/dolphin-emu/events{/privacy}', 'received_events_url': 'https://api.github.com/users/dolphin-emu/received_events', 'type': 'Organization', 'user_view_type': 'public', 'site_admin': False}, 'html_url': 'https://github.com/dolphin-emu/dolphin', 'description': 'Dolphin is a GameCube / Wii emulator, allowing you to play games for these two platforms on PC with improvements.', 'fork': False, 'url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'forks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/forks', 'keys_url': 'https://api.github.com/repos/dolphin-emu/dolphin/keys{/key_id}', 'collaborators_url': 'https://api.github.com/repos/dolphin-emu/dolphin/collaborators{/collaborator}', 'teams_url': 'https://api.github.com/repos/dolphin-emu/dolphin/teams', 'hooks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/hooks', 'issue_events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/events{/number}', 'events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/events', 'assignees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/assignees{/user}', 'branches_url': 'https://api.github.com/repos/dolphin-emu/dolphin/branches{/branch}', 'tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/tags', 'blobs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/blobs{/sha}', 'git_tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/tags{/sha}', 'git_refs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/refs{/sha}', 'trees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/trees{/sha}', 'statuses_url': 'https://api.github.com/repos/dolphin-emu/dolphin/statuses/{sha}', 'languages_url': 'https://api.github.com/repos/dolphin-emu/dolphin/languages', 'stargazers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/stargazers', 'contributors_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contributors', 'subscribers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscribers', 'subscription_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscription', 'commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/commits{/sha}', 'git_commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/commits{/sha}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/comments{/number}', 'issue_comment_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments{/number}', 'contents_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contents/{+path}', 'compare_url': 'https://api.github.com/repos/dolphin-emu/dolphin/compare/{base}...{head}', 'merges_url': 'https://api.github.com/repos/dolphin-emu/dolphin/merges', 'archive_url': 'https://api.github.com/repos/dolphin-emu/dolphin/{archive_format}{/ref}', 'downloads_url': 'https://api.github.com/repos/dolphin-emu/dolphin/downloads', 'issues_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues{/number}', 'pulls_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls{/number}', 'milestones_url': 'https://api.github.com/repos/dolphin-emu/dolphin/milestones{/number}', 'notifications_url': 'https://api.github.com/repos/dolphin-emu/dolphin/notifications{?since,all,participating}', 'labels_url': 'https://api.github.com/repos/dolphin-emu/dolphin/labels{/name}', 'releases_url': 'https://api.github.com/repos/dolphin-emu/dolphin/releases{/id}', 'deployments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/deployments', 'created_at': '2013-07-22T09:07:57Z', 'updated_at': '2026-02-02T06:22:16Z', 'pushed_at': '2026-01-31T23:02:09Z', 'git_url': 'git://github.com/dolphin-emu/dolphin.git', 'ssh_url': 'git@github.com:dolphin-emu/dolphin.git', 'clone_url': 'https://github.com/dolphin-emu/dolphin.git', 'svn_url': 'https://github.com/dolphin-emu/dolphin', 'homepage': 'https://dolphin-emu.org', 'size': 544012, 'stargazers_count': 14574, 'watchers_count': 14574, 'language': 'C++', 'has_issues': False, 'has_projects': False, 'has_downloads': True, 'has_wiki': True, 'has_pages': False, 'has_discussions': False, 'forks_count': 2967, 'mirror_url': None, 'archived': False, 'disabled': False, 'open_issues_count': 360, 'license': {'key': 'other', 'name': 'Other', 'spdx_id': 'NOASSERTION', 'url': None, 'node_id': 'MDc6TGljZW5zZTA='}, 'allow_forking': True, 'is_template': False, 'web_commit_signoff_required': False, 'topics': ['cpp', 'emulation', 'emulator', 'gamecube', 'wii'], 'visibility': 'public', 'forks': 2967, 'open_issues': 360, 'watchers': 14574, 'default_branch': 'master', 'custom_properties': {}}, 'organization': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'url': 'https://api.github.com/orgs/dolphin-emu', 'repos_url': 'https://api.github.com/orgs/dolphin-emu/repos', 'events_url': 'https://api.github.com/orgs/dolphin-emu/events', 'hooks_url': 'https://api.github.com/orgs/dolphin-emu/hooks', 'issues_url': 'https://api.github.com/orgs/dolphin-emu/issues', 'members_url': 'https://api.github.com/orgs/dolphin-emu/members{/member}', 'public_members_url': 'https://api.github.com/orgs/dolphin-emu/public_members{/member}', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'description': ''}, 'sender': {'login': 'sepalani', 'id': 7890055, 'node_id': 'MDQ6VXNlcjc4OTAwNTU=', 'avatar_url': 'https://avatars.githubusercontent.com/u/7890055?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/sepalani', 'html_url': 'https://github.com/sepalani', 'followers_url': 'https://api.github.com/users/sepalani/followers', 'following_url': 'https://api.github.com/users/sepalani/following{/other_user}', 'gists_url': 'https://api.github.com/users/sepalani/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/sepalani/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/sepalani/subscriptions', 'organizations_url': 'https://api.github.com/users/sepalani/orgs', 'repos_url': 'https://api.github.com/users/sepalani/repos', 'events_url': 'https://api.github.com/users/sepalani/events{/privacy}', 'received_events_url': 'https://api.github.com/users/sepalani/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'installation': {'id': 35437981, 'node_id': 'MDIzOkludGVncmF0aW9uSW5zdGFsbGF0aW9uMzU0Mzc5ODE='}}, 'type': 'gh_issue_comment'}
2026-02-02T08:27:49.938572 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'sepalani', 'action': 'created', 'id': 14260, 'title': 'Externals: Update cpp-ipc to latest for mingw compatibility', 'url': 'https://github.com/dolphin-emu/dolphin/pull/14260#issuecomment-3833690255', 'safe_author': True, 'body': '@dolphin-emu-bot rebuild', 'raw': {'action': 'created', 'issue': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14260', 'repository_url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'labels_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14260/labels{/name}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14260/comments', 'events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14260/events', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/14260', 'id': 3789545274, 'node_id': 'PR_kwDOALCn2M678v8s', 'number': 14260, 'title': 'Externals: Update cpp-ipc to latest for mingw compatibility', 'user': {'login': 'cscd98', 'id': 1188869, 'node_id': 'MDQ6VXNlcjExODg4Njk=', 'avatar_url': 'https://avatars.githubusercontent.com/u/1188869?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/cscd98', 'html_url': 'https://github.com/cscd98', 'followers_url': 'https://api.github.com/users/cscd98/followers', 'following_url': 'https://api.github.com/users/cscd98/following{/other_user}', 'gists_url': 'https://api.github.com/users/cscd98/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/cscd98/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/cscd98/subscriptions', 'organizations_url': 'https://api.github.com/users/cscd98/orgs', 'repos_url': 'https://api.github.com/users/cscd98/repos', 'events_url': 'https://api.github.com/users/cscd98/events{/privacy}', 'received_events_url': 'https://api.github.com/users/cscd98/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'labels': [], 'state': 'open', 'locked': False, 'assignee': None, 'assignees': [], 'milestone': None, 'comments': 8, 'created_at': '2026-01-07T16:57:40Z', 'updated_at': '2026-02-02T08:27:47Z', 'closed_at': None, 'author_association': 'CONTRIBUTOR', 'type': None, 'active_lock_reason': None, 'draft': False, 'pull_request': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/14260', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/14260', 'diff_url': 'https://github.com/dolphin-emu/dolphin/pull/14260.diff', 'patch_url': 'https://github.com/dolphin-emu/dolphin/pull/14260.patch', 'merged_at': None}, 'body': 'This has this https://github.com/mutouyun/cpp-ipc/pull/169 merged in as some new Windows.h includes made it in breaking mingw compatibility, so this fixes that.\r\n\r\nNeeds testing.', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14260/reactions', 'total_count': 0, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 0, 'confused': 0, 'heart': 0, 'rocket': 0, 'eyes': 0}, 'timeline_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14260/timeline', 'performed_via_github_app': None, 'state_reason': None}, 'comment': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments/3833690255', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/14260#issuecomment-3833690255', 'issue_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14260', 'id': 3833690255, 'node_id': 'IC_kwDOALCn2M7kgXiP', 'user': {'login': 'sepalani', 'id': 7890055, 'node_id': 'MDQ6VXNlcjc4OTAwNTU=', 'avatar_url': 'https://avatars.githubusercontent.com/u/7890055?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/sepalani', 'html_url': 'https://github.com/sepalani', 'followers_url': 'https://api.github.com/users/sepalani/followers', 'following_url': 'https://api.github.com/users/sepalani/following{/other_user}', 'gists_url': 'https://api.github.com/users/sepalani/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/sepalani/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/sepalani/subscriptions', 'organizations_url': 'https://api.github.com/users/sepalani/orgs', 'repos_url': 'https://api.github.com/users/sepalani/repos', 'events_url': 'https://api.github.com/users/sepalani/events{/privacy}', 'received_events_url': 'https://api.github.com/users/sepalani/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'created_at': '2026-02-02T08:27:47Z', 'updated_at': '2026-02-02T08:27:47Z', 'body': '@dolphin-emu-bot rebuild', 'author_association': 'MEMBER', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments/3833690255/reactions', 'total_count': 0, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 0, 'confused': 0, 'heart': 0, 'rocket': 0, 'eyes': 0}, 'performed_via_github_app': None}, 'repository': {'id': 11577304, 'node_id': 'MDEwOlJlcG9zaXRvcnkxMTU3NzMwNA==', 'name': 'dolphin', 'full_name': 'dolphin-emu/dolphin', 'private': False, 'owner': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/dolphin-emu', 'html_url': 'https://github.com/dolphin-emu', 'followers_url': 'https://api.github.com/users/dolphin-emu/followers', 'following_url': 'https://api.github.com/users/dolphin-emu/following{/other_user}', 'gists_url': 'https://api.github.com/users/dolphin-emu/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/dolphin-emu/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/dolphin-emu/subscriptions', 'organizations_url': 'https://api.github.com/users/dolphin-emu/orgs', 'repos_url': 'https://api.github.com/users/dolphin-emu/repos', 'events_url': 'https://api.github.com/users/dolphin-emu/events{/privacy}', 'received_events_url': 'https://api.github.com/users/dolphin-emu/received_events', 'type': 'Organization', 'user_view_type': 'public', 'site_admin': False}, 'html_url': 'https://github.com/dolphin-emu/dolphin', 'description': 'Dolphin is a GameCube / Wii emulator, allowing you to play games for these two platforms on PC with improvements.', 'fork': False, 'url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'forks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/forks', 'keys_url': 'https://api.github.com/repos/dolphin-emu/dolphin/keys{/key_id}', 'collaborators_url': 'https://api.github.com/repos/dolphin-emu/dolphin/collaborators{/collaborator}', 'teams_url': 'https://api.github.com/repos/dolphin-emu/dolphin/teams', 'hooks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/hooks', 'issue_events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/events{/number}', 'events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/events', 'assignees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/assignees{/user}', 'branches_url': 'https://api.github.com/repos/dolphin-emu/dolphin/branches{/branch}', 'tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/tags', 'blobs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/blobs{/sha}', 'git_tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/tags{/sha}', 'git_refs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/refs{/sha}', 'trees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/trees{/sha}', 'statuses_url': 'https://api.github.com/repos/dolphin-emu/dolphin/statuses/{sha}', 'languages_url': 'https://api.github.com/repos/dolphin-emu/dolphin/languages', 'stargazers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/stargazers', 'contributors_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contributors', 'subscribers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscribers', 'subscription_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscription', 'commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/commits{/sha}', 'git_commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/commits{/sha}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/comments{/number}', 'issue_comment_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments{/number}', 'contents_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contents/{+path}', 'compare_url': 'https://api.github.com/repos/dolphin-emu/dolphin/compare/{base}...{head}', 'merges_url': 'https://api.github.com/repos/dolphin-emu/dolphin/merges', 'archive_url': 'https://api.github.com/repos/dolphin-emu/dolphin/{archive_format}{/ref}', 'downloads_url': 'https://api.github.com/repos/dolphin-emu/dolphin/downloads', 'issues_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues{/number}', 'pulls_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls{/number}', 'milestones_url': 'https://api.github.com/repos/dolphin-emu/dolphin/milestones{/number}', 'notifications_url': 'https://api.github.com/repos/dolphin-emu/dolphin/notifications{?since,all,participating}', 'labels_url': 'https://api.github.com/repos/dolphin-emu/dolphin/labels{/name}', 'releases_url': 'https://api.github.com/repos/dolphin-emu/dolphin/releases{/id}', 'deployments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/deployments', 'created_at': '2013-07-22T09:07:57Z', 'updated_at': '2026-02-02T06:22:16Z', 'pushed_at': '2026-01-31T23:02:09Z', 'git_url': 'git://github.com/dolphin-emu/dolphin.git', 'ssh_url': 'git@github.com:dolphin-emu/dolphin.git', 'clone_url': 'https://github.com/dolphin-emu/dolphin.git', 'svn_url': 'https://github.com/dolphin-emu/dolphin', 'homepage': 'https://dolphin-emu.org', 'size': 544012, 'stargazers_count': 14574, 'watchers_count': 14574, 'language': 'C++', 'has_issues': False, 'has_projects': False, 'has_downloads': True, 'has_wiki': True, 'has_pages': False, 'has_discussions': False, 'forks_count': 2967, 'mirror_url': None, 'archived': False, 'disabled': False, 'open_issues_count': 360, 'license': {'key': 'other', 'name': 'Other', 'spdx_id': 'NOASSERTION', 'url': None, 'node_id': 'MDc6TGljZW5zZTA='}, 'allow_forking': True, 'is_template': False, 'web_commit_signoff_required': False, 'topics': ['cpp', 'emulation', 'emulator', 'gamecube', 'wii'], 'visibility': 'public', 'forks': 2967, 'open_issues': 360, 'watchers': 14574, 'default_branch': 'master', 'custom_properties': {}}, 'organization': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'url': 'https://api.github.com/orgs/dolphin-emu', 'repos_url': 'https://api.github.com/orgs/dolphin-emu/repos', 'events_url': 'https://api.github.com/orgs/dolphin-emu/events', 'hooks_url': 'https://api.github.com/orgs/dolphin-emu/hooks', 'issues_url': 'https://api.github.com/orgs/dolphin-emu/issues', 'members_url': 'https://api.github.com/orgs/dolphin-emu/members{/member}', 'public_members_url': 'https://api.github.com/orgs/dolphin-emu/public_members{/member}', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'description': ''}, 'sender': {'login': 'sepalani', 'id': 7890055, 'node_id': 'MDQ6VXNlcjc4OTAwNTU=', 'avatar_url': 'https://avatars.githubusercontent.com/u/7890055?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/sepalani', 'html_url': 'https://github.com/sepalani', 'followers_url': 'https://api.github.com/users/sepalani/followers', 'following_url': 'https://api.github.com/users/sepalani/following{/other_user}', 'gists_url': 'https://api.github.com/users/sepalani/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/sepalani/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/sepalani/subscriptions', 'organizations_url': 'https://api.github.com/users/sepalani/orgs', 'repos_url': 'https://api.github.com/users/sepalani/repos', 'events_url': 'https://api.github.com/users/sepalani/events{/privacy}', 'received_events_url': 'https://api.github.com/users/sepalani/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'installation': {'id': 35437981, 'node_id': 'MDIzOkludGVncmF0aW9uSW5zdGFsbGF0aW9uMzU0Mzc5ODE='}}, 'type': 'gh_issue_comment'}
2026-02-02T08:13:07.555027 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'cscd98', 'action': 'created', 'id': 14260, 'title': 'Externals: Update cpp-ipc to latest for mingw compatibility', 'url': 'https://github.com/dolphin-emu/dolphin/pull/14260#issuecomment-3833617208', 'safe_author': False, 'body': '@cristian64 now ready for testing :+1: (now builds fine with freebsd)', 'raw': {'action': 'created', 'issue': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14260', 'repository_url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'labels_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14260/labels{/name}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14260/comments', 'events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14260/events', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/14260', 'id': 3789545274, 'node_id': 'PR_kwDOALCn2M678v8s', 'number': 14260, 'title': 'Externals: Update cpp-ipc to latest for mingw compatibility', 'user': {'login': 'cscd98', 'id': 1188869, 'node_id': 'MDQ6VXNlcjExODg4Njk=', 'avatar_url': 'https://avatars.githubusercontent.com/u/1188869?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/cscd98', 'html_url': 'https://github.com/cscd98', 'followers_url': 'https://api.github.com/users/cscd98/followers', 'following_url': 'https://api.github.com/users/cscd98/following{/other_user}', 'gists_url': 'https://api.github.com/users/cscd98/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/cscd98/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/cscd98/subscriptions', 'organizations_url': 'https://api.github.com/users/cscd98/orgs', 'repos_url': 'https://api.github.com/users/cscd98/repos', 'events_url': 'https://api.github.com/users/cscd98/events{/privacy}', 'received_events_url': 'https://api.github.com/users/cscd98/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'labels': [], 'state': 'open', 'locked': False, 'assignee': None, 'assignees': [], 'milestone': None, 'comments': 7, 'created_at': '2026-01-07T16:57:40Z', 'updated_at': '2026-02-02T08:13:05Z', 'closed_at': None, 'author_association': 'CONTRIBUTOR', 'type': None, 'active_lock_reason': None, 'draft': False, 'pull_request': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/14260', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/14260', 'diff_url': 'https://github.com/dolphin-emu/dolphin/pull/14260.diff', 'patch_url': 'https://github.com/dolphin-emu/dolphin/pull/14260.patch', 'merged_at': None}, 'body': 'This has this https://github.com/mutouyun/cpp-ipc/pull/169 merged in as some new Windows.h includes made it in breaking mingw compatibility, so this fixes that.\r\n\r\nNeeds testing.', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14260/reactions', 'total_count': 0, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 0, 'confused': 0, 'heart': 0, 'rocket': 0, 'eyes': 0}, 'timeline_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14260/timeline', 'performed_via_github_app': None, 'state_reason': None}, 'comment': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments/3833617208', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/14260#issuecomment-3833617208', 'issue_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14260', 'id': 3833617208, 'node_id': 'IC_kwDOALCn2M7kgFs4', 'user': {'login': 'cscd98', 'id': 1188869, 'node_id': 'MDQ6VXNlcjExODg4Njk=', 'avatar_url': 'https://avatars.githubusercontent.com/u/1188869?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/cscd98', 'html_url': 'https://github.com/cscd98', 'followers_url': 'https://api.github.com/users/cscd98/followers', 'following_url': 'https://api.github.com/users/cscd98/following{/other_user}', 'gists_url': 'https://api.github.com/users/cscd98/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/cscd98/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/cscd98/subscriptions', 'organizations_url': 'https://api.github.com/users/cscd98/orgs', 'repos_url': 'https://api.github.com/users/cscd98/repos', 'events_url': 'https://api.github.com/users/cscd98/events{/privacy}', 'received_events_url': 'https://api.github.com/users/cscd98/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'created_at': '2026-02-02T08:13:05Z', 'updated_at': '2026-02-02T08:13:05Z', 'body': '@cristian64 now ready for testing :+1: (now builds fine with freebsd)', 'author_association': 'CONTRIBUTOR', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments/3833617208/reactions', 'total_count': 0, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 0, 'confused': 0, 'heart': 0, 'rocket': 0, 'eyes': 0}, 'performed_via_github_app': None}, 'repository': {'id': 11577304, 'node_id': 'MDEwOlJlcG9zaXRvcnkxMTU3NzMwNA==', 'name': 'dolphin', 'full_name': 'dolphin-emu/dolphin', 'private': False, 'owner': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/dolphin-emu', 'html_url': 'https://github.com/dolphin-emu', 'followers_url': 'https://api.github.com/users/dolphin-emu/followers', 'following_url': 'https://api.github.com/users/dolphin-emu/following{/other_user}', 'gists_url': 'https://api.github.com/users/dolphin-emu/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/dolphin-emu/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/dolphin-emu/subscriptions', 'organizations_url': 'https://api.github.com/users/dolphin-emu/orgs', 'repos_url': 'https://api.github.com/users/dolphin-emu/repos', 'events_url': 'https://api.github.com/users/dolphin-emu/events{/privacy}', 'received_events_url': 'https://api.github.com/users/dolphin-emu/received_events', 'type': 'Organization', 'user_view_type': 'public', 'site_admin': False}, 'html_url': 'https://github.com/dolphin-emu/dolphin', 'description': 'Dolphin is a GameCube / Wii emulator, allowing you to play games for these two platforms on PC with improvements.', 'fork': False, 'url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'forks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/forks', 'keys_url': 'https://api.github.com/repos/dolphin-emu/dolphin/keys{/key_id}', 'collaborators_url': 'https://api.github.com/repos/dolphin-emu/dolphin/collaborators{/collaborator}', 'teams_url': 'https://api.github.com/repos/dolphin-emu/dolphin/teams', 'hooks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/hooks', 'issue_events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/events{/number}', 'events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/events', 'assignees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/assignees{/user}', 'branches_url': 'https://api.github.com/repos/dolphin-emu/dolphin/branches{/branch}', 'tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/tags', 'blobs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/blobs{/sha}', 'git_tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/tags{/sha}', 'git_refs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/refs{/sha}', 'trees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/trees{/sha}', 'statuses_url': 'https://api.github.com/repos/dolphin-emu/dolphin/statuses/{sha}', 'languages_url': 'https://api.github.com/repos/dolphin-emu/dolphin/languages', 'stargazers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/stargazers', 'contributors_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contributors', 'subscribers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscribers', 'subscription_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscription', 'commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/commits{/sha}', 'git_commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/commits{/sha}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/comments{/number}', 'issue_comment_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments{/number}', 'contents_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contents/{+path}', 'compare_url': 'https://api.github.com/repos/dolphin-emu/dolphin/compare/{base}...{head}', 'merges_url': 'https://api.github.com/repos/dolphin-emu/dolphin/merges', 'archive_url': 'https://api.github.com/repos/dolphin-emu/dolphin/{archive_format}{/ref}', 'downloads_url': 'https://api.github.com/repos/dolphin-emu/dolphin/downloads', 'issues_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues{/number}', 'pulls_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls{/number}', 'milestones_url': 'https://api.github.com/repos/dolphin-emu/dolphin/milestones{/number}', 'notifications_url': 'https://api.github.com/repos/dolphin-emu/dolphin/notifications{?since,all,participating}', 'labels_url': 'https://api.github.com/repos/dolphin-emu/dolphin/labels{/name}', 'releases_url': 'https://api.github.com/repos/dolphin-emu/dolphin/releases{/id}', 'deployments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/deployments', 'created_at': '2013-07-22T09:07:57Z', 'updated_at': '2026-02-02T06:22:16Z', 'pushed_at': '2026-01-31T23:02:09Z', 'git_url': 'git://github.com/dolphin-emu/dolphin.git', 'ssh_url': 'git@github.com:dolphin-emu/dolphin.git', 'clone_url': 'https://github.com/dolphin-emu/dolphin.git', 'svn_url': 'https://github.com/dolphin-emu/dolphin', 'homepage': 'https://dolphin-emu.org', 'size': 544012, 'stargazers_count': 14574, 'watchers_count': 14574, 'language': 'C++', 'has_issues': False, 'has_projects': False, 'has_downloads': True, 'has_wiki': True, 'has_pages': False, 'has_discussions': False, 'forks_count': 2967, 'mirror_url': None, 'archived': False, 'disabled': False, 'open_issues_count': 360, 'license': {'key': 'other', 'name': 'Other', 'spdx_id': 'NOASSERTION', 'url': None, 'node_id': 'MDc6TGljZW5zZTA='}, 'allow_forking': True, 'is_template': False, 'web_commit_signoff_required': False, 'topics': ['cpp', 'emulation', 'emulator', 'gamecube', 'wii'], 'visibility': 'public', 'forks': 2967, 'open_issues': 360, 'watchers': 14574, 'default_branch': 'master', 'custom_properties': {}}, 'organization': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'url': 'https://api.github.com/orgs/dolphin-emu', 'repos_url': 'https://api.github.com/orgs/dolphin-emu/repos', 'events_url': 'https://api.github.com/orgs/dolphin-emu/events', 'hooks_url': 'https://api.github.com/orgs/dolphin-emu/hooks', 'issues_url': 'https://api.github.com/orgs/dolphin-emu/issues', 'members_url': 'https://api.github.com/orgs/dolphin-emu/members{/member}', 'public_members_url': 'https://api.github.com/orgs/dolphin-emu/public_members{/member}', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'description': ''}, 'sender': {'login': 'cscd98', 'id': 1188869, 'node_id': 'MDQ6VXNlcjExODg4Njk=', 'avatar_url': 'https://avatars.githubusercontent.com/u/1188869?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/cscd98', 'html_url': 'https://github.com/cscd98', 'followers_url': 'https://api.github.com/users/cscd98/followers', 'following_url': 'https://api.github.com/users/cscd98/following{/other_user}', 'gists_url': 'https://api.github.com/users/cscd98/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/cscd98/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/cscd98/subscriptions', 'organizations_url': 'https://api.github.com/users/cscd98/orgs', 'repos_url': 'https://api.github.com/users/cscd98/repos', 'events_url': 'https://api.github.com/users/cscd98/events{/privacy}', 'received_events_url': 'https://api.github.com/users/cscd98/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'installation': {'id': 35437981, 'node_id': 'MDIzOkludGVncmF0aW9uSW5zdGFsbGF0aW9uMzU0Mzc5ODE='}}, 'type': 'gh_issue_comment'}
2026-02-01T21:48:51.558619 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'jordan-woyak', 'action': 'created', 'id': 14308, 'title': 'StringUtil: Make UTF16ToUTF8 and UTF8ToUTF16 use custom encoding/decoding implementation.', 'url': 'https://github.com/dolphin-emu/dolphin/pull/14308#issuecomment-3832120460', 'safe_author': True, 'body': '> To be compliant with RFC 3629, we should also reject all surrogate code points when decoding UTF-8. Looks good other than that.\r\n\r\nDone.', 'raw': {'action': 'created', 'issue': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14308', 'repository_url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'labels_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14308/labels{/name}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14308/comments', 'events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14308/events', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/14308', 'id': 3863094147, 'node_id': 'PR_kwDOALCn2M6_vEBN', 'number': 14308, 'title': 'StringUtil: Make UTF16ToUTF8 and UTF8ToUTF16 use custom encoding/decoding implementation.', 'user': {'login': 'jordan-woyak', 'id': 1768214, 'node_id': 'MDQ6VXNlcjE3NjgyMTQ=', 'avatar_url': 'https://avatars.githubusercontent.com/u/1768214?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/jordan-woyak', 'html_url': 'https://github.com/jordan-woyak', 'followers_url': 'https://api.github.com/users/jordan-woyak/followers', 'following_url': 'https://api.github.com/users/jordan-woyak/following{/other_user}', 'gists_url': 'https://api.github.com/users/jordan-woyak/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/jordan-woyak/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/jordan-woyak/subscriptions', 'organizations_url': 'https://api.github.com/users/jordan-woyak/orgs', 'repos_url': 'https://api.github.com/users/jordan-woyak/repos', 'events_url': 'https://api.github.com/users/jordan-woyak/events{/privacy}', 'received_events_url': 'https://api.github.com/users/jordan-woyak/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'labels': [], 'state': 'open', 'locked': False, 'assignee': None, 'assignees': [], 'milestone': None, 'comments': 2, 'created_at': '2026-01-28T01:16:38Z', 'updated_at': '2026-02-01T21:48:48Z', 'closed_at': None, 'author_association': 'MEMBER', 'type': None, 'active_lock_reason': None, 'draft': False, 'pull_request': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/14308', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/14308', 'diff_url': 'https://github.com/dolphin-emu/dolphin/pull/14308.diff', 'patch_url': 'https://github.com/dolphin-emu/dolphin/pull/14308.patch', 'merged_at': None}, 'body': "Replaces: #14282.\r\nFixes: https://bugs.dolphin-emu.org/issues/13962\r\n\r\nNote that `std::wstring_convert` throws exceptions on invalid sequences, and it's deprecated, to be removed in C++26.\r\n\r\nThis PR replaces usage of `wstring_convert` with hand-rolled UTF8/16 encoding/decoding.\r\n\r\nI'd usually rather just use an existing libarary (like iconv) but..\r\n- The iconv dependency isn't set up in the Windows build.\r\n- WinAPI's `MultiByteToWideChar` can't output to a `std::u16string` without strict aliasing violations or a memcpy.\r\n- UTF8/16 really isn't overly complicated.\r\n- These hand-rolled versions seem to be 5-10x faster than iconv in my simple tests.\r\n- We have more control over how semi-invalid sequences are handled. [This issue](https://bugs.dolphin-emu.org/issues/12502) will be fixed if QString conversion functions are changed to use the custom decoder.\r\n\r\nIf we wanted to entirely eliminate the iconv dependency, CP1252 would be easy enough to implement, but ShiftJIS looks like it isn't so fun.\r\nEdit: I have these implemented locally if we want to do that. ShiftJIS wasn't so bad.. just some giant tables.", 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14308/reactions', 'total_count': 1, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 1, 'confused': 0, 'heart': 0, 'rocket': 0, 'eyes': 0}, 'timeline_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14308/timeline', 'performed_via_github_app': None, 'state_reason': None}, 'comment': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments/3832120460', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/14308#issuecomment-3832120460', 'issue_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14308', 'id': 3832120460, 'node_id': 'IC_kwDOALCn2M7kaYSM', 'user': {'login': 'jordan-woyak', 'id': 1768214, 'node_id': 'MDQ6VXNlcjE3NjgyMTQ=', 'avatar_url': 'https://avatars.githubusercontent.com/u/1768214?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/jordan-woyak', 'html_url': 'https://github.com/jordan-woyak', 'followers_url': 'https://api.github.com/users/jordan-woyak/followers', 'following_url': 'https://api.github.com/users/jordan-woyak/following{/other_user}', 'gists_url': 'https://api.github.com/users/jordan-woyak/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/jordan-woyak/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/jordan-woyak/subscriptions', 'organizations_url': 'https://api.github.com/users/jordan-woyak/orgs', 'repos_url': 'https://api.github.com/users/jordan-woyak/repos', 'events_url': 'https://api.github.com/users/jordan-woyak/events{/privacy}', 'received_events_url': 'https://api.github.com/users/jordan-woyak/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'created_at': '2026-02-01T21:48:48Z', 'updated_at': '2026-02-01T21:48:48Z', 'body': '> To be compliant with RFC 3629, we should also reject all surrogate code points when decoding UTF-8. Looks good other than that.\r\n\r\nDone.', 'author_association': 'MEMBER', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments/3832120460/reactions', 'total_count': 0, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 0, 'confused': 0, 'heart': 0, 'rocket': 0, 'eyes': 0}, 'performed_via_github_app': None}, 'repository': {'id': 11577304, 'node_id': 'MDEwOlJlcG9zaXRvcnkxMTU3NzMwNA==', 'name': 'dolphin', 'full_name': 'dolphin-emu/dolphin', 'private': False, 'owner': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/dolphin-emu', 'html_url': 'https://github.com/dolphin-emu', 'followers_url': 'https://api.github.com/users/dolphin-emu/followers', 'following_url': 'https://api.github.com/users/dolphin-emu/following{/other_user}', 'gists_url': 'https://api.github.com/users/dolphin-emu/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/dolphin-emu/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/dolphin-emu/subscriptions', 'organizations_url': 'https://api.github.com/users/dolphin-emu/orgs', 'repos_url': 'https://api.github.com/users/dolphin-emu/repos', 'events_url': 'https://api.github.com/users/dolphin-emu/events{/privacy}', 'received_events_url': 'https://api.github.com/users/dolphin-emu/received_events', 'type': 'Organization', 'user_view_type': 'public', 'site_admin': False}, 'html_url': 'https://github.com/dolphin-emu/dolphin', 'description': 'Dolphin is a GameCube / Wii emulator, allowing you to play games for these two platforms on PC with improvements.', 'fork': False, 'url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'forks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/forks', 'keys_url': 'https://api.github.com/repos/dolphin-emu/dolphin/keys{/key_id}', 'collaborators_url': 'https://api.github.com/repos/dolphin-emu/dolphin/collaborators{/collaborator}', 'teams_url': 'https://api.github.com/repos/dolphin-emu/dolphin/teams', 'hooks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/hooks', 'issue_events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/events{/number}', 'events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/events', 'assignees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/assignees{/user}', 'branches_url': 'https://api.github.com/repos/dolphin-emu/dolphin/branches{/branch}', 'tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/tags', 'blobs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/blobs{/sha}', 'git_tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/tags{/sha}', 'git_refs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/refs{/sha}', 'trees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/trees{/sha}', 'statuses_url': 'https://api.github.com/repos/dolphin-emu/dolphin/statuses/{sha}', 'languages_url': 'https://api.github.com/repos/dolphin-emu/dolphin/languages', 'stargazers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/stargazers', 'contributors_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contributors', 'subscribers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscribers', 'subscription_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscription', 'commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/commits{/sha}', 'git_commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/commits{/sha}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/comments{/number}', 'issue_comment_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments{/number}', 'contents_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contents/{+path}', 'compare_url': 'https://api.github.com/repos/dolphin-emu/dolphin/compare/{base}...{head}', 'merges_url': 'https://api.github.com/repos/dolphin-emu/dolphin/merges', 'archive_url': 'https://api.github.com/repos/dolphin-emu/dolphin/{archive_format}{/ref}', 'downloads_url': 'https://api.github.com/repos/dolphin-emu/dolphin/downloads', 'issues_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues{/number}', 'pulls_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls{/number}', 'milestones_url': 'https://api.github.com/repos/dolphin-emu/dolphin/milestones{/number}', 'notifications_url': 'https://api.github.com/repos/dolphin-emu/dolphin/notifications{?since,all,participating}', 'labels_url': 'https://api.github.com/repos/dolphin-emu/dolphin/labels{/name}', 'releases_url': 'https://api.github.com/repos/dolphin-emu/dolphin/releases{/id}', 'deployments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/deployments', 'created_at': '2013-07-22T09:07:57Z', 'updated_at': '2026-02-01T19:55:00Z', 'pushed_at': '2026-01-31T23:02:09Z', 'git_url': 'git://github.com/dolphin-emu/dolphin.git', 'ssh_url': 'git@github.com:dolphin-emu/dolphin.git', 'clone_url': 'https://github.com/dolphin-emu/dolphin.git', 'svn_url': 'https://github.com/dolphin-emu/dolphin', 'homepage': 'https://dolphin-emu.org', 'size': 544012, 'stargazers_count': 14572, 'watchers_count': 14572, 'language': 'C++', 'has_issues': False, 'has_projects': False, 'has_downloads': True, 'has_wiki': True, 'has_pages': False, 'has_discussions': False, 'forks_count': 2967, 'mirror_url': None, 'archived': False, 'disabled': False, 'open_issues_count': 360, 'license': {'key': 'other', 'name': 'Other', 'spdx_id': 'NOASSERTION', 'url': None, 'node_id': 'MDc6TGljZW5zZTA='}, 'allow_forking': True, 'is_template': False, 'web_commit_signoff_required': False, 'topics': ['cpp', 'emulation', 'emulator', 'gamecube', 'wii'], 'visibility': 'public', 'forks': 2967, 'open_issues': 360, 'watchers': 14572, 'default_branch': 'master', 'custom_properties': {}}, 'organization': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'url': 'https://api.github.com/orgs/dolphin-emu', 'repos_url': 'https://api.github.com/orgs/dolphin-emu/repos', 'events_url': 'https://api.github.com/orgs/dolphin-emu/events', 'hooks_url': 'https://api.github.com/orgs/dolphin-emu/hooks', 'issues_url': 'https://api.github.com/orgs/dolphin-emu/issues', 'members_url': 'https://api.github.com/orgs/dolphin-emu/members{/member}', 'public_members_url': 'https://api.github.com/orgs/dolphin-emu/public_members{/member}', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'description': ''}, 'sender': {'login': 'jordan-woyak', 'id': 1768214, 'node_id': 'MDQ6VXNlcjE3NjgyMTQ=', 'avatar_url': 'https://avatars.githubusercontent.com/u/1768214?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/jordan-woyak', 'html_url': 'https://github.com/jordan-woyak', 'followers_url': 'https://api.github.com/users/jordan-woyak/followers', 'following_url': 'https://api.github.com/users/jordan-woyak/following{/other_user}', 'gists_url': 'https://api.github.com/users/jordan-woyak/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/jordan-woyak/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/jordan-woyak/subscriptions', 'organizations_url': 'https://api.github.com/users/jordan-woyak/orgs', 'repos_url': 'https://api.github.com/users/jordan-woyak/repos', 'events_url': 'https://api.github.com/users/jordan-woyak/events{/privacy}', 'received_events_url': 'https://api.github.com/users/jordan-woyak/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'installation': {'id': 35437981, 'node_id': 'MDIzOkludGVncmF0aW9uSW5zdGFsbGF0aW9uMzU0Mzc5ODE='}}, 'type': 'gh_issue_comment'}
2026-02-01T20:27:31.435185 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'Dentomologist', 'action': 'edited', 'id': 14315, 'title': 'RiivolutionParser: Fix XML Param Parsing', 'url': 'https://github.com/dolphin-emu/dolphin/pull/14315#issuecomment-3831860087', 'safe_author': True, 'body': '~~`game_id_no_region`, `game_region`, and `game_developer` are views into the return value of `substr`, the lifetime of which ends at the end of the expression. Those variables should be `std::string` instead of `std::string_view`.~~', 'raw': {'action': 'edited', 'changes': {'body': {'from': '`game_id_no_region`, `game_region`, and `game_developer` are views into the return value of `substr`, the lifetime of which ends at the end of the expression. Those variables should be `std::string` instead of `std::string_view`. '}}, 'issue': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14315', 'repository_url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'labels_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14315/labels{/name}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14315/comments', 'events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14315/events', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/14315', 'id': 3882697746, 'node_id': 'PR_kwDOALCn2M7Av0U8', 'number': 14315, 'title': 'RiivolutionParser: Fix XML Param Parsing', 'user': {'login': 'Gabriela-Orzechowska', 'id': 85825993, 'node_id': 'MDQ6VXNlcjg1ODI1OTkz', 'avatar_url': 'https://avatars.githubusercontent.com/u/85825993?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/Gabriela-Orzechowska', 'html_url': 'https://github.com/Gabriela-Orzechowska', 'followers_url': 'https://api.github.com/users/Gabriela-Orzechowska/followers', 'following_url': 'https://api.github.com/users/Gabriela-Orzechowska/following{/other_user}', 'gists_url': 'https://api.github.com/users/Gabriela-Orzechowska/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/Gabriela-Orzechowska/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/Gabriela-Orzechowska/subscriptions', 'organizations_url': 'https://api.github.com/users/Gabriela-Orzechowska/orgs', 'repos_url': 'https://api.github.com/users/Gabriela-Orzechowska/repos', 'events_url': 'https://api.github.com/users/Gabriela-Orzechowska/events{/privacy}', 'received_events_url': 'https://api.github.com/users/Gabriela-Orzechowska/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'labels': [], 'state': 'open', 'locked': False, 'assignee': None, 'assignees': [], 'milestone': None, 'comments': 6, 'created_at': '2026-02-01T18:58:25Z', 'updated_at': '2026-02-01T20:22:06Z', 'closed_at': None, 'author_association': 'NONE', 'type': None, 'active_lock_reason': None, 'draft': False, 'pull_request': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/14315', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/14315', 'diff_url': 'https://github.com/dolphin-emu/dolphin/pull/14315.diff', 'patch_url': 'https://github.com/dolphin-emu/dolphin/pull/14315.patch', 'merged_at': None}, 'body': 'This PR fixes an issue with wrong param parsing of Riivolution XML files. Usage of `std::string_view` creates corruption and invalid values of the parameters.\r\n<img width="357" height="158" alt="image" src="https://github.com/user-attachments/assets/45d48cae-e21e-4953-80b7-511d8f7ac9e3" />\r\nSimply replacing it with `std::string` fixes the issue.\r\n<img width="359" height="165" alt="image" src="https://github.com/user-attachments/assets/ede15ed1-deef-4af5-94a7-44ab988eaa0b" />\r\n', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14315/reactions', 'total_count': 1, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 0, 'confused': 0, 'heart': 1, 'rocket': 0, 'eyes': 0}, 'timeline_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14315/timeline', 'performed_via_github_app': None, 'state_reason': None}, 'comment': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments/3831860087', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/14315#issuecomment-3831860087', 'issue_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14315', 'id': 3831860087, 'node_id': 'IC_kwDOALCn2M7kZYt3', 'user': {'login': 'Dentomologist', 'id': 73494713, 'node_id': 'MDQ6VXNlcjczNDk0NzEz', 'avatar_url': 'https://avatars.githubusercontent.com/u/73494713?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/Dentomologist', 'html_url': 'https://github.com/Dentomologist', 'followers_url': 'https://api.github.com/users/Dentomologist/followers', 'following_url': 'https://api.github.com/users/Dentomologist/following{/other_user}', 'gists_url': 'https://api.github.com/users/Dentomologist/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/Dentomologist/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/Dentomologist/subscriptions', 'organizations_url': 'https://api.github.com/users/Dentomologist/orgs', 'repos_url': 'https://api.github.com/users/Dentomologist/repos', 'events_url': 'https://api.github.com/users/Dentomologist/events{/privacy}', 'received_events_url': 'https://api.github.com/users/Dentomologist/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'created_at': '2026-02-01T19:50:11Z', 'updated_at': '2026-02-01T20:27:29Z', 'body': '~~`game_id_no_region`, `game_region`, and `game_developer` are views into the return value of `substr`, the lifetime of which ends at the end of the expression. Those variables should be `std::string` instead of `std::string_view`.~~', 'author_association': 'MEMBER', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments/3831860087/reactions', 'total_count': 0, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 0, 'confused': 0, 'heart': 0, 'rocket': 0, 'eyes': 0}, 'performed_via_github_app': None}, 'repository': {'id': 11577304, 'node_id': 'MDEwOlJlcG9zaXRvcnkxMTU3NzMwNA==', 'name': 'dolphin', 'full_name': 'dolphin-emu/dolphin', 'private': False, 'owner': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/dolphin-emu', 'html_url': 'https://github.com/dolphin-emu', 'followers_url': 'https://api.github.com/users/dolphin-emu/followers', 'following_url': 'https://api.github.com/users/dolphin-emu/following{/other_user}', 'gists_url': 'https://api.github.com/users/dolphin-emu/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/dolphin-emu/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/dolphin-emu/subscriptions', 'organizations_url': 'https://api.github.com/users/dolphin-emu/orgs', 'repos_url': 'https://api.github.com/users/dolphin-emu/repos', 'events_url': 'https://api.github.com/users/dolphin-emu/events{/privacy}', 'received_events_url': 'https://api.github.com/users/dolphin-emu/received_events', 'type': 'Organization', 'user_view_type': 'public', 'site_admin': False}, 'html_url': 'https://github.com/dolphin-emu/dolphin', 'description': 'Dolphin is a GameCube / Wii emulator, allowing you to play games for these two platforms on PC with improvements.', 'fork': False, 'url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'forks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/forks', 'keys_url': 'https://api.github.com/repos/dolphin-emu/dolphin/keys{/key_id}', 'collaborators_url': 'https://api.github.com/repos/dolphin-emu/dolphin/collaborators{/collaborator}', 'teams_url': 'https://api.github.com/repos/dolphin-emu/dolphin/teams', 'hooks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/hooks', 'issue_events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/events{/number}', 'events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/events', 'assignees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/assignees{/user}', 'branches_url': 'https://api.github.com/repos/dolphin-emu/dolphin/branches{/branch}', 'tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/tags', 'blobs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/blobs{/sha}', 'git_tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/tags{/sha}', 'git_refs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/refs{/sha}', 'trees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/trees{/sha}', 'statuses_url': 'https://api.github.com/repos/dolphin-emu/dolphin/statuses/{sha}', 'languages_url': 'https://api.github.com/repos/dolphin-emu/dolphin/languages', 'stargazers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/stargazers', 'contributors_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contributors', 'subscribers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscribers', 'subscription_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscription', 'commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/commits{/sha}', 'git_commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/commits{/sha}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/comments{/number}', 'issue_comment_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments{/number}', 'contents_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contents/{+path}', 'compare_url': 'https://api.github.com/repos/dolphin-emu/dolphin/compare/{base}...{head}', 'merges_url': 'https://api.github.com/repos/dolphin-emu/dolphin/merges', 'archive_url': 'https://api.github.com/repos/dolphin-emu/dolphin/{archive_format}{/ref}', 'downloads_url': 'https://api.github.com/repos/dolphin-emu/dolphin/downloads', 'issues_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues{/number}', 'pulls_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls{/number}', 'milestones_url': 'https://api.github.com/repos/dolphin-emu/dolphin/milestones{/number}', 'notifications_url': 'https://api.github.com/repos/dolphin-emu/dolphin/notifications{?since,all,participating}', 'labels_url': 'https://api.github.com/repos/dolphin-emu/dolphin/labels{/name}', 'releases_url': 'https://api.github.com/repos/dolphin-emu/dolphin/releases{/id}', 'deployments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/deployments', 'created_at': '2013-07-22T09:07:57Z', 'updated_at': '2026-02-01T19:55:00Z', 'pushed_at': '2026-01-31T23:02:09Z', 'git_url': 'git://github.com/dolphin-emu/dolphin.git', 'ssh_url': 'git@github.com:dolphin-emu/dolphin.git', 'clone_url': 'https://github.com/dolphin-emu/dolphin.git', 'svn_url': 'https://github.com/dolphin-emu/dolphin', 'homepage': 'https://dolphin-emu.org', 'size': 544012, 'stargazers_count': 14572, 'watchers_count': 14572, 'language': 'C++', 'has_issues': False, 'has_projects': False, 'has_downloads': True, 'has_wiki': True, 'has_pages': False, 'has_discussions': False, 'forks_count': 2967, 'mirror_url': None, 'archived': False, 'disabled': False, 'open_issues_count': 360, 'license': {'key': 'other', 'name': 'Other', 'spdx_id': 'NOASSERTION', 'url': None, 'node_id': 'MDc6TGljZW5zZTA='}, 'allow_forking': True, 'is_template': False, 'web_commit_signoff_required': False, 'topics': ['cpp', 'emulation', 'emulator', 'gamecube', 'wii'], 'visibility': 'public', 'forks': 2967, 'open_issues': 360, 'watchers': 14572, 'default_branch': 'master', 'custom_properties': {}}, 'organization': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'url': 'https://api.github.com/orgs/dolphin-emu', 'repos_url': 'https://api.github.com/orgs/dolphin-emu/repos', 'events_url': 'https://api.github.com/orgs/dolphin-emu/events', 'hooks_url': 'https://api.github.com/orgs/dolphin-emu/hooks', 'issues_url': 'https://api.github.com/orgs/dolphin-emu/issues', 'members_url': 'https://api.github.com/orgs/dolphin-emu/members{/member}', 'public_members_url': 'https://api.github.com/orgs/dolphin-emu/public_members{/member}', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'description': ''}, 'sender': {'login': 'Dentomologist', 'id': 73494713, 'node_id': 'MDQ6VXNlcjczNDk0NzEz', 'avatar_url': 'https://avatars.githubusercontent.com/u/73494713?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/Dentomologist', 'html_url': 'https://github.com/Dentomologist', 'followers_url': 'https://api.github.com/users/Dentomologist/followers', 'following_url': 'https://api.github.com/users/Dentomologist/following{/other_user}', 'gists_url': 'https://api.github.com/users/Dentomologist/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/Dentomologist/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/Dentomologist/subscriptions', 'organizations_url': 'https://api.github.com/users/Dentomologist/orgs', 'repos_url': 'https://api.github.com/users/Dentomologist/repos', 'events_url': 'https://api.github.com/users/Dentomologist/events{/privacy}', 'received_events_url': 'https://api.github.com/users/Dentomologist/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'installation': {'id': 35437981, 'node_id': 'MDIzOkludGVncmF0aW9uSW5zdGFsbGF0aW9uMzU0Mzc5ODE='}}, 'type': 'gh_issue_comment'}
2026-02-01T20:22:09.222792 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'Dentomologist', 'action': 'created', 'id': 14315, 'title': 'RiivolutionParser: Fix XML Param Parsing', 'url': 'https://github.com/dolphin-emu/dolphin/pull/14315#issuecomment-3831916842', 'safe_author': True, 'body': "> `std::string_view::substr` doesn't return an `std::string`, it reuses the same backing memory as the `std::string_view` it was called on. So those variables are views into `game_id`, which is valid for the whole `GeneratePatches` call.\r\n\r\n... whoops, got that mixed up with `std::string::substr`.", 'raw': {'action': 'created', 'issue': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14315', 'repository_url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'labels_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14315/labels{/name}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14315/comments', 'events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14315/events', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/14315', 'id': 3882697746, 'node_id': 'PR_kwDOALCn2M7Av0U8', 'number': 14315, 'title': 'RiivolutionParser: Fix XML Param Parsing', 'user': {'login': 'Gabriela-Orzechowska', 'id': 85825993, 'node_id': 'MDQ6VXNlcjg1ODI1OTkz', 'avatar_url': 'https://avatars.githubusercontent.com/u/85825993?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/Gabriela-Orzechowska', 'html_url': 'https://github.com/Gabriela-Orzechowska', 'followers_url': 'https://api.github.com/users/Gabriela-Orzechowska/followers', 'following_url': 'https://api.github.com/users/Gabriela-Orzechowska/following{/other_user}', 'gists_url': 'https://api.github.com/users/Gabriela-Orzechowska/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/Gabriela-Orzechowska/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/Gabriela-Orzechowska/subscriptions', 'organizations_url': 'https://api.github.com/users/Gabriela-Orzechowska/orgs', 'repos_url': 'https://api.github.com/users/Gabriela-Orzechowska/repos', 'events_url': 'https://api.github.com/users/Gabriela-Orzechowska/events{/privacy}', 'received_events_url': 'https://api.github.com/users/Gabriela-Orzechowska/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'labels': [], 'state': 'open', 'locked': False, 'assignee': None, 'assignees': [], 'milestone': None, 'comments': 6, 'created_at': '2026-02-01T18:58:25Z', 'updated_at': '2026-02-01T20:22:06Z', 'closed_at': None, 'author_association': 'NONE', 'type': None, 'active_lock_reason': None, 'draft': False, 'pull_request': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/14315', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/14315', 'diff_url': 'https://github.com/dolphin-emu/dolphin/pull/14315.diff', 'patch_url': 'https://github.com/dolphin-emu/dolphin/pull/14315.patch', 'merged_at': None}, 'body': 'This PR fixes an issue with wrong param parsing of Riivolution XML files. Usage of `std::string_view` creates corruption and invalid values of the parameters.\r\n<img width="357" height="158" alt="image" src="https://github.com/user-attachments/assets/45d48cae-e21e-4953-80b7-511d8f7ac9e3" />\r\nSimply replacing it with `std::string` fixes the issue.\r\n<img width="359" height="165" alt="image" src="https://github.com/user-attachments/assets/ede15ed1-deef-4af5-94a7-44ab988eaa0b" />\r\n', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14315/reactions', 'total_count': 1, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 0, 'confused': 0, 'heart': 1, 'rocket': 0, 'eyes': 0}, 'timeline_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14315/timeline', 'performed_via_github_app': None, 'state_reason': None}, 'comment': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments/3831916842', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/14315#issuecomment-3831916842', 'issue_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14315', 'id': 3831916842, 'node_id': 'IC_kwDOALCn2M7kZmkq', 'user': {'login': 'Dentomologist', 'id': 73494713, 'node_id': 'MDQ6VXNlcjczNDk0NzEz', 'avatar_url': 'https://avatars.githubusercontent.com/u/73494713?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/Dentomologist', 'html_url': 'https://github.com/Dentomologist', 'followers_url': 'https://api.github.com/users/Dentomologist/followers', 'following_url': 'https://api.github.com/users/Dentomologist/following{/other_user}', 'gists_url': 'https://api.github.com/users/Dentomologist/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/Dentomologist/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/Dentomologist/subscriptions', 'organizations_url': 'https://api.github.com/users/Dentomologist/orgs', 'repos_url': 'https://api.github.com/users/Dentomologist/repos', 'events_url': 'https://api.github.com/users/Dentomologist/events{/privacy}', 'received_events_url': 'https://api.github.com/users/Dentomologist/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'created_at': '2026-02-01T20:22:06Z', 'updated_at': '2026-02-01T20:22:06Z', 'body': "> `std::string_view::substr` doesn't return an `std::string`, it reuses the same backing memory as the `std::string_view` it was called on. So those variables are views into `game_id`, which is valid for the whole `GeneratePatches` call.\r\n\r\n... whoops, got that mixed up with `std::string::substr`.", 'author_association': 'MEMBER', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments/3831916842/reactions', 'total_count': 0, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 0, 'confused': 0, 'heart': 0, 'rocket': 0, 'eyes': 0}, 'performed_via_github_app': None}, 'repository': {'id': 11577304, 'node_id': 'MDEwOlJlcG9zaXRvcnkxMTU3NzMwNA==', 'name': 'dolphin', 'full_name': 'dolphin-emu/dolphin', 'private': False, 'owner': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/dolphin-emu', 'html_url': 'https://github.com/dolphin-emu', 'followers_url': 'https://api.github.com/users/dolphin-emu/followers', 'following_url': 'https://api.github.com/users/dolphin-emu/following{/other_user}', 'gists_url': 'https://api.github.com/users/dolphin-emu/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/dolphin-emu/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/dolphin-emu/subscriptions', 'organizations_url': 'https://api.github.com/users/dolphin-emu/orgs', 'repos_url': 'https://api.github.com/users/dolphin-emu/repos', 'events_url': 'https://api.github.com/users/dolphin-emu/events{/privacy}', 'received_events_url': 'https://api.github.com/users/dolphin-emu/received_events', 'type': 'Organization', 'user_view_type': 'public', 'site_admin': False}, 'html_url': 'https://github.com/dolphin-emu/dolphin', 'description': 'Dolphin is a GameCube / Wii emulator, allowing you to play games for these two platforms on PC with improvements.', 'fork': False, 'url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'forks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/forks', 'keys_url': 'https://api.github.com/repos/dolphin-emu/dolphin/keys{/key_id}', 'collaborators_url': 'https://api.github.com/repos/dolphin-emu/dolphin/collaborators{/collaborator}', 'teams_url': 'https://api.github.com/repos/dolphin-emu/dolphin/teams', 'hooks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/hooks', 'issue_events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/events{/number}', 'events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/events', 'assignees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/assignees{/user}', 'branches_url': 'https://api.github.com/repos/dolphin-emu/dolphin/branches{/branch}', 'tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/tags', 'blobs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/blobs{/sha}', 'git_tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/tags{/sha}', 'git_refs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/refs{/sha}', 'trees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/trees{/sha}', 'statuses_url': 'https://api.github.com/repos/dolphin-emu/dolphin/statuses/{sha}', 'languages_url': 'https://api.github.com/repos/dolphin-emu/dolphin/languages', 'stargazers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/stargazers', 'contributors_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contributors', 'subscribers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscribers', 'subscription_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscription', 'commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/commits{/sha}', 'git_commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/commits{/sha}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/comments{/number}', 'issue_comment_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments{/number}', 'contents_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contents/{+path}', 'compare_url': 'https://api.github.com/repos/dolphin-emu/dolphin/compare/{base}...{head}', 'merges_url': 'https://api.github.com/repos/dolphin-emu/dolphin/merges', 'archive_url': 'https://api.github.com/repos/dolphin-emu/dolphin/{archive_format}{/ref}', 'downloads_url': 'https://api.github.com/repos/dolphin-emu/dolphin/downloads', 'issues_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues{/number}', 'pulls_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls{/number}', 'milestones_url': 'https://api.github.com/repos/dolphin-emu/dolphin/milestones{/number}', 'notifications_url': 'https://api.github.com/repos/dolphin-emu/dolphin/notifications{?since,all,participating}', 'labels_url': 'https://api.github.com/repos/dolphin-emu/dolphin/labels{/name}', 'releases_url': 'https://api.github.com/repos/dolphin-emu/dolphin/releases{/id}', 'deployments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/deployments', 'created_at': '2013-07-22T09:07:57Z', 'updated_at': '2026-02-01T19:55:00Z', 'pushed_at': '2026-01-31T23:02:09Z', 'git_url': 'git://github.com/dolphin-emu/dolphin.git', 'ssh_url': 'git@github.com:dolphin-emu/dolphin.git', 'clone_url': 'https://github.com/dolphin-emu/dolphin.git', 'svn_url': 'https://github.com/dolphin-emu/dolphin', 'homepage': 'https://dolphin-emu.org', 'size': 544012, 'stargazers_count': 14572, 'watchers_count': 14572, 'language': 'C++', 'has_issues': False, 'has_projects': False, 'has_downloads': True, 'has_wiki': True, 'has_pages': False, 'has_discussions': False, 'forks_count': 2967, 'mirror_url': None, 'archived': False, 'disabled': False, 'open_issues_count': 360, 'license': {'key': 'other', 'name': 'Other', 'spdx_id': 'NOASSERTION', 'url': None, 'node_id': 'MDc6TGljZW5zZTA='}, 'allow_forking': True, 'is_template': False, 'web_commit_signoff_required': False, 'topics': ['cpp', 'emulation', 'emulator', 'gamecube', 'wii'], 'visibility': 'public', 'forks': 2967, 'open_issues': 360, 'watchers': 14572, 'default_branch': 'master', 'custom_properties': {}}, 'organization': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'url': 'https://api.github.com/orgs/dolphin-emu', 'repos_url': 'https://api.github.com/orgs/dolphin-emu/repos', 'events_url': 'https://api.github.com/orgs/dolphin-emu/events', 'hooks_url': 'https://api.github.com/orgs/dolphin-emu/hooks', 'issues_url': 'https://api.github.com/orgs/dolphin-emu/issues', 'members_url': 'https://api.github.com/orgs/dolphin-emu/members{/member}', 'public_members_url': 'https://api.github.com/orgs/dolphin-emu/public_members{/member}', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'description': ''}, 'sender': {'login': 'Dentomologist', 'id': 73494713, 'node_id': 'MDQ6VXNlcjczNDk0NzEz', 'avatar_url': 'https://avatars.githubusercontent.com/u/73494713?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/Dentomologist', 'html_url': 'https://github.com/Dentomologist', 'followers_url': 'https://api.github.com/users/Dentomologist/followers', 'following_url': 'https://api.github.com/users/Dentomologist/following{/other_user}', 'gists_url': 'https://api.github.com/users/Dentomologist/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/Dentomologist/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/Dentomologist/subscriptions', 'organizations_url': 'https://api.github.com/users/Dentomologist/orgs', 'repos_url': 'https://api.github.com/users/Dentomologist/repos', 'events_url': 'https://api.github.com/users/Dentomologist/events{/privacy}', 'received_events_url': 'https://api.github.com/users/Dentomologist/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'installation': {'id': 35437981, 'node_id': 'MDIzOkludGVncmF0aW9uSW5zdGFsbGF0aW9uMzU0Mzc5ODE='}}, 'type': 'gh_issue_comment'}
2026-02-01T20:21:09.791156 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'Dentomologist', 'action': 'edited', 'id': 14315, 'title': 'RiivolutionParser: Fix XML Param Parsing', 'url': 'https://github.com/dolphin-emu/dolphin/pull/14315#issuecomment-3831877200', 'safe_author': True, 'body': "> `game_id_no_region`, `game_region`, and `game_developer` are views into the return value of `substr`, the lifetime of which ends at the end of the expression. Those variables should be `std::string` instead of `std::string_view`.\r\n\r\n`std::string_view::substr` doesn't return an `std::string`, it reuses the same backing memory as the `std::string_view` it was called on. So those variables are views into `game_id`, which is valid for the whole `GeneratePatches` call.", 'raw': {'action': 'edited', 'changes': {'body': {'from': "> `std::string_view::substr` doesn't return an `std::string`, it reuses the same backing memory as the `std::string_view` it was called on. So those variables are views into `game_id`, which is valid for the whole `GeneratePatches` call.\r\n\r\n... whoops, got that mixed up with `std::string::substr`."}}, 'issue': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14315', 'repository_url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'labels_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14315/labels{/name}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14315/comments', 'events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14315/events', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/14315', 'id': 3882697746, 'node_id': 'PR_kwDOALCn2M7Av0U8', 'number': 14315, 'title': 'RiivolutionParser: Fix XML Param Parsing', 'user': {'login': 'Gabriela-Orzechowska', 'id': 85825993, 'node_id': 'MDQ6VXNlcjg1ODI1OTkz', 'avatar_url': 'https://avatars.githubusercontent.com/u/85825993?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/Gabriela-Orzechowska', 'html_url': 'https://github.com/Gabriela-Orzechowska', 'followers_url': 'https://api.github.com/users/Gabriela-Orzechowska/followers', 'following_url': 'https://api.github.com/users/Gabriela-Orzechowska/following{/other_user}', 'gists_url': 'https://api.github.com/users/Gabriela-Orzechowska/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/Gabriela-Orzechowska/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/Gabriela-Orzechowska/subscriptions', 'organizations_url': 'https://api.github.com/users/Gabriela-Orzechowska/orgs', 'repos_url': 'https://api.github.com/users/Gabriela-Orzechowska/repos', 'events_url': 'https://api.github.com/users/Gabriela-Orzechowska/events{/privacy}', 'received_events_url': 'https://api.github.com/users/Gabriela-Orzechowska/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'labels': [], 'state': 'open', 'locked': False, 'assignee': None, 'assignees': [], 'milestone': None, 'comments': 5, 'created_at': '2026-02-01T18:58:25Z', 'updated_at': '2026-02-01T20:12:08Z', 'closed_at': None, 'author_association': 'NONE', 'type': None, 'active_lock_reason': None, 'draft': False, 'pull_request': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/14315', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/14315', 'diff_url': 'https://github.com/dolphin-emu/dolphin/pull/14315.diff', 'patch_url': 'https://github.com/dolphin-emu/dolphin/pull/14315.patch', 'merged_at': None}, 'body': 'This PR fixes an issue with wrong param parsing of Riivolution XML files. Usage of `std::string_view` creates corruption and invalid values of the parameters.\r\n<img width="357" height="158" alt="image" src="https://github.com/user-attachments/assets/45d48cae-e21e-4953-80b7-511d8f7ac9e3" />\r\nSimply replacing it with `std::string` fixes the issue.\r\n<img width="359" height="165" alt="image" src="https://github.com/user-attachments/assets/ede15ed1-deef-4af5-94a7-44ab988eaa0b" />\r\n', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14315/reactions', 'total_count': 1, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 0, 'confused': 0, 'heart': 1, 'rocket': 0, 'eyes': 0}, 'timeline_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14315/timeline', 'performed_via_github_app': None, 'state_reason': None}, 'comment': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments/3831877200', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/14315#issuecomment-3831877200', 'issue_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14315', 'id': 3831877200, 'node_id': 'IC_kwDOALCn2M7kZc5Q', 'user': {'login': 'JosJuice', 'id': 6716818, 'node_id': 'MDQ6VXNlcjY3MTY4MTg=', 'avatar_url': 'https://avatars.githubusercontent.com/u/6716818?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/JosJuice', 'html_url': 'https://github.com/JosJuice', 'followers_url': 'https://api.github.com/users/JosJuice/followers', 'following_url': 'https://api.github.com/users/JosJuice/following{/other_user}', 'gists_url': 'https://api.github.com/users/JosJuice/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/JosJuice/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/JosJuice/subscriptions', 'organizations_url': 'https://api.github.com/users/JosJuice/orgs', 'repos_url': 'https://api.github.com/users/JosJuice/repos', 'events_url': 'https://api.github.com/users/JosJuice/events{/privacy}', 'received_events_url': 'https://api.github.com/users/JosJuice/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'created_at': '2026-02-01T19:59:46Z', 'updated_at': '2026-02-01T20:21:08Z', 'body': "> `game_id_no_region`, `game_region`, and `game_developer` are views into the return value of `substr`, the lifetime of which ends at the end of the expression. Those variables should be `std::string` instead of `std::string_view`.\r\n\r\n`std::string_view::substr` doesn't return an `std::string`, it reuses the same backing memory as the `std::string_view` it was called on. So those variables are views into `game_id`, which is valid for the whole `GeneratePatches` call.", 'author_association': 'MEMBER', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments/3831877200/reactions', 'total_count': 0, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 0, 'confused': 0, 'heart': 0, 'rocket': 0, 'eyes': 0}, 'performed_via_github_app': None}, 'repository': {'id': 11577304, 'node_id': 'MDEwOlJlcG9zaXRvcnkxMTU3NzMwNA==', 'name': 'dolphin', 'full_name': 'dolphin-emu/dolphin', 'private': False, 'owner': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/dolphin-emu', 'html_url': 'https://github.com/dolphin-emu', 'followers_url': 'https://api.github.com/users/dolphin-emu/followers', 'following_url': 'https://api.github.com/users/dolphin-emu/following{/other_user}', 'gists_url': 'https://api.github.com/users/dolphin-emu/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/dolphin-emu/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/dolphin-emu/subscriptions', 'organizations_url': 'https://api.github.com/users/dolphin-emu/orgs', 'repos_url': 'https://api.github.com/users/dolphin-emu/repos', 'events_url': 'https://api.github.com/users/dolphin-emu/events{/privacy}', 'received_events_url': 'https://api.github.com/users/dolphin-emu/received_events', 'type': 'Organization', 'user_view_type': 'public', 'site_admin': False}, 'html_url': 'https://github.com/dolphin-emu/dolphin', 'description': 'Dolphin is a GameCube / Wii emulator, allowing you to play games for these two platforms on PC with improvements.', 'fork': False, 'url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'forks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/forks', 'keys_url': 'https://api.github.com/repos/dolphin-emu/dolphin/keys{/key_id}', 'collaborators_url': 'https://api.github.com/repos/dolphin-emu/dolphin/collaborators{/collaborator}', 'teams_url': 'https://api.github.com/repos/dolphin-emu/dolphin/teams', 'hooks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/hooks', 'issue_events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/events{/number}', 'events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/events', 'assignees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/assignees{/user}', 'branches_url': 'https://api.github.com/repos/dolphin-emu/dolphin/branches{/branch}', 'tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/tags', 'blobs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/blobs{/sha}', 'git_tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/tags{/sha}', 'git_refs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/refs{/sha}', 'trees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/trees{/sha}', 'statuses_url': 'https://api.github.com/repos/dolphin-emu/dolphin/statuses/{sha}', 'languages_url': 'https://api.github.com/repos/dolphin-emu/dolphin/languages', 'stargazers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/stargazers', 'contributors_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contributors', 'subscribers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscribers', 'subscription_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscription', 'commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/commits{/sha}', 'git_commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/commits{/sha}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/comments{/number}', 'issue_comment_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments{/number}', 'contents_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contents/{+path}', 'compare_url': 'https://api.github.com/repos/dolphin-emu/dolphin/compare/{base}...{head}', 'merges_url': 'https://api.github.com/repos/dolphin-emu/dolphin/merges', 'archive_url': 'https://api.github.com/repos/dolphin-emu/dolphin/{archive_format}{/ref}', 'downloads_url': 'https://api.github.com/repos/dolphin-emu/dolphin/downloads', 'issues_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues{/number}', 'pulls_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls{/number}', 'milestones_url': 'https://api.github.com/repos/dolphin-emu/dolphin/milestones{/number}', 'notifications_url': 'https://api.github.com/repos/dolphin-emu/dolphin/notifications{?since,all,participating}', 'labels_url': 'https://api.github.com/repos/dolphin-emu/dolphin/labels{/name}', 'releases_url': 'https://api.github.com/repos/dolphin-emu/dolphin/releases{/id}', 'deployments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/deployments', 'created_at': '2013-07-22T09:07:57Z', 'updated_at': '2026-02-01T19:55:00Z', 'pushed_at': '2026-01-31T23:02:09Z', 'git_url': 'git://github.com/dolphin-emu/dolphin.git', 'ssh_url': 'git@github.com:dolphin-emu/dolphin.git', 'clone_url': 'https://github.com/dolphin-emu/dolphin.git', 'svn_url': 'https://github.com/dolphin-emu/dolphin', 'homepage': 'https://dolphin-emu.org', 'size': 544012, 'stargazers_count': 14572, 'watchers_count': 14572, 'language': 'C++', 'has_issues': False, 'has_projects': False, 'has_downloads': True, 'has_wiki': True, 'has_pages': False, 'has_discussions': False, 'forks_count': 2967, 'mirror_url': None, 'archived': False, 'disabled': False, 'open_issues_count': 360, 'license': {'key': 'other', 'name': 'Other', 'spdx_id': 'NOASSERTION', 'url': None, 'node_id': 'MDc6TGljZW5zZTA='}, 'allow_forking': True, 'is_template': False, 'web_commit_signoff_required': False, 'topics': ['cpp', 'emulation', 'emulator', 'gamecube', 'wii'], 'visibility': 'public', 'forks': 2967, 'open_issues': 360, 'watchers': 14572, 'default_branch': 'master', 'custom_properties': {}}, 'organization': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'url': 'https://api.github.com/orgs/dolphin-emu', 'repos_url': 'https://api.github.com/orgs/dolphin-emu/repos', 'events_url': 'https://api.github.com/orgs/dolphin-emu/events', 'hooks_url': 'https://api.github.com/orgs/dolphin-emu/hooks', 'issues_url': 'https://api.github.com/orgs/dolphin-emu/issues', 'members_url': 'https://api.github.com/orgs/dolphin-emu/members{/member}', 'public_members_url': 'https://api.github.com/orgs/dolphin-emu/public_members{/member}', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'description': ''}, 'sender': {'login': 'Dentomologist', 'id': 73494713, 'node_id': 'MDQ6VXNlcjczNDk0NzEz', 'avatar_url': 'https://avatars.githubusercontent.com/u/73494713?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/Dentomologist', 'html_url': 'https://github.com/Dentomologist', 'followers_url': 'https://api.github.com/users/Dentomologist/followers', 'following_url': 'https://api.github.com/users/Dentomologist/following{/other_user}', 'gists_url': 'https://api.github.com/users/Dentomologist/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/Dentomologist/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/Dentomologist/subscriptions', 'organizations_url': 'https://api.github.com/users/Dentomologist/orgs', 'repos_url': 'https://api.github.com/users/Dentomologist/repos', 'events_url': 'https://api.github.com/users/Dentomologist/events{/privacy}', 'received_events_url': 'https://api.github.com/users/Dentomologist/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'installation': {'id': 35437981, 'node_id': 'MDIzOkludGVncmF0aW9uSW5zdGFsbGF0aW9uMzU0Mzc5ODE='}}, 'type': 'gh_issue_comment'}
2026-02-01T20:12:10.024975 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'Dentomologist', 'action': 'edited', 'id': 14315, 'title': 'RiivolutionParser: Fix XML Param Parsing', 'url': 'https://github.com/dolphin-emu/dolphin/pull/14315#issuecomment-3831877200', 'safe_author': True, 'body': "> `std::string_view::substr` doesn't return an `std::string`, it reuses the same backing memory as the `std::string_view` it was called on. So those variables are views into `game_id`, which is valid for the whole `GeneratePatches` call.\r\n\r\n... whoops, got that mixed up with `std::string::substr`.", 'raw': {'action': 'edited', 'changes': {'body': {'from': "> `game_id_no_region`, `game_region`, and `game_developer` are views into the return value of `substr`, the lifetime of which ends at the end of the expression. Those variables should be `std::string` instead of `std::string_view`. \r\n\r\n`std::string_view::substr` doesn't return an `std::string`, it reuses the same backing memory as the `std::string_view` it was called on. So those variables are views into `game_id`, which is valid for the whole `GeneratePatches` call."}}, 'issue': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14315', 'repository_url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'labels_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14315/labels{/name}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14315/comments', 'events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14315/events', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/14315', 'id': 3882697746, 'node_id': 'PR_kwDOALCn2M7Av0U8', 'number': 14315, 'title': 'RiivolutionParser: Fix XML Param Parsing', 'user': {'login': 'Gabriela-Orzechowska', 'id': 85825993, 'node_id': 'MDQ6VXNlcjg1ODI1OTkz', 'avatar_url': 'https://avatars.githubusercontent.com/u/85825993?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/Gabriela-Orzechowska', 'html_url': 'https://github.com/Gabriela-Orzechowska', 'followers_url': 'https://api.github.com/users/Gabriela-Orzechowska/followers', 'following_url': 'https://api.github.com/users/Gabriela-Orzechowska/following{/other_user}', 'gists_url': 'https://api.github.com/users/Gabriela-Orzechowska/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/Gabriela-Orzechowska/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/Gabriela-Orzechowska/subscriptions', 'organizations_url': 'https://api.github.com/users/Gabriela-Orzechowska/orgs', 'repos_url': 'https://api.github.com/users/Gabriela-Orzechowska/repos', 'events_url': 'https://api.github.com/users/Gabriela-Orzechowska/events{/privacy}', 'received_events_url': 'https://api.github.com/users/Gabriela-Orzechowska/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'labels': [], 'state': 'open', 'locked': False, 'assignee': None, 'assignees': [], 'milestone': None, 'comments': 5, 'created_at': '2026-02-01T18:58:25Z', 'updated_at': '2026-02-01T19:59:46Z', 'closed_at': None, 'author_association': 'NONE', 'type': None, 'active_lock_reason': None, 'draft': False, 'pull_request': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/14315', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/14315', 'diff_url': 'https://github.com/dolphin-emu/dolphin/pull/14315.diff', 'patch_url': 'https://github.com/dolphin-emu/dolphin/pull/14315.patch', 'merged_at': None}, 'body': 'This PR fixes an issue with wrong param parsing of Riivolution XML files. Usage of `std::string_view` creates corruption and invalid values of the parameters.\r\n<img width="357" height="158" alt="image" src="https://github.com/user-attachments/assets/45d48cae-e21e-4953-80b7-511d8f7ac9e3" />\r\nSimply replacing it with `std::string` fixes the issue.\r\n<img width="359" height="165" alt="image" src="https://github.com/user-attachments/assets/ede15ed1-deef-4af5-94a7-44ab988eaa0b" />\r\n', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14315/reactions', 'total_count': 1, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 0, 'confused': 0, 'heart': 1, 'rocket': 0, 'eyes': 0}, 'timeline_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14315/timeline', 'performed_via_github_app': None, 'state_reason': None}, 'comment': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments/3831877200', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/14315#issuecomment-3831877200', 'issue_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14315', 'id': 3831877200, 'node_id': 'IC_kwDOALCn2M7kZc5Q', 'user': {'login': 'JosJuice', 'id': 6716818, 'node_id': 'MDQ6VXNlcjY3MTY4MTg=', 'avatar_url': 'https://avatars.githubusercontent.com/u/6716818?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/JosJuice', 'html_url': 'https://github.com/JosJuice', 'followers_url': 'https://api.github.com/users/JosJuice/followers', 'following_url': 'https://api.github.com/users/JosJuice/following{/other_user}', 'gists_url': 'https://api.github.com/users/JosJuice/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/JosJuice/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/JosJuice/subscriptions', 'organizations_url': 'https://api.github.com/users/JosJuice/orgs', 'repos_url': 'https://api.github.com/users/JosJuice/repos', 'events_url': 'https://api.github.com/users/JosJuice/events{/privacy}', 'received_events_url': 'https://api.github.com/users/JosJuice/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'created_at': '2026-02-01T19:59:46Z', 'updated_at': '2026-02-01T20:12:08Z', 'body': "> `std::string_view::substr` doesn't return an `std::string`, it reuses the same backing memory as the `std::string_view` it was called on. So those variables are views into `game_id`, which is valid for the whole `GeneratePatches` call.\r\n\r\n... whoops, got that mixed up with `std::string::substr`.", 'author_association': 'MEMBER', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments/3831877200/reactions', 'total_count': 0, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 0, 'confused': 0, 'heart': 0, 'rocket': 0, 'eyes': 0}, 'performed_via_github_app': None}, 'repository': {'id': 11577304, 'node_id': 'MDEwOlJlcG9zaXRvcnkxMTU3NzMwNA==', 'name': 'dolphin', 'full_name': 'dolphin-emu/dolphin', 'private': False, 'owner': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/dolphin-emu', 'html_url': 'https://github.com/dolphin-emu', 'followers_url': 'https://api.github.com/users/dolphin-emu/followers', 'following_url': 'https://api.github.com/users/dolphin-emu/following{/other_user}', 'gists_url': 'https://api.github.com/users/dolphin-emu/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/dolphin-emu/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/dolphin-emu/subscriptions', 'organizations_url': 'https://api.github.com/users/dolphin-emu/orgs', 'repos_url': 'https://api.github.com/users/dolphin-emu/repos', 'events_url': 'https://api.github.com/users/dolphin-emu/events{/privacy}', 'received_events_url': 'https://api.github.com/users/dolphin-emu/received_events', 'type': 'Organization', 'user_view_type': 'public', 'site_admin': False}, 'html_url': 'https://github.com/dolphin-emu/dolphin', 'description': 'Dolphin is a GameCube / Wii emulator, allowing you to play games for these two platforms on PC with improvements.', 'fork': False, 'url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'forks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/forks', 'keys_url': 'https://api.github.com/repos/dolphin-emu/dolphin/keys{/key_id}', 'collaborators_url': 'https://api.github.com/repos/dolphin-emu/dolphin/collaborators{/collaborator}', 'teams_url': 'https://api.github.com/repos/dolphin-emu/dolphin/teams', 'hooks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/hooks', 'issue_events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/events{/number}', 'events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/events', 'assignees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/assignees{/user}', 'branches_url': 'https://api.github.com/repos/dolphin-emu/dolphin/branches{/branch}', 'tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/tags', 'blobs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/blobs{/sha}', 'git_tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/tags{/sha}', 'git_refs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/refs{/sha}', 'trees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/trees{/sha}', 'statuses_url': 'https://api.github.com/repos/dolphin-emu/dolphin/statuses/{sha}', 'languages_url': 'https://api.github.com/repos/dolphin-emu/dolphin/languages', 'stargazers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/stargazers', 'contributors_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contributors', 'subscribers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscribers', 'subscription_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscription', 'commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/commits{/sha}', 'git_commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/commits{/sha}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/comments{/number}', 'issue_comment_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments{/number}', 'contents_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contents/{+path}', 'compare_url': 'https://api.github.com/repos/dolphin-emu/dolphin/compare/{base}...{head}', 'merges_url': 'https://api.github.com/repos/dolphin-emu/dolphin/merges', 'archive_url': 'https://api.github.com/repos/dolphin-emu/dolphin/{archive_format}{/ref}', 'downloads_url': 'https://api.github.com/repos/dolphin-emu/dolphin/downloads', 'issues_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues{/number}', 'pulls_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls{/number}', 'milestones_url': 'https://api.github.com/repos/dolphin-emu/dolphin/milestones{/number}', 'notifications_url': 'https://api.github.com/repos/dolphin-emu/dolphin/notifications{?since,all,participating}', 'labels_url': 'https://api.github.com/repos/dolphin-emu/dolphin/labels{/name}', 'releases_url': 'https://api.github.com/repos/dolphin-emu/dolphin/releases{/id}', 'deployments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/deployments', 'created_at': '2013-07-22T09:07:57Z', 'updated_at': '2026-02-01T19:55:00Z', 'pushed_at': '2026-01-31T23:02:09Z', 'git_url': 'git://github.com/dolphin-emu/dolphin.git', 'ssh_url': 'git@github.com:dolphin-emu/dolphin.git', 'clone_url': 'https://github.com/dolphin-emu/dolphin.git', 'svn_url': 'https://github.com/dolphin-emu/dolphin', 'homepage': 'https://dolphin-emu.org', 'size': 544012, 'stargazers_count': 14572, 'watchers_count': 14572, 'language': 'C++', 'has_issues': False, 'has_projects': False, 'has_downloads': True, 'has_wiki': True, 'has_pages': False, 'has_discussions': False, 'forks_count': 2967, 'mirror_url': None, 'archived': False, 'disabled': False, 'open_issues_count': 360, 'license': {'key': 'other', 'name': 'Other', 'spdx_id': 'NOASSERTION', 'url': None, 'node_id': 'MDc6TGljZW5zZTA='}, 'allow_forking': True, 'is_template': False, 'web_commit_signoff_required': False, 'topics': ['cpp', 'emulation', 'emulator', 'gamecube', 'wii'], 'visibility': 'public', 'forks': 2967, 'open_issues': 360, 'watchers': 14572, 'default_branch': 'master', 'custom_properties': {}}, 'organization': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'url': 'https://api.github.com/orgs/dolphin-emu', 'repos_url': 'https://api.github.com/orgs/dolphin-emu/repos', 'events_url': 'https://api.github.com/orgs/dolphin-emu/events', 'hooks_url': 'https://api.github.com/orgs/dolphin-emu/hooks', 'issues_url': 'https://api.github.com/orgs/dolphin-emu/issues', 'members_url': 'https://api.github.com/orgs/dolphin-emu/members{/member}', 'public_members_url': 'https://api.github.com/orgs/dolphin-emu/public_members{/member}', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'description': ''}, 'sender': {'login': 'Dentomologist', 'id': 73494713, 'node_id': 'MDQ6VXNlcjczNDk0NzEz', 'avatar_url': 'https://avatars.githubusercontent.com/u/73494713?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/Dentomologist', 'html_url': 'https://github.com/Dentomologist', 'followers_url': 'https://api.github.com/users/Dentomologist/followers', 'following_url': 'https://api.github.com/users/Dentomologist/following{/other_user}', 'gists_url': 'https://api.github.com/users/Dentomologist/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/Dentomologist/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/Dentomologist/subscriptions', 'organizations_url': 'https://api.github.com/users/Dentomologist/orgs', 'repos_url': 'https://api.github.com/users/Dentomologist/repos', 'events_url': 'https://api.github.com/users/Dentomologist/events{/privacy}', 'received_events_url': 'https://api.github.com/users/Dentomologist/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'installation': {'id': 35437981, 'node_id': 'MDIzOkludGVncmF0aW9uSW5zdGFsbGF0aW9uMzU0Mzc5ODE='}}, 'type': 'gh_issue_comment'}
2026-02-01T19:59:48.727527 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'JosJuice', 'action': 'created', 'id': 14315, 'title': 'RiivolutionParser: Fix XML Param Parsing', 'url': 'https://github.com/dolphin-emu/dolphin/pull/14315#issuecomment-3831877200', 'safe_author': True, 'body': "> `game_id_no_region`, `game_region`, and `game_developer` are views into the return value of `substr`, the lifetime of which ends at the end of the expression. Those variables should be `std::string` instead of `std::string_view`. \r\n\r\n`std::string_view::substr` doesn't return an `std::string`, it reuses the same backing memory as the `std::string_view` it was called on. So those variables are views into `game_id`, which is valid for the whole `GeneratePatches` call.", 'raw': {'action': 'created', 'issue': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14315', 'repository_url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'labels_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14315/labels{/name}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14315/comments', 'events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14315/events', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/14315', 'id': 3882697746, 'node_id': 'PR_kwDOALCn2M7Av0U8', 'number': 14315, 'title': 'RiivolutionParser: Fix XML Param Parsing', 'user': {'login': 'Gabriela-Orzechowska', 'id': 85825993, 'node_id': 'MDQ6VXNlcjg1ODI1OTkz', 'avatar_url': 'https://avatars.githubusercontent.com/u/85825993?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/Gabriela-Orzechowska', 'html_url': 'https://github.com/Gabriela-Orzechowska', 'followers_url': 'https://api.github.com/users/Gabriela-Orzechowska/followers', 'following_url': 'https://api.github.com/users/Gabriela-Orzechowska/following{/other_user}', 'gists_url': 'https://api.github.com/users/Gabriela-Orzechowska/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/Gabriela-Orzechowska/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/Gabriela-Orzechowska/subscriptions', 'organizations_url': 'https://api.github.com/users/Gabriela-Orzechowska/orgs', 'repos_url': 'https://api.github.com/users/Gabriela-Orzechowska/repos', 'events_url': 'https://api.github.com/users/Gabriela-Orzechowska/events{/privacy}', 'received_events_url': 'https://api.github.com/users/Gabriela-Orzechowska/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'labels': [], 'state': 'open', 'locked': False, 'assignee': None, 'assignees': [], 'milestone': None, 'comments': 5, 'created_at': '2026-02-01T18:58:25Z', 'updated_at': '2026-02-01T19:59:46Z', 'closed_at': None, 'author_association': 'NONE', 'type': None, 'active_lock_reason': None, 'draft': False, 'pull_request': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/14315', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/14315', 'diff_url': 'https://github.com/dolphin-emu/dolphin/pull/14315.diff', 'patch_url': 'https://github.com/dolphin-emu/dolphin/pull/14315.patch', 'merged_at': None}, 'body': 'This PR fixes an issue with wrong param parsing of Riivolution XML files. Usage of `std::string_view` creates corruption and invalid values of the parameters.\r\n<img width="357" height="158" alt="image" src="https://github.com/user-attachments/assets/45d48cae-e21e-4953-80b7-511d8f7ac9e3" />\r\nSimply replacing it with `std::string` fixes the issue.\r\n<img width="359" height="165" alt="image" src="https://github.com/user-attachments/assets/ede15ed1-deef-4af5-94a7-44ab988eaa0b" />\r\n', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14315/reactions', 'total_count': 1, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 0, 'confused': 0, 'heart': 1, 'rocket': 0, 'eyes': 0}, 'timeline_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14315/timeline', 'performed_via_github_app': None, 'state_reason': None}, 'comment': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments/3831877200', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/14315#issuecomment-3831877200', 'issue_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14315', 'id': 3831877200, 'node_id': 'IC_kwDOALCn2M7kZc5Q', 'user': {'login': 'JosJuice', 'id': 6716818, 'node_id': 'MDQ6VXNlcjY3MTY4MTg=', 'avatar_url': 'https://avatars.githubusercontent.com/u/6716818?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/JosJuice', 'html_url': 'https://github.com/JosJuice', 'followers_url': 'https://api.github.com/users/JosJuice/followers', 'following_url': 'https://api.github.com/users/JosJuice/following{/other_user}', 'gists_url': 'https://api.github.com/users/JosJuice/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/JosJuice/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/JosJuice/subscriptions', 'organizations_url': 'https://api.github.com/users/JosJuice/orgs', 'repos_url': 'https://api.github.com/users/JosJuice/repos', 'events_url': 'https://api.github.com/users/JosJuice/events{/privacy}', 'received_events_url': 'https://api.github.com/users/JosJuice/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'created_at': '2026-02-01T19:59:46Z', 'updated_at': '2026-02-01T19:59:46Z', 'body': "> `game_id_no_region`, `game_region`, and `game_developer` are views into the return value of `substr`, the lifetime of which ends at the end of the expression. Those variables should be `std::string` instead of `std::string_view`. \r\n\r\n`std::string_view::substr` doesn't return an `std::string`, it reuses the same backing memory as the `std::string_view` it was called on. So those variables are views into `game_id`, which is valid for the whole `GeneratePatches` call.", 'author_association': 'MEMBER', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments/3831877200/reactions', 'total_count': 0, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 0, 'confused': 0, 'heart': 0, 'rocket': 0, 'eyes': 0}, 'performed_via_github_app': None}, 'repository': {'id': 11577304, 'node_id': 'MDEwOlJlcG9zaXRvcnkxMTU3NzMwNA==', 'name': 'dolphin', 'full_name': 'dolphin-emu/dolphin', 'private': False, 'owner': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/dolphin-emu', 'html_url': 'https://github.com/dolphin-emu', 'followers_url': 'https://api.github.com/users/dolphin-emu/followers', 'following_url': 'https://api.github.com/users/dolphin-emu/following{/other_user}', 'gists_url': 'https://api.github.com/users/dolphin-emu/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/dolphin-emu/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/dolphin-emu/subscriptions', 'organizations_url': 'https://api.github.com/users/dolphin-emu/orgs', 'repos_url': 'https://api.github.com/users/dolphin-emu/repos', 'events_url': 'https://api.github.com/users/dolphin-emu/events{/privacy}', 'received_events_url': 'https://api.github.com/users/dolphin-emu/received_events', 'type': 'Organization', 'user_view_type': 'public', 'site_admin': False}, 'html_url': 'https://github.com/dolphin-emu/dolphin', 'description': 'Dolphin is a GameCube / Wii emulator, allowing you to play games for these two platforms on PC with improvements.', 'fork': False, 'url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'forks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/forks', 'keys_url': 'https://api.github.com/repos/dolphin-emu/dolphin/keys{/key_id}', 'collaborators_url': 'https://api.github.com/repos/dolphin-emu/dolphin/collaborators{/collaborator}', 'teams_url': 'https://api.github.com/repos/dolphin-emu/dolphin/teams', 'hooks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/hooks', 'issue_events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/events{/number}', 'events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/events', 'assignees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/assignees{/user}', 'branches_url': 'https://api.github.com/repos/dolphin-emu/dolphin/branches{/branch}', 'tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/tags', 'blobs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/blobs{/sha}', 'git_tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/tags{/sha}', 'git_refs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/refs{/sha}', 'trees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/trees{/sha}', 'statuses_url': 'https://api.github.com/repos/dolphin-emu/dolphin/statuses/{sha}', 'languages_url': 'https://api.github.com/repos/dolphin-emu/dolphin/languages', 'stargazers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/stargazers', 'contributors_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contributors', 'subscribers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscribers', 'subscription_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscription', 'commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/commits{/sha}', 'git_commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/commits{/sha}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/comments{/number}', 'issue_comment_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments{/number}', 'contents_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contents/{+path}', 'compare_url': 'https://api.github.com/repos/dolphin-emu/dolphin/compare/{base}...{head}', 'merges_url': 'https://api.github.com/repos/dolphin-emu/dolphin/merges', 'archive_url': 'https://api.github.com/repos/dolphin-emu/dolphin/{archive_format}{/ref}', 'downloads_url': 'https://api.github.com/repos/dolphin-emu/dolphin/downloads', 'issues_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues{/number}', 'pulls_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls{/number}', 'milestones_url': 'https://api.github.com/repos/dolphin-emu/dolphin/milestones{/number}', 'notifications_url': 'https://api.github.com/repos/dolphin-emu/dolphin/notifications{?since,all,participating}', 'labels_url': 'https://api.github.com/repos/dolphin-emu/dolphin/labels{/name}', 'releases_url': 'https://api.github.com/repos/dolphin-emu/dolphin/releases{/id}', 'deployments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/deployments', 'created_at': '2013-07-22T09:07:57Z', 'updated_at': '2026-02-01T19:55:00Z', 'pushed_at': '2026-01-31T23:02:09Z', 'git_url': 'git://github.com/dolphin-emu/dolphin.git', 'ssh_url': 'git@github.com:dolphin-emu/dolphin.git', 'clone_url': 'https://github.com/dolphin-emu/dolphin.git', 'svn_url': 'https://github.com/dolphin-emu/dolphin', 'homepage': 'https://dolphin-emu.org', 'size': 544012, 'stargazers_count': 14572, 'watchers_count': 14572, 'language': 'C++', 'has_issues': False, 'has_projects': False, 'has_downloads': True, 'has_wiki': True, 'has_pages': False, 'has_discussions': False, 'forks_count': 2967, 'mirror_url': None, 'archived': False, 'disabled': False, 'open_issues_count': 360, 'license': {'key': 'other', 'name': 'Other', 'spdx_id': 'NOASSERTION', 'url': None, 'node_id': 'MDc6TGljZW5zZTA='}, 'allow_forking': True, 'is_template': False, 'web_commit_signoff_required': False, 'topics': ['cpp', 'emulation', 'emulator', 'gamecube', 'wii'], 'visibility': 'public', 'forks': 2967, 'open_issues': 360, 'watchers': 14572, 'default_branch': 'master', 'custom_properties': {}}, 'organization': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'url': 'https://api.github.com/orgs/dolphin-emu', 'repos_url': 'https://api.github.com/orgs/dolphin-emu/repos', 'events_url': 'https://api.github.com/orgs/dolphin-emu/events', 'hooks_url': 'https://api.github.com/orgs/dolphin-emu/hooks', 'issues_url': 'https://api.github.com/orgs/dolphin-emu/issues', 'members_url': 'https://api.github.com/orgs/dolphin-emu/members{/member}', 'public_members_url': 'https://api.github.com/orgs/dolphin-emu/public_members{/member}', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'description': ''}, 'sender': {'login': 'JosJuice', 'id': 6716818, 'node_id': 'MDQ6VXNlcjY3MTY4MTg=', 'avatar_url': 'https://avatars.githubusercontent.com/u/6716818?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/JosJuice', 'html_url': 'https://github.com/JosJuice', 'followers_url': 'https://api.github.com/users/JosJuice/followers', 'following_url': 'https://api.github.com/users/JosJuice/following{/other_user}', 'gists_url': 'https://api.github.com/users/JosJuice/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/JosJuice/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/JosJuice/subscriptions', 'organizations_url': 'https://api.github.com/users/JosJuice/orgs', 'repos_url': 'https://api.github.com/users/JosJuice/repos', 'events_url': 'https://api.github.com/users/JosJuice/events{/privacy}', 'received_events_url': 'https://api.github.com/users/JosJuice/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'installation': {'id': 35437981, 'node_id': 'MDIzOkludGVncmF0aW9uSW5zdGFsbGF0aW9uMzU0Mzc5ODE='}}, 'type': 'gh_issue_comment'}
2026-02-01T19:53:41.054198 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'Gabriela-Orzechowska', 'action': 'created', 'id': 14315, 'title': 'RiivolutionParser: Fix XML Param Parsing', 'url': 'https://github.com/dolphin-emu/dolphin/pull/14315#issuecomment-3831864511', 'safe_author': False, 'body': 'Yeah it seems like std::pair constructor create a string copy which is pretty much immediately destroyed, causing string_view to point to wrong data.\r\n\r\nDid some more checks and found a cleaner solution, keeping string_view, which is just replacing\r\n```\r\n replacements.emplace_back(std::pair{"{$" + param.first + "}", param.second});\r\n ```\r\n with\r\n ```\r\n replacements.emplace_back("{$" + param.first + "}", param.second);\r\n ```', 'raw': {'action': 'created', 'issue': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14315', 'repository_url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'labels_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14315/labels{/name}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14315/comments', 'events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14315/events', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/14315', 'id': 3882697746, 'node_id': 'PR_kwDOALCn2M7Av0U8', 'number': 14315, 'title': 'RiivolutionParser: Fix XML Param Parsing', 'user': {'login': 'Gabriela-Orzechowska', 'id': 85825993, 'node_id': 'MDQ6VXNlcjg1ODI1OTkz', 'avatar_url': 'https://avatars.githubusercontent.com/u/85825993?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/Gabriela-Orzechowska', 'html_url': 'https://github.com/Gabriela-Orzechowska', 'followers_url': 'https://api.github.com/users/Gabriela-Orzechowska/followers', 'following_url': 'https://api.github.com/users/Gabriela-Orzechowska/following{/other_user}', 'gists_url': 'https://api.github.com/users/Gabriela-Orzechowska/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/Gabriela-Orzechowska/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/Gabriela-Orzechowska/subscriptions', 'organizations_url': 'https://api.github.com/users/Gabriela-Orzechowska/orgs', 'repos_url': 'https://api.github.com/users/Gabriela-Orzechowska/repos', 'events_url': 'https://api.github.com/users/Gabriela-Orzechowska/events{/privacy}', 'received_events_url': 'https://api.github.com/users/Gabriela-Orzechowska/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'labels': [], 'state': 'open', 'locked': False, 'assignee': None, 'assignees': [], 'milestone': None, 'comments': 4, 'created_at': '2026-02-01T18:58:25Z', 'updated_at': '2026-02-01T19:53:39Z', 'closed_at': None, 'author_association': 'NONE', 'type': None, 'active_lock_reason': None, 'draft': False, 'pull_request': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/14315', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/14315', 'diff_url': 'https://github.com/dolphin-emu/dolphin/pull/14315.diff', 'patch_url': 'https://github.com/dolphin-emu/dolphin/pull/14315.patch', 'merged_at': None}, 'body': 'This PR fixes an issue with wrong param parsing of Riivolution XML files. Usage of `std::string_view` creates corruption and invalid values of the parameters.\r\n<img width="357" height="158" alt="image" src="https://github.com/user-attachments/assets/45d48cae-e21e-4953-80b7-511d8f7ac9e3" />\r\nSimply replacing it with `std::string` fixes the issue.\r\n<img width="359" height="165" alt="image" src="https://github.com/user-attachments/assets/ede15ed1-deef-4af5-94a7-44ab988eaa0b" />\r\n', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14315/reactions', 'total_count': 1, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 0, 'confused': 0, 'heart': 1, 'rocket': 0, 'eyes': 0}, 'timeline_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14315/timeline', 'performed_via_github_app': None, 'state_reason': None}, 'comment': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments/3831864511', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/14315#issuecomment-3831864511', 'issue_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14315', 'id': 3831864511, 'node_id': 'IC_kwDOALCn2M7kZZy_', 'user': {'login': 'Gabriela-Orzechowska', 'id': 85825993, 'node_id': 'MDQ6VXNlcjg1ODI1OTkz', 'avatar_url': 'https://avatars.githubusercontent.com/u/85825993?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/Gabriela-Orzechowska', 'html_url': 'https://github.com/Gabriela-Orzechowska', 'followers_url': 'https://api.github.com/users/Gabriela-Orzechowska/followers', 'following_url': 'https://api.github.com/users/Gabriela-Orzechowska/following{/other_user}', 'gists_url': 'https://api.github.com/users/Gabriela-Orzechowska/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/Gabriela-Orzechowska/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/Gabriela-Orzechowska/subscriptions', 'organizations_url': 'https://api.github.com/users/Gabriela-Orzechowska/orgs', 'repos_url': 'https://api.github.com/users/Gabriela-Orzechowska/repos', 'events_url': 'https://api.github.com/users/Gabriela-Orzechowska/events{/privacy}', 'received_events_url': 'https://api.github.com/users/Gabriela-Orzechowska/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'created_at': '2026-02-01T19:53:38Z', 'updated_at': '2026-02-01T19:53:38Z', 'body': 'Yeah it seems like std::pair constructor create a string copy which is pretty much immediately destroyed, causing string_view to point to wrong data.\r\n\r\nDid some more checks and found a cleaner solution, keeping string_view, which is just replacing\r\n```\r\n replacements.emplace_back(std::pair{"{$" + param.first + "}", param.second});\r\n ```\r\n with\r\n ```\r\n replacements.emplace_back("{$" + param.first + "}", param.second);\r\n ```', 'author_association': 'NONE', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments/3831864511/reactions', 'total_count': 0, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 0, 'confused': 0, 'heart': 0, 'rocket': 0, 'eyes': 0}, 'performed_via_github_app': None}, 'repository': {'id': 11577304, 'node_id': 'MDEwOlJlcG9zaXRvcnkxMTU3NzMwNA==', 'name': 'dolphin', 'full_name': 'dolphin-emu/dolphin', 'private': False, 'owner': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/dolphin-emu', 'html_url': 'https://github.com/dolphin-emu', 'followers_url': 'https://api.github.com/users/dolphin-emu/followers', 'following_url': 'https://api.github.com/users/dolphin-emu/following{/other_user}', 'gists_url': 'https://api.github.com/users/dolphin-emu/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/dolphin-emu/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/dolphin-emu/subscriptions', 'organizations_url': 'https://api.github.com/users/dolphin-emu/orgs', 'repos_url': 'https://api.github.com/users/dolphin-emu/repos', 'events_url': 'https://api.github.com/users/dolphin-emu/events{/privacy}', 'received_events_url': 'https://api.github.com/users/dolphin-emu/received_events', 'type': 'Organization', 'user_view_type': 'public', 'site_admin': False}, 'html_url': 'https://github.com/dolphin-emu/dolphin', 'description': 'Dolphin is a GameCube / Wii emulator, allowing you to play games for these two platforms on PC with improvements.', 'fork': False, 'url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'forks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/forks', 'keys_url': 'https://api.github.com/repos/dolphin-emu/dolphin/keys{/key_id}', 'collaborators_url': 'https://api.github.com/repos/dolphin-emu/dolphin/collaborators{/collaborator}', 'teams_url': 'https://api.github.com/repos/dolphin-emu/dolphin/teams', 'hooks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/hooks', 'issue_events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/events{/number}', 'events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/events', 'assignees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/assignees{/user}', 'branches_url': 'https://api.github.com/repos/dolphin-emu/dolphin/branches{/branch}', 'tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/tags', 'blobs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/blobs{/sha}', 'git_tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/tags{/sha}', 'git_refs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/refs{/sha}', 'trees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/trees{/sha}', 'statuses_url': 'https://api.github.com/repos/dolphin-emu/dolphin/statuses/{sha}', 'languages_url': 'https://api.github.com/repos/dolphin-emu/dolphin/languages', 'stargazers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/stargazers', 'contributors_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contributors', 'subscribers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscribers', 'subscription_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscription', 'commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/commits{/sha}', 'git_commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/commits{/sha}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/comments{/number}', 'issue_comment_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments{/number}', 'contents_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contents/{+path}', 'compare_url': 'https://api.github.com/repos/dolphin-emu/dolphin/compare/{base}...{head}', 'merges_url': 'https://api.github.com/repos/dolphin-emu/dolphin/merges', 'archive_url': 'https://api.github.com/repos/dolphin-emu/dolphin/{archive_format}{/ref}', 'downloads_url': 'https://api.github.com/repos/dolphin-emu/dolphin/downloads', 'issues_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues{/number}', 'pulls_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls{/number}', 'milestones_url': 'https://api.github.com/repos/dolphin-emu/dolphin/milestones{/number}', 'notifications_url': 'https://api.github.com/repos/dolphin-emu/dolphin/notifications{?since,all,participating}', 'labels_url': 'https://api.github.com/repos/dolphin-emu/dolphin/labels{/name}', 'releases_url': 'https://api.github.com/repos/dolphin-emu/dolphin/releases{/id}', 'deployments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/deployments', 'created_at': '2013-07-22T09:07:57Z', 'updated_at': '2026-02-01T17:27:38Z', 'pushed_at': '2026-01-31T23:02:09Z', 'git_url': 'git://github.com/dolphin-emu/dolphin.git', 'ssh_url': 'git@github.com:dolphin-emu/dolphin.git', 'clone_url': 'https://github.com/dolphin-emu/dolphin.git', 'svn_url': 'https://github.com/dolphin-emu/dolphin', 'homepage': 'https://dolphin-emu.org', 'size': 544012, 'stargazers_count': 14571, 'watchers_count': 14571, 'language': 'C++', 'has_issues': False, 'has_projects': False, 'has_downloads': True, 'has_wiki': True, 'has_pages': False, 'has_discussions': False, 'forks_count': 2967, 'mirror_url': None, 'archived': False, 'disabled': False, 'open_issues_count': 360, 'license': {'key': 'other', 'name': 'Other', 'spdx_id': 'NOASSERTION', 'url': None, 'node_id': 'MDc6TGljZW5zZTA='}, 'allow_forking': True, 'is_template': False, 'web_commit_signoff_required': False, 'topics': ['cpp', 'emulation', 'emulator', 'gamecube', 'wii'], 'visibility': 'public', 'forks': 2967, 'open_issues': 360, 'watchers': 14571, 'default_branch': 'master', 'custom_properties': {}}, 'organization': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'url': 'https://api.github.com/orgs/dolphin-emu', 'repos_url': 'https://api.github.com/orgs/dolphin-emu/repos', 'events_url': 'https://api.github.com/orgs/dolphin-emu/events', 'hooks_url': 'https://api.github.com/orgs/dolphin-emu/hooks', 'issues_url': 'https://api.github.com/orgs/dolphin-emu/issues', 'members_url': 'https://api.github.com/orgs/dolphin-emu/members{/member}', 'public_members_url': 'https://api.github.com/orgs/dolphin-emu/public_members{/member}', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'description': ''}, 'sender': {'login': 'Gabriela-Orzechowska', 'id': 85825993, 'node_id': 'MDQ6VXNlcjg1ODI1OTkz', 'avatar_url': 'https://avatars.githubusercontent.com/u/85825993?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/Gabriela-Orzechowska', 'html_url': 'https://github.com/Gabriela-Orzechowska', 'followers_url': 'https://api.github.com/users/Gabriela-Orzechowska/followers', 'following_url': 'https://api.github.com/users/Gabriela-Orzechowska/following{/other_user}', 'gists_url': 'https://api.github.com/users/Gabriela-Orzechowska/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/Gabriela-Orzechowska/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/Gabriela-Orzechowska/subscriptions', 'organizations_url': 'https://api.github.com/users/Gabriela-Orzechowska/orgs', 'repos_url': 'https://api.github.com/users/Gabriela-Orzechowska/repos', 'events_url': 'https://api.github.com/users/Gabriela-Orzechowska/events{/privacy}', 'received_events_url': 'https://api.github.com/users/Gabriela-Orzechowska/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'installation': {'id': 35437981, 'node_id': 'MDIzOkludGVncmF0aW9uSW5zdGFsbGF0aW9uMzU0Mzc5ODE='}}, 'type': 'gh_issue_comment'}
Recent 'gh_pull_request' events
2026-02-03T23:32:40.513235 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'jordan-woyak', 'action': 'opened', 'id': 14318, 'title': 'Core: Make RunOnCPUThread always non-blocking.', 'base_ref_name': 'master', 'url': 'https://github.com/dolphin-emu/dolphin/pull/14318', 'head_ref_name': 'RunOnCPUThread-always-non-blocking', 'safe_author': True, 'base_sha': 'dd2b94cd4a71fb6abda72f58d27434ef2d8b0a5f', 'head_sha': '201aa659066b41a2cfe89b615213bcb093fcbe79', 'merged': False, 'requested_reviewers': [], 'type': 'gh_pull_request'}
2026-02-03T22:50:54.189859 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'jordan-woyak', 'action': 'closed', 'id': 13594, 'title': 'State: Simplify interthread communication and general cleanups.', 'base_ref_name': 'master', 'url': 'https://github.com/dolphin-emu/dolphin/pull/13594', 'head_ref_name': 'state-cleanups', 'safe_author': True, 'base_sha': '2a771937cf0a4a45cd466d3eea089a083f73f3c0', 'head_sha': '2322437f96bc53f1edcd9100daedb5b54c6e0fc4', 'merged': True, 'requested_reviewers': [], 'type': 'gh_pull_request'}
2026-02-03T22:35:19.459909 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'JosJuice', 'action': 'synchronize', 'id': 14218, 'title': 'Jit64: Make ABI_CallFunction generic', 'base_ref_name': 'master', 'url': 'https://github.com/dolphin-emu/dolphin/pull/14218', 'head_ref_name': 'jit64-abi-call-function-parameters', 'safe_author': True, 'base_sha': 'eac7b290423efea867c2c7f3f2b2792d05418bdd', 'head_sha': '72a6ad6fac50dccf5f37921497b92e75e84bbf40', 'merged': False, 'requested_reviewers': [], 'type': 'gh_pull_request'}
2026-02-03T22:06:12.686110 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'JosJuice', 'action': 'synchronize', 'id': 13768, 'title': 'Core: Create fastmem mappings for page address translation', 'base_ref_name': 'master', 'url': 'https://github.com/dolphin-emu/dolphin/pull/13768', 'head_ref_name': 'page-table-fastmem-2', 'safe_author': True, 'base_sha': 'eac7b290423efea867c2c7f3f2b2792d05418bdd', 'head_sha': '32856fd15531890d65379376c8cadfc3611b36cb', 'merged': False, 'requested_reviewers': [], 'type': 'gh_pull_request'}
2026-02-03T22:04:37.178271 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'JosJuice', 'action': 'synchronize', 'id': 13768, 'title': 'Core: Create fastmem mappings for page address translation', 'base_ref_name': 'master', 'url': 'https://github.com/dolphin-emu/dolphin/pull/13768', 'head_ref_name': 'page-table-fastmem-2', 'safe_author': True, 'base_sha': 'eac7b290423efea867c2c7f3f2b2792d05418bdd', 'head_sha': 'a7301d7d406bc5955303a13e61327c5b17645b7a', 'merged': False, 'requested_reviewers': [], 'type': 'gh_pull_request'}
2026-02-03T13:20:38.893374 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'bgermann', 'action': 'synchronize', 'id': 13997, 'title': 'Externals: Update minizip-ng to 4.1.0', 'base_ref_name': 'master', 'url': 'https://github.com/dolphin-emu/dolphin/pull/13997', 'head_ref_name': 'minizip-ng', 'safe_author': False, 'base_sha': 'eac7b290423efea867c2c7f3f2b2792d05418bdd', 'head_sha': 'df85a2c99d667c537a5a896cd81954fb63fab027', 'merged': False, 'requested_reviewers': [], 'type': 'gh_pull_request'}
2026-02-02T21:07:21.898023 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'JosJuice', 'action': 'opened', 'id': 14317, 'title': 'Optimize JitBaseBlockCache::ErasePhysicalRange', 'base_ref_name': 'master', 'url': 'https://github.com/dolphin-emu/dolphin/pull/14317', 'head_ref_name': 'jit-cache-macro-loop', 'safe_author': True, 'base_sha': 'c7de9162765452fc2787f3ba4f0092394067d603', 'head_sha': 'c8180071a552720e5436e908afc4eda7e87be08c', 'merged': False, 'requested_reviewers': [], 'type': 'gh_pull_request'}
2026-02-02T20:05:53.902333 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'JMC47', 'action': 'closed', 'id': 14316, 'title': 'ResourcePack: Fix loading resource packs', 'base_ref_name': 'master', 'url': 'https://github.com/dolphin-emu/dolphin/pull/14316', 'head_ref_name': 'resourcefix', 'safe_author': True, 'base_sha': '6711d77b9901ed0a4418203db49ed4d8576e6fe0', 'head_sha': 'd87e8ab7ff66f3efcedeb5a0ab869b537933aada', 'merged': True, 'requested_reviewers': [], 'type': 'gh_pull_request'}
2026-02-02T19:49:56.204599 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'jordan-woyak', 'action': 'review_requested', 'id': 14316, 'title': 'ResourcePack: Fix loading resource packs', 'base_ref_name': 'master', 'url': 'https://github.com/dolphin-emu/dolphin/pull/14316', 'head_ref_name': 'resourcefix', 'safe_author': True, 'base_sha': '6711d77b9901ed0a4418203db49ed4d8576e6fe0', 'head_sha': 'd87e8ab7ff66f3efcedeb5a0ab869b537933aada', 'merged': False, 'requested_reviewers': [{'login': 'iwubcode', 'id': 15224722, 'node_id': 'MDQ6VXNlcjE1MjI0NzIy', 'avatar_url': 'https://avatars.githubusercontent.com/u/15224722?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/iwubcode', 'html_url': 'https://github.com/iwubcode', 'followers_url': 'https://api.github.com/users/iwubcode/followers', 'following_url': 'https://api.github.com/users/iwubcode/following{/other_user}', 'gists_url': 'https://api.github.com/users/iwubcode/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/iwubcode/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/iwubcode/subscriptions', 'organizations_url': 'https://api.github.com/users/iwubcode/orgs', 'repos_url': 'https://api.github.com/users/iwubcode/repos', 'events_url': 'https://api.github.com/users/iwubcode/events{/privacy}', 'received_events_url': 'https://api.github.com/users/iwubcode/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}], 'type': 'gh_pull_request'}
2026-02-02T19:47:30.545050 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'jordan-woyak', 'action': 'synchronize', 'id': 13844, 'title': 'Core: Triforce support', 'base_ref_name': 'master', 'url': 'https://github.com/dolphin-emu/dolphin/pull/13844', 'head_ref_name': 'master', 'safe_author': True, 'base_sha': '6711d77b9901ed0a4418203db49ed4d8576e6fe0', 'head_sha': '66380234a68eb4d8106aa3004b0e531a06f5cd24', 'merged': False, 'requested_reviewers': [], 'type': 'gh_pull_request'}
2026-02-02T19:02:51.844832 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'JoshuaVandaele', 'action': 'opened', 'id': 14316, 'title': 'ResourcePack: Fix loading resource packs', 'base_ref_name': 'master', 'url': 'https://github.com/dolphin-emu/dolphin/pull/14316', 'head_ref_name': 'resourcefix', 'safe_author': True, 'base_sha': '6711d77b9901ed0a4418203db49ed4d8576e6fe0', 'head_sha': 'd87e8ab7ff66f3efcedeb5a0ab869b537933aada', 'merged': False, 'requested_reviewers': [], 'type': 'gh_pull_request'}
2026-02-02T13:19:20.069306 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'cscd98', 'action': 'synchronize', 'id': 14260, 'title': 'Externals: Update cpp-ipc to latest for mingw compatibility', 'base_ref_name': 'master', 'url': 'https://github.com/dolphin-emu/dolphin/pull/14260', 'head_ref_name': 'cpp-ipc-update', 'safe_author': False, 'base_sha': '6711d77b9901ed0a4418203db49ed4d8576e6fe0', 'head_sha': 'd75794b774d64f50d4bad334660d8f9068f241bd', 'merged': False, 'requested_reviewers': [], 'type': 'gh_pull_request'}
2026-02-02T08:11:45.865362 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'cscd98', 'action': 'synchronize', 'id': 14260, 'title': 'Externals: Update cpp-ipc to latest for mingw compatibility', 'base_ref_name': 'master', 'url': 'https://github.com/dolphin-emu/dolphin/pull/14260', 'head_ref_name': 'cpp-ipc-update', 'safe_author': False, 'base_sha': '6711d77b9901ed0a4418203db49ed4d8576e6fe0', 'head_sha': '512972c1cd4a10111412d619e464fc72a964101e', 'merged': False, 'requested_reviewers': [], 'type': 'gh_pull_request'}
2026-02-01T21:57:37.007539 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'jordan-woyak', 'action': 'synchronize', 'id': 14308, 'title': 'StringUtil: Make UTF16ToUTF8 and UTF8ToUTF16 use custom encoding/decoding implementation.', 'base_ref_name': 'master', 'url': 'https://github.com/dolphin-emu/dolphin/pull/14308', 'head_ref_name': 'character-encoding', 'safe_author': True, 'base_sha': '6711d77b9901ed0a4418203db49ed4d8576e6fe0', 'head_sha': 'f07ac93e55496dbafad9d3d2ad6d36c6f5c016a6', 'merged': False, 'requested_reviewers': [], 'type': 'gh_pull_request'}
2026-02-01T21:52:48.036883 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'jordan-woyak', 'action': 'edited', 'id': 14308, 'title': 'StringUtil: Make UTF16ToUTF8 and UTF8ToUTF16 use custom encoding/decoding implementation.', 'base_ref_name': 'master', 'url': 'https://github.com/dolphin-emu/dolphin/pull/14308', 'head_ref_name': 'character-encoding', 'safe_author': True, 'base_sha': '6711d77b9901ed0a4418203db49ed4d8576e6fe0', 'head_sha': '21cf436482e458914158b5ed05c1278fa3831c25', 'merged': False, 'requested_reviewers': [], 'type': 'gh_pull_request'}
2026-02-01T21:47:29.021412 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'jordan-woyak', 'action': 'synchronize', 'id': 14308, 'title': 'StringUtil: Make UTF16ToUTF8 and UTF8ToUTF16 use custom encoding/decoding implementation.', 'base_ref_name': 'master', 'url': 'https://github.com/dolphin-emu/dolphin/pull/14308', 'head_ref_name': 'character-encoding', 'safe_author': True, 'base_sha': '6711d77b9901ed0a4418203db49ed4d8576e6fe0', 'head_sha': '21cf436482e458914158b5ed05c1278fa3831c25', 'merged': False, 'requested_reviewers': [], 'type': 'gh_pull_request'}
2026-02-01T18:58:28.030639 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'Gabriela-Orzechowska', 'action': 'opened', 'id': 14315, 'title': 'RiivolutionParser: Fix XML Param Parsing', 'base_ref_name': 'master', 'url': 'https://github.com/dolphin-emu/dolphin/pull/14315', 'head_ref_name': 'fix_riivo_params', 'safe_author': False, 'base_sha': '6711d77b9901ed0a4418203db49ed4d8576e6fe0', 'head_sha': '20bc275e97f8facc7a6edc6355acad17ecfcee13', 'merged': False, 'requested_reviewers': [], 'type': 'gh_pull_request'}
2026-02-01T12:47:55.922868 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'jordan-woyak', 'action': 'synchronize', 'id': 14308, 'title': 'StringUtil: Make UTF16ToUTF8 and UTF8ToUTF16 use custom encoding/decoding implementation.', 'base_ref_name': 'master', 'url': 'https://github.com/dolphin-emu/dolphin/pull/14308', 'head_ref_name': 'character-encoding', 'safe_author': True, 'base_sha': '6711d77b9901ed0a4418203db49ed4d8576e6fe0', 'head_sha': 'dd6584210e8b2b33ce7ea43cdff5bf36dc1270a4', 'merged': False, 'requested_reviewers': [], 'type': 'gh_pull_request'}
2026-02-01T12:44:24.216882 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'JosJuice', 'action': 'synchronize', 'id': 13768, 'title': 'Core: Create fastmem mappings for page address translation', 'base_ref_name': 'master', 'url': 'https://github.com/dolphin-emu/dolphin/pull/13768', 'head_ref_name': 'page-table-fastmem-2', 'safe_author': True, 'base_sha': '6711d77b9901ed0a4418203db49ed4d8576e6fe0', 'head_sha': 'e88ee42d345e0b2a85cc6bd44c7258eb8be3c6b8', 'merged': False, 'requested_reviewers': [], 'type': 'gh_pull_request'}
2026-02-01T12:41:55.951659 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'jordan-woyak', 'action': 'synchronize', 'id': 14308, 'title': 'StringUtil: Make UTF16ToUTF8 and UTF8ToUTF16 use custom encoding/decoding implementation.', 'base_ref_name': 'master', 'url': 'https://github.com/dolphin-emu/dolphin/pull/14308', 'head_ref_name': 'character-encoding', 'safe_author': True, 'base_sha': '6711d77b9901ed0a4418203db49ed4d8576e6fe0', 'head_sha': 'cf8dd36ee0593e548c307fae826a44858425db82', 'merged': False, 'requested_reviewers': [], 'type': 'gh_pull_request'}
2026-02-01T05:44:40.208060 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'JMC47', 'action': 'opened', 'id': 14314, 'title': 'GameINI: Force Safe Texture Cache to Samurai Shodown Anthology', 'base_ref_name': 'master', 'url': 'https://github.com/dolphin-emu/dolphin/pull/14314', 'head_ref_name': 'shodown', 'safe_author': True, 'base_sha': '6711d77b9901ed0a4418203db49ed4d8576e6fe0', 'head_sha': 'ac19ef5452a1abcd7bcfc9d166fcdbe28c714ba1', 'merged': False, 'requested_reviewers': [], 'type': 'gh_pull_request'}
2026-01-31T23:30:11.213531 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'jordan-woyak', 'action': 'synchronize', 'id': 14313, 'title': 'Core/Common: Remove UTF8ToTStr and TStrToUTF8. Replace TCHAR with WCHAR.', 'base_ref_name': 'master', 'url': 'https://github.com/dolphin-emu/dolphin/pull/14313', 'head_ref_name': 'no-tchar', 'safe_author': True, 'base_sha': '6711d77b9901ed0a4418203db49ed4d8576e6fe0', 'head_sha': 'e95ead2b474f4341aebbe1024f1d73ac9a28fce0', 'merged': False, 'requested_reviewers': [], 'type': 'gh_pull_request'}
2026-01-31T23:26:49.502043 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'jordan-woyak', 'action': 'opened', 'id': 14313, 'title': 'Core/Common: Remove UTF8ToTStr and TStrToUTF8. Replace TCHAR with WCHAR.', 'base_ref_name': 'master', 'url': 'https://github.com/dolphin-emu/dolphin/pull/14313', 'head_ref_name': 'no-tchar', 'safe_author': True, 'base_sha': '6711d77b9901ed0a4418203db49ed4d8576e6fe0', 'head_sha': 'c3c998305790626281c2268f1c035cccfc364a6e', 'merged': False, 'requested_reviewers': [], 'type': 'gh_pull_request'}
2026-01-31T23:02:12.501094 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'Dentomologist', 'action': 'closed', 'id': 14204, 'title': 'Update Comments Based On Hardware Test', 'base_ref_name': 'master', 'url': 'https://github.com/dolphin-emu/dolphin/pull/14204', 'head_ref_name': 'update-comment', 'safe_author': True, 'base_sha': '75bc9a474edab83c8751b73939a7136990c3c118', 'head_sha': 'f31f11c0a4b37decebd0e0a349bdc33691fdd657', 'merged': True, 'requested_reviewers': [], 'type': 'gh_pull_request'}
2026-01-31T22:45:21.262733 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'jordan-woyak', 'action': 'closed', 'id': 14312, 'title': 'WGL: Correctly load wglDestroyPbufferARB extension', 'base_ref_name': 'master', 'url': 'https://github.com/dolphin-emu/dolphin/pull/14312', 'head_ref_name': 'wglDestroyPbufferARB', 'safe_author': True, 'base_sha': 'b1020186519d690a9b64a215f00da7453ab2803b', 'head_sha': 'e6bc8fb342169d9936645fe1fdebe296d92bc44b', 'merged': True, 'requested_reviewers': [], 'type': 'gh_pull_request'}
Recent 'gh_pull_request_comment' events
2026-02-03T04:58:23.579054 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'jordan-woyak', 'action': 'created', 'id': 14218, 'hash': '41c26ea85f34764a41e9f4e79211cf9fe6bad48c', 'url': 'https://github.com/dolphin-emu/dolphin/pull/14218#discussion_r2757167327', 'is_part_of_review': True, 'type': 'gh_pull_request_comment'}
2026-02-03T04:24:35.371305 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'jordan-woyak', 'action': 'created', 'id': 14140, 'hash': 'd58818f317de389fb1ddded9412a1d9b2a29462e', 'url': 'https://github.com/dolphin-emu/dolphin/pull/14140#discussion_r2757133526', 'is_part_of_review': True, 'type': 'gh_pull_request_comment'}
2026-02-02T17:28:07.901821 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'JosJuice', 'action': 'created', 'id': 13844, 'hash': '5bf8fe93286ebe4f08fb469a44f259a8397c89e0', 'url': 'https://github.com/dolphin-emu/dolphin/pull/13844#discussion_r2755469094', 'is_part_of_review': False, 'type': 'gh_pull_request_comment'}
2026-02-01T22:54:22.629706 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'sepalani', 'action': 'created', 'id': 13844, 'hash': '5bf8fe93286ebe4f08fb469a44f259a8397c89e0', 'url': 'https://github.com/dolphin-emu/dolphin/pull/13844#discussion_r2752074638', 'is_part_of_review': False, 'type': 'gh_pull_request_comment'}
2026-02-01T19:49:22.122892 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'sepalani', 'action': 'created', 'id': 13844, 'hash': '5bf8fe93286ebe4f08fb469a44f259a8397c89e0', 'url': 'https://github.com/dolphin-emu/dolphin/pull/13844#discussion_r2751867162', 'is_part_of_review': True, 'type': 'gh_pull_request_comment'}
2026-02-01T15:40:29.400981 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'JosJuice', 'action': 'created', 'id': 13844, 'hash': '5bf8fe93286ebe4f08fb469a44f259a8397c89e0', 'url': 'https://github.com/dolphin-emu/dolphin/pull/13844#discussion_r2751386784', 'is_part_of_review': True, 'type': 'gh_pull_request_comment'}
2026-02-01T15:40:29.356305 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'JosJuice', 'action': 'created', 'id': 13844, 'hash': '5bf8fe93286ebe4f08fb469a44f259a8397c89e0', 'url': 'https://github.com/dolphin-emu/dolphin/pull/13844#discussion_r2751395550', 'is_part_of_review': True, 'type': 'gh_pull_request_comment'}
2026-02-01T15:40:29.269440 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'JosJuice', 'action': 'created', 'id': 13844, 'hash': '5bf8fe93286ebe4f08fb469a44f259a8397c89e0', 'url': 'https://github.com/dolphin-emu/dolphin/pull/13844#discussion_r2751374597', 'is_part_of_review': True, 'type': 'gh_pull_request_comment'}
2026-02-01T15:40:29.228139 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'JosJuice', 'action': 'created', 'id': 13844, 'hash': '5bf8fe93286ebe4f08fb469a44f259a8397c89e0', 'url': 'https://github.com/dolphin-emu/dolphin/pull/13844#discussion_r2751296052', 'is_part_of_review': True, 'type': 'gh_pull_request_comment'}
2026-02-01T15:40:29.168946 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'JosJuice', 'action': 'created', 'id': 13844, 'hash': '5bf8fe93286ebe4f08fb469a44f259a8397c89e0', 'url': 'https://github.com/dolphin-emu/dolphin/pull/13844#discussion_r2751294423', 'is_part_of_review': True, 'type': 'gh_pull_request_comment'}
2026-02-01T15:40:29.123212 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'JosJuice', 'action': 'created', 'id': 13844, 'hash': '5bf8fe93286ebe4f08fb469a44f259a8397c89e0', 'url': 'https://github.com/dolphin-emu/dolphin/pull/13844#discussion_r2751418497', 'is_part_of_review': True, 'type': 'gh_pull_request_comment'}
2026-02-01T15:40:29.099522 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'JosJuice', 'action': 'created', 'id': 13844, 'hash': '5bf8fe93286ebe4f08fb469a44f259a8397c89e0', 'url': 'https://github.com/dolphin-emu/dolphin/pull/13844#discussion_r2751389783', 'is_part_of_review': True, 'type': 'gh_pull_request_comment'}
2026-02-01T11:45:30.419065 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'JosJuice', 'action': 'created', 'id': 13768, 'hash': '3fdce1e8bd9901e5d852dea23a271ca48434ac37', 'url': 'https://github.com/dolphin-emu/dolphin/pull/13768#discussion_r2751118036', 'is_part_of_review': False, 'type': 'gh_pull_request_comment'}
2026-01-31T19:02:47.389693 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'JosJuice', 'action': 'created', 'id': 13844, 'hash': '510fec88be973bc870021f2d0347593cdc285864', 'url': 'https://github.com/dolphin-emu/dolphin/pull/13844#discussion_r2749868593', 'is_part_of_review': False, 'type': 'gh_pull_request_comment'}
2026-01-31T19:01:53.469382 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'JosJuice', 'action': 'created', 'id': 13844, 'hash': '510fec88be973bc870021f2d0347593cdc285864', 'url': 'https://github.com/dolphin-emu/dolphin/pull/13844#discussion_r2749867718', 'is_part_of_review': False, 'type': 'gh_pull_request_comment'}
2026-01-31T18:51:16.136805 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'sepalani', 'action': 'created', 'id': 13844, 'hash': '510fec88be973bc870021f2d0347593cdc285864', 'url': 'https://github.com/dolphin-emu/dolphin/pull/13844#discussion_r2749856368', 'is_part_of_review': False, 'type': 'gh_pull_request_comment'}
2026-01-31T18:42:27.964070 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'sepalani', 'action': 'edited', 'id': 13844, 'hash': '510fec88be973bc870021f2d0347593cdc285864', 'url': 'https://github.com/dolphin-emu/dolphin/pull/13844#discussion_r2749846854', 'is_part_of_review': False, 'type': 'gh_pull_request_comment'}
2026-01-31T18:40:52.729042 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'sepalani', 'action': 'created', 'id': 13844, 'hash': '510fec88be973bc870021f2d0347593cdc285864', 'url': 'https://github.com/dolphin-emu/dolphin/pull/13844#discussion_r2749846854', 'is_part_of_review': False, 'type': 'gh_pull_request_comment'}
2026-01-31T13:41:21.008211 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'jordan-woyak', 'action': 'edited', 'id': 14308, 'hash': 'b89301e80b16df4559224d21f11bcbdf314140b1', 'url': 'https://github.com/dolphin-emu/dolphin/pull/14308#discussion_r2749544190', 'is_part_of_review': False, 'type': 'gh_pull_request_comment'}
2026-01-31T13:33:17.726485 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'JosJuice', 'action': 'created', 'id': 13844, 'hash': '510fec88be973bc870021f2d0347593cdc285864', 'url': 'https://github.com/dolphin-emu/dolphin/pull/13844#discussion_r2749561611', 'is_part_of_review': True, 'type': 'gh_pull_request_comment'}
2026-01-31T13:33:17.091883 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'JosJuice', 'action': 'created', 'id': 13844, 'hash': '510fec88be973bc870021f2d0347593cdc285864', 'url': 'https://github.com/dolphin-emu/dolphin/pull/13844#discussion_r2749561110', 'is_part_of_review': True, 'type': 'gh_pull_request_comment'}
2026-01-31T13:33:15.688585 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'JosJuice', 'action': 'created', 'id': 13844, 'hash': '510fec88be973bc870021f2d0347593cdc285864', 'url': 'https://github.com/dolphin-emu/dolphin/pull/13844#discussion_r2749563661', 'is_part_of_review': True, 'type': 'gh_pull_request_comment'}
2026-01-31T13:33:15.652154 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'JosJuice', 'action': 'created', 'id': 13844, 'hash': '510fec88be973bc870021f2d0347593cdc285864', 'url': 'https://github.com/dolphin-emu/dolphin/pull/13844#discussion_r2749564419', 'is_part_of_review': True, 'type': 'gh_pull_request_comment'}
2026-01-31T13:33:15.509330 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'JosJuice', 'action': 'created', 'id': 13844, 'hash': '510fec88be973bc870021f2d0347593cdc285864', 'url': 'https://github.com/dolphin-emu/dolphin/pull/13844#discussion_r2749562072', 'is_part_of_review': True, 'type': 'gh_pull_request_comment'}
2026-01-31T13:33:15.422596 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'JosJuice', 'action': 'created', 'id': 13844, 'hash': '510fec88be973bc870021f2d0347593cdc285864', 'url': 'https://github.com/dolphin-emu/dolphin/pull/13844#discussion_r2749557847', 'is_part_of_review': True, 'type': 'gh_pull_request_comment'}
Recent 'gh_pull_request_review' events
2026-02-03T22:48:25.697637 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'jordan-woyak', 'action': 'submitted', 'pr_id': 13768, 'pr_title': 'Core: Create fastmem mappings for page address translation', 'state': 'approved', 'url': 'https://github.com/dolphin-emu/dolphin/pull/13768#pullrequestreview-3748007210', 'comments': [], 'type': 'gh_pull_request_review'}
2026-02-03T04:59:38.731134 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'jordan-woyak', 'action': 'submitted', 'pr_id': 14218, 'pr_title': 'Jit64: Make ABI_CallFunction generic', 'state': 'commented', 'url': 'https://github.com/dolphin-emu/dolphin/pull/14218#pullrequestreview-3742915580', 'comments': [{'id': 2757167327, 'node_id': 'PRRC_kwDOALCn2M6kVwjf', 'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2757167327', 'pull_request_review_id': 3742915580, 'diff_hunk': '@@ -377,6 +423,11 @@ class XEmitter\n void ABI_CalculateFrameSize(BitSet32 mask, size_t rsp_alignment, size_t needed_frame_size,\n size_t* shadowp, size_t* subtractionp, size_t* xmm_offsetp);\n \n+ // This function solves the "parallel moves" problem that\'s common in compilers.\n+ // The arguments are mutated!\n+ void ParallelMoves(RegisterMove* begin, RegisterMove* end, std::array<u8, 16>* source_reg_uses,\n+ std::array<u8, 16>* destination_reg_uses);', 'path': 'Source/Core/Common/x64Emitter.h', 'position': 84, 'original_position': 84, 'commit_id': '41c26ea85f34764a41e9f4e79211cf9fe6bad48c', 'user': {'login': 'jordan-woyak', 'id': 1768214, 'node_id': 'MDQ6VXNlcjE3NjgyMTQ=', 'avatar_url': 'https://avatars.githubusercontent.com/u/1768214?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/jordan-woyak', 'html_url': 'https://github.com/jordan-woyak', 'followers_url': 'https://api.github.com/users/jordan-woyak/followers', 'following_url': 'https://api.github.com/users/jordan-woyak/following{/other_user}', 'gists_url': 'https://api.github.com/users/jordan-woyak/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/jordan-woyak/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/jordan-woyak/subscriptions', 'organizations_url': 'https://api.github.com/users/jordan-woyak/orgs', 'repos_url': 'https://api.github.com/users/jordan-woyak/repos', 'events_url': 'https://api.github.com/users/jordan-woyak/events{/privacy}', 'received_events_url': 'https://api.github.com/users/jordan-woyak/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'body': '```suggestion\n void ParallelMoves(std::span<RegisterMove> pending_moves, std::array<u8, 16>* source_reg_uses,\n std::array<u8, 16>* destination_reg_uses);\n```\nI suppose a `span` would be cleaner than two pointers.\n\nThen the call site becomes just:\n```cpp\nParallelMoves(pending_moves, &source_reg_uses, &destination_reg_uses);\n```', 'created_at': '2026-02-03T04:42:30Z', 'updated_at': '2026-02-03T04:55:05Z', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/14218#discussion_r2757167327', 'pull_request_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/14218', 'author_association': 'MEMBER', '_links': {'self': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2757167327'}, 'html': {'href': 'https://github.com/dolphin-emu/dolphin/pull/14218#discussion_r2757167327'}, 'pull_request': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/14218'}}, 'original_commit_id': '41c26ea85f34764a41e9f4e79211cf9fe6bad48c', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2757167327/reactions', 'total_count': 0, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 0, 'confused': 0, 'heart': 0, 'rocket': 0, 'eyes': 0}}], 'type': 'gh_pull_request_review'}
2026-02-03T04:07:47.475546 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'jordan-woyak', 'action': 'submitted', 'pr_id': 13768, 'pr_title': 'Core: Create fastmem mappings for page address translation', 'state': 'commented', 'url': 'https://github.com/dolphin-emu/dolphin/pull/13768#pullrequestreview-3742805702', 'comments': [{'id': 2757073772, 'node_id': 'PRRC_kwDOALCn2M6kVZts', 'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2757073772', 'pull_request_review_id': 3742805702, 'diff_hunk': '@@ -305,6 +319,183 @@ void MemoryManager::UpdateLogicalMemory(const PowerPC::BatTable& dbat_table)\n }\n }\n \n+void MemoryManager::AddPageTableMapping(u32 logical_address, u32 translated_address, bool writeable)\n+{\n+ if (!m_is_fastmem_arena_initialized)\n+ return;\n+\n+ switch (m_host_page_type)\n+ {\n+ case HostPageType::SmallPages:\n+ return AddHostPageTableMapping(logical_address, translated_address, writeable,\n+ PowerPC::HW_PAGE_SIZE);\n+ case HostPageType::LargePages:\n+ return TryAddLargePageTableMapping(logical_address, translated_address, writeable);\n+ default:\n+ return;\n+ }\n+}\n+\n+void MemoryManager::TryAddLargePageTableMapping(u32 logical_address, u32 translated_address,\n+ bool writeable)\n+{\n+ const bool add_readable =\n+ TryAddLargePageTableMapping(logical_address, translated_address, m_large_readable_pages);\n+\n+ const bool add_writeable =\n+ writeable &&\n+ TryAddLargePageTableMapping(logical_address, translated_address, m_large_writeable_pages);\n+\n+ if (add_readable || add_writeable)\n+ {\n+ AddHostPageTableMapping(logical_address & ~(m_page_size - 1),\n+ translated_address & ~(m_page_size - 1), add_writeable, m_page_size);\n+ }\n+}\n+\n+bool MemoryManager::TryAddLargePageTableMapping(u32 logical_address, u32 translated_address,\n+ std::map<u32, std::vector<u32>>& map)\n+{\n+ std::vector<u32>& entries = map[logical_address & ~(m_page_size - 1)];\n+\n+ if (entries.empty())\n+ entries = std::vector<u32>(m_guest_pages_per_host_page, INVALID_MAPPING);\n+\n+ entries[(logical_address & (m_page_size - 1)) / PowerPC::HW_PAGE_SIZE] = translated_address;\n+\n+ return CanCreateHostMappingForGuestPages(entries);\n+}\n+\n+bool MemoryManager::CanCreateHostMappingForGuestPages(const std::vector<u32>& entries) const\n+{\n+ const u32 translated_address = entries[0];\n+ if ((translated_address & (m_page_size - 1)) != 0)\n+ return false;\n+\n+ for (size_t i = 1; i < m_guest_pages_per_host_page; ++i)\n+ {\n+ if (entries[i] != translated_address + i * PowerPC::HW_PAGE_SIZE)\n+ return false;\n+ }\n+\n+ return true;\n+}\n+\n+void MemoryManager::AddHostPageTableMapping(u32 logical_address, u32 translated_address,\n+ bool writeable, u32 logical_size)\n+{\n+ for (const auto& physical_region : m_physical_regions)\n+ {\n+ if (!physical_region.active)\n+ continue;\n+\n+ u32 mapping_address = physical_region.physical_address;', 'path': 'Source/Core/Core/HW/Memmap.cpp', 'position': 212, 'original_position': 212, 'commit_id': 'e88ee42d345e0b2a85cc6bd44c7258eb8be3c6b8', 'user': {'login': 'jordan-woyak', 'id': 1768214, 'node_id': 'MDQ6VXNlcjE3NjgyMTQ=', 'avatar_url': 'https://avatars.githubusercontent.com/u/1768214?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/jordan-woyak', 'html_url': 'https://github.com/jordan-woyak', 'followers_url': 'https://api.github.com/users/jordan-woyak/followers', 'following_url': 'https://api.github.com/users/jordan-woyak/following{/other_user}', 'gists_url': 'https://api.github.com/users/jordan-woyak/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/jordan-woyak/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/jordan-woyak/subscriptions', 'organizations_url': 'https://api.github.com/users/jordan-woyak/orgs', 'repos_url': 'https://api.github.com/users/jordan-woyak/repos', 'events_url': 'https://api.github.com/users/jordan-woyak/events{/privacy}', 'received_events_url': 'https://api.github.com/users/jordan-woyak/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'body': '```suggestion\n const u32 mapping_address = physical_region.physical_address;\n```\nIt looks like every variable in this function can be `const`.', 'created_at': '2026-02-03T03:59:35Z', 'updated_at': '2026-02-03T04:07:45Z', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/13768#discussion_r2757073772', 'pull_request_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13768', 'author_association': 'MEMBER', '_links': {'self': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2757073772'}, 'html': {'href': 'https://github.com/dolphin-emu/dolphin/pull/13768#discussion_r2757073772'}, 'pull_request': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13768'}}, 'original_commit_id': 'e88ee42d345e0b2a85cc6bd44c7258eb8be3c6b8', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2757073772/reactions', 'total_count': 0, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 0, 'confused': 0, 'heart': 0, 'rocket': 0, 'eyes': 0}}, {'id': 2757080594, 'node_id': 'PRRC_kwDOALCn2M6kVbYS', 'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2757080594', 'pull_request_review_id': 3742805702, 'diff_hunk': '@@ -127,6 +127,22 @@ inline u64 swap64(const u8* data)\n return swap64(value);\n }\n \n+inline void swap16(u8* data, u16 value)', 'path': 'Source/Core/Common/Swap.h', 'position': 4, 'original_position': 4, 'commit_id': 'e88ee42d345e0b2a85cc6bd44c7258eb8be3c6b8', 'user': {'login': 'jordan-woyak', 'id': 1768214, 'node_id': 'MDQ6VXNlcjE3NjgyMTQ=', 'avatar_url': 'https://avatars.githubusercontent.com/u/1768214?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/jordan-woyak', 'html_url': 'https://github.com/jordan-woyak', 'followers_url': 'https://api.github.com/users/jordan-woyak/followers', 'following_url': 'https://api.github.com/users/jordan-woyak/following{/other_user}', 'gists_url': 'https://api.github.com/users/jordan-woyak/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/jordan-woyak/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/jordan-woyak/subscriptions', 'organizations_url': 'https://api.github.com/users/jordan-woyak/orgs', 'repos_url': 'https://api.github.com/users/jordan-woyak/repos', 'events_url': 'https://api.github.com/users/jordan-woyak/events{/privacy}', 'received_events_url': 'https://api.github.com/users/jordan-woyak/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'body': 'I think the unique behavior of these new swap functions warrants them having different names instead of creating more overloads.\n\n`WriteSwapped16`, maybe?', 'created_at': '2026-02-03T04:03:04Z', 'updated_at': '2026-02-03T04:07:45Z', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/13768#discussion_r2757080594', 'pull_request_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13768', 'author_association': 'MEMBER', '_links': {'self': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2757080594'}, 'html': {'href': 'https://github.com/dolphin-emu/dolphin/pull/13768#discussion_r2757080594'}, 'pull_request': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13768'}}, 'original_commit_id': 'e88ee42d345e0b2a85cc6bd44c7258eb8be3c6b8', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2757080594/reactions', 'total_count': 0, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 0, 'confused': 0, 'heart': 0, 'rocket': 0, 'eyes': 0}}], 'type': 'gh_pull_request_review'}
2026-02-02T20:03:29.296321 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'iwubcode', 'action': 'submitted', 'pr_id': 14316, 'pr_title': 'ResourcePack: Fix loading resource packs', 'state': 'approved', 'url': 'https://github.com/dolphin-emu/dolphin/pull/14316#pullrequestreview-3741520820', 'comments': [], 'type': 'gh_pull_request_review'}
2026-02-02T19:49:46.913162 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'jordan-woyak', 'action': 'submitted', 'pr_id': 14316, 'pr_title': 'ResourcePack: Fix loading resource packs', 'state': 'approved', 'url': 'https://github.com/dolphin-emu/dolphin/pull/14316#pullrequestreview-3741478957', 'comments': [], 'type': 'gh_pull_request_review'}
2026-02-02T17:28:07.677512 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'JosJuice', 'action': 'submitted', 'pr_id': 13844, 'pr_title': 'Core: Triforce support', 'state': 'commented', 'url': 'https://github.com/dolphin-emu/dolphin/pull/13844#pullrequestreview-3740914372', 'comments': [{'id': 2755469094, 'node_id': 'PRRC_kwDOALCn2M6kPR8m', 'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2755469094', 'pull_request_review_id': 3740914372, 'diff_hunk': '@@ -0,0 +1,2708 @@\n+// Copyright 2025 Dolphin Emulator Project\n+// SPDX-License-Identifier: GPL-2.0-or-later\n+\n+#include "Core/HW/SI/SI_DeviceAMBaseboard.h"\n+\n+#include <algorithm>\n+#include <numeric>\n+#include <string>\n+\n+#include <fmt/format.h>\n+\n+#include "Common/Buffer.h"\n+#include "Common/CommonTypes.h"\n+#include "Common/FileUtil.h"\n+#include "Common/IOFile.h"\n+#include "Common/Logging/Log.h"\n+#include "Common/MsgHandler.h"\n+#include "Common/Swap.h"\n+\n+#include "Core/Boot/Boot.h"\n+#include "Core/BootManager.h"\n+#include "Core/Config/MainSettings.h"\n+#include "Core/ConfigManager.h"\n+#include "Core/Core.h"\n+#include "Core/CoreTiming.h"\n+#include "Core/HW/DVD/AMMediaboard.h"\n+#include "Core/HW/DVD/DVDInterface.h"\n+#include "Core/HW/EXI/EXI.h"\n+#include "Core/HW/GCPad.h"\n+#include "Core/HW/MMIO.h"\n+#include "Core/HW/Memmap.h"\n+#include "Core/HW/ProcessorInterface.h"\n+#include "Core/HW/SI/SI.h"\n+#include "Core/HW/SI/SI_Device.h"\n+#include "Core/HW/SI/SI_DeviceGCController.h"\n+#include "Core/HW/SystemTimers.h"\n+#include "Core/Movie.h"\n+#include "Core/NetPlayProto.h"\n+#include "Core/System.h"\n+\n+#include "InputCommon/GCPadStatus.h"\n+\n+namespace SerialInterface\n+{\n+void JVSIOMessage::Start(int node)\n+{\n+ m_last_start = m_pointer;\n+ const u8 header[3] = {0xE0, (u8)node, 0};\n+ m_checksum = 0;\n+ AddData(header, 3, 1);\n+}\n+\n+void JVSIOMessage::AddData(const u8* dst, size_t len, int sync = 0)\n+{\n+ if (m_pointer + len >= sizeof(m_message))\n+ {\n+ PanicAlertFmt("JVSIOMessage overrun!");\n+ return;\n+ }\n+\n+ while (len--)\n+ {\n+ const u8 c = *dst++;\n+ if (!sync && ((c == 0xE0) || (c == 0xD0)))\n+ {\n+ if (m_pointer + 2 > sizeof(m_message))\n+ {\n+ PanicAlertFmt("JVSIOMessage overrun!");\n+ break;\n+ }\n+ m_message[m_pointer++] = 0xD0;\n+ m_message[m_pointer++] = c - 1;\n+ }\n+ else\n+ {\n+ if (m_pointer >= sizeof(m_message))\n+ {\n+ PanicAlertFmt("JVSIOMessage overrun!");\n+ break;\n+ }\n+ m_message[m_pointer++] = c;\n+ }\n+\n+ if (!sync)\n+ m_checksum += c;\n+ sync = 0;\n+ }\n+}\n+\n+void JVSIOMessage::AddData(const void* data, size_t len)\n+{\n+ AddData(static_cast<const u8*>(data), len);\n+}\n+\n+void JVSIOMessage::AddData(const char* data)\n+{\n+ AddData(data, strlen(data));\n+}\n+\n+void JVSIOMessage::AddData(u32 n)\n+{\n+ const u8 cs = n;\n+ AddData(&cs, 1);\n+}\n+\n+void JVSIOMessage::End()\n+{\n+ const u32 len = m_pointer - m_last_start;\n+ if (m_last_start + 2 < sizeof(m_message) && len >= 3)\n+ {\n+ m_message[m_last_start + 2] = len - 2; // assuming len <0xD0\n+ AddData(m_checksum + len - 2);\n+ }\n+ else\n+ {\n+ PanicAlertFmt("JVSIOMessage: Not enough space for checksum!");\n+ }\n+}\n+\n+static constexpr u8 CheckSumXOR(const u8* data, u32 length)\n+{\n+ return std::accumulate(data, data + length, u8{}, std::bit_xor());\n+}\n+\n+static constexpr char s_cdr_program_version[] = {" Version 1.22,2003/09/19,171-8213B"};\n+static constexpr char s_cdr_boot_version[] = {" Version 1.04,2003/06/17,171-8213B"};\n+static constexpr u8 s_cdr_card_data[] = {\n+ 0x00, 0x6E, 0x00, 0x00, 0x01, 0x00, 0x00, 0x06, 0x00, 0x00, 0x07, 0x00, 0x00, 0x0B,\n+ 0x00, 0x00, 0x0E, 0x00, 0x00, 0x10, 0x00, 0x00, 0x17, 0x00, 0x00, 0x19, 0x00, 0x00,\n+ 0x1A, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x20, 0x00,\n+ 0x00, 0x22, 0x00, 0x00, 0x23, 0x00, 0x00, 0x24, 0x00, 0x00, 0x27, 0x00, 0x00, 0x28,\n+ 0x00, 0x00, 0x2C, 0x00, 0x00, 0x2F, 0x00, 0x00, 0x34, 0x00, 0x00, 0x35, 0x00, 0x00,\n+ 0x37, 0x00, 0x00, 0x38, 0x00, 0x00, 0x39, 0x00, 0x00, 0x3D, 0x00};\n+\n+const constexpr u8 s_region_flags[] = "\\x00\\x00\\x30\\x00"\n+ // "\\x01\\xfe\\x00\\x00" // JAPAN\n+ "\\x02\\xfd\\x00\\x00" // USA\n+ //"\\x03\\xfc\\x00\\x00" // export\n+ "\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff";\n+// AM-Baseboard device on SI\n+CSIDevice_AMBaseboard::CSIDevice_AMBaseboard(Core::System& system, SIDevices device,\n+ int device_number)\n+ : ISIDevice(system, device, device_number)\n+{\n+ // Card ID\n+ m_ic_card_data[0x20] = 0x95;\n+ m_ic_card_data[0x21] = 0x71;\n+\n+ if (AMMediaboard::GetGameType() == KeyOfAvalon)\n+ {\n+ m_ic_card_data[0x22] = 0x26;\n+ m_ic_card_data[0x23] = 0x40;\n+ }\n+ else if (AMMediaboard::GetGameType() == VirtuaStriker4)\n+ {\n+ m_ic_card_data[0x22] = 0x44;\n+ m_ic_card_data[0x23] = 0x00;\n+ }\n+\n+ // Use count\n+ m_ic_card_data[0x28] = 0xFF;\n+ m_ic_card_data[0x29] = 0xFF;\n+}\n+\n+constexpr u32 SI_XFER_LENGTH_MASK = 0x7f;\n+\n+// Translate [0,1,2,...,126,127] to [128,1,2,...,126,127]\n+constexpr s32 ConvertSILengthField(u32 field)\n+{\n+ return ((field - 1) & SI_XFER_LENGTH_MASK) + 1;\n+}\n+\n+void CSIDevice_AMBaseboard::ICCardSendReply(ICCommand* iccommand, u8* buffer, u32* length)\n+{\n+ iccommand->status = Common::swap16(iccommand->status);\n+\n+ const auto iccommand_data = reinterpret_cast<const u8*>(iccommand);\n+ const u8 crc = CheckSumXOR(iccommand_data + 2, iccommand->pktlen - 1);\n+\n+ for (u32 i = 0; i < iccommand->pktlen + 1; ++i)\n+ {\n+ buffer[(*length)++] = iccommand_data[i];\n+ }\n+\n+ buffer[(*length)++] = crc;\n+}\n+\n+void CSIDevice_AMBaseboard::SwapBuffers(u8* buffer, u32* buffer_length)\n+{\n+ memcpy(m_last[1], buffer, 0x80); // Save current buffer\n+ memcpy(buffer, m_last[0], 0x80); // Load previous buffer\n+ memcpy(m_last[0], m_last[1], 0x80); // Update history\n+\n+ m_lastptr[1] = *buffer_length; // Swap lengths\n+ *buffer_length = m_lastptr[0];\n+ m_lastptr[0] = m_lastptr[1];\n+}\n+\n+int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length)\n+{\n+ const auto& serial_interface = m_system.GetSerialInterface();\n+ u32 buffer_length = ConvertSILengthField(serial_interface.GetInLength());\n+\n+ // Debug logging\n+ ISIDevice::RunBuffer(buffer, buffer_length);\n+\n+ u32 buffer_position = 0;\n+ while (buffer_position < buffer_length)\n+ {\n+ BaseBoardCommand command = static_cast<BaseBoardCommand>(buffer[buffer_position]);\n+ buffer_position++;\n+\n+ switch (command)\n+ {\n+ case BaseBoardCommand::GCAM_Reset: // Returns ID and dip switches\n+ {\n+ const u32 id = Common::swap32(SI_AM_BASEBOARD | 0x100);\n+ std::memcpy(buffer, &id, sizeof(id));\n+ return sizeof(id);\n+ }\n+ break;\n+ case BaseBoardCommand::GCAM_Command:\n+ {\n+ u32 checksum = 0;\n+ for (u32 i = 0; i < buffer_length; ++i)\n+ checksum += buffer[i];\n+\n+ std::array<u8, 0x80> data_out{};\n+ u32 data_offset = 0;\n+\n+ static u32 dip_switch_1 = 0xFE;\n+ static u32 dip_switch_0 = 0xFF;\n+\n+ data_out[data_offset++] = 1;\n+ data_out[data_offset++] = 1;\n+\n+ u8* data_in = buffer + 2;\n+ if (buffer_position >= buffer_length)\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: buffer overflow (position={}, length={})",\n+ buffer_position, buffer_length);\n+ buffer_position = buffer_length;\n+ break;\n+ }\n+\n+ const u32 requested_size = buffer[buffer_position] + 2;\n+ if (requested_size > buffer_length)\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: requested size ({}) bigger than buffer\'s ({})",\n+ requested_size, buffer_length);\n+ buffer_position = buffer_length;\n+ break;\n+ }\n+ u8* const data_in_end = buffer + requested_size;\n+\n+ // Helper to check that iterating over data n times is safe,\n+ // i.e. *data++ at most lead to data.end()\n+ auto validate_data_in_out = [&](u32 n_in, u32 n_out, std::string_view command) -> bool {\n+ if (data_in + n_in > data_in_end)\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: data_in overflow in {}", command);\n+ else if (u64{data_offset} + n_out > data_out.size())\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: data_out overflow in {}", command);\n+ else\n+ return true;\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "Overflow details:\\n"\n+ " - data_in(begin={}, current={}, end={}, n_in={})\\n"\n+ " - data_out(offset={}, size={}, n_out={})\\n"\n+ " - buffer(position={}, length={})",\n+ fmt::ptr(buffer + 2), fmt::ptr(data_in), fmt::ptr(data_in_end), n_in,\n+ data_offset, data_out.size(), n_out, buffer_position, buffer_length);\n+ data_in = data_in_end;\n+ return false;\n+ };\n+\n+ while (data_in < data_in_end)\n+ {\n+ const u32 gcam_command = *data_in++;\n+ switch (GCAMCommand(gcam_command))\n+ {\n+ case GCAMCommand::StatusSwitches:\n+ {\n+ if (!validate_data_in_out(1, 4, "StatusSwitches"))\n+ break;\n+\n+ const u8 status = *data_in++;\n+ DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x10, {:02x} (READ STATUS&SWITCHES)",\n+ status);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x2;\n+\n+ // We read Test/Service from the JVS-I/O SwitchesInput instead\n+ //\n+ // const GCPadStatus pad_status = Pad::GetStatus(ISIDevice::m_device_number);\n+ // baseboard test/service switches\n+ // if (pad_status.button & PAD_BUTTON_Y)\t// Test\n+ // dip_switch_0 &= ~0x80;\n+ // if (pad_status.button & PAD_BUTTON_X)\t// Service\n+ // dip_switch_0 &= ~0x40;\n+\n+ // Horizontal Scanning Frequency switch\n+ // Required for F-Zero AX booting via Sega Boot\n+ if (AMMediaboard::GetGameType() == FZeroAX ||\n+ AMMediaboard::GetGameType() == FZeroAXMonster)\n+ {\n+ dip_switch_0 &= ~0x20;\n+ }\n+\n+ // Disable camera in MKGP1/2\n+ if (AMMediaboard::GetGameType() == MarioKartGP ||\n+ AMMediaboard::GetGameType() == MarioKartGP2)\n+ {\n+ dip_switch_0 &= ~0x10;\n+ }\n+\n+ data_out[data_offset++] = dip_switch_0;\n+ data_out[data_offset++] = dip_switch_1;\n+ break;\n+ }\n+ case GCAMCommand::SerialNumber:\n+ {\n+ if (!validate_data_in_out(1, 18, "SerialNumber"))\n+ break;\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x11, {:02x} (READ SERIAL NR)",\n+ *data_in);\n+ data_in++;\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 16;\n+ memcpy(data_out.data() + data_offset, "AADE-01B98394904", 16);\n+\n+ data_offset += 16;\n+ break;\n+ }\n+ case GCAMCommand::Unknown_12:\n+ if (!validate_data_in_out(2, 2, "Unknown_12"))\n+ break;\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x12, {:02x} {:02x}", data_in[0],\n+ data_in[1]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x00;\n+\n+ data_in += 2;\n+ break;\n+ case GCAMCommand::Unknown_14:\n+ if (!validate_data_in_out(2, 2, "Unknown_14"))\n+ break;\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x14, {:02x} {:02x}", data_in[0],\n+ data_in[1]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x00;\n+\n+ data_in += 2;\n+ break;\n+ case GCAMCommand::FirmVersion:\n+ if (!validate_data_in_out(1, 4, "FirmVersion"))\n+ break;\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x15, {:02x} (READ FIRM VERSION)",\n+ *data_in);\n+ data_in++;\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x02;\n+ // 00.26\n+ data_out[data_offset++] = 0x00;\n+ data_out[data_offset++] = 0x26;\n+ break;\n+ case GCAMCommand::FPGAVersion:\n+ if (!validate_data_in_out(1, 4, "FPGAVersion"))\n+ break;\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x16, {:02x} (READ FPGA VERSION)",\n+ *data_in);\n+ data_in++;\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x02;\n+ // 07.06\n+ data_out[data_offset++] = 0x07;\n+ data_out[data_offset++] = 0x06;\n+ break;\n+ case GCAMCommand::RegionSettings:\n+ {\n+ if (!validate_data_in_out(5, 0x16, "RegionSettings"))\n+ break;\n+\n+ // Used by SegaBoot for region checks (dev mode skips this check)\n+ // In some games this also controls the displayed language\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x1F, {:02x} {:02x} {:02x} {:02x} {:02x} (REGION)",\n+ data_in[0], data_in[1], data_in[2], data_in[3], data_in[4]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x14;\n+\n+ for (int i = 0; i < 0x14; ++i)\n+ data_out[data_offset++] = s_region_flags[i];\n+\n+ data_in += 5;\n+ }\n+ break;\n+ // No reply\n+ // Note: Always sends three bytes even though size is set to two\n+ case GCAMCommand::Unknown_21:\n+ {\n+ if (!validate_data_in_out(4, 0, "Unknown_21"))\n+ break;\n+\n+ DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x21, {:02x}, {:02x}, {:02x}, {:02x}",\n+ data_in[0], data_in[1], data_in[2], data_in[3]);\n+ data_in += 4;\n+ }\n+ break;\n+ // No reply\n+ // Note: Always sends six bytes\n+ case GCAMCommand::Unknown_22:\n+ {\n+ if (!validate_data_in_out(7, 0, "Unknown_22"))\n+ break;\n+\n+ DEBUG_LOG_FMT(\n+ SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x22, {:02x}, {:02x}, {:02x}, {:02x}, {:02x}, {:02x}, {:02x}",\n+ data_in[0], data_in[1], data_in[2], data_in[3], data_in[4], data_in[5], data_in[6]);\n+\n+ const u32 in_size = data_in[0] + 1;\n+ if (!validate_data_in_out(in_size, 0, "Unknown_22"))\n+ break;\n+ data_in += in_size;\n+ }\n+ break;\n+ case GCAMCommand::Unknown_23:\n+ if (!validate_data_in_out(2, 2, "Unknown_23"))\n+ break;\n+\n+ DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x23, {:02x} {:02x}", data_in[0],\n+ data_in[1]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x00;\n+\n+ data_in += 2;\n+ break;\n+ case GCAMCommand::Unknown_24:\n+ if (!validate_data_in_out(2, 2, "Unknown_24"))\n+ break;\n+\n+ DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x24, {:02x} {:02x}", data_in[0],\n+ data_in[1]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x00;\n+\n+ data_in += 2;\n+ break;\n+ case GCAMCommand::SerialA:\n+ {\n+ if (!validate_data_in_out(1, 0, "SerialA"))\n+ break;\n+\n+ const u32 length = *data_in++;\n+ if (length)\n+ {\n+ if (!validate_data_in_out(length, 0, "SerialA"))\n+ break;\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x31, length=0x{:02x}, hexdump:\\n{}",\n+ length, HexDump(data_in, length));\n+\n+ // Serial - Wheel\n+ if (AMMediaboard::GetGameType() == MarioKartGP ||\n+ AMMediaboard::GetGameType() == MarioKartGP2)\n+ {\n+ if (!validate_data_in_out(10, 2, "SerialA (Wheel)"))\n+ break;\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x31, (WHEEL) {:02x}{:02x} {:02x}{:02x} {:02x} {:02x} "\n+ "{:02x} {:02x} {:02x} {:02x}",\n+ data_in[0], data_in[1], data_in[2], data_in[3], data_in[4], data_in[5],\n+ data_in[6], data_in[7], data_in[8], data_in[9]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x03;\n+\n+ switch (m_wheel_init)\n+ {\n+ case 0:\n+ if (!validate_data_in_out(0, 3, "SerialA (Wheel)"))\n+ break;\n+ data_out[data_offset++] = \'E\'; // Error\n+ data_out[data_offset++] = \'0\';\n+ data_out[data_offset++] = \'0\';\n+ m_wheel_init++;\n+ break;\n+ case 1:\n+ if (!validate_data_in_out(0, 3, "SerialA (Wheel)"))\n+ break;\n+ data_out[data_offset++] = \'C\'; // Power Off\n+ data_out[data_offset++] = \'0\';\n+ data_out[data_offset++] = \'6\';\n+ // Only turn on when a wheel is connected\n+ if (serial_interface.GetDeviceType(1) == SerialInterface::SIDEVICE_GC_STEERING)\n+ {\n+ m_wheel_init++;\n+ }\n+ break;\n+ case 2:\n+ if (!validate_data_in_out(0, 3, "SerialA (Wheel)"))\n+ break;\n+ data_out[data_offset++] = \'C\'; // Power On\n+ data_out[data_offset++] = \'0\';\n+ data_out[data_offset++] = \'1\';\n+ break;\n+ default:\n+ break;\n+ }\n+\n+ // u16 CenteringForce= ptr(6);\n+ // u16 FrictionForce = ptr(8);\n+ // u16 Roll = ptr(10);\n+ if (!validate_data_in_out(length, 0, "SerialA (Wheel)"))\n+ break;\n+ data_in += length;\n+ break;\n+ }\n+\n+ // Serial - Unknown\n+ if (AMMediaboard::GetGameType() == GekitouProYakyuu)\n+ {\n+ if (!validate_data_in_out(sizeof(u32), 0, "SerialA (Unknown)"))\n+ break;\n+ const u32 serial_command = Common::BitCastPtr<u32>(data_in);\n+\n+ if (serial_command == 0x00001000)\n+ {\n+ if (!validate_data_in_out(0, 5, "SerialA (Unknown)"))\n+ break;\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x03;\n+ data_out[data_offset++] = 1;\n+ data_out[data_offset++] = 2;\n+ data_out[data_offset++] = 3;\n+ }\n+\n+ if (!validate_data_in_out(length, 0, "SerialA (Unknown)"))\n+ break;\n+ data_in += length;\n+ break;\n+ }\n+\n+ // Serial IC-CARD / Serial Deck Reader\n+ if (AMMediaboard::GetGameType() == VirtuaStriker4 ||\n+ AMMediaboard::GetGameType() == VirtuaStriker4_2006 ||\n+ AMMediaboard::GetGameType() == KeyOfAvalon)\n+ {\n+ if (!validate_data_in_out(2, 0, "SerialA (IC-CARD)"))\n+ break;\n+ u32 serial_command = data_in[1];\n+\n+ ICCommand icco;\n+\n+ // Set default reply\n+ icco.pktcmd = gcam_command;\n+ icco.pktlen = 7;\n+ icco.fixed = 0x10;\n+ icco.command = serial_command;\n+ icco.flag = 0;\n+ icco.length = 2;\n+ icco.status = 0;\n+ icco.extlen = 0;\n+\n+ // Check for rest of data from the write pages command\n+ if (m_ic_write_size && m_ic_write_offset)\n+ {\n+ const u32 size = data_in[1];\n+\n+ if (!validate_data_in_out(size + 2, 0, "SerialA (IC-CARD)"))\n+ break;\n+ DEBUG_LOG_FMT(SERIALINTERFACE_CARD, "Command: {}", HexDump(data_in, size + 2));\n+\n+ INFO_LOG_FMT(\n+ SERIALINTERFACE_CARD,\n+ "GC-AM: Command 25 (IC-CARD) Write Pages: Off:{:x} Size:{:x} PSize:{:x}",\n+ m_ic_write_offset, m_ic_write_size, size);\n+\n+ if (u64{m_ic_write_offset} + size > sizeof(m_ic_write_buffer))\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 25 (IC-CARD) m_ic_write_buffer overflow:\\n"\n+ " - m_ic_write_buffer(offset={}, size={})\\n"\n+ " - size={}\\n",\n+ m_ic_write_offset, sizeof(m_ic_write_buffer), size);\n+ data_in = data_in_end;\n+ break;\n+ }\n+ memcpy(m_ic_write_buffer + m_ic_write_offset, data_in + 2, size);\n+\n+ m_ic_write_offset += size;\n+\n+ if (m_ic_write_offset > m_ic_write_size)\n+ {\n+ m_ic_write_offset = 0;\n+\n+ const u16 page = m_ic_write_buffer[5];\n+ const u16 count = m_ic_write_buffer[7];\n+\n+ if ((page * 8 + count * 8) > sizeof(m_ic_card_data) ||\n+ (10 + count * 8) > sizeof(m_ic_write_buffer))\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 25 (IC-CARD) Write Pages overflow:\\n"\n+ " - m_ic_card_data(offset={}, size={})\\n"\n+ " - m_ic_write_buffer(offset={}, size={})\\n"\n+ " - size={}, page={}, count={}\\n",\n+ page * 8, sizeof(m_ic_card_data), 10, sizeof(m_ic_write_buffer),\n+ count * 8, page, count);\n+ data_in = data_in_end;\n+ break;\n+ }\n+ memcpy(m_ic_card_data + page * 8, m_ic_write_buffer + 10, count * 8);\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 25 (IC-CARD) Write Pages:{} Count:{}({:x})", page,\n+ count, size);\n+\n+ icco.command = WritePages;\n+\n+ if (!validate_data_in_out(0, icco.pktlen + 2, "SerialA (IC-CARD)"))\n+ break;\n+ ICCardSendReply(&icco, data_out.data(), &data_offset);\n+ }\n+ if (!validate_data_in_out(length, 0, "SerialA (IC-CARD)"))\n+ break;\n+ data_in += length;\n+ break;\n+ }\n+\n+ switch (ICCARDCommand(serial_command))\n+ {\n+ case ICCARDCommand::GetStatus:\n+ icco.status = m_ic_card_state;\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (IC-CARD) Get Status:{:02x}", m_ic_card_state);\n+ break;\n+ case ICCARDCommand::SetBaudrate:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (IC-CARD) Set Baudrate");\n+ break;\n+ case ICCARDCommand::FieldOn:\n+ m_ic_card_state |= 0x10;\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (IC-CARD) Field On");\n+ break;\n+ case ICCARDCommand::InsertCheck:\n+ icco.status = m_ic_card_status;\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (IC-CARD) Insert Check:{:02x}", m_ic_card_status);\n+ break;\n+ case ICCARDCommand::AntiCollision:\n+ icco.extlen = 8;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ // Card ID\n+ icco.extdata[0] = 0x00;\n+ icco.extdata[1] = 0x00;\n+ icco.extdata[2] = 0x54;\n+ icco.extdata[3] = 0x4D;\n+ icco.extdata[4] = 0x50;\n+ icco.extdata[5] = 0x00;\n+ icco.extdata[6] = 0x00;\n+ icco.extdata[7] = 0x00;\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (IC-CARD) Anti Collision");\n+ break;\n+ case ICCARDCommand::SelectCard:\n+ icco.extlen = 8;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ // Session\n+ icco.extdata[0] = 0x00;\n+ icco.extdata[1] = m_ic_card_session;\n+ icco.extdata[2] = 0x00;\n+ icco.extdata[3] = 0x00;\n+ icco.extdata[4] = 0x00;\n+ icco.extdata[5] = 0x00;\n+ icco.extdata[6] = 0x00;\n+ icco.extdata[7] = 0x00;\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (IC-CARD) Select Card:{}",\n+ m_ic_card_session);\n+ break;\n+ case ICCARDCommand::ReadPage:\n+ case ICCARDCommand::ReadUseCount:\n+ {\n+ if (!validate_data_in_out(8, 0, "SerialA (IC-CARD)"))\n+ break;\n+ const u16 page = Common::swap16(data_in + 6) & 0xFF; // 255 is max page\n+\n+ icco.extlen = 8;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ memcpy(icco.extdata, m_ic_card_data + page * 8, 8);\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 31 (IC-CARD) Read Page:{}",\n+ page);\n+ break;\n+ }\n+ case ICCARDCommand::WritePage:\n+ {\n+ if (!validate_data_in_out(10, 0, "SerialA (IC-CARD)"))\n+ break;\n+ const u16 page = Common::swap16(data_in + 8) & 0xFF; // 255 is max page\n+\n+ // Write only one page\n+ if (page == 4)\n+ {\n+ icco.status = 0x80;\n+ }\n+ else\n+ {\n+ if (!validate_data_in_out(18, 0, "SerialA (IC-CARD)"))\n+ break;\n+ memcpy(m_ic_card_data + page * 8, data_in + 10, 8);\n+ }\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (IC-CARD) Write Page:{}",\n+ page);\n+ break;\n+ }\n+ case ICCARDCommand::DecreaseUseCount:\n+ {\n+ if (!validate_data_in_out(8, 0, "SerialA (IC-CARD)"))\n+ break;\n+ const u16 page = Common::swap16(data_in + 6) & 0xFF; // 255 is max page\n+\n+ icco.extlen = 2;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ auto ic_card_data = Common::BitCastPtr<u16>(m_ic_card_data + 0x28);\n+ ic_card_data = ic_card_data - 1;\n+\n+ // Counter\n+ icco.extdata[0] = m_ic_card_data[0x28];\n+ icco.extdata[1] = m_ic_card_data[0x29];\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 31 (IC-CARD) Decrease Use Count:{}", page);\n+ break;\n+ }\n+ case ICCARDCommand::ReadPages:\n+ {\n+ if (!validate_data_in_out(10, 0, "SerialA (IC-CARD)"))\n+ break;\n+ const u16 page = Common::swap16(data_in + 6) & 0xFF; // 255 is max page\n+ const u16 count = Common::swap16(data_in + 8);\n+\n+ const u32 offs = page * 8;\n+ u32 cnt = count * 8;\n+\n+ // Limit read size to not overwrite the reply buffer\n+ const std::size_t reply_buffer_size = sizeof(icco.extdata) - 1;\n+ if (data_offset > reply_buffer_size)\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 31 (IC-CARD) Read Pages overflow:"\n+ " offset={} > buffer_size={}",\n+ data_offset, reply_buffer_size);\n+ data_in = data_in_end;\n+ break;\n+ }\n+ if (cnt > reply_buffer_size - data_offset)\n+ {\n+ cnt = 5 * 8;\n+ }\n+\n+ icco.extlen = cnt;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ if (offs + cnt > sizeof(icco.extdata))\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 31 (IC-CARD) Read Pages overflow:"\n+ " offset={} + count={} > buffer_size={}",\n+ offs, cnt, sizeof(icco.extdata));\n+ data_in = data_in_end;\n+ break;\n+ }\n+ memcpy(icco.extdata, m_ic_card_data + offs, cnt);\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 31 (IC-CARD) Read Pages:{} Count:{}", page, count);\n+ break;\n+ }\n+ case ICCARDCommand::WritePages:\n+ {\n+ if (!validate_data_in_out(10, 0, "SerialA (IC-CARD)"))\n+ break;\n+ const u16 pksize = length;\n+ const u16 size = Common::swap16(data_in + 2);\n+ const u16 page = Common::swap16(data_in + 6) & 0xFF; // 255 is max page\n+ const u16 count = Common::swap16(data_in + 8);\n+\n+ // We got a complete packet\n+ if (pksize - 5 == size)\n+ {\n+ if (page == 4) // Read Only Page, must return error\n+ {\n+ icco.status = 0x80;\n+ }\n+ else\n+ {\n+ if (page * 8 + count * 8 > sizeof(m_ic_card_data))\n+ {\n+ ERROR_LOG_FMT(\n+ SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (IC-CARD) Data overflow: Pages:{} Count:{}({:x})",\n+ page, count, size);\n+ }\n+ else\n+ {\n+ if (!validate_data_in_out(13 + count * 8, 0, "SerialA (IC-CARD)"))\n+ break;\n+ memcpy(m_ic_card_data + page * 8, data_in + 13, count * 8);\n+ }\n+ }\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (IC-CARD) Write Pages:{} Count:{}({:x})", page,\n+ count, size);\n+ }\n+ // VirtuaStriker 4 splits the writes over multiple packets\n+ else\n+ {\n+ if (!validate_data_in_out(2 + pksize, 0, "SerialA (IC-CARD)"))\n+ break;\n+ memcpy(m_ic_write_buffer, data_in + 2, pksize);\n+ m_ic_write_offset += pksize;\n+ m_ic_write_size = size;\n+ }\n+ break;\n+ }\n+ default:\n+ // Handle Deck Reader commands\n+ if (!validate_data_in_out(1, 0, "SerialA (DECK READER)"))\n+ break;\n+ serial_command = data_in[0];\n+ icco.command = serial_command;\n+ icco.flag = 0;\n+ switch (CDReaderCommand(serial_command))\n+ {\n+ case CDReaderCommand::ProgramVersion:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Program Version");\n+\n+ icco.extlen = (u32)strlen(s_cdr_program_version);\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ memcpy(icco.extdata, s_cdr_program_version, icco.extlen);\n+ break;\n+ case CDReaderCommand::BootVersion:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Boot Version");\n+\n+ icco.extlen = (u32)strlen(s_cdr_boot_version);\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ memcpy(icco.extdata, s_cdr_boot_version, icco.extlen);\n+ break;\n+ case CDReaderCommand::ShutterGet:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Shutter Get");\n+\n+ icco.extlen = 4;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ icco.extdata[0] = 0;\n+ icco.extdata[1] = 0;\n+ icco.extdata[2] = 0;\n+ icco.extdata[3] = 0;\n+ break;\n+ case CDReaderCommand::CameraCheck:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Camera Check");\n+\n+ icco.extlen = 6;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ icco.extdata[0] = 0x23;\n+ icco.extdata[1] = 0x28;\n+ icco.extdata[2] = 0x45;\n+ icco.extdata[3] = 0x29;\n+ icco.extdata[4] = 0x45;\n+ icco.extdata[5] = 0x29;\n+ break;\n+ case CDReaderCommand::ProgramChecksum:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Program Checksum");\n+\n+ icco.extlen = 4;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ icco.extdata[0] = 0x23;\n+ icco.extdata[1] = 0x28;\n+ icco.extdata[2] = 0x45;\n+ icco.extdata[3] = 0x29;\n+ break;\n+ case CDReaderCommand::BootChecksum:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Boot Checksum");\n+\n+ icco.extlen = 4;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ icco.extdata[0] = 0x23;\n+ icco.extdata[1] = 0x28;\n+ icco.extdata[2] = 0x45;\n+ icco.extdata[3] = 0x29;\n+ break;\n+ case CDReaderCommand::SelfTest:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (DECK READER) Self Test");\n+ icco.flag = 0x00;\n+ break;\n+ case CDReaderCommand::SensLock:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (DECK READER) Sens Lock");\n+ icco.flag = 0x01;\n+ break;\n+ case CDReaderCommand::SensCard:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (DECK READER) Sens Card");\n+ break;\n+ case CDReaderCommand::ShutterCard:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Shutter Card");\n+ break;\n+ case CDReaderCommand::ReadCard:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (DECK READER) Read Card");\n+\n+ icco.fixed = 0xAA;\n+ icco.flag = 0xAA;\n+ icco.extlen = sizeof(s_cdr_card_data);\n+ icco.length = 0x72;\n+ icco.status = Common::swap16(icco.extlen);\n+\n+ icco.pktlen += icco.extlen;\n+\n+ memcpy(icco.extdata, s_cdr_card_data, sizeof(s_cdr_card_data));\n+\n+ break;\n+ default:\n+ if (!validate_data_in_out(14, 0, "SerialA (DECK READER)"))\n+ break;\n+ WARN_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (IC-Card) {:02x} {:02x} {:02x} {:02x} {:02x} "\n+ "{:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x}",\n+ data_in[2], data_in[3], data_in[4], data_in[5], data_in[6],\n+ data_in[7], data_in[8], data_in[9], data_in[10], data_in[11],\n+ data_in[12], data_in[13]);\n+ break;\n+ }\n+ break;\n+ }\n+\n+ if (!validate_data_in_out(0, icco.pktlen + 2, "SerialA (IC-CARD)"))\n+ break;\n+ ICCardSendReply(&icco, data_out.data(), &data_offset);\n+\n+ if (!validate_data_in_out(2, 0, "SerialA (IC-CARD)"))\n+ break;\n+ data_in += length;\n+ break;\n+ }\n+ }\n+\n+ u32 command_offset = 0;\n+ while (command_offset < length)\n+ {\n+ // All commands are OR\'d with 0x80\n+ // Last byte is checksum which we don\'t care about\n+ if (!validate_data_in_out(command_offset + 4, 0, "SerialA"))\n+ break;\n+ const u32 serial_command = Common::swap32(data_in + command_offset) ^ 0x80000000;\n+\n+ if (AMMediaboard::GetGameType() == FZeroAX ||\n+ AMMediaboard::GetGameType() == FZeroAXMonster)\n+ {\n+ INFO_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x31 (MOTOR) Length:{:02x} Command:{:04x}({:02x})",\n+ length, (serial_command >> 8) & 0xFFFF, serial_command >> 24);\n+ }\n+ else\n+ {\n+ INFO_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x31 (SERIAL) Command:{:06x}",\n+ serial_command);\n+\n+ if (serial_command == 0x801000)\n+ {\n+ if (!validate_data_in_out(0, 4, "SerialA"))\n+ break;\n+ data_out[data_offset++] = 0x31;\n+ data_out[data_offset++] = 0x02;\n+ data_out[data_offset++] = 0xFF;\n+ data_out[data_offset++] = 0x01;\n+ }\n+ }\n+\n+ command_offset += sizeof(u32);\n+\n+ if (AMMediaboard::GetGameType() == FZeroAX ||\n+ AMMediaboard::GetGameType() == FZeroAXMonster)\n+ {\n+ if (command_offset + 5 >= std::size(m_motor_reply))\n+ {\n+ ERROR_LOG_FMT(\n+ SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x31 (MOTOR) overflow: offset={} >= motor_reply_size={}",\n+ command_offset + 5, std::size(m_motor_reply));\n+ data_in = data_in_end;\n+ break;\n+ }\n+\n+ // Status\n+ m_motor_reply[command_offset + 2] = 0;\n+ m_motor_reply[command_offset + 3] = 0;\n+\n+ // Error\n+ m_motor_reply[command_offset + 4] = 0;\n+\n+ switch (serial_command >> 24)\n+ {\n+ case 0:\n+ case 1: // Set Maximum?\n+ case 2:\n+ break;\n+\n+ // 0x00-0x40: left\n+ // 0x40-0x80: right\n+ case 4: // Move Steering Wheel\n+ // Left\n+ if (serial_command & 0x010000)\n+ {\n+ m_motor_force_y = -((s16)serial_command & 0xFF00);\n+ }\n+ else // Right\n+ {\n+ m_motor_force_y = (serial_command - 0x4000) & 0xFF00;\n+ }\n+\n+ m_motor_force_y *= 2;\n+\n+ // FFB\n+ if (m_motor_init == 2)\n+ {\n+ if (serial_interface.GetDeviceType(1) == SerialInterface::SIDEVICE_GC_STEERING)\n+ {\n+ const GCPadStatus pad_status = Pad::GetStatus(1);\n+ if (pad_status.isConnected)\n+ {\n+ const ControlState mapped_strength = (double)(m_motor_force_y >> 8) / 127.f;\n+\n+ Pad::Rumble(1, mapped_strength);\n+ INFO_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x31 (MOTOR) mapped_strength:{}",\n+ mapped_strength);\n+ }\n+ }\n+ }\n+ break;\n+ case 6: // nice\n+ case 9:\n+ default:\n+ break;\n+ // Switch back to normal controls\n+ case 7:\n+ m_motor_init = 2;\n+ break;\n+ // Reset\n+ case 0x7F:\n+ m_motor_init = 1;\n+ memset(m_motor_reply, 0, sizeof(m_motor_reply));\n+ break;\n+ }\n+\n+ // Checksum\n+ m_motor_reply[command_offset + 5] = m_motor_reply[command_offset + 2] ^\n+ m_motor_reply[command_offset + 3] ^\n+ m_motor_reply[command_offset + 4];\n+ }\n+ }\n+\n+ if (length == 0)\n+ {\n+ if (!validate_data_in_out(length, 2, "SerialA"))\n+ break;\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x00;\n+ }\n+ else\n+ {\n+ if (m_motor_init)\n+ {\n+ // Motor\n+ m_motor_reply[0] = gcam_command;\n+ m_motor_reply[1] = length; // Same out as in size\n+\n+ const u32 reply_size = m_motor_reply[1] + 2;\n+ if (!validate_data_in_out(length, reply_size, "SerialA"))\n+ break;\n+\n+ if (reply_size > sizeof(m_motor_reply))\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command SerialA, reply_size={} too big for m_motor_reply",\n+ reply_size);\n+ data_in = data_in_end;\n+ break;\n+ }\n+ memcpy(data_out.data() + data_offset, m_motor_reply, reply_size);\n+ data_offset += reply_size;\n+ }\n+ }\n+\n+ if (!validate_data_in_out(length, 0, "SerialA"))\n+ {\n+ break;\n+ }\n+ data_in += length;\n+ break;\n+ }\n+ case GCAMCommand::SerialB:\n+ {\n+ DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 32 (CARD-Interface)");\n+ if (!validate_data_in_out(1, 0, "SerialB"))\n+ break;\n+ const u32 length = *data_in++;\n+ if (!validate_data_in_out(length, 0, "SerialB"))\n+ break;\n+ if (length)\n+ {\n+ // Send Card Reply\n+ if (length == 1 && data_in[0] == 0x05)\n+ {\n+ if (m_card_read_length)\n+ {\n+ if (!validate_data_in_out(0, 1, "SerialB"))\n+ break;\n+ data_out[data_offset++] = gcam_command;\n+ u32 read_length = m_card_read_length - m_card_read;\n+\n+ if (AMMediaboard::GetGameType() == FZeroAX)\n+ {\n+ read_length = std::min<u32>(read_length, 0x2F);\n+ }\n+\n+ if (!validate_data_in_out(0, 1, "SerialB"))\n+ break;\n+ data_out[data_offset++] = read_length; // 0x2F (max size per packet)\n+\n+ if (!validate_data_in_out(0, read_length, "SerialB"))\n+ break;\n+ if (u64{m_card_read} + read_length > sizeof(m_card_read_packet))\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command SerialB, m_card_read_packet overflow:\\n"\n+ " - m_card_read_packet = {}\\n"\n+ " - m_card_read = {}\\n"\n+ " - read_length = {}",\n+ fmt::ptr(m_card_read_packet), m_card_read, read_length);\n+ data_in = data_in_end;\n+ break;\n+ }\n+ memcpy(data_out.data() + data_offset, m_card_read_packet + m_card_read,\n+ read_length);\n+\n+ data_offset += read_length;\n+ m_card_read += read_length;\n+\n+ if (m_card_read >= m_card_read_length)\n+ m_card_read_length = 0;\n+\n+ data_in += length;\n+ break;\n+ }\n+\n+ if (!validate_data_in_out(0, 5, "SerialB"))\n+ break;\n+\n+ data_out[data_offset++] = gcam_command;\n+ const u32 command_length_offset = data_offset;\n+ data_out[data_offset++] = 0x00; // len\n+\n+ data_out[data_offset++] = 0x02;\n+ const u32 checksum_start = data_offset;\n+\n+ data_out[data_offset++] = 0x00; // 0x00 len\n+\n+ data_out[data_offset++] = m_card_command; // 0x01 command\n+\n+ switch (CARDCommand(m_card_command))\n+ {\n+ case CARDCommand::Init:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x00; // 0x02\n+ data_out[data_offset++] = 0x30; // 0x03\n+ break;\n+ case CARDCommand::GetState:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x20 | m_card_bit; // 0x02\n+\n+ // bit 0: Please take your card\n+ // bit 1: Endless waiting causes UNK_E to be called\n+ data_out[data_offset++] = 0x00; // 0x03\n+ break;\n+ case CARDCommand::Read:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x02; // 0x02\n+ data_out[data_offset++] = 0x53; // 0x03\n+ break;\n+ case CARDCommand::IsPresent:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x22; // 0x02\n+ data_out[data_offset++] = 0x30; // 0x03\n+ break;\n+ case CARDCommand::Write:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x02; // 0x02\n+ data_out[data_offset++] = 0x00; // 0x03\n+ break;\n+ case CARDCommand::SetPrintParam:\n+ case CARDCommand::RegisterFont:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x00; // 0x02\n+ data_out[data_offset++] = 0x00; // 0x03\n+ break;\n+ case CARDCommand::WriteInfo:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x02; // 0x02\n+ data_out[data_offset++] = 0x00; // 0x03\n+ break;\n+ case CARDCommand::Erase:\n+ // TODO: CARDCommand::Erase is not handled.\n+ break;\n+ case CARDCommand::Eject:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ if (AMMediaboard::GetGameType() == FZeroAX)\n+ {\n+ data_out[data_offset++] = 0x01; // 0x02\n+ }\n+ else\n+ {\n+ data_out[data_offset++] = 0x31; // 0x02\n+ }\n+ data_out[data_offset++] = 0x30; // 0x03\n+ break;\n+ case CARDCommand::Clean:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x02; // 0x02\n+ data_out[data_offset++] = 0x00; // 0x03\n+ break;\n+ case CARDCommand::Load:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x02; // 0x02\n+ data_out[data_offset++] = 0x30; // 0x03\n+ break;\n+ case CARDCommand::SetShutter:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x00; // 0x02\n+ data_out[data_offset++] = 0x00; // 0x03\n+ break;\n+ }\n+\n+ if (!validate_data_in_out(0, 3, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x30; // 0x04\n+ data_out[data_offset++] = 0x00; // 0x05\n+\n+ data_out[data_offset++] = 0x03; // 0x06\n+\n+ data_out[checksum_start] = data_offset - checksum_start; // 0x00 len\n+\n+ if (!validate_data_in_out(0, 1, "SerialB"))\n+ break;\n+ data_out[data_offset] = 0; // 0x07\n+ for (u32 i = 0; i < data_out[checksum_start]; ++i)\n+ data_out[data_offset] ^= data_out[checksum_start + i];\n+\n+ if (!validate_data_in_out(0, 1, "SerialB"))\n+ break;\n+ data_offset++;\n+\n+ data_out[command_length_offset] = data_out[checksum_start] + 2;\n+ }\n+ else\n+ {\n+ if (!validate_data_in_out(length, 0, "SerialB"))\n+ break;\n+ if (u64{m_card_offset} + length > std::size(m_card_buffer))\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command SerialB, m_card_buffer overflow:\\n"\n+ " - m_card_buffer = {}\\n"\n+ " - m_card_offset = {}\\n"\n+ " - length = {}",\n+ fmt::ptr(m_card_buffer), m_card_offset, length);\n+ data_in = data_in_end;\n+ break;\n+ }\n+ for (u32 i = 0; i < length; ++i)\n+ m_card_buffer[m_card_offset + i] = data_in[i];\n+\n+ m_card_offset += length;\n+\n+ // Check if we got a complete command\n+ if (m_card_buffer[0] == 0x02)\n+ {\n+ if (m_card_buffer[1] == m_card_offset - 2)\n+ {\n+ if (m_card_buffer[m_card_offset - 2] == 0x03)', 'path': 'Source/Core/Core/HW/SI/SI_DeviceAMBaseboard.cpp', 'position': 1345, 'original_position': 1345, 'commit_id': '5bf8fe93286ebe4f08fb469a44f259a8397c89e0', 'user': {'login': 'JosJuice', 'id': 6716818, 'node_id': 'MDQ6VXNlcjY3MTY4MTg=', 'avatar_url': 'https://avatars.githubusercontent.com/u/6716818?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/JosJuice', 'html_url': 'https://github.com/JosJuice', 'followers_url': 'https://api.github.com/users/JosJuice/followers', 'following_url': 'https://api.github.com/users/JosJuice/following{/other_user}', 'gists_url': 'https://api.github.com/users/JosJuice/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/JosJuice/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/JosJuice/subscriptions', 'organizations_url': 'https://api.github.com/users/JosJuice/orgs', 'repos_url': 'https://api.github.com/users/JosJuice/repos', 'events_url': 'https://api.github.com/users/JosJuice/events{/privacy}', 'received_events_url': 'https://api.github.com/users/JosJuice/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'body': "> Can't `length = 1` if `data_in[0] != 0x05`, so `m_card_offset - 2` be unsafe even if incremented by `length`?\r\n\r\nOh, `data_in` is incremented in between getting `length` from `data_in` and the for loop that copies from `data_in` to `m_card_buffer`... Okay, then it's not true that `m_card_buffer[0]` has the same value as `length`, so ignore what I said about it being safe.", 'created_at': '2026-02-02T17:28:05Z', 'updated_at': '2026-02-02T17:28:05Z', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/13844#discussion_r2755469094', 'pull_request_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13844', 'author_association': 'MEMBER', '_links': {'self': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2755469094'}, 'html': {'href': 'https://github.com/dolphin-emu/dolphin/pull/13844#discussion_r2755469094'}, 'pull_request': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13844'}}, 'original_commit_id': '5bf8fe93286ebe4f08fb469a44f259a8397c89e0', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2755469094/reactions', 'total_count': 0, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 0, 'confused': 0, 'heart': 0, 'rocket': 0, 'eyes': 0}, 'in_reply_to_id': 2751294423}], 'type': 'gh_pull_request_review'}
2026-02-01T22:54:22.565648 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'sepalani', 'action': 'submitted', 'pr_id': 13844, 'pr_title': 'Core: Triforce support', 'state': 'commented', 'url': 'https://github.com/dolphin-emu/dolphin/pull/13844#pullrequestreview-3736773397', 'comments': [{'id': 2752074638, 'node_id': 'PRRC_kwDOALCn2M6kCVOO', 'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2752074638', 'pull_request_review_id': 3736773397, 'diff_hunk': '@@ -0,0 +1,2708 @@\n+// Copyright 2025 Dolphin Emulator Project\n+// SPDX-License-Identifier: GPL-2.0-or-later\n+\n+#include "Core/HW/SI/SI_DeviceAMBaseboard.h"\n+\n+#include <algorithm>\n+#include <numeric>\n+#include <string>\n+\n+#include <fmt/format.h>\n+\n+#include "Common/Buffer.h"\n+#include "Common/CommonTypes.h"\n+#include "Common/FileUtil.h"\n+#include "Common/IOFile.h"\n+#include "Common/Logging/Log.h"\n+#include "Common/MsgHandler.h"\n+#include "Common/Swap.h"\n+\n+#include "Core/Boot/Boot.h"\n+#include "Core/BootManager.h"\n+#include "Core/Config/MainSettings.h"\n+#include "Core/ConfigManager.h"\n+#include "Core/Core.h"\n+#include "Core/CoreTiming.h"\n+#include "Core/HW/DVD/AMMediaboard.h"\n+#include "Core/HW/DVD/DVDInterface.h"\n+#include "Core/HW/EXI/EXI.h"\n+#include "Core/HW/GCPad.h"\n+#include "Core/HW/MMIO.h"\n+#include "Core/HW/Memmap.h"\n+#include "Core/HW/ProcessorInterface.h"\n+#include "Core/HW/SI/SI.h"\n+#include "Core/HW/SI/SI_Device.h"\n+#include "Core/HW/SI/SI_DeviceGCController.h"\n+#include "Core/HW/SystemTimers.h"\n+#include "Core/Movie.h"\n+#include "Core/NetPlayProto.h"\n+#include "Core/System.h"\n+\n+#include "InputCommon/GCPadStatus.h"\n+\n+namespace SerialInterface\n+{\n+void JVSIOMessage::Start(int node)\n+{\n+ m_last_start = m_pointer;\n+ const u8 header[3] = {0xE0, (u8)node, 0};\n+ m_checksum = 0;\n+ AddData(header, 3, 1);\n+}\n+\n+void JVSIOMessage::AddData(const u8* dst, size_t len, int sync = 0)\n+{\n+ if (m_pointer + len >= sizeof(m_message))\n+ {\n+ PanicAlertFmt("JVSIOMessage overrun!");\n+ return;\n+ }\n+\n+ while (len--)\n+ {\n+ const u8 c = *dst++;\n+ if (!sync && ((c == 0xE0) || (c == 0xD0)))\n+ {\n+ if (m_pointer + 2 > sizeof(m_message))\n+ {\n+ PanicAlertFmt("JVSIOMessage overrun!");\n+ break;\n+ }\n+ m_message[m_pointer++] = 0xD0;\n+ m_message[m_pointer++] = c - 1;\n+ }\n+ else\n+ {\n+ if (m_pointer >= sizeof(m_message))\n+ {\n+ PanicAlertFmt("JVSIOMessage overrun!");\n+ break;\n+ }\n+ m_message[m_pointer++] = c;\n+ }\n+\n+ if (!sync)\n+ m_checksum += c;\n+ sync = 0;\n+ }\n+}\n+\n+void JVSIOMessage::AddData(const void* data, size_t len)\n+{\n+ AddData(static_cast<const u8*>(data), len);\n+}\n+\n+void JVSIOMessage::AddData(const char* data)\n+{\n+ AddData(data, strlen(data));\n+}\n+\n+void JVSIOMessage::AddData(u32 n)\n+{\n+ const u8 cs = n;\n+ AddData(&cs, 1);\n+}\n+\n+void JVSIOMessage::End()\n+{\n+ const u32 len = m_pointer - m_last_start;\n+ if (m_last_start + 2 < sizeof(m_message) && len >= 3)\n+ {\n+ m_message[m_last_start + 2] = len - 2; // assuming len <0xD0\n+ AddData(m_checksum + len - 2);\n+ }\n+ else\n+ {\n+ PanicAlertFmt("JVSIOMessage: Not enough space for checksum!");\n+ }\n+}\n+\n+static constexpr u8 CheckSumXOR(const u8* data, u32 length)\n+{\n+ return std::accumulate(data, data + length, u8{}, std::bit_xor());\n+}\n+\n+static constexpr char s_cdr_program_version[] = {" Version 1.22,2003/09/19,171-8213B"};\n+static constexpr char s_cdr_boot_version[] = {" Version 1.04,2003/06/17,171-8213B"};\n+static constexpr u8 s_cdr_card_data[] = {\n+ 0x00, 0x6E, 0x00, 0x00, 0x01, 0x00, 0x00, 0x06, 0x00, 0x00, 0x07, 0x00, 0x00, 0x0B,\n+ 0x00, 0x00, 0x0E, 0x00, 0x00, 0x10, 0x00, 0x00, 0x17, 0x00, 0x00, 0x19, 0x00, 0x00,\n+ 0x1A, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x20, 0x00,\n+ 0x00, 0x22, 0x00, 0x00, 0x23, 0x00, 0x00, 0x24, 0x00, 0x00, 0x27, 0x00, 0x00, 0x28,\n+ 0x00, 0x00, 0x2C, 0x00, 0x00, 0x2F, 0x00, 0x00, 0x34, 0x00, 0x00, 0x35, 0x00, 0x00,\n+ 0x37, 0x00, 0x00, 0x38, 0x00, 0x00, 0x39, 0x00, 0x00, 0x3D, 0x00};\n+\n+const constexpr u8 s_region_flags[] = "\\x00\\x00\\x30\\x00"\n+ // "\\x01\\xfe\\x00\\x00" // JAPAN\n+ "\\x02\\xfd\\x00\\x00" // USA\n+ //"\\x03\\xfc\\x00\\x00" // export\n+ "\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff";\n+// AM-Baseboard device on SI\n+CSIDevice_AMBaseboard::CSIDevice_AMBaseboard(Core::System& system, SIDevices device,\n+ int device_number)\n+ : ISIDevice(system, device, device_number)\n+{\n+ // Card ID\n+ m_ic_card_data[0x20] = 0x95;\n+ m_ic_card_data[0x21] = 0x71;\n+\n+ if (AMMediaboard::GetGameType() == KeyOfAvalon)\n+ {\n+ m_ic_card_data[0x22] = 0x26;\n+ m_ic_card_data[0x23] = 0x40;\n+ }\n+ else if (AMMediaboard::GetGameType() == VirtuaStriker4)\n+ {\n+ m_ic_card_data[0x22] = 0x44;\n+ m_ic_card_data[0x23] = 0x00;\n+ }\n+\n+ // Use count\n+ m_ic_card_data[0x28] = 0xFF;\n+ m_ic_card_data[0x29] = 0xFF;\n+}\n+\n+constexpr u32 SI_XFER_LENGTH_MASK = 0x7f;\n+\n+// Translate [0,1,2,...,126,127] to [128,1,2,...,126,127]\n+constexpr s32 ConvertSILengthField(u32 field)\n+{\n+ return ((field - 1) & SI_XFER_LENGTH_MASK) + 1;\n+}\n+\n+void CSIDevice_AMBaseboard::ICCardSendReply(ICCommand* iccommand, u8* buffer, u32* length)\n+{\n+ iccommand->status = Common::swap16(iccommand->status);\n+\n+ const auto iccommand_data = reinterpret_cast<const u8*>(iccommand);\n+ const u8 crc = CheckSumXOR(iccommand_data + 2, iccommand->pktlen - 1);\n+\n+ for (u32 i = 0; i < iccommand->pktlen + 1; ++i)\n+ {\n+ buffer[(*length)++] = iccommand_data[i];\n+ }\n+\n+ buffer[(*length)++] = crc;\n+}\n+\n+void CSIDevice_AMBaseboard::SwapBuffers(u8* buffer, u32* buffer_length)\n+{\n+ memcpy(m_last[1], buffer, 0x80); // Save current buffer\n+ memcpy(buffer, m_last[0], 0x80); // Load previous buffer\n+ memcpy(m_last[0], m_last[1], 0x80); // Update history\n+\n+ m_lastptr[1] = *buffer_length; // Swap lengths\n+ *buffer_length = m_lastptr[0];\n+ m_lastptr[0] = m_lastptr[1];\n+}\n+\n+int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length)\n+{\n+ const auto& serial_interface = m_system.GetSerialInterface();\n+ u32 buffer_length = ConvertSILengthField(serial_interface.GetInLength());\n+\n+ // Debug logging\n+ ISIDevice::RunBuffer(buffer, buffer_length);\n+\n+ u32 buffer_position = 0;\n+ while (buffer_position < buffer_length)\n+ {\n+ BaseBoardCommand command = static_cast<BaseBoardCommand>(buffer[buffer_position]);\n+ buffer_position++;\n+\n+ switch (command)\n+ {\n+ case BaseBoardCommand::GCAM_Reset: // Returns ID and dip switches\n+ {\n+ const u32 id = Common::swap32(SI_AM_BASEBOARD | 0x100);\n+ std::memcpy(buffer, &id, sizeof(id));\n+ return sizeof(id);\n+ }\n+ break;\n+ case BaseBoardCommand::GCAM_Command:\n+ {\n+ u32 checksum = 0;\n+ for (u32 i = 0; i < buffer_length; ++i)\n+ checksum += buffer[i];\n+\n+ std::array<u8, 0x80> data_out{};\n+ u32 data_offset = 0;\n+\n+ static u32 dip_switch_1 = 0xFE;\n+ static u32 dip_switch_0 = 0xFF;\n+\n+ data_out[data_offset++] = 1;\n+ data_out[data_offset++] = 1;\n+\n+ u8* data_in = buffer + 2;\n+ if (buffer_position >= buffer_length)\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: buffer overflow (position={}, length={})",\n+ buffer_position, buffer_length);\n+ buffer_position = buffer_length;\n+ break;\n+ }\n+\n+ const u32 requested_size = buffer[buffer_position] + 2;\n+ if (requested_size > buffer_length)\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: requested size ({}) bigger than buffer\'s ({})",\n+ requested_size, buffer_length);\n+ buffer_position = buffer_length;\n+ break;\n+ }\n+ u8* const data_in_end = buffer + requested_size;\n+\n+ // Helper to check that iterating over data n times is safe,\n+ // i.e. *data++ at most lead to data.end()\n+ auto validate_data_in_out = [&](u32 n_in, u32 n_out, std::string_view command) -> bool {\n+ if (data_in + n_in > data_in_end)\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: data_in overflow in {}", command);\n+ else if (u64{data_offset} + n_out > data_out.size())\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: data_out overflow in {}", command);\n+ else\n+ return true;\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "Overflow details:\\n"\n+ " - data_in(begin={}, current={}, end={}, n_in={})\\n"\n+ " - data_out(offset={}, size={}, n_out={})\\n"\n+ " - buffer(position={}, length={})",\n+ fmt::ptr(buffer + 2), fmt::ptr(data_in), fmt::ptr(data_in_end), n_in,\n+ data_offset, data_out.size(), n_out, buffer_position, buffer_length);\n+ data_in = data_in_end;\n+ return false;\n+ };\n+\n+ while (data_in < data_in_end)\n+ {\n+ const u32 gcam_command = *data_in++;\n+ switch (GCAMCommand(gcam_command))\n+ {\n+ case GCAMCommand::StatusSwitches:\n+ {\n+ if (!validate_data_in_out(1, 4, "StatusSwitches"))\n+ break;\n+\n+ const u8 status = *data_in++;\n+ DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x10, {:02x} (READ STATUS&SWITCHES)",\n+ status);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x2;\n+\n+ // We read Test/Service from the JVS-I/O SwitchesInput instead\n+ //\n+ // const GCPadStatus pad_status = Pad::GetStatus(ISIDevice::m_device_number);\n+ // baseboard test/service switches\n+ // if (pad_status.button & PAD_BUTTON_Y)\t// Test\n+ // dip_switch_0 &= ~0x80;\n+ // if (pad_status.button & PAD_BUTTON_X)\t// Service\n+ // dip_switch_0 &= ~0x40;\n+\n+ // Horizontal Scanning Frequency switch\n+ // Required for F-Zero AX booting via Sega Boot\n+ if (AMMediaboard::GetGameType() == FZeroAX ||\n+ AMMediaboard::GetGameType() == FZeroAXMonster)\n+ {\n+ dip_switch_0 &= ~0x20;\n+ }\n+\n+ // Disable camera in MKGP1/2\n+ if (AMMediaboard::GetGameType() == MarioKartGP ||\n+ AMMediaboard::GetGameType() == MarioKartGP2)\n+ {\n+ dip_switch_0 &= ~0x10;\n+ }\n+\n+ data_out[data_offset++] = dip_switch_0;\n+ data_out[data_offset++] = dip_switch_1;\n+ break;\n+ }\n+ case GCAMCommand::SerialNumber:\n+ {\n+ if (!validate_data_in_out(1, 18, "SerialNumber"))\n+ break;\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x11, {:02x} (READ SERIAL NR)",\n+ *data_in);\n+ data_in++;\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 16;\n+ memcpy(data_out.data() + data_offset, "AADE-01B98394904", 16);\n+\n+ data_offset += 16;\n+ break;\n+ }\n+ case GCAMCommand::Unknown_12:\n+ if (!validate_data_in_out(2, 2, "Unknown_12"))\n+ break;\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x12, {:02x} {:02x}", data_in[0],\n+ data_in[1]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x00;\n+\n+ data_in += 2;\n+ break;\n+ case GCAMCommand::Unknown_14:\n+ if (!validate_data_in_out(2, 2, "Unknown_14"))\n+ break;\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x14, {:02x} {:02x}", data_in[0],\n+ data_in[1]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x00;\n+\n+ data_in += 2;\n+ break;\n+ case GCAMCommand::FirmVersion:\n+ if (!validate_data_in_out(1, 4, "FirmVersion"))\n+ break;\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x15, {:02x} (READ FIRM VERSION)",\n+ *data_in);\n+ data_in++;\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x02;\n+ // 00.26\n+ data_out[data_offset++] = 0x00;\n+ data_out[data_offset++] = 0x26;\n+ break;\n+ case GCAMCommand::FPGAVersion:\n+ if (!validate_data_in_out(1, 4, "FPGAVersion"))\n+ break;\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x16, {:02x} (READ FPGA VERSION)",\n+ *data_in);\n+ data_in++;\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x02;\n+ // 07.06\n+ data_out[data_offset++] = 0x07;\n+ data_out[data_offset++] = 0x06;\n+ break;\n+ case GCAMCommand::RegionSettings:\n+ {\n+ if (!validate_data_in_out(5, 0x16, "RegionSettings"))\n+ break;\n+\n+ // Used by SegaBoot for region checks (dev mode skips this check)\n+ // In some games this also controls the displayed language\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x1F, {:02x} {:02x} {:02x} {:02x} {:02x} (REGION)",\n+ data_in[0], data_in[1], data_in[2], data_in[3], data_in[4]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x14;\n+\n+ for (int i = 0; i < 0x14; ++i)\n+ data_out[data_offset++] = s_region_flags[i];\n+\n+ data_in += 5;\n+ }\n+ break;\n+ // No reply\n+ // Note: Always sends three bytes even though size is set to two\n+ case GCAMCommand::Unknown_21:\n+ {\n+ if (!validate_data_in_out(4, 0, "Unknown_21"))\n+ break;\n+\n+ DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x21, {:02x}, {:02x}, {:02x}, {:02x}",\n+ data_in[0], data_in[1], data_in[2], data_in[3]);\n+ data_in += 4;\n+ }\n+ break;\n+ // No reply\n+ // Note: Always sends six bytes\n+ case GCAMCommand::Unknown_22:\n+ {\n+ if (!validate_data_in_out(7, 0, "Unknown_22"))\n+ break;\n+\n+ DEBUG_LOG_FMT(\n+ SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x22, {:02x}, {:02x}, {:02x}, {:02x}, {:02x}, {:02x}, {:02x}",\n+ data_in[0], data_in[1], data_in[2], data_in[3], data_in[4], data_in[5], data_in[6]);\n+\n+ const u32 in_size = data_in[0] + 1;\n+ if (!validate_data_in_out(in_size, 0, "Unknown_22"))\n+ break;\n+ data_in += in_size;\n+ }\n+ break;\n+ case GCAMCommand::Unknown_23:\n+ if (!validate_data_in_out(2, 2, "Unknown_23"))\n+ break;\n+\n+ DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x23, {:02x} {:02x}", data_in[0],\n+ data_in[1]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x00;\n+\n+ data_in += 2;\n+ break;\n+ case GCAMCommand::Unknown_24:\n+ if (!validate_data_in_out(2, 2, "Unknown_24"))\n+ break;\n+\n+ DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x24, {:02x} {:02x}", data_in[0],\n+ data_in[1]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x00;\n+\n+ data_in += 2;\n+ break;\n+ case GCAMCommand::SerialA:\n+ {\n+ if (!validate_data_in_out(1, 0, "SerialA"))\n+ break;\n+\n+ const u32 length = *data_in++;\n+ if (length)\n+ {\n+ if (!validate_data_in_out(length, 0, "SerialA"))\n+ break;\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x31, length=0x{:02x}, hexdump:\\n{}",\n+ length, HexDump(data_in, length));\n+\n+ // Serial - Wheel\n+ if (AMMediaboard::GetGameType() == MarioKartGP ||\n+ AMMediaboard::GetGameType() == MarioKartGP2)\n+ {\n+ if (!validate_data_in_out(10, 2, "SerialA (Wheel)"))\n+ break;\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x31, (WHEEL) {:02x}{:02x} {:02x}{:02x} {:02x} {:02x} "\n+ "{:02x} {:02x} {:02x} {:02x}",\n+ data_in[0], data_in[1], data_in[2], data_in[3], data_in[4], data_in[5],\n+ data_in[6], data_in[7], data_in[8], data_in[9]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x03;\n+\n+ switch (m_wheel_init)\n+ {\n+ case 0:\n+ if (!validate_data_in_out(0, 3, "SerialA (Wheel)"))\n+ break;\n+ data_out[data_offset++] = \'E\'; // Error\n+ data_out[data_offset++] = \'0\';\n+ data_out[data_offset++] = \'0\';\n+ m_wheel_init++;\n+ break;\n+ case 1:\n+ if (!validate_data_in_out(0, 3, "SerialA (Wheel)"))\n+ break;\n+ data_out[data_offset++] = \'C\'; // Power Off\n+ data_out[data_offset++] = \'0\';\n+ data_out[data_offset++] = \'6\';\n+ // Only turn on when a wheel is connected\n+ if (serial_interface.GetDeviceType(1) == SerialInterface::SIDEVICE_GC_STEERING)\n+ {\n+ m_wheel_init++;\n+ }\n+ break;\n+ case 2:\n+ if (!validate_data_in_out(0, 3, "SerialA (Wheel)"))\n+ break;\n+ data_out[data_offset++] = \'C\'; // Power On\n+ data_out[data_offset++] = \'0\';\n+ data_out[data_offset++] = \'1\';\n+ break;\n+ default:\n+ break;\n+ }\n+\n+ // u16 CenteringForce= ptr(6);\n+ // u16 FrictionForce = ptr(8);\n+ // u16 Roll = ptr(10);\n+ if (!validate_data_in_out(length, 0, "SerialA (Wheel)"))\n+ break;\n+ data_in += length;\n+ break;\n+ }\n+\n+ // Serial - Unknown\n+ if (AMMediaboard::GetGameType() == GekitouProYakyuu)\n+ {\n+ if (!validate_data_in_out(sizeof(u32), 0, "SerialA (Unknown)"))\n+ break;\n+ const u32 serial_command = Common::BitCastPtr<u32>(data_in);\n+\n+ if (serial_command == 0x00001000)\n+ {\n+ if (!validate_data_in_out(0, 5, "SerialA (Unknown)"))\n+ break;\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x03;\n+ data_out[data_offset++] = 1;\n+ data_out[data_offset++] = 2;\n+ data_out[data_offset++] = 3;\n+ }\n+\n+ if (!validate_data_in_out(length, 0, "SerialA (Unknown)"))\n+ break;\n+ data_in += length;\n+ break;\n+ }\n+\n+ // Serial IC-CARD / Serial Deck Reader\n+ if (AMMediaboard::GetGameType() == VirtuaStriker4 ||\n+ AMMediaboard::GetGameType() == VirtuaStriker4_2006 ||\n+ AMMediaboard::GetGameType() == KeyOfAvalon)\n+ {\n+ if (!validate_data_in_out(2, 0, "SerialA (IC-CARD)"))\n+ break;\n+ u32 serial_command = data_in[1];\n+\n+ ICCommand icco;\n+\n+ // Set default reply\n+ icco.pktcmd = gcam_command;\n+ icco.pktlen = 7;\n+ icco.fixed = 0x10;\n+ icco.command = serial_command;\n+ icco.flag = 0;\n+ icco.length = 2;\n+ icco.status = 0;\n+ icco.extlen = 0;\n+\n+ // Check for rest of data from the write pages command\n+ if (m_ic_write_size && m_ic_write_offset)\n+ {\n+ const u32 size = data_in[1];\n+\n+ if (!validate_data_in_out(size + 2, 0, "SerialA (IC-CARD)"))\n+ break;\n+ DEBUG_LOG_FMT(SERIALINTERFACE_CARD, "Command: {}", HexDump(data_in, size + 2));\n+\n+ INFO_LOG_FMT(\n+ SERIALINTERFACE_CARD,\n+ "GC-AM: Command 25 (IC-CARD) Write Pages: Off:{:x} Size:{:x} PSize:{:x}",\n+ m_ic_write_offset, m_ic_write_size, size);\n+\n+ if (u64{m_ic_write_offset} + size > sizeof(m_ic_write_buffer))\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 25 (IC-CARD) m_ic_write_buffer overflow:\\n"\n+ " - m_ic_write_buffer(offset={}, size={})\\n"\n+ " - size={}\\n",\n+ m_ic_write_offset, sizeof(m_ic_write_buffer), size);\n+ data_in = data_in_end;\n+ break;\n+ }\n+ memcpy(m_ic_write_buffer + m_ic_write_offset, data_in + 2, size);\n+\n+ m_ic_write_offset += size;\n+\n+ if (m_ic_write_offset > m_ic_write_size)\n+ {\n+ m_ic_write_offset = 0;\n+\n+ const u16 page = m_ic_write_buffer[5];\n+ const u16 count = m_ic_write_buffer[7];\n+\n+ if ((page * 8 + count * 8) > sizeof(m_ic_card_data) ||\n+ (10 + count * 8) > sizeof(m_ic_write_buffer))\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 25 (IC-CARD) Write Pages overflow:\\n"\n+ " - m_ic_card_data(offset={}, size={})\\n"\n+ " - m_ic_write_buffer(offset={}, size={})\\n"\n+ " - size={}, page={}, count={}\\n",\n+ page * 8, sizeof(m_ic_card_data), 10, sizeof(m_ic_write_buffer),\n+ count * 8, page, count);\n+ data_in = data_in_end;\n+ break;\n+ }\n+ memcpy(m_ic_card_data + page * 8, m_ic_write_buffer + 10, count * 8);\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 25 (IC-CARD) Write Pages:{} Count:{}({:x})", page,\n+ count, size);\n+\n+ icco.command = WritePages;\n+\n+ if (!validate_data_in_out(0, icco.pktlen + 2, "SerialA (IC-CARD)"))\n+ break;\n+ ICCardSendReply(&icco, data_out.data(), &data_offset);\n+ }\n+ if (!validate_data_in_out(length, 0, "SerialA (IC-CARD)"))\n+ break;\n+ data_in += length;\n+ break;\n+ }\n+\n+ switch (ICCARDCommand(serial_command))\n+ {\n+ case ICCARDCommand::GetStatus:\n+ icco.status = m_ic_card_state;\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (IC-CARD) Get Status:{:02x}", m_ic_card_state);\n+ break;\n+ case ICCARDCommand::SetBaudrate:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (IC-CARD) Set Baudrate");\n+ break;\n+ case ICCARDCommand::FieldOn:\n+ m_ic_card_state |= 0x10;\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (IC-CARD) Field On");\n+ break;\n+ case ICCARDCommand::InsertCheck:\n+ icco.status = m_ic_card_status;\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (IC-CARD) Insert Check:{:02x}", m_ic_card_status);\n+ break;\n+ case ICCARDCommand::AntiCollision:\n+ icco.extlen = 8;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ // Card ID\n+ icco.extdata[0] = 0x00;\n+ icco.extdata[1] = 0x00;\n+ icco.extdata[2] = 0x54;\n+ icco.extdata[3] = 0x4D;\n+ icco.extdata[4] = 0x50;\n+ icco.extdata[5] = 0x00;\n+ icco.extdata[6] = 0x00;\n+ icco.extdata[7] = 0x00;\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (IC-CARD) Anti Collision");\n+ break;\n+ case ICCARDCommand::SelectCard:\n+ icco.extlen = 8;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ // Session\n+ icco.extdata[0] = 0x00;\n+ icco.extdata[1] = m_ic_card_session;\n+ icco.extdata[2] = 0x00;\n+ icco.extdata[3] = 0x00;\n+ icco.extdata[4] = 0x00;\n+ icco.extdata[5] = 0x00;\n+ icco.extdata[6] = 0x00;\n+ icco.extdata[7] = 0x00;\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (IC-CARD) Select Card:{}",\n+ m_ic_card_session);\n+ break;\n+ case ICCARDCommand::ReadPage:\n+ case ICCARDCommand::ReadUseCount:\n+ {\n+ if (!validate_data_in_out(8, 0, "SerialA (IC-CARD)"))\n+ break;\n+ const u16 page = Common::swap16(data_in + 6) & 0xFF; // 255 is max page\n+\n+ icco.extlen = 8;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ memcpy(icco.extdata, m_ic_card_data + page * 8, 8);\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 31 (IC-CARD) Read Page:{}",\n+ page);\n+ break;\n+ }\n+ case ICCARDCommand::WritePage:\n+ {\n+ if (!validate_data_in_out(10, 0, "SerialA (IC-CARD)"))\n+ break;\n+ const u16 page = Common::swap16(data_in + 8) & 0xFF; // 255 is max page\n+\n+ // Write only one page\n+ if (page == 4)\n+ {\n+ icco.status = 0x80;\n+ }\n+ else\n+ {\n+ if (!validate_data_in_out(18, 0, "SerialA (IC-CARD)"))\n+ break;\n+ memcpy(m_ic_card_data + page * 8, data_in + 10, 8);\n+ }\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (IC-CARD) Write Page:{}",\n+ page);\n+ break;\n+ }\n+ case ICCARDCommand::DecreaseUseCount:\n+ {\n+ if (!validate_data_in_out(8, 0, "SerialA (IC-CARD)"))\n+ break;\n+ const u16 page = Common::swap16(data_in + 6) & 0xFF; // 255 is max page\n+\n+ icco.extlen = 2;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ auto ic_card_data = Common::BitCastPtr<u16>(m_ic_card_data + 0x28);\n+ ic_card_data = ic_card_data - 1;\n+\n+ // Counter\n+ icco.extdata[0] = m_ic_card_data[0x28];\n+ icco.extdata[1] = m_ic_card_data[0x29];\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 31 (IC-CARD) Decrease Use Count:{}", page);\n+ break;\n+ }\n+ case ICCARDCommand::ReadPages:\n+ {\n+ if (!validate_data_in_out(10, 0, "SerialA (IC-CARD)"))\n+ break;\n+ const u16 page = Common::swap16(data_in + 6) & 0xFF; // 255 is max page\n+ const u16 count = Common::swap16(data_in + 8);\n+\n+ const u32 offs = page * 8;\n+ u32 cnt = count * 8;\n+\n+ // Limit read size to not overwrite the reply buffer\n+ const std::size_t reply_buffer_size = sizeof(icco.extdata) - 1;\n+ if (data_offset > reply_buffer_size)\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 31 (IC-CARD) Read Pages overflow:"\n+ " offset={} > buffer_size={}",\n+ data_offset, reply_buffer_size);\n+ data_in = data_in_end;\n+ break;\n+ }\n+ if (cnt > reply_buffer_size - data_offset)\n+ {\n+ cnt = 5 * 8;\n+ }\n+\n+ icco.extlen = cnt;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ if (offs + cnt > sizeof(icco.extdata))\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 31 (IC-CARD) Read Pages overflow:"\n+ " offset={} + count={} > buffer_size={}",\n+ offs, cnt, sizeof(icco.extdata));\n+ data_in = data_in_end;\n+ break;\n+ }\n+ memcpy(icco.extdata, m_ic_card_data + offs, cnt);\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 31 (IC-CARD) Read Pages:{} Count:{}", page, count);\n+ break;\n+ }\n+ case ICCARDCommand::WritePages:\n+ {\n+ if (!validate_data_in_out(10, 0, "SerialA (IC-CARD)"))\n+ break;\n+ const u16 pksize = length;\n+ const u16 size = Common::swap16(data_in + 2);\n+ const u16 page = Common::swap16(data_in + 6) & 0xFF; // 255 is max page\n+ const u16 count = Common::swap16(data_in + 8);\n+\n+ // We got a complete packet\n+ if (pksize - 5 == size)\n+ {\n+ if (page == 4) // Read Only Page, must return error\n+ {\n+ icco.status = 0x80;\n+ }\n+ else\n+ {\n+ if (page * 8 + count * 8 > sizeof(m_ic_card_data))\n+ {\n+ ERROR_LOG_FMT(\n+ SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (IC-CARD) Data overflow: Pages:{} Count:{}({:x})",\n+ page, count, size);\n+ }\n+ else\n+ {\n+ if (!validate_data_in_out(13 + count * 8, 0, "SerialA (IC-CARD)"))\n+ break;\n+ memcpy(m_ic_card_data + page * 8, data_in + 13, count * 8);\n+ }\n+ }\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (IC-CARD) Write Pages:{} Count:{}({:x})", page,\n+ count, size);\n+ }\n+ // VirtuaStriker 4 splits the writes over multiple packets\n+ else\n+ {\n+ if (!validate_data_in_out(2 + pksize, 0, "SerialA (IC-CARD)"))\n+ break;\n+ memcpy(m_ic_write_buffer, data_in + 2, pksize);\n+ m_ic_write_offset += pksize;\n+ m_ic_write_size = size;\n+ }\n+ break;\n+ }\n+ default:\n+ // Handle Deck Reader commands\n+ if (!validate_data_in_out(1, 0, "SerialA (DECK READER)"))\n+ break;\n+ serial_command = data_in[0];\n+ icco.command = serial_command;\n+ icco.flag = 0;\n+ switch (CDReaderCommand(serial_command))\n+ {\n+ case CDReaderCommand::ProgramVersion:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Program Version");\n+\n+ icco.extlen = (u32)strlen(s_cdr_program_version);\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ memcpy(icco.extdata, s_cdr_program_version, icco.extlen);\n+ break;\n+ case CDReaderCommand::BootVersion:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Boot Version");\n+\n+ icco.extlen = (u32)strlen(s_cdr_boot_version);\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ memcpy(icco.extdata, s_cdr_boot_version, icco.extlen);\n+ break;\n+ case CDReaderCommand::ShutterGet:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Shutter Get");\n+\n+ icco.extlen = 4;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ icco.extdata[0] = 0;\n+ icco.extdata[1] = 0;\n+ icco.extdata[2] = 0;\n+ icco.extdata[3] = 0;\n+ break;\n+ case CDReaderCommand::CameraCheck:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Camera Check");\n+\n+ icco.extlen = 6;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ icco.extdata[0] = 0x23;\n+ icco.extdata[1] = 0x28;\n+ icco.extdata[2] = 0x45;\n+ icco.extdata[3] = 0x29;\n+ icco.extdata[4] = 0x45;\n+ icco.extdata[5] = 0x29;\n+ break;\n+ case CDReaderCommand::ProgramChecksum:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Program Checksum");\n+\n+ icco.extlen = 4;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ icco.extdata[0] = 0x23;\n+ icco.extdata[1] = 0x28;\n+ icco.extdata[2] = 0x45;\n+ icco.extdata[3] = 0x29;\n+ break;\n+ case CDReaderCommand::BootChecksum:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Boot Checksum");\n+\n+ icco.extlen = 4;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ icco.extdata[0] = 0x23;\n+ icco.extdata[1] = 0x28;\n+ icco.extdata[2] = 0x45;\n+ icco.extdata[3] = 0x29;\n+ break;\n+ case CDReaderCommand::SelfTest:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (DECK READER) Self Test");\n+ icco.flag = 0x00;\n+ break;\n+ case CDReaderCommand::SensLock:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (DECK READER) Sens Lock");\n+ icco.flag = 0x01;\n+ break;\n+ case CDReaderCommand::SensCard:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (DECK READER) Sens Card");\n+ break;\n+ case CDReaderCommand::ShutterCard:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Shutter Card");\n+ break;\n+ case CDReaderCommand::ReadCard:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (DECK READER) Read Card");\n+\n+ icco.fixed = 0xAA;\n+ icco.flag = 0xAA;\n+ icco.extlen = sizeof(s_cdr_card_data);\n+ icco.length = 0x72;\n+ icco.status = Common::swap16(icco.extlen);\n+\n+ icco.pktlen += icco.extlen;\n+\n+ memcpy(icco.extdata, s_cdr_card_data, sizeof(s_cdr_card_data));\n+\n+ break;\n+ default:\n+ if (!validate_data_in_out(14, 0, "SerialA (DECK READER)"))\n+ break;\n+ WARN_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (IC-Card) {:02x} {:02x} {:02x} {:02x} {:02x} "\n+ "{:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x}",\n+ data_in[2], data_in[3], data_in[4], data_in[5], data_in[6],\n+ data_in[7], data_in[8], data_in[9], data_in[10], data_in[11],\n+ data_in[12], data_in[13]);\n+ break;\n+ }\n+ break;\n+ }\n+\n+ if (!validate_data_in_out(0, icco.pktlen + 2, "SerialA (IC-CARD)"))\n+ break;\n+ ICCardSendReply(&icco, data_out.data(), &data_offset);\n+\n+ if (!validate_data_in_out(2, 0, "SerialA (IC-CARD)"))\n+ break;\n+ data_in += length;\n+ break;\n+ }\n+ }\n+\n+ u32 command_offset = 0;\n+ while (command_offset < length)\n+ {\n+ // All commands are OR\'d with 0x80\n+ // Last byte is checksum which we don\'t care about\n+ if (!validate_data_in_out(command_offset + 4, 0, "SerialA"))\n+ break;\n+ const u32 serial_command = Common::swap32(data_in + command_offset) ^ 0x80000000;\n+\n+ if (AMMediaboard::GetGameType() == FZeroAX ||\n+ AMMediaboard::GetGameType() == FZeroAXMonster)\n+ {\n+ INFO_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x31 (MOTOR) Length:{:02x} Command:{:04x}({:02x})",\n+ length, (serial_command >> 8) & 0xFFFF, serial_command >> 24);\n+ }\n+ else\n+ {\n+ INFO_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x31 (SERIAL) Command:{:06x}",\n+ serial_command);\n+\n+ if (serial_command == 0x801000)\n+ {\n+ if (!validate_data_in_out(0, 4, "SerialA"))\n+ break;\n+ data_out[data_offset++] = 0x31;\n+ data_out[data_offset++] = 0x02;\n+ data_out[data_offset++] = 0xFF;\n+ data_out[data_offset++] = 0x01;\n+ }\n+ }\n+\n+ command_offset += sizeof(u32);\n+\n+ if (AMMediaboard::GetGameType() == FZeroAX ||\n+ AMMediaboard::GetGameType() == FZeroAXMonster)\n+ {\n+ if (command_offset + 5 >= std::size(m_motor_reply))\n+ {\n+ ERROR_LOG_FMT(\n+ SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x31 (MOTOR) overflow: offset={} >= motor_reply_size={}",\n+ command_offset + 5, std::size(m_motor_reply));\n+ data_in = data_in_end;\n+ break;\n+ }\n+\n+ // Status\n+ m_motor_reply[command_offset + 2] = 0;\n+ m_motor_reply[command_offset + 3] = 0;\n+\n+ // Error\n+ m_motor_reply[command_offset + 4] = 0;\n+\n+ switch (serial_command >> 24)\n+ {\n+ case 0:\n+ case 1: // Set Maximum?\n+ case 2:\n+ break;\n+\n+ // 0x00-0x40: left\n+ // 0x40-0x80: right\n+ case 4: // Move Steering Wheel\n+ // Left\n+ if (serial_command & 0x010000)\n+ {\n+ m_motor_force_y = -((s16)serial_command & 0xFF00);\n+ }\n+ else // Right\n+ {\n+ m_motor_force_y = (serial_command - 0x4000) & 0xFF00;\n+ }\n+\n+ m_motor_force_y *= 2;\n+\n+ // FFB\n+ if (m_motor_init == 2)\n+ {\n+ if (serial_interface.GetDeviceType(1) == SerialInterface::SIDEVICE_GC_STEERING)\n+ {\n+ const GCPadStatus pad_status = Pad::GetStatus(1);\n+ if (pad_status.isConnected)\n+ {\n+ const ControlState mapped_strength = (double)(m_motor_force_y >> 8) / 127.f;\n+\n+ Pad::Rumble(1, mapped_strength);\n+ INFO_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x31 (MOTOR) mapped_strength:{}",\n+ mapped_strength);\n+ }\n+ }\n+ }\n+ break;\n+ case 6: // nice\n+ case 9:\n+ default:\n+ break;\n+ // Switch back to normal controls\n+ case 7:\n+ m_motor_init = 2;\n+ break;\n+ // Reset\n+ case 0x7F:\n+ m_motor_init = 1;\n+ memset(m_motor_reply, 0, sizeof(m_motor_reply));\n+ break;\n+ }\n+\n+ // Checksum\n+ m_motor_reply[command_offset + 5] = m_motor_reply[command_offset + 2] ^\n+ m_motor_reply[command_offset + 3] ^\n+ m_motor_reply[command_offset + 4];\n+ }\n+ }\n+\n+ if (length == 0)\n+ {\n+ if (!validate_data_in_out(length, 2, "SerialA"))\n+ break;\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x00;\n+ }\n+ else\n+ {\n+ if (m_motor_init)\n+ {\n+ // Motor\n+ m_motor_reply[0] = gcam_command;\n+ m_motor_reply[1] = length; // Same out as in size\n+\n+ const u32 reply_size = m_motor_reply[1] + 2;\n+ if (!validate_data_in_out(length, reply_size, "SerialA"))\n+ break;\n+\n+ if (reply_size > sizeof(m_motor_reply))\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command SerialA, reply_size={} too big for m_motor_reply",\n+ reply_size);\n+ data_in = data_in_end;\n+ break;\n+ }\n+ memcpy(data_out.data() + data_offset, m_motor_reply, reply_size);\n+ data_offset += reply_size;\n+ }\n+ }\n+\n+ if (!validate_data_in_out(length, 0, "SerialA"))\n+ {\n+ break;\n+ }\n+ data_in += length;\n+ break;\n+ }\n+ case GCAMCommand::SerialB:\n+ {\n+ DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 32 (CARD-Interface)");\n+ if (!validate_data_in_out(1, 0, "SerialB"))\n+ break;\n+ const u32 length = *data_in++;\n+ if (!validate_data_in_out(length, 0, "SerialB"))\n+ break;\n+ if (length)\n+ {\n+ // Send Card Reply\n+ if (length == 1 && data_in[0] == 0x05)\n+ {\n+ if (m_card_read_length)\n+ {\n+ if (!validate_data_in_out(0, 1, "SerialB"))\n+ break;\n+ data_out[data_offset++] = gcam_command;\n+ u32 read_length = m_card_read_length - m_card_read;\n+\n+ if (AMMediaboard::GetGameType() == FZeroAX)\n+ {\n+ read_length = std::min<u32>(read_length, 0x2F);\n+ }\n+\n+ if (!validate_data_in_out(0, 1, "SerialB"))\n+ break;\n+ data_out[data_offset++] = read_length; // 0x2F (max size per packet)\n+\n+ if (!validate_data_in_out(0, read_length, "SerialB"))\n+ break;\n+ if (u64{m_card_read} + read_length > sizeof(m_card_read_packet))\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command SerialB, m_card_read_packet overflow:\\n"\n+ " - m_card_read_packet = {}\\n"\n+ " - m_card_read = {}\\n"\n+ " - read_length = {}",\n+ fmt::ptr(m_card_read_packet), m_card_read, read_length);\n+ data_in = data_in_end;\n+ break;\n+ }\n+ memcpy(data_out.data() + data_offset, m_card_read_packet + m_card_read,\n+ read_length);\n+\n+ data_offset += read_length;\n+ m_card_read += read_length;\n+\n+ if (m_card_read >= m_card_read_length)\n+ m_card_read_length = 0;\n+\n+ data_in += length;\n+ break;\n+ }\n+\n+ if (!validate_data_in_out(0, 5, "SerialB"))\n+ break;\n+\n+ data_out[data_offset++] = gcam_command;\n+ const u32 command_length_offset = data_offset;\n+ data_out[data_offset++] = 0x00; // len\n+\n+ data_out[data_offset++] = 0x02;\n+ const u32 checksum_start = data_offset;\n+\n+ data_out[data_offset++] = 0x00; // 0x00 len\n+\n+ data_out[data_offset++] = m_card_command; // 0x01 command\n+\n+ switch (CARDCommand(m_card_command))\n+ {\n+ case CARDCommand::Init:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x00; // 0x02\n+ data_out[data_offset++] = 0x30; // 0x03\n+ break;\n+ case CARDCommand::GetState:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x20 | m_card_bit; // 0x02\n+\n+ // bit 0: Please take your card\n+ // bit 1: Endless waiting causes UNK_E to be called\n+ data_out[data_offset++] = 0x00; // 0x03\n+ break;\n+ case CARDCommand::Read:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x02; // 0x02\n+ data_out[data_offset++] = 0x53; // 0x03\n+ break;\n+ case CARDCommand::IsPresent:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x22; // 0x02\n+ data_out[data_offset++] = 0x30; // 0x03\n+ break;\n+ case CARDCommand::Write:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x02; // 0x02\n+ data_out[data_offset++] = 0x00; // 0x03\n+ break;\n+ case CARDCommand::SetPrintParam:\n+ case CARDCommand::RegisterFont:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x00; // 0x02\n+ data_out[data_offset++] = 0x00; // 0x03\n+ break;\n+ case CARDCommand::WriteInfo:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x02; // 0x02\n+ data_out[data_offset++] = 0x00; // 0x03\n+ break;\n+ case CARDCommand::Erase:\n+ // TODO: CARDCommand::Erase is not handled.\n+ break;\n+ case CARDCommand::Eject:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ if (AMMediaboard::GetGameType() == FZeroAX)\n+ {\n+ data_out[data_offset++] = 0x01; // 0x02\n+ }\n+ else\n+ {\n+ data_out[data_offset++] = 0x31; // 0x02\n+ }\n+ data_out[data_offset++] = 0x30; // 0x03\n+ break;\n+ case CARDCommand::Clean:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x02; // 0x02\n+ data_out[data_offset++] = 0x00; // 0x03\n+ break;\n+ case CARDCommand::Load:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x02; // 0x02\n+ data_out[data_offset++] = 0x30; // 0x03\n+ break;\n+ case CARDCommand::SetShutter:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x00; // 0x02\n+ data_out[data_offset++] = 0x00; // 0x03\n+ break;\n+ }\n+\n+ if (!validate_data_in_out(0, 3, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x30; // 0x04\n+ data_out[data_offset++] = 0x00; // 0x05\n+\n+ data_out[data_offset++] = 0x03; // 0x06\n+\n+ data_out[checksum_start] = data_offset - checksum_start; // 0x00 len\n+\n+ if (!validate_data_in_out(0, 1, "SerialB"))\n+ break;\n+ data_out[data_offset] = 0; // 0x07\n+ for (u32 i = 0; i < data_out[checksum_start]; ++i)\n+ data_out[data_offset] ^= data_out[checksum_start + i];\n+\n+ if (!validate_data_in_out(0, 1, "SerialB"))\n+ break;\n+ data_offset++;\n+\n+ data_out[command_length_offset] = data_out[checksum_start] + 2;\n+ }\n+ else\n+ {\n+ if (!validate_data_in_out(length, 0, "SerialB"))\n+ break;\n+ if (u64{m_card_offset} + length > std::size(m_card_buffer))\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command SerialB, m_card_buffer overflow:\\n"\n+ " - m_card_buffer = {}\\n"\n+ " - m_card_offset = {}\\n"\n+ " - length = {}",\n+ fmt::ptr(m_card_buffer), m_card_offset, length);\n+ data_in = data_in_end;\n+ break;\n+ }\n+ for (u32 i = 0; i < length; ++i)\n+ m_card_buffer[m_card_offset + i] = data_in[i];\n+\n+ m_card_offset += length;\n+\n+ // Check if we got a complete command\n+ if (m_card_buffer[0] == 0x02)\n+ {\n+ if (m_card_buffer[1] == m_card_offset - 2)\n+ {\n+ if (m_card_buffer[m_card_offset - 2] == 0x03)', 'path': 'Source/Core/Core/HW/SI/SI_DeviceAMBaseboard.cpp', 'position': 1345, 'original_position': 1345, 'commit_id': '5bf8fe93286ebe4f08fb469a44f259a8397c89e0', 'user': {'login': 'sepalani', 'id': 7890055, 'node_id': 'MDQ6VXNlcjc4OTAwNTU=', 'avatar_url': 'https://avatars.githubusercontent.com/u/7890055?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/sepalani', 'html_url': 'https://github.com/sepalani', 'followers_url': 'https://api.github.com/users/sepalani/followers', 'following_url': 'https://api.github.com/users/sepalani/following{/other_user}', 'gists_url': 'https://api.github.com/users/sepalani/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/sepalani/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/sepalani/subscriptions', 'organizations_url': 'https://api.github.com/users/sepalani/orgs', 'repos_url': 'https://api.github.com/users/sepalani/repos', 'events_url': 'https://api.github.com/users/sepalani/events{/privacy}', 'received_events_url': 'https://api.github.com/users/sepalani/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'body': "> I think `m_card_buffer[0]` contains the same value as `length`. If that's the case, this array offset is safe, but this is hard to reason about. If my analysis is correct, could we do something like replacing `m_card_buffer[0] == 0x02` with `length == 0x02`, or replacing `m_card_offset - 2` with a copy of `m_card_offset` from before we incremented it by `length`?\r\n\r\nCan't `length = 1` if `data_in[0] != 0x05`, so `m_card_offset - 2` be unsafe even if incremented by `length`?\r\n\r\nI'm not sure that `m_card_buffer[0]` is guaranteed to be `length` which was defined above _(before entering the if/else statement)_ as follows:\r\n```C++\r\nconst u32 length = *data_in++;\r\n```\r\nAssuming `data_in` can be arbitrary data, so can `m_card_buffer[0]` and the following:\r\n```C++\r\n// m_card_buffer[m_card_offset = 0] = data_in[0]\r\nfor (u32 i = 0; i < length; ++i)\r\n m_card_buffer[m_card_offset + i] = data_in[i];\r\n\r\n m_card_offset += length;\r\n```\r\n\r\nMoreover, `m_card_offset` isn't (re)set to 0 before the operation.", 'created_at': '2026-02-01T22:54:20Z', 'updated_at': '2026-02-01T22:54:20Z', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/13844#discussion_r2752074638', 'pull_request_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13844', 'author_association': 'MEMBER', '_links': {'self': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2752074638'}, 'html': {'href': 'https://github.com/dolphin-emu/dolphin/pull/13844#discussion_r2752074638'}, 'pull_request': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13844'}}, 'original_commit_id': '5bf8fe93286ebe4f08fb469a44f259a8397c89e0', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2752074638/reactions', 'total_count': 0, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 0, 'confused': 0, 'heart': 0, 'rocket': 0, 'eyes': 0}, 'in_reply_to_id': 2751294423}], 'type': 'gh_pull_request_review'}
2026-02-01T19:49:21.914600 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'sepalani', 'action': 'submitted', 'pr_id': 13844, 'pr_title': 'Core: Triforce support', 'state': 'commented', 'url': 'https://github.com/dolphin-emu/dolphin/pull/13844#pullrequestreview-3736546050', 'comments': [{'id': 2751867162, 'node_id': 'PRRC_kwDOALCn2M6kBika', 'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2751867162', 'pull_request_review_id': 3736546050, 'diff_hunk': '@@ -0,0 +1,2708 @@\n+// Copyright 2025 Dolphin Emulator Project\n+// SPDX-License-Identifier: GPL-2.0-or-later\n+\n+#include "Core/HW/SI/SI_DeviceAMBaseboard.h"\n+\n+#include <algorithm>\n+#include <numeric>\n+#include <string>\n+\n+#include <fmt/format.h>\n+\n+#include "Common/Buffer.h"\n+#include "Common/CommonTypes.h"\n+#include "Common/FileUtil.h"\n+#include "Common/IOFile.h"\n+#include "Common/Logging/Log.h"\n+#include "Common/MsgHandler.h"\n+#include "Common/Swap.h"\n+\n+#include "Core/Boot/Boot.h"\n+#include "Core/BootManager.h"\n+#include "Core/Config/MainSettings.h"\n+#include "Core/ConfigManager.h"\n+#include "Core/Core.h"\n+#include "Core/CoreTiming.h"\n+#include "Core/HW/DVD/AMMediaboard.h"\n+#include "Core/HW/DVD/DVDInterface.h"\n+#include "Core/HW/EXI/EXI.h"\n+#include "Core/HW/GCPad.h"\n+#include "Core/HW/MMIO.h"\n+#include "Core/HW/Memmap.h"\n+#include "Core/HW/ProcessorInterface.h"\n+#include "Core/HW/SI/SI.h"\n+#include "Core/HW/SI/SI_Device.h"\n+#include "Core/HW/SI/SI_DeviceGCController.h"\n+#include "Core/HW/SystemTimers.h"\n+#include "Core/Movie.h"\n+#include "Core/NetPlayProto.h"\n+#include "Core/System.h"\n+\n+#include "InputCommon/GCPadStatus.h"\n+\n+namespace SerialInterface\n+{\n+void JVSIOMessage::Start(int node)\n+{\n+ m_last_start = m_pointer;\n+ const u8 header[3] = {0xE0, (u8)node, 0};\n+ m_checksum = 0;\n+ AddData(header, 3, 1);\n+}\n+\n+void JVSIOMessage::AddData(const u8* dst, size_t len, int sync = 0)\n+{\n+ if (m_pointer + len >= sizeof(m_message))\n+ {\n+ PanicAlertFmt("JVSIOMessage overrun!");\n+ return;\n+ }\n+\n+ while (len--)\n+ {\n+ const u8 c = *dst++;\n+ if (!sync && ((c == 0xE0) || (c == 0xD0)))\n+ {\n+ if (m_pointer + 2 > sizeof(m_message))\n+ {\n+ PanicAlertFmt("JVSIOMessage overrun!");\n+ break;\n+ }\n+ m_message[m_pointer++] = 0xD0;\n+ m_message[m_pointer++] = c - 1;\n+ }\n+ else\n+ {\n+ if (m_pointer >= sizeof(m_message))\n+ {\n+ PanicAlertFmt("JVSIOMessage overrun!");\n+ break;\n+ }\n+ m_message[m_pointer++] = c;\n+ }\n+\n+ if (!sync)\n+ m_checksum += c;\n+ sync = 0;\n+ }\n+}\n+\n+void JVSIOMessage::AddData(const void* data, size_t len)\n+{\n+ AddData(static_cast<const u8*>(data), len);\n+}\n+\n+void JVSIOMessage::AddData(const char* data)\n+{\n+ AddData(data, strlen(data));\n+}\n+\n+void JVSIOMessage::AddData(u32 n)\n+{\n+ const u8 cs = n;\n+ AddData(&cs, 1);\n+}\n+\n+void JVSIOMessage::End()\n+{\n+ const u32 len = m_pointer - m_last_start;\n+ if (m_last_start + 2 < sizeof(m_message) && len >= 3)\n+ {\n+ m_message[m_last_start + 2] = len - 2; // assuming len <0xD0\n+ AddData(m_checksum + len - 2);\n+ }\n+ else\n+ {\n+ PanicAlertFmt("JVSIOMessage: Not enough space for checksum!");\n+ }\n+}\n+\n+static constexpr u8 CheckSumXOR(const u8* data, u32 length)\n+{\n+ return std::accumulate(data, data + length, u8{}, std::bit_xor());\n+}\n+\n+static constexpr char s_cdr_program_version[] = {" Version 1.22,2003/09/19,171-8213B"};\n+static constexpr char s_cdr_boot_version[] = {" Version 1.04,2003/06/17,171-8213B"};\n+static constexpr u8 s_cdr_card_data[] = {\n+ 0x00, 0x6E, 0x00, 0x00, 0x01, 0x00, 0x00, 0x06, 0x00, 0x00, 0x07, 0x00, 0x00, 0x0B,\n+ 0x00, 0x00, 0x0E, 0x00, 0x00, 0x10, 0x00, 0x00, 0x17, 0x00, 0x00, 0x19, 0x00, 0x00,\n+ 0x1A, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x20, 0x00,\n+ 0x00, 0x22, 0x00, 0x00, 0x23, 0x00, 0x00, 0x24, 0x00, 0x00, 0x27, 0x00, 0x00, 0x28,\n+ 0x00, 0x00, 0x2C, 0x00, 0x00, 0x2F, 0x00, 0x00, 0x34, 0x00, 0x00, 0x35, 0x00, 0x00,\n+ 0x37, 0x00, 0x00, 0x38, 0x00, 0x00, 0x39, 0x00, 0x00, 0x3D, 0x00};\n+\n+const constexpr u8 s_region_flags[] = "\\x00\\x00\\x30\\x00"\n+ // "\\x01\\xfe\\x00\\x00" // JAPAN\n+ "\\x02\\xfd\\x00\\x00" // USA\n+ //"\\x03\\xfc\\x00\\x00" // export\n+ "\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff";\n+// AM-Baseboard device on SI\n+CSIDevice_AMBaseboard::CSIDevice_AMBaseboard(Core::System& system, SIDevices device,\n+ int device_number)\n+ : ISIDevice(system, device, device_number)\n+{\n+ // Card ID\n+ m_ic_card_data[0x20] = 0x95;\n+ m_ic_card_data[0x21] = 0x71;\n+\n+ if (AMMediaboard::GetGameType() == KeyOfAvalon)\n+ {\n+ m_ic_card_data[0x22] = 0x26;\n+ m_ic_card_data[0x23] = 0x40;\n+ }\n+ else if (AMMediaboard::GetGameType() == VirtuaStriker4)\n+ {\n+ m_ic_card_data[0x22] = 0x44;\n+ m_ic_card_data[0x23] = 0x00;\n+ }\n+\n+ // Use count\n+ m_ic_card_data[0x28] = 0xFF;\n+ m_ic_card_data[0x29] = 0xFF;\n+}\n+\n+constexpr u32 SI_XFER_LENGTH_MASK = 0x7f;\n+\n+// Translate [0,1,2,...,126,127] to [128,1,2,...,126,127]\n+constexpr s32 ConvertSILengthField(u32 field)\n+{\n+ return ((field - 1) & SI_XFER_LENGTH_MASK) + 1;\n+}\n+\n+void CSIDevice_AMBaseboard::ICCardSendReply(ICCommand* iccommand, u8* buffer, u32* length)\n+{\n+ iccommand->status = Common::swap16(iccommand->status);\n+\n+ const auto iccommand_data = reinterpret_cast<const u8*>(iccommand);\n+ const u8 crc = CheckSumXOR(iccommand_data + 2, iccommand->pktlen - 1);\n+\n+ for (u32 i = 0; i < iccommand->pktlen + 1; ++i)\n+ {\n+ buffer[(*length)++] = iccommand_data[i];\n+ }\n+\n+ buffer[(*length)++] = crc;\n+}\n+\n+void CSIDevice_AMBaseboard::SwapBuffers(u8* buffer, u32* buffer_length)\n+{\n+ memcpy(m_last[1], buffer, 0x80); // Save current buffer\n+ memcpy(buffer, m_last[0], 0x80); // Load previous buffer\n+ memcpy(m_last[0], m_last[1], 0x80); // Update history\n+\n+ m_lastptr[1] = *buffer_length; // Swap lengths\n+ *buffer_length = m_lastptr[0];\n+ m_lastptr[0] = m_lastptr[1];\n+}\n+\n+int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length)\n+{\n+ const auto& serial_interface = m_system.GetSerialInterface();\n+ u32 buffer_length = ConvertSILengthField(serial_interface.GetInLength());\n+\n+ // Debug logging\n+ ISIDevice::RunBuffer(buffer, buffer_length);\n+\n+ u32 buffer_position = 0;\n+ while (buffer_position < buffer_length)\n+ {\n+ BaseBoardCommand command = static_cast<BaseBoardCommand>(buffer[buffer_position]);\n+ buffer_position++;\n+\n+ switch (command)\n+ {\n+ case BaseBoardCommand::GCAM_Reset: // Returns ID and dip switches\n+ {\n+ const u32 id = Common::swap32(SI_AM_BASEBOARD | 0x100);\n+ std::memcpy(buffer, &id, sizeof(id));\n+ return sizeof(id);\n+ }\n+ break;\n+ case BaseBoardCommand::GCAM_Command:\n+ {\n+ u32 checksum = 0;\n+ for (u32 i = 0; i < buffer_length; ++i)\n+ checksum += buffer[i];\n+\n+ std::array<u8, 0x80> data_out{};\n+ u32 data_offset = 0;\n+\n+ static u32 dip_switch_1 = 0xFE;\n+ static u32 dip_switch_0 = 0xFF;\n+\n+ data_out[data_offset++] = 1;\n+ data_out[data_offset++] = 1;\n+\n+ u8* data_in = buffer + 2;\n+ if (buffer_position >= buffer_length)\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: buffer overflow (position={}, length={})",\n+ buffer_position, buffer_length);\n+ buffer_position = buffer_length;\n+ break;\n+ }\n+\n+ const u32 requested_size = buffer[buffer_position] + 2;\n+ if (requested_size > buffer_length)\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: requested size ({}) bigger than buffer\'s ({})",\n+ requested_size, buffer_length);\n+ buffer_position = buffer_length;\n+ break;\n+ }\n+ u8* const data_in_end = buffer + requested_size;\n+\n+ // Helper to check that iterating over data n times is safe,\n+ // i.e. *data++ at most lead to data.end()\n+ auto validate_data_in_out = [&](u32 n_in, u32 n_out, std::string_view command) -> bool {\n+ if (data_in + n_in > data_in_end)\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: data_in overflow in {}", command);\n+ else if (u64{data_offset} + n_out > data_out.size())\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: data_out overflow in {}", command);\n+ else\n+ return true;\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "Overflow details:\\n"\n+ " - data_in(begin={}, current={}, end={}, n_in={})\\n"\n+ " - data_out(offset={}, size={}, n_out={})\\n"\n+ " - buffer(position={}, length={})",\n+ fmt::ptr(buffer + 2), fmt::ptr(data_in), fmt::ptr(data_in_end), n_in,\n+ data_offset, data_out.size(), n_out, buffer_position, buffer_length);\n+ data_in = data_in_end;\n+ return false;\n+ };\n+\n+ while (data_in < data_in_end)\n+ {\n+ const u32 gcam_command = *data_in++;\n+ switch (GCAMCommand(gcam_command))\n+ {\n+ case GCAMCommand::StatusSwitches:\n+ {\n+ if (!validate_data_in_out(1, 4, "StatusSwitches"))\n+ break;\n+\n+ const u8 status = *data_in++;\n+ DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x10, {:02x} (READ STATUS&SWITCHES)",\n+ status);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x2;\n+\n+ // We read Test/Service from the JVS-I/O SwitchesInput instead\n+ //\n+ // const GCPadStatus pad_status = Pad::GetStatus(ISIDevice::m_device_number);\n+ // baseboard test/service switches\n+ // if (pad_status.button & PAD_BUTTON_Y)\t// Test\n+ // dip_switch_0 &= ~0x80;\n+ // if (pad_status.button & PAD_BUTTON_X)\t// Service\n+ // dip_switch_0 &= ~0x40;\n+\n+ // Horizontal Scanning Frequency switch\n+ // Required for F-Zero AX booting via Sega Boot\n+ if (AMMediaboard::GetGameType() == FZeroAX ||\n+ AMMediaboard::GetGameType() == FZeroAXMonster)\n+ {\n+ dip_switch_0 &= ~0x20;\n+ }\n+\n+ // Disable camera in MKGP1/2\n+ if (AMMediaboard::GetGameType() == MarioKartGP ||\n+ AMMediaboard::GetGameType() == MarioKartGP2)\n+ {\n+ dip_switch_0 &= ~0x10;\n+ }\n+\n+ data_out[data_offset++] = dip_switch_0;\n+ data_out[data_offset++] = dip_switch_1;\n+ break;\n+ }\n+ case GCAMCommand::SerialNumber:\n+ {\n+ if (!validate_data_in_out(1, 18, "SerialNumber"))\n+ break;\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x11, {:02x} (READ SERIAL NR)",\n+ *data_in);\n+ data_in++;\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 16;\n+ memcpy(data_out.data() + data_offset, "AADE-01B98394904", 16);\n+\n+ data_offset += 16;\n+ break;\n+ }\n+ case GCAMCommand::Unknown_12:\n+ if (!validate_data_in_out(2, 2, "Unknown_12"))\n+ break;\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x12, {:02x} {:02x}", data_in[0],\n+ data_in[1]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x00;\n+\n+ data_in += 2;\n+ break;\n+ case GCAMCommand::Unknown_14:\n+ if (!validate_data_in_out(2, 2, "Unknown_14"))\n+ break;\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x14, {:02x} {:02x}", data_in[0],\n+ data_in[1]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x00;\n+\n+ data_in += 2;\n+ break;\n+ case GCAMCommand::FirmVersion:\n+ if (!validate_data_in_out(1, 4, "FirmVersion"))\n+ break;\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x15, {:02x} (READ FIRM VERSION)",\n+ *data_in);\n+ data_in++;\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x02;\n+ // 00.26\n+ data_out[data_offset++] = 0x00;\n+ data_out[data_offset++] = 0x26;\n+ break;\n+ case GCAMCommand::FPGAVersion:\n+ if (!validate_data_in_out(1, 4, "FPGAVersion"))\n+ break;\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x16, {:02x} (READ FPGA VERSION)",\n+ *data_in);\n+ data_in++;\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x02;\n+ // 07.06\n+ data_out[data_offset++] = 0x07;\n+ data_out[data_offset++] = 0x06;\n+ break;\n+ case GCAMCommand::RegionSettings:\n+ {\n+ if (!validate_data_in_out(5, 0x16, "RegionSettings"))\n+ break;\n+\n+ // Used by SegaBoot for region checks (dev mode skips this check)\n+ // In some games this also controls the displayed language\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x1F, {:02x} {:02x} {:02x} {:02x} {:02x} (REGION)",\n+ data_in[0], data_in[1], data_in[2], data_in[3], data_in[4]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x14;\n+\n+ for (int i = 0; i < 0x14; ++i)\n+ data_out[data_offset++] = s_region_flags[i];\n+\n+ data_in += 5;\n+ }\n+ break;\n+ // No reply\n+ // Note: Always sends three bytes even though size is set to two\n+ case GCAMCommand::Unknown_21:\n+ {\n+ if (!validate_data_in_out(4, 0, "Unknown_21"))\n+ break;\n+\n+ DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x21, {:02x}, {:02x}, {:02x}, {:02x}",\n+ data_in[0], data_in[1], data_in[2], data_in[3]);\n+ data_in += 4;\n+ }\n+ break;\n+ // No reply\n+ // Note: Always sends six bytes\n+ case GCAMCommand::Unknown_22:\n+ {\n+ if (!validate_data_in_out(7, 0, "Unknown_22"))\n+ break;\n+\n+ DEBUG_LOG_FMT(\n+ SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x22, {:02x}, {:02x}, {:02x}, {:02x}, {:02x}, {:02x}, {:02x}",\n+ data_in[0], data_in[1], data_in[2], data_in[3], data_in[4], data_in[5], data_in[6]);\n+\n+ const u32 in_size = data_in[0] + 1;\n+ if (!validate_data_in_out(in_size, 0, "Unknown_22"))\n+ break;\n+ data_in += in_size;\n+ }\n+ break;\n+ case GCAMCommand::Unknown_23:\n+ if (!validate_data_in_out(2, 2, "Unknown_23"))\n+ break;\n+\n+ DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x23, {:02x} {:02x}", data_in[0],\n+ data_in[1]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x00;\n+\n+ data_in += 2;\n+ break;\n+ case GCAMCommand::Unknown_24:\n+ if (!validate_data_in_out(2, 2, "Unknown_24"))\n+ break;\n+\n+ DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x24, {:02x} {:02x}", data_in[0],\n+ data_in[1]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x00;\n+\n+ data_in += 2;\n+ break;\n+ case GCAMCommand::SerialA:\n+ {\n+ if (!validate_data_in_out(1, 0, "SerialA"))\n+ break;\n+\n+ const u32 length = *data_in++;\n+ if (length)\n+ {\n+ if (!validate_data_in_out(length, 0, "SerialA"))\n+ break;\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x31, length=0x{:02x}, hexdump:\\n{}",\n+ length, HexDump(data_in, length));\n+\n+ // Serial - Wheel\n+ if (AMMediaboard::GetGameType() == MarioKartGP ||\n+ AMMediaboard::GetGameType() == MarioKartGP2)\n+ {\n+ if (!validate_data_in_out(10, 2, "SerialA (Wheel)"))\n+ break;\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x31, (WHEEL) {:02x}{:02x} {:02x}{:02x} {:02x} {:02x} "\n+ "{:02x} {:02x} {:02x} {:02x}",\n+ data_in[0], data_in[1], data_in[2], data_in[3], data_in[4], data_in[5],\n+ data_in[6], data_in[7], data_in[8], data_in[9]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x03;\n+\n+ switch (m_wheel_init)\n+ {\n+ case 0:\n+ if (!validate_data_in_out(0, 3, "SerialA (Wheel)"))\n+ break;\n+ data_out[data_offset++] = \'E\'; // Error\n+ data_out[data_offset++] = \'0\';\n+ data_out[data_offset++] = \'0\';\n+ m_wheel_init++;\n+ break;\n+ case 1:\n+ if (!validate_data_in_out(0, 3, "SerialA (Wheel)"))\n+ break;\n+ data_out[data_offset++] = \'C\'; // Power Off\n+ data_out[data_offset++] = \'0\';\n+ data_out[data_offset++] = \'6\';\n+ // Only turn on when a wheel is connected\n+ if (serial_interface.GetDeviceType(1) == SerialInterface::SIDEVICE_GC_STEERING)\n+ {\n+ m_wheel_init++;\n+ }\n+ break;\n+ case 2:\n+ if (!validate_data_in_out(0, 3, "SerialA (Wheel)"))\n+ break;\n+ data_out[data_offset++] = \'C\'; // Power On\n+ data_out[data_offset++] = \'0\';\n+ data_out[data_offset++] = \'1\';\n+ break;\n+ default:\n+ break;\n+ }\n+\n+ // u16 CenteringForce= ptr(6);\n+ // u16 FrictionForce = ptr(8);\n+ // u16 Roll = ptr(10);\n+ if (!validate_data_in_out(length, 0, "SerialA (Wheel)"))\n+ break;\n+ data_in += length;\n+ break;\n+ }\n+\n+ // Serial - Unknown\n+ if (AMMediaboard::GetGameType() == GekitouProYakyuu)\n+ {\n+ if (!validate_data_in_out(sizeof(u32), 0, "SerialA (Unknown)"))\n+ break;\n+ const u32 serial_command = Common::BitCastPtr<u32>(data_in);\n+\n+ if (serial_command == 0x00001000)\n+ {\n+ if (!validate_data_in_out(0, 5, "SerialA (Unknown)"))\n+ break;\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x03;\n+ data_out[data_offset++] = 1;\n+ data_out[data_offset++] = 2;\n+ data_out[data_offset++] = 3;\n+ }\n+\n+ if (!validate_data_in_out(length, 0, "SerialA (Unknown)"))\n+ break;\n+ data_in += length;\n+ break;\n+ }\n+\n+ // Serial IC-CARD / Serial Deck Reader\n+ if (AMMediaboard::GetGameType() == VirtuaStriker4 ||\n+ AMMediaboard::GetGameType() == VirtuaStriker4_2006 ||\n+ AMMediaboard::GetGameType() == KeyOfAvalon)\n+ {\n+ if (!validate_data_in_out(2, 0, "SerialA (IC-CARD)"))\n+ break;\n+ u32 serial_command = data_in[1];\n+\n+ ICCommand icco;\n+\n+ // Set default reply\n+ icco.pktcmd = gcam_command;\n+ icco.pktlen = 7;\n+ icco.fixed = 0x10;\n+ icco.command = serial_command;\n+ icco.flag = 0;\n+ icco.length = 2;\n+ icco.status = 0;\n+ icco.extlen = 0;\n+\n+ // Check for rest of data from the write pages command\n+ if (m_ic_write_size && m_ic_write_offset)\n+ {\n+ const u32 size = data_in[1];\n+\n+ if (!validate_data_in_out(size + 2, 0, "SerialA (IC-CARD)"))\n+ break;\n+ DEBUG_LOG_FMT(SERIALINTERFACE_CARD, "Command: {}", HexDump(data_in, size + 2));\n+\n+ INFO_LOG_FMT(\n+ SERIALINTERFACE_CARD,\n+ "GC-AM: Command 25 (IC-CARD) Write Pages: Off:{:x} Size:{:x} PSize:{:x}",\n+ m_ic_write_offset, m_ic_write_size, size);\n+\n+ if (u64{m_ic_write_offset} + size > sizeof(m_ic_write_buffer))\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 25 (IC-CARD) m_ic_write_buffer overflow:\\n"\n+ " - m_ic_write_buffer(offset={}, size={})\\n"\n+ " - size={}\\n",\n+ m_ic_write_offset, sizeof(m_ic_write_buffer), size);\n+ data_in = data_in_end;\n+ break;\n+ }\n+ memcpy(m_ic_write_buffer + m_ic_write_offset, data_in + 2, size);\n+\n+ m_ic_write_offset += size;\n+\n+ if (m_ic_write_offset > m_ic_write_size)\n+ {\n+ m_ic_write_offset = 0;\n+\n+ const u16 page = m_ic_write_buffer[5];\n+ const u16 count = m_ic_write_buffer[7];\n+\n+ if ((page * 8 + count * 8) > sizeof(m_ic_card_data) ||\n+ (10 + count * 8) > sizeof(m_ic_write_buffer))\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 25 (IC-CARD) Write Pages overflow:\\n"\n+ " - m_ic_card_data(offset={}, size={})\\n"\n+ " - m_ic_write_buffer(offset={}, size={})\\n"\n+ " - size={}, page={}, count={}\\n",\n+ page * 8, sizeof(m_ic_card_data), 10, sizeof(m_ic_write_buffer),\n+ count * 8, page, count);\n+ data_in = data_in_end;\n+ break;\n+ }\n+ memcpy(m_ic_card_data + page * 8, m_ic_write_buffer + 10, count * 8);\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 25 (IC-CARD) Write Pages:{} Count:{}({:x})", page,\n+ count, size);\n+\n+ icco.command = WritePages;\n+\n+ if (!validate_data_in_out(0, icco.pktlen + 2, "SerialA (IC-CARD)"))\n+ break;\n+ ICCardSendReply(&icco, data_out.data(), &data_offset);\n+ }\n+ if (!validate_data_in_out(length, 0, "SerialA (IC-CARD)"))\n+ break;\n+ data_in += length;\n+ break;\n+ }\n+\n+ switch (ICCARDCommand(serial_command))\n+ {\n+ case ICCARDCommand::GetStatus:\n+ icco.status = m_ic_card_state;\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (IC-CARD) Get Status:{:02x}", m_ic_card_state);\n+ break;\n+ case ICCARDCommand::SetBaudrate:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (IC-CARD) Set Baudrate");\n+ break;\n+ case ICCARDCommand::FieldOn:\n+ m_ic_card_state |= 0x10;\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (IC-CARD) Field On");\n+ break;\n+ case ICCARDCommand::InsertCheck:\n+ icco.status = m_ic_card_status;\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (IC-CARD) Insert Check:{:02x}", m_ic_card_status);\n+ break;\n+ case ICCARDCommand::AntiCollision:\n+ icco.extlen = 8;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ // Card ID\n+ icco.extdata[0] = 0x00;\n+ icco.extdata[1] = 0x00;\n+ icco.extdata[2] = 0x54;\n+ icco.extdata[3] = 0x4D;\n+ icco.extdata[4] = 0x50;\n+ icco.extdata[5] = 0x00;\n+ icco.extdata[6] = 0x00;\n+ icco.extdata[7] = 0x00;\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (IC-CARD) Anti Collision");\n+ break;\n+ case ICCARDCommand::SelectCard:\n+ icco.extlen = 8;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ // Session\n+ icco.extdata[0] = 0x00;\n+ icco.extdata[1] = m_ic_card_session;\n+ icco.extdata[2] = 0x00;\n+ icco.extdata[3] = 0x00;\n+ icco.extdata[4] = 0x00;\n+ icco.extdata[5] = 0x00;\n+ icco.extdata[6] = 0x00;\n+ icco.extdata[7] = 0x00;\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (IC-CARD) Select Card:{}",\n+ m_ic_card_session);\n+ break;\n+ case ICCARDCommand::ReadPage:\n+ case ICCARDCommand::ReadUseCount:\n+ {\n+ if (!validate_data_in_out(8, 0, "SerialA (IC-CARD)"))\n+ break;\n+ const u16 page = Common::swap16(data_in + 6) & 0xFF; // 255 is max page\n+\n+ icco.extlen = 8;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ memcpy(icco.extdata, m_ic_card_data + page * 8, 8);\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 31 (IC-CARD) Read Page:{}",\n+ page);\n+ break;\n+ }\n+ case ICCARDCommand::WritePage:\n+ {\n+ if (!validate_data_in_out(10, 0, "SerialA (IC-CARD)"))\n+ break;\n+ const u16 page = Common::swap16(data_in + 8) & 0xFF; // 255 is max page\n+\n+ // Write only one page\n+ if (page == 4)\n+ {\n+ icco.status = 0x80;\n+ }\n+ else\n+ {\n+ if (!validate_data_in_out(18, 0, "SerialA (IC-CARD)"))\n+ break;\n+ memcpy(m_ic_card_data + page * 8, data_in + 10, 8);\n+ }\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (IC-CARD) Write Page:{}",\n+ page);\n+ break;\n+ }\n+ case ICCARDCommand::DecreaseUseCount:\n+ {\n+ if (!validate_data_in_out(8, 0, "SerialA (IC-CARD)"))\n+ break;\n+ const u16 page = Common::swap16(data_in + 6) & 0xFF; // 255 is max page\n+\n+ icco.extlen = 2;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ auto ic_card_data = Common::BitCastPtr<u16>(m_ic_card_data + 0x28);\n+ ic_card_data = ic_card_data - 1;\n+\n+ // Counter\n+ icco.extdata[0] = m_ic_card_data[0x28];\n+ icco.extdata[1] = m_ic_card_data[0x29];\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 31 (IC-CARD) Decrease Use Count:{}", page);\n+ break;\n+ }\n+ case ICCARDCommand::ReadPages:\n+ {\n+ if (!validate_data_in_out(10, 0, "SerialA (IC-CARD)"))\n+ break;\n+ const u16 page = Common::swap16(data_in + 6) & 0xFF; // 255 is max page\n+ const u16 count = Common::swap16(data_in + 8);\n+\n+ const u32 offs = page * 8;\n+ u32 cnt = count * 8;\n+\n+ // Limit read size to not overwrite the reply buffer\n+ const std::size_t reply_buffer_size = sizeof(icco.extdata) - 1;\n+ if (data_offset > reply_buffer_size)\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 31 (IC-CARD) Read Pages overflow:"\n+ " offset={} > buffer_size={}",\n+ data_offset, reply_buffer_size);\n+ data_in = data_in_end;\n+ break;\n+ }\n+ if (cnt > reply_buffer_size - data_offset)\n+ {\n+ cnt = 5 * 8;\n+ }\n+\n+ icco.extlen = cnt;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ if (offs + cnt > sizeof(icco.extdata))\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 31 (IC-CARD) Read Pages overflow:"\n+ " offset={} + count={} > buffer_size={}",\n+ offs, cnt, sizeof(icco.extdata));\n+ data_in = data_in_end;\n+ break;\n+ }\n+ memcpy(icco.extdata, m_ic_card_data + offs, cnt);\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 31 (IC-CARD) Read Pages:{} Count:{}", page, count);\n+ break;\n+ }\n+ case ICCARDCommand::WritePages:\n+ {\n+ if (!validate_data_in_out(10, 0, "SerialA (IC-CARD)"))\n+ break;\n+ const u16 pksize = length;\n+ const u16 size = Common::swap16(data_in + 2);\n+ const u16 page = Common::swap16(data_in + 6) & 0xFF; // 255 is max page\n+ const u16 count = Common::swap16(data_in + 8);\n+\n+ // We got a complete packet\n+ if (pksize - 5 == size)\n+ {\n+ if (page == 4) // Read Only Page, must return error\n+ {\n+ icco.status = 0x80;\n+ }\n+ else\n+ {\n+ if (page * 8 + count * 8 > sizeof(m_ic_card_data))\n+ {\n+ ERROR_LOG_FMT(\n+ SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (IC-CARD) Data overflow: Pages:{} Count:{}({:x})",\n+ page, count, size);\n+ }\n+ else\n+ {\n+ if (!validate_data_in_out(13 + count * 8, 0, "SerialA (IC-CARD)"))\n+ break;\n+ memcpy(m_ic_card_data + page * 8, data_in + 13, count * 8);\n+ }\n+ }\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (IC-CARD) Write Pages:{} Count:{}({:x})", page,\n+ count, size);\n+ }\n+ // VirtuaStriker 4 splits the writes over multiple packets\n+ else\n+ {\n+ if (!validate_data_in_out(2 + pksize, 0, "SerialA (IC-CARD)"))\n+ break;\n+ memcpy(m_ic_write_buffer, data_in + 2, pksize);\n+ m_ic_write_offset += pksize;\n+ m_ic_write_size = size;\n+ }\n+ break;\n+ }\n+ default:\n+ // Handle Deck Reader commands\n+ if (!validate_data_in_out(1, 0, "SerialA (DECK READER)"))\n+ break;\n+ serial_command = data_in[0];\n+ icco.command = serial_command;\n+ icco.flag = 0;\n+ switch (CDReaderCommand(serial_command))\n+ {\n+ case CDReaderCommand::ProgramVersion:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Program Version");\n+\n+ icco.extlen = (u32)strlen(s_cdr_program_version);\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ memcpy(icco.extdata, s_cdr_program_version, icco.extlen);\n+ break;\n+ case CDReaderCommand::BootVersion:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Boot Version");\n+\n+ icco.extlen = (u32)strlen(s_cdr_boot_version);\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ memcpy(icco.extdata, s_cdr_boot_version, icco.extlen);\n+ break;\n+ case CDReaderCommand::ShutterGet:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Shutter Get");\n+\n+ icco.extlen = 4;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ icco.extdata[0] = 0;\n+ icco.extdata[1] = 0;\n+ icco.extdata[2] = 0;\n+ icco.extdata[3] = 0;\n+ break;\n+ case CDReaderCommand::CameraCheck:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Camera Check");\n+\n+ icco.extlen = 6;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ icco.extdata[0] = 0x23;\n+ icco.extdata[1] = 0x28;\n+ icco.extdata[2] = 0x45;\n+ icco.extdata[3] = 0x29;\n+ icco.extdata[4] = 0x45;\n+ icco.extdata[5] = 0x29;\n+ break;\n+ case CDReaderCommand::ProgramChecksum:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Program Checksum");\n+\n+ icco.extlen = 4;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ icco.extdata[0] = 0x23;\n+ icco.extdata[1] = 0x28;\n+ icco.extdata[2] = 0x45;\n+ icco.extdata[3] = 0x29;\n+ break;\n+ case CDReaderCommand::BootChecksum:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Boot Checksum");\n+\n+ icco.extlen = 4;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ icco.extdata[0] = 0x23;\n+ icco.extdata[1] = 0x28;\n+ icco.extdata[2] = 0x45;\n+ icco.extdata[3] = 0x29;\n+ break;\n+ case CDReaderCommand::SelfTest:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (DECK READER) Self Test");\n+ icco.flag = 0x00;\n+ break;\n+ case CDReaderCommand::SensLock:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (DECK READER) Sens Lock");\n+ icco.flag = 0x01;\n+ break;\n+ case CDReaderCommand::SensCard:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (DECK READER) Sens Card");\n+ break;\n+ case CDReaderCommand::ShutterCard:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Shutter Card");\n+ break;\n+ case CDReaderCommand::ReadCard:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (DECK READER) Read Card");\n+\n+ icco.fixed = 0xAA;\n+ icco.flag = 0xAA;\n+ icco.extlen = sizeof(s_cdr_card_data);\n+ icco.length = 0x72;\n+ icco.status = Common::swap16(icco.extlen);\n+\n+ icco.pktlen += icco.extlen;\n+\n+ memcpy(icco.extdata, s_cdr_card_data, sizeof(s_cdr_card_data));\n+\n+ break;\n+ default:\n+ if (!validate_data_in_out(14, 0, "SerialA (DECK READER)"))\n+ break;\n+ WARN_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (IC-Card) {:02x} {:02x} {:02x} {:02x} {:02x} "\n+ "{:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x}",\n+ data_in[2], data_in[3], data_in[4], data_in[5], data_in[6],\n+ data_in[7], data_in[8], data_in[9], data_in[10], data_in[11],\n+ data_in[12], data_in[13]);\n+ break;\n+ }\n+ break;\n+ }\n+\n+ if (!validate_data_in_out(0, icco.pktlen + 2, "SerialA (IC-CARD)"))\n+ break;\n+ ICCardSendReply(&icco, data_out.data(), &data_offset);\n+\n+ if (!validate_data_in_out(2, 0, "SerialA (IC-CARD)"))\n+ break;\n+ data_in += length;\n+ break;\n+ }\n+ }\n+\n+ u32 command_offset = 0;\n+ while (command_offset < length)\n+ {\n+ // All commands are OR\'d with 0x80\n+ // Last byte is checksum which we don\'t care about\n+ if (!validate_data_in_out(command_offset + 4, 0, "SerialA"))\n+ break;\n+ const u32 serial_command = Common::swap32(data_in + command_offset) ^ 0x80000000;\n+\n+ if (AMMediaboard::GetGameType() == FZeroAX ||\n+ AMMediaboard::GetGameType() == FZeroAXMonster)\n+ {\n+ INFO_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x31 (MOTOR) Length:{:02x} Command:{:04x}({:02x})",\n+ length, (serial_command >> 8) & 0xFFFF, serial_command >> 24);\n+ }\n+ else\n+ {\n+ INFO_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x31 (SERIAL) Command:{:06x}",\n+ serial_command);\n+\n+ if (serial_command == 0x801000)\n+ {\n+ if (!validate_data_in_out(0, 4, "SerialA"))\n+ break;\n+ data_out[data_offset++] = 0x31;\n+ data_out[data_offset++] = 0x02;\n+ data_out[data_offset++] = 0xFF;\n+ data_out[data_offset++] = 0x01;\n+ }\n+ }\n+\n+ command_offset += sizeof(u32);\n+\n+ if (AMMediaboard::GetGameType() == FZeroAX ||\n+ AMMediaboard::GetGameType() == FZeroAXMonster)\n+ {\n+ if (command_offset + 5 >= std::size(m_motor_reply))\n+ {\n+ ERROR_LOG_FMT(\n+ SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x31 (MOTOR) overflow: offset={} >= motor_reply_size={}",\n+ command_offset + 5, std::size(m_motor_reply));\n+ data_in = data_in_end;\n+ break;\n+ }\n+\n+ // Status\n+ m_motor_reply[command_offset + 2] = 0;\n+ m_motor_reply[command_offset + 3] = 0;\n+\n+ // Error\n+ m_motor_reply[command_offset + 4] = 0;\n+\n+ switch (serial_command >> 24)\n+ {\n+ case 0:\n+ case 1: // Set Maximum?\n+ case 2:\n+ break;\n+\n+ // 0x00-0x40: left\n+ // 0x40-0x80: right\n+ case 4: // Move Steering Wheel\n+ // Left\n+ if (serial_command & 0x010000)\n+ {\n+ m_motor_force_y = -((s16)serial_command & 0xFF00);\n+ }\n+ else // Right\n+ {\n+ m_motor_force_y = (serial_command - 0x4000) & 0xFF00;\n+ }\n+\n+ m_motor_force_y *= 2;\n+\n+ // FFB\n+ if (m_motor_init == 2)\n+ {\n+ if (serial_interface.GetDeviceType(1) == SerialInterface::SIDEVICE_GC_STEERING)\n+ {\n+ const GCPadStatus pad_status = Pad::GetStatus(1);\n+ if (pad_status.isConnected)\n+ {\n+ const ControlState mapped_strength = (double)(m_motor_force_y >> 8) / 127.f;\n+\n+ Pad::Rumble(1, mapped_strength);\n+ INFO_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x31 (MOTOR) mapped_strength:{}",\n+ mapped_strength);\n+ }\n+ }\n+ }\n+ break;\n+ case 6: // nice\n+ case 9:\n+ default:\n+ break;\n+ // Switch back to normal controls\n+ case 7:\n+ m_motor_init = 2;\n+ break;\n+ // Reset\n+ case 0x7F:\n+ m_motor_init = 1;\n+ memset(m_motor_reply, 0, sizeof(m_motor_reply));\n+ break;\n+ }\n+\n+ // Checksum\n+ m_motor_reply[command_offset + 5] = m_motor_reply[command_offset + 2] ^\n+ m_motor_reply[command_offset + 3] ^\n+ m_motor_reply[command_offset + 4];\n+ }\n+ }\n+\n+ if (length == 0)\n+ {\n+ if (!validate_data_in_out(length, 2, "SerialA"))\n+ break;\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x00;\n+ }\n+ else\n+ {\n+ if (m_motor_init)\n+ {\n+ // Motor\n+ m_motor_reply[0] = gcam_command;\n+ m_motor_reply[1] = length; // Same out as in size\n+\n+ const u32 reply_size = m_motor_reply[1] + 2;\n+ if (!validate_data_in_out(length, reply_size, "SerialA"))\n+ break;\n+\n+ if (reply_size > sizeof(m_motor_reply))\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command SerialA, reply_size={} too big for m_motor_reply",\n+ reply_size);\n+ data_in = data_in_end;\n+ break;\n+ }\n+ memcpy(data_out.data() + data_offset, m_motor_reply, reply_size);\n+ data_offset += reply_size;\n+ }\n+ }\n+\n+ if (!validate_data_in_out(length, 0, "SerialA"))\n+ {\n+ break;\n+ }\n+ data_in += length;\n+ break;\n+ }\n+ case GCAMCommand::SerialB:\n+ {\n+ DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 32 (CARD-Interface)");\n+ if (!validate_data_in_out(1, 0, "SerialB"))\n+ break;\n+ const u32 length = *data_in++;\n+ if (!validate_data_in_out(length, 0, "SerialB"))\n+ break;\n+ if (length)\n+ {\n+ // Send Card Reply\n+ if (length == 1 && data_in[0] == 0x05)\n+ {\n+ if (m_card_read_length)\n+ {\n+ if (!validate_data_in_out(0, 1, "SerialB"))\n+ break;\n+ data_out[data_offset++] = gcam_command;\n+ u32 read_length = m_card_read_length - m_card_read;\n+\n+ if (AMMediaboard::GetGameType() == FZeroAX)\n+ {\n+ read_length = std::min<u32>(read_length, 0x2F);\n+ }\n+\n+ if (!validate_data_in_out(0, 1, "SerialB"))\n+ break;\n+ data_out[data_offset++] = read_length; // 0x2F (max size per packet)\n+\n+ if (!validate_data_in_out(0, read_length, "SerialB"))\n+ break;\n+ if (u64{m_card_read} + read_length > sizeof(m_card_read_packet))\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command SerialB, m_card_read_packet overflow:\\n"\n+ " - m_card_read_packet = {}\\n"\n+ " - m_card_read = {}\\n"\n+ " - read_length = {}",\n+ fmt::ptr(m_card_read_packet), m_card_read, read_length);\n+ data_in = data_in_end;\n+ break;\n+ }\n+ memcpy(data_out.data() + data_offset, m_card_read_packet + m_card_read,\n+ read_length);\n+\n+ data_offset += read_length;\n+ m_card_read += read_length;\n+\n+ if (m_card_read >= m_card_read_length)\n+ m_card_read_length = 0;\n+\n+ data_in += length;\n+ break;\n+ }\n+\n+ if (!validate_data_in_out(0, 5, "SerialB"))\n+ break;\n+\n+ data_out[data_offset++] = gcam_command;\n+ const u32 command_length_offset = data_offset;\n+ data_out[data_offset++] = 0x00; // len\n+\n+ data_out[data_offset++] = 0x02;\n+ const u32 checksum_start = data_offset;\n+\n+ data_out[data_offset++] = 0x00; // 0x00 len\n+\n+ data_out[data_offset++] = m_card_command; // 0x01 command\n+\n+ switch (CARDCommand(m_card_command))\n+ {\n+ case CARDCommand::Init:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x00; // 0x02\n+ data_out[data_offset++] = 0x30; // 0x03\n+ break;\n+ case CARDCommand::GetState:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x20 | m_card_bit; // 0x02\n+\n+ // bit 0: Please take your card\n+ // bit 1: Endless waiting causes UNK_E to be called\n+ data_out[data_offset++] = 0x00; // 0x03\n+ break;\n+ case CARDCommand::Read:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x02; // 0x02\n+ data_out[data_offset++] = 0x53; // 0x03\n+ break;\n+ case CARDCommand::IsPresent:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x22; // 0x02\n+ data_out[data_offset++] = 0x30; // 0x03\n+ break;\n+ case CARDCommand::Write:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x02; // 0x02\n+ data_out[data_offset++] = 0x00; // 0x03\n+ break;\n+ case CARDCommand::SetPrintParam:\n+ case CARDCommand::RegisterFont:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x00; // 0x02\n+ data_out[data_offset++] = 0x00; // 0x03\n+ break;\n+ case CARDCommand::WriteInfo:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x02; // 0x02\n+ data_out[data_offset++] = 0x00; // 0x03\n+ break;\n+ case CARDCommand::Erase:\n+ // TODO: CARDCommand::Erase is not handled.\n+ break;\n+ case CARDCommand::Eject:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ if (AMMediaboard::GetGameType() == FZeroAX)\n+ {\n+ data_out[data_offset++] = 0x01; // 0x02\n+ }\n+ else\n+ {\n+ data_out[data_offset++] = 0x31; // 0x02\n+ }\n+ data_out[data_offset++] = 0x30; // 0x03\n+ break;\n+ case CARDCommand::Clean:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x02; // 0x02\n+ data_out[data_offset++] = 0x00; // 0x03\n+ break;\n+ case CARDCommand::Load:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x02; // 0x02\n+ data_out[data_offset++] = 0x30; // 0x03\n+ break;\n+ case CARDCommand::SetShutter:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x00; // 0x02\n+ data_out[data_offset++] = 0x00; // 0x03\n+ break;\n+ }\n+\n+ if (!validate_data_in_out(0, 3, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x30; // 0x04\n+ data_out[data_offset++] = 0x00; // 0x05\n+\n+ data_out[data_offset++] = 0x03; // 0x06\n+\n+ data_out[checksum_start] = data_offset - checksum_start; // 0x00 len\n+\n+ if (!validate_data_in_out(0, 1, "SerialB"))\n+ break;\n+ data_out[data_offset] = 0; // 0x07\n+ for (u32 i = 0; i < data_out[checksum_start]; ++i)\n+ data_out[data_offset] ^= data_out[checksum_start + i];\n+\n+ if (!validate_data_in_out(0, 1, "SerialB"))\n+ break;\n+ data_offset++;\n+\n+ data_out[command_length_offset] = data_out[checksum_start] + 2;\n+ }\n+ else\n+ {\n+ if (!validate_data_in_out(length, 0, "SerialB"))\n+ break;\n+ if (u64{m_card_offset} + length > std::size(m_card_buffer))\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command SerialB, m_card_buffer overflow:\\n"\n+ " - m_card_buffer = {}\\n"\n+ " - m_card_offset = {}\\n"\n+ " - length = {}",\n+ fmt::ptr(m_card_buffer), m_card_offset, length);\n+ data_in = data_in_end;\n+ break;\n+ }\n+ for (u32 i = 0; i < length; ++i)\n+ m_card_buffer[m_card_offset + i] = data_in[i];\n+\n+ m_card_offset += length;\n+\n+ // Check if we got a complete command\n+ if (m_card_buffer[0] == 0x02)\n+ {\n+ if (m_card_buffer[1] == m_card_offset - 2)\n+ {\n+ if (m_card_buffer[m_card_offset - 2] == 0x03)\n+ {\n+ m_card_command = m_card_buffer[2];\n+\n+ switch (CARDCommand(m_card_command))\n+ {\n+ case CARDCommand::Init:\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD Init");\n+\n+ m_card_write_length = 0;\n+ m_card_bit = 0;\n+ m_card_memory_size = 0;\n+ m_card_state_call_count = 0;\n+ break;\n+ case CARDCommand::GetState:\n+ {\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD GetState({:02X})",\n+ m_card_bit);\n+\n+ if (m_card_memory_size == 0)\n+ {\n+ const std::string card_filename(\n+ fmt::format("{}tricard_{}.bin", File::GetUserPath(D_TRIUSER_IDX),\n+ SConfig::GetInstance().GetGameID()));\n+\n+ if (File::Exists(card_filename))\n+ {\n+ File::IOFile card(card_filename, "rb+");\n+ m_card_memory_size = static_cast<u32>(card.GetSize());\n+ if (m_card_memory_size > sizeof(m_card_memory))\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command CARD GetState overflow:\\n"\n+ " - file name = {}\\n"\n+ " - file size = {}\\n"\n+ " - card size = {}",\n+ card_filename, m_card_memory_size, sizeof(m_card_memory));\n+ data_in = data_in_end;\n+ break;\n+ }\n+ card.ReadBytes(m_card_memory, m_card_memory_size);\n+ card.Close();\n+\n+ m_card_is_inserted = true;\n+ }\n+ }\n+\n+ if (AMMediaboard::GetGameType() == FZeroAX && m_card_memory_size)\n+ {\n+ m_card_state_call_count++;\n+ if (m_card_state_call_count > 10)\n+ {\n+ if (m_card_bit & 2)\n+ m_card_bit &= ~2u;\n+ else\n+ m_card_bit |= 2;\n+\n+ m_card_state_call_count = 0;\n+ }\n+ }\n+\n+ if (m_card_clean == 1)\n+ {\n+ m_card_clean = 2;\n+ }\n+ else if (m_card_clean == 2)\n+ {\n+ const std::string card_filename(\n+ fmt::format("{}tricard_{}.bin", File::GetUserPath(D_TRIUSER_IDX),\n+ SConfig::GetInstance().GetGameID()));\n+\n+ if (File::Exists(card_filename))\n+ {\n+ m_card_memory_size = static_cast<u32>(File::GetSize(card_filename));\n+ if (m_card_memory_size)\n+ {\n+ if (AMMediaboard::GetGameType() == FZeroAX)\n+ {\n+ m_card_bit = 2;\n+ }\n+ else\n+ {\n+ m_card_bit = 1;\n+ }\n+ }\n+ }\n+ m_card_clean = 0;\n+ }\n+ break;\n+ }\n+ case CARDCommand::IsPresent:\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD IsPresent");\n+ break;\n+ case CARDCommand::RegisterFont:\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD RegisterFont");\n+ break;\n+ case CARDCommand::Load:\n+ {\n+ const u8 mode = m_card_buffer[6];\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD Load({:02X})",\n+ mode);\n+ break;\n+ }\n+ case CARDCommand::Clean:\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD Clean");\n+ m_card_clean = 1;\n+ break;\n+ case CARDCommand::Read:\n+ {\n+ const u8 mode = m_card_buffer[6];\n+ const u8 bitmode = m_card_buffer[7];\n+ const u8 track = m_card_buffer[8];\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command CARD Read({:02X},{:02X},{:02X})", mode,\n+ bitmode, track);\n+\n+ // Prepare read packet\n+ memset(m_card_read_packet, 0, 0xDB);\n+\n+ const std::string card_filename(\n+ fmt::format("{}tricard_{}.bin", File::GetUserPath(D_TRIUSER_IDX),\n+ SConfig::GetInstance().GetGameID()));\n+\n+ if (File::Exists(card_filename))\n+ {\n+ File::IOFile card(card_filename, "rb+");\n+ if (m_card_memory_size == 0)\n+ {\n+ m_card_memory_size = static_cast<u32>(card.GetSize());\n+ }\n+\n+ if (m_card_memory_size > sizeof(m_card_memory))\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command CARD Read overflow:\\n"\n+ " - file name = {}\\n"\n+ " - file size = {}\\n"\n+ " - card size = {}",\n+ card_filename, m_card_memory_size, sizeof(m_card_memory));\n+ data_in = data_in_end;\n+ break;\n+ }\n+ card.ReadBytes(m_card_memory, m_card_memory_size);\n+ card.Close();\n+\n+ m_card_is_inserted = true;\n+ }\n+ else if (m_card_memory_size > sizeof(m_card_memory))\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command CARD Read overflow:\\n"\n+ " - requested size = {}\\n"\n+ " - card size = {}",\n+ m_card_memory_size, sizeof(m_card_memory));\n+ data_in = data_in_end;\n+ break;\n+ }\n+\n+ m_card_read_packet[0] = 0x02; // SUB CMD\n+ m_card_read_packet[1] = 0x00; // SUB CMDLen\n+\n+ m_card_read_packet[2] = 0x33; // CARD CMD\n+\n+ if (m_card_is_inserted) // CARD Status\n+ {\n+ m_card_read_packet[3] = 0x31;\n+ }\n+ else\n+ {\n+ m_card_read_packet[3] = 0x30;\n+ }\n+\n+ m_card_read_packet[4] = 0x30;\n+ m_card_read_packet[5] = 0x30;\n+\n+ u32 packet_offset = 6;\n+ // Data reply\n+ static_assert(sizeof(m_card_read_packet) >= sizeof(m_card_memory) + 6);\n+ memcpy(m_card_read_packet + packet_offset, m_card_memory, m_card_memory_size);\n+ packet_offset += m_card_memory_size;\n+\n+ static_assert(sizeof(m_card_read_packet) >= sizeof(m_card_memory) + 7);\n+ m_card_read_packet[packet_offset++] = 0x03;\n+\n+ m_card_read_packet[1] = packet_offset - 1; // SUB CMDLen\n+\n+ static_assert(sizeof(m_card_read_packet) >= sizeof(m_card_memory) + 8);\n+ for (u32 i = 0; i < packet_offset - 1; ++i)\n+ m_card_read_packet[packet_offset] ^= m_card_read_packet[1 + i];\n+\n+ static_assert(sizeof(m_card_read_packet) >= sizeof(m_card_memory) + 9);\n+ packet_offset++;\n+\n+ m_card_read_length = packet_offset;\n+ m_card_read = 0;\n+ break;\n+ }\n+ case CARDCommand::Write:\n+ {\n+ const u8 mode = m_card_buffer[6];\n+ const u8 bitmode = m_card_buffer[7];\n+ const u8 track = m_card_buffer[8];\n+\n+ m_card_memory_size = m_card_buffer[1] - 9;\n+ if (m_card_memory_size > sizeof(m_card_memory))\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command CARD Write overflow:\\n"\n+ " - write size = {}\\n"\n+ " - card size = {}",\n+ m_card_memory_size, sizeof(m_card_memory));\n+ data_in = data_in_end;\n+ break;\n+ }\n+\n+ static_assert(sizeof(m_card_buffer) >= sizeof(m_card_memory) + 9);\n+ memcpy(m_card_memory, m_card_buffer + 9, m_card_memory_size);\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command CARD Write: {:02X} {:02X} {:02X} {}", mode,\n+ bitmode, track, m_card_memory_size);\n+\n+ const std::string card_filename(File::GetUserPath(D_TRIUSER_IDX) +\n+ "tricard_" +\n+ SConfig::GetInstance().GetGameID() + ".bin");\n+\n+ File::IOFile card(card_filename, "wb+");\n+ card.WriteBytes(m_card_memory, m_card_memory_size);\n+ card.Close();\n+\n+ m_card_bit = 2;\n+\n+ m_card_state_call_count = 0;\n+ break;\n+ }\n+ case CARDCommand::SetPrintParam:\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD SetPrintParam");\n+ break;\n+ case CARDCommand::WriteInfo:\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD WriteInfo");\n+ break;\n+ case CARDCommand::Erase:\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD Erase");\n+ break;\n+ case CARDCommand::Eject:\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD Eject");\n+ if (AMMediaboard::GetGameType() != FZeroAX)\n+ {\n+ m_card_bit = 0;\n+ }\n+ break;\n+ case CARDCommand::SetShutter:\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD SetShutter");\n+ if (AMMediaboard::GetGameType() != FZeroAX)\n+ {\n+ m_card_bit = 0;\n+ }\n+ // Close\n+ if (m_card_buffer[6] == 0x30)\n+ {\n+ m_card_shutter = false;\n+ }\n+ // Open\n+ else if (m_card_buffer[6] == 0x31)\n+ {\n+ m_card_shutter = true;\n+ }\n+ break;\n+ default:\n+ ERROR_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: CARD:Unhandled command!");\n+ ERROR_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: CARD:[{:08X}]", m_card_command);\n+ // hexdump( m_card_buffer, m_card_offset );\n+ break;\n+ }\n+ m_card_offset = 0;\n+ }\n+ }\n+ }\n+\n+ if (!validate_data_in_out(0, 3, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x32;\n+ data_out[data_offset++] = 0x01; // len\n+ data_out[data_offset++] = 0x06; // OK\n+ }\n+ }\n+ else\n+ {\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x00; // len\n+ }\n+ data_in += length;\n+ break;\n+ }\n+ case GCAMCommand::JVSIOA:\n+ case GCAMCommand::JVSIOB:\n+ {\n+ if (!validate_data_in_out(4, 0, "JVSIO"))\n+ break;\n+\n+ JVSIOMessage message;\n+\n+ static int delay = 0;\n+\n+ const u8* const frame = &data_in[0];\n+ const u8 nr_bytes = frame[3]; // Byte after E0 xx\n+ u32 frame_len = nr_bytes + 3; // Header(2) + length byte + payload + checksum\n+\n+ u8 jvs_buf[0x80];\n+\n+ frame_len = std::min<u32>(frame_len, sizeof(jvs_buf));\n+\n+ if (!validate_data_in_out(frame_len, 0, "JVSIO"))\n+ break;\n+ DEBUG_LOG_FMT(SERIALINTERFACE_JVSIO, "GC-AM: Command {:02x} (JVS IO), hexdump:\\n{}",\n+ gcam_command, HexDump(data_in, frame_len));\n+\n+ memcpy(jvs_buf, frame, frame_len);\n+\n+ // Extract node and payload pointers\n+ u8 node = jvs_buf[2];\n+ u8* jvs_io = jvs_buf + 4; // First payload byte\n+ u8* const jvs_end = jvs_buf + frame_len; // One byte before checksum\n+ u8* const jvs_begin = jvs_io;\n+\n+ message.Start(0);\n+ message.AddData(1);\n+\n+ // Helper to check that iterating over jvs_io n times is safe,\n+ // i.e. *jvs_io++ at most lead to jvs_end\n+ auto validate_jvs_io = [&](u32 n, std::string_view command) -> bool {\n+ if (jvs_io + n > jvs_end)\n+ ERROR_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: overflow in {}", command);\n+ else\n+ return true;\n+ ERROR_LOG_FMT(SERIALINTERFACE_JVSIO,\n+ "Overflow details:\\n"\n+ " - jvs_io(begin={}, current={}, end={}, n={})\\n"\n+ " - delay={}, node={}\\n"\n+ " - frame(begin={}, len={})",\n+ fmt::ptr(jvs_begin), fmt::ptr(jvs_io), fmt::ptr(jvs_end), n, delay, node,\n+ fmt::ptr(frame), frame_len);\n+ jvs_io = jvs_end;\n+ return false;\n+ };\n+\n+ // Now iterate over the payload\n+ while (jvs_io < jvs_end)\n+ {\n+ const u8 jvsio_command = *jvs_io++;\n+ DEBUG_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO:node={}, command={:02x}", node,\n+ jvsio_command);\n+\n+ switch (JVSIOCommand(jvsio_command))\n+ {\n+ case JVSIOCommand::IOID:\n+ message.AddData(StatusOkay);\n+ switch (AMMediaboard::GetGameType())\n+ {\n+ case FZeroAX:\n+ // Specific version that enables DX mode on AX machines, all this does is enable the\n+ // motion of a chair\n+ message.AddData("SEGA ENTERPRISES,LTD.;837-13844-01 I/O CNTL BD2 ;");\n+ break;\n+ case FZeroAXMonster:\n+ case MarioKartGP:\n+ case MarioKartGP2:\n+ default:\n+ message.AddData("namco ltd.;FCA-1;Ver1.01;JPN,Multipurpose + Rotary Encoder");\n+ break;\n+ case VirtuaStriker3:\n+ case VirtuaStriker4:\n+ case VirtuaStriker4_2006:\n+ message.AddData("SEGA ENTERPRISES,LTD.;I/O BD JVS;837-13551;Ver1.00");\n+ break;\n+ }\n+ NOTICE_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: Command 0x10, BoardID");\n+ message.AddData((u32)0);\n+ break;\n+ case JVSIOCommand::CommandRevision:\n+ message.AddData(StatusOkay);\n+ message.AddData(0x11);\n+ NOTICE_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: Command 0x11, CommandRevision");\n+ break;\n+ case JVSIOCommand::JVRevision:\n+ message.AddData(StatusOkay);\n+ message.AddData(0x20);\n+ NOTICE_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: Command 0x12, JVRevision");\n+ break;\n+ case JVSIOCommand::CommunicationVersion:\n+ message.AddData(StatusOkay);\n+ message.AddData(0x10);\n+ NOTICE_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: Command 0x13, CommunicationVersion");\n+ break;\n+\n+ // Slave features:\n+ //\n+ // Inputs:\n+ // 0x01: Switch input: players, buttons\n+ // 0x02: Coin input: slots\n+ // 0x03: Analog input: channels, bits\n+ // 0x04: Rotary input: channels\n+ // 0x05: Keycode input: 0,0,0 ?\n+ // 0x06: Screen position input: X bits, Y bits, channels\n+ //\n+ // Outputs:\n+ // 0x10: Card system: slots\n+ // 0x11: Medal hopper: channels\n+ // 0x12: GPO-out: slots\n+ // 0x13: Analog output: channels\n+ // 0x14: Character output: width, height, type\n+ // 0x15: Backup\n+ case JVSIOCommand::CheckFunctionality:\n+ message.AddData(StatusOkay);\n+ switch (AMMediaboard::GetGameType())\n+ {\n+ case FZeroAX:\n+ case FZeroAXMonster:\n+ // 2 Player (12bit) (p2=paddles), 1 Coin slot, 6 Analog-in\n+ // message.AddData((void *)"\\x01\\x02\\x0C\\x00", 4);\n+ // message.AddData((void *)"\\x02\\x01\\x00\\x00", 4);\n+ // message.AddData((void *)"\\x03\\x06\\x00\\x00", 4);\n+ // message.AddData((void *)"\\x00\\x00\\x00\\x00", 4);\n+ //\n+ // DX Version: 2 Player (22bit) (p2=paddles), 2 Coin slot, 8 Analog-in,\n+ // 22 Driver-out\n+ message.AddData("\\x01\\x02\\x12\\x00", 4);\n+ message.AddData("\\x02\\x02\\x00\\x00", 4);\n+ message.AddData("\\x03\\x08\\x0A\\x00", 4);\n+ message.AddData("\\x12\\x16\\x00\\x00", 4);\n+ message.AddData("\\x00\\x00\\x00\\x00", 4);\n+ break;\n+ case VirtuaStriker3:\n+ // 2 Player (13bit), 2 Coin slot, 4 Analog-in, 1 CARD, 8 Driver-out\n+ message.AddData("\\x01\\x02\\x0D\\x00", 4);\n+ message.AddData("\\x02\\x02\\x00\\x00", 4);\n+ message.AddData("\\x10\\x01\\x00\\x00", 4);\n+ message.AddData("\\x12\\x08\\x00\\x00", 4);\n+ message.AddData("\\x00\\x00\\x00\\x00", 4);\n+ break;\n+ case GekitouProYakyuu:\n+ // 2 Player (13bit), 2 Coin slot, 4 Analog-in, 1 CARD, 8 Driver-out\n+ message.AddData("\\x01\\x02\\x0D\\x00", 4);\n+ message.AddData("\\x02\\x02\\x00\\x00", 4);\n+ message.AddData("\\x03\\x04\\x00\\x00", 4);\n+ message.AddData("\\x10\\x01\\x00\\x00", 4);\n+ message.AddData("\\x12\\x08\\x00\\x00", 4);\n+ message.AddData("\\x00\\x00\\x00\\x00", 4);\n+ break;\n+ case VirtuaStriker4:\n+ case VirtuaStriker4_2006:\n+ // 2 Player (13bit), 1 Coin slot, 4 Analog-in, 1 CARD\n+ message.AddData("\\x01\\x02\\x0D\\x00", 4);\n+ message.AddData("\\x02\\x01\\x00\\x00", 4);\n+ message.AddData("\\x03\\x04\\x00\\x00", 4);\n+ message.AddData("\\x10\\x01\\x00\\x00", 4);\n+ message.AddData("\\x00\\x00\\x00\\x00", 4);\n+ break;\n+ case KeyOfAvalon:\n+ // 1 Player (15bit), 1 Coin slot, 3 Analog-in, Touch, 1 CARD, 1 Driver-out\n+ // (Unconfirmed)\n+ message.AddData("\\x01\\x01\\x0F\\x00", 4);\n+ message.AddData("\\x02\\x01\\x00\\x00", 4);\n+ message.AddData("\\x03\\x03\\x00\\x00", 4);\n+ message.AddData("\\x06\\x10\\x10\\x01", 4);\n+ message.AddData("\\x10\\x01\\x00\\x00", 4);\n+ message.AddData("\\x12\\x01\\x00\\x00", 4);\n+ message.AddData("\\x00\\x00\\x00\\x00", 4);\n+ break;\n+ case MarioKartGP:\n+ case MarioKartGP2:\n+ default:\n+ // 1 Player (15bit), 1 Coin slot, 3 Analog-in, 1 CARD, 1 Driver-out\n+ message.AddData("\\x01\\x01\\x0F\\x00", 4);\n+ message.AddData("\\x02\\x01\\x00\\x00", 4);\n+ message.AddData("\\x03\\x03\\x00\\x00", 4);\n+ message.AddData("\\x10\\x01\\x00\\x00", 4);\n+ message.AddData("\\x12\\x01\\x00\\x00", 4);\n+ message.AddData("\\x00\\x00\\x00\\x00", 4);\n+ break;\n+ }\n+ NOTICE_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: Command 0x14, CheckFunctionality");\n+ break;\n+ case JVSIOCommand::MainID:\n+ {\n+ const u8* const main_id = jvs_io;\n+ while (jvs_io < jvs_end && *jvs_io++)\n+ {\n+ }\n+ if (main_id < jvs_io)\n+ {\n+ DEBUG_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: Command MainId:\\n{}",\n+ HexDump(main_id, jvs_io - main_id));\n+ }\n+ message.AddData(StatusOkay);\n+ break;\n+ }\n+ case JVSIOCommand::SwitchesInput:\n+ {\n+ if (!validate_jvs_io(2, "SwitchesInput"))\n+ break;\n+ const u32 player_count = *jvs_io++;\n+ const u32 player_byte_count = *jvs_io++;\n+\n+ DEBUG_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: Command 0x20, SwitchInputs: {} {}",\n+ player_count, player_byte_count);\n+\n+ message.AddData(StatusOkay);\n+\n+ GCPadStatus pad_status = Pad::GetStatus(0);\n+\n+ // Test button\n+ if (pad_status.switches & SWITCH_TEST)\n+ {\n+ // Trying to access the test menu without SegaBoot present will cause a crash\n+ if (AMMediaboard::GetTestMenu())\n+ {\n+ message.AddData(0x80);\n+ }\n+ else\n+ {\n+ PanicAlertFmt("Test menu is disabled due missing SegaBoot");\n+ }\n+ }\n+ else\n+ {\n+ message.AddData((u32)0x00);\n+ }\n+\n+ for (u32 i = 0; i < player_count; ++i)\n+ {\n+ u8 player_data[3]{};\n+\n+ // Service button\n+ if (pad_status.switches & SWITCH_SERVICE)\n+ player_data[0] |= 0x40;\n+\n+ switch (AMMediaboard::GetGameType())\n+ {\n+ // Controller configuration for F-Zero AX (DX)\n+ case FZeroAX:\n+ if (i == 0)\n+ {\n+ if (m_fzdx_seatbelt)\n+ {\n+ player_data[0] |= 0x01;\n+ }\n+\n+ // Start\n+ if (pad_status.button & PAD_BUTTON_START)\n+ player_data[0] |= 0x80;\n+ // Boost\n+ if (pad_status.button & PAD_BUTTON_A)\n+ player_data[0] |= 0x02;\n+ // View Change 1\n+ if (pad_status.button & PAD_BUTTON_RIGHT)\n+ player_data[0] |= 0x20;\n+ // View Change 2\n+ if (pad_status.button & PAD_BUTTON_LEFT)\n+ player_data[0] |= 0x10;\n+ // View Change 3\n+ if (pad_status.button & PAD_BUTTON_UP)\n+ player_data[0] |= 0x08;\n+ // View Change 4\n+ if (pad_status.button & PAD_BUTTON_DOWN)\n+ player_data[0] |= 0x04;\n+ player_data[1] = m_rx_reply & 0xF0;\n+ }\n+ else if (i == 1)\n+ {\n+ // Paddle left\n+ if (pad_status.button & PAD_BUTTON_X)\n+ player_data[0] |= 0x20;\n+ // Paddle right\n+ if (pad_status.button & PAD_BUTTON_Y)\n+ player_data[0] |= 0x10;\n+\n+ if (m_fzdx_motion_stop)\n+ {\n+ player_data[0] |= 2;\n+ }\n+ if (m_fzdx_sensor_right)\n+ {\n+ player_data[0] |= 4;\n+ }\n+ if (m_fzdx_sensor_left)\n+ {\n+ player_data[0] |= 8;\n+ }\n+\n+ player_data[1] = m_rx_reply << 4;\n+ }\n+ break;\n+ // Controller configuration for F-Zero AX MonsterRide\n+ case FZeroAXMonster:\n+ if (i == 0)\n+ {\n+ if (m_fzcc_sensor)\n+ {\n+ player_data[0] |= 0x01;\n+ }\n+\n+ // Start\n+ if (pad_status.button & PAD_BUTTON_START)\n+ player_data[0] |= 0x80;\n+ // Boost\n+ if (pad_status.button & PAD_BUTTON_A)\n+ player_data[0] |= 0x02;\n+ // View Change 1\n+ if (pad_status.button & PAD_BUTTON_RIGHT)\n+ player_data[0] |= 0x20;\n+ // View Change 2\n+ if (pad_status.button & PAD_BUTTON_LEFT)\n+ player_data[0] |= 0x10;\n+ // View Change 3\n+ if (pad_status.button & PAD_BUTTON_UP)\n+ player_data[0] |= 0x08;\n+ // View Change 4\n+ if (pad_status.button & PAD_BUTTON_DOWN)\n+ player_data[0] |= 0x04;\n+\n+ player_data[1] = m_rx_reply & 0xF0;\n+ }\n+ else if (i == 1)\n+ {\n+ // Paddle left\n+ if (pad_status.button & PAD_BUTTON_X)\n+ player_data[0] |= 0x20;\n+ // Paddle right\n+ if (pad_status.button & PAD_BUTTON_Y)\n+ player_data[0] |= 0x10;\n+\n+ if (m_fzcc_seatbelt)\n+ {\n+ player_data[0] |= 2;\n+ }\n+ if (m_fzcc_service)\n+ {\n+ player_data[0] |= 4;\n+ }\n+ if (m_fzcc_emergency)\n+ {\n+ player_data[0] |= 8;\n+ }\n+ }\n+ break;\n+ // Controller configuration for Virtua Striker 3 games\n+ case VirtuaStriker3:\n+ pad_status = Pad::GetStatus(i);\n+ // Start\n+ if (pad_status.button & PAD_BUTTON_START)\n+ player_data[0] |= 0x80;\n+ // Shoot\n+ if (pad_status.button & PAD_BUTTON_B)\n+ player_data[0] |= 0x01;\n+ // Short Pass\n+ if (pad_status.button & PAD_BUTTON_A)\n+ player_data[1] |= 0x80;\n+ // Long Pass\n+ if (pad_status.button & PAD_BUTTON_X)\n+ player_data[0] |= 0x02;\n+ // Left\n+ if (pad_status.button & PAD_BUTTON_LEFT)\n+ player_data[0] |= 0x08;\n+ // Up\n+ if (pad_status.button & PAD_BUTTON_UP)\n+ player_data[0] |= 0x20;\n+ // Right\n+ if (pad_status.button & PAD_BUTTON_RIGHT)\n+ player_data[0] |= 0x04;\n+ // Down\n+ if (pad_status.button & PAD_BUTTON_DOWN)\n+ player_data[0] |= 0x10;\n+ break;\n+ // Controller configuration for Virtua Striker 4 games\n+ case VirtuaStriker4:\n+ case VirtuaStriker4_2006:\n+ {\n+ pad_status = Pad::GetStatus(i);\n+ // Start\n+ if (pad_status.button & PAD_BUTTON_START)\n+ player_data[0] |= 0x80;\n+ // Long Pass\n+ if (pad_status.button & PAD_BUTTON_X)\n+ player_data[0] |= 0x01;\n+ // Short Pass\n+ if (pad_status.button & PAD_BUTTON_A)\n+ player_data[0] |= 0x02;\n+ // Shoot\n+ if (pad_status.button & PAD_BUTTON_B)\n+ player_data[1] |= 0x80;\n+ // Dash\n+ if (pad_status.button & PAD_BUTTON_Y)\n+ player_data[1] |= 0x40;\n+ // Tactics (U)\n+ if (pad_status.button & PAD_BUTTON_LEFT)\n+ player_data[0] |= 0x20;\n+ // Tactics (M)\n+ if (pad_status.button & PAD_BUTTON_UP)\n+ player_data[0] |= 0x08;\n+ // Tactics (D)\n+ if (pad_status.button & PAD_BUTTON_RIGHT)\n+ player_data[0] |= 0x04;\n+\n+ if (i == 0)\n+ {\n+ player_data[0] |= 0x10; // IC-Card Switch ON\n+\n+ // IC-Card Lock\n+ if (pad_status.button & PAD_BUTTON_DOWN)\n+ player_data[1] |= 0x20;\n+ }\n+ }\n+ break;\n+ // Controller configuration for Gekitou Pro Yakyuu\n+ case GekitouProYakyuu:\n+ pad_status = Pad::GetStatus(i);\n+ // Start\n+ if (pad_status.button & PAD_BUTTON_START)\n+ player_data[0] |= 0x80;\n+ // A\n+ if (pad_status.button & PAD_BUTTON_B)\n+ player_data[0] |= 0x01;\n+ // B\n+ if (pad_status.button & PAD_BUTTON_A)\n+ player_data[0] |= 0x02;\n+ // Gekitou\n+ if (pad_status.button & PAD_TRIGGER_L)\n+ player_data[1] |= 0x80;\n+ // Left\n+ if (pad_status.button & PAD_BUTTON_LEFT)\n+ player_data[0] |= 0x08;\n+ // Up\n+ if (pad_status.button & PAD_BUTTON_UP)\n+ player_data[0] |= 0x20;\n+ // Right\n+ if (pad_status.button & PAD_BUTTON_RIGHT)\n+ player_data[0] |= 0x04;\n+ // Down\n+ if (pad_status.button & PAD_BUTTON_DOWN)\n+ player_data[0] |= 0x10;\n+ break;\n+ // Controller configuration for Mario Kart and other games\n+ default:\n+ case MarioKartGP:\n+ case MarioKartGP2:\n+ {\n+ // Start\n+ if (pad_status.button & PAD_BUTTON_START)\n+ player_data[0] |= 0x80;\n+ // Item button\n+ if (pad_status.button & PAD_BUTTON_A)\n+ player_data[1] |= 0x20;\n+ // VS-Cancel button\n+ if (pad_status.button & PAD_BUTTON_B)\n+ player_data[1] |= 0x02;\n+ }\n+ break;\n+ case KeyOfAvalon:\n+ {\n+ // Debug On\n+ if (pad_status.button & PAD_BUTTON_START)\n+ player_data[0] |= 0x80;\n+ // Switch 1\n+ if (pad_status.button & PAD_BUTTON_A)\n+ player_data[0] |= 0x04;\n+ // Switch 2\n+ if (pad_status.button & PAD_BUTTON_B)\n+ player_data[0] |= 0x08;\n+ // Toggle inserted card\n+ if (pad_status.button & PAD_TRIGGER_L)\n+ {\n+ m_ic_card_status ^= ICCARDStatus::NoCard;\n+ }\n+ }\n+ break;\n+ }\n+\n+ for (u32 j = 0; j < player_byte_count; ++j)\n+ message.AddData(player_data[j]);\n+ }\n+ break;\n+ }\n+ case JVSIOCommand::CoinInput:\n+ {\n+ if (!validate_jvs_io(1, "CoinInput"))\n+ break;\n+ const u32 slots = *jvs_io++;\n+ message.AddData(StatusOkay);\n+ for (u32 i = 0; i < slots; i++)\n+ {\n+ GCPadStatus pad_status = Pad::GetStatus(i);\n+ if ((pad_status.switches & SWITCH_COIN) && !m_coin_pressed[i])\n+ {\n+ m_coin[i]++;\n+ }\n+ m_coin_pressed[i] = pad_status.switches & SWITCH_COIN;\n+ message.AddData((m_coin[i] >> 8) & 0x3f);\n+ message.AddData(m_coin[i] & 0xff);\n+ }\n+ DEBUG_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: Command 0x21, CoinInput: {}", slots);\n+ break;\n+ }\n+ case JVSIOCommand::AnalogInput:\n+ {\n+ if (!validate_jvs_io(1, "AnalogInput"))\n+ break;\n+ message.AddData(StatusOkay);\n+\n+ const u32 analogs = *jvs_io++;\n+ GCPadStatus pad_status = Pad::GetStatus(0);\n+\n+ DEBUG_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: Command 0x22, AnalogInput: {}",\n+ analogs);\n+\n+ switch (AMMediaboard::GetGameType())\n+ {\n+ case FZeroAX:\n+ case FZeroAXMonster:\n+ // Steering\n+ if (m_motor_init == 1)\n+ {\n+ if (m_motor_force_y > 0)\n+ {\n+ message.AddData(0x80 - (m_motor_force_y >> 8));\n+ }\n+ else\n+ {\n+ message.AddData((m_motor_force_y >> 8));\n+ }\n+ message.AddData((u8)0);\n+\n+ message.AddData(pad_status.stickY);\n+ message.AddData((u8)0);\n+ }\n+ else\n+ {\n+ // The center for the Y axis is expected to be 78h this adjusts that\n+ message.AddData(pad_status.stickX - 12);\n+ message.AddData((u8)0);\n+\n+ message.AddData(pad_status.stickY);\n+ message.AddData((u8)0);\n+ }\n+\n+ // Unused\n+ message.AddData((u8)0);\n+ message.AddData((u8)0);\n+ message.AddData((u8)0);\n+ message.AddData((u8)0);\n+\n+ // Gas\n+ message.AddData(pad_status.triggerRight);\n+ message.AddData((u8)0);\n+\n+ // Brake\n+ message.AddData(pad_status.triggerLeft);\n+ message.AddData((u8)0);\n+\n+ message.AddData((u8)0x80); // Motion Stop\n+ message.AddData((u8)0);\n+\n+ message.AddData((u8)0);\n+ message.AddData((u8)0);\n+\n+ break;\n+ case VirtuaStriker4:\n+ case VirtuaStriker4_2006:\n+ {\n+ message.AddData(-pad_status.stickY);\n+ message.AddData((u8)0);\n+ message.AddData(pad_status.stickX);\n+ message.AddData((u8)0);\n+\n+ pad_status = Pad::GetStatus(1);\n+\n+ message.AddData(-pad_status.stickY);\n+ message.AddData((u8)0);\n+ message.AddData(pad_status.stickX);\n+ message.AddData((u8)0);\n+ }\n+ break;\n+ default:\n+ case MarioKartGP:\n+ case MarioKartGP2:\n+ // Steering\n+ message.AddData(pad_status.stickX);\n+ message.AddData((u8)0);\n+\n+ // Gas\n+ message.AddData(pad_status.triggerRight);\n+ message.AddData((u8)0);\n+\n+ // Brake\n+ message.AddData(pad_status.triggerLeft);\n+ message.AddData((u8)0);\n+ break;\n+ }\n+ break;\n+ }\n+ case JVSIOCommand::PositionInput:\n+ {\n+ if (!validate_jvs_io(1, "PositionInput"))\n+ break;\n+ const u32 channel = *jvs_io++;\n+\n+ const GCPadStatus pad_status = Pad::GetStatus(0);\n+\n+ if (pad_status.button & PAD_TRIGGER_R)\n+ {\n+ // Tap at center of screen (~320,240)\n+ message.AddData("\\x01\\x00\\x8C\\x01\\x95",\n+ 5); // X=320 (0x0140), Y=240 (0x00F0)\n+ }\n+ else\n+ {\n+ message.AddData("\\x01\\xFF\\xFF\\xFF\\xFF", 5);\n+ }\n+\n+ DEBUG_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: Command 0x25, PositionInput:{}",\n+ channel);\n+ break;\n+ }\n+ case JVSIOCommand::CoinSubOutput:\n+ {\n+ if (!validate_jvs_io(3, "CoinSubOutput"))\n+ break;\n+ const u32 slot = *jvs_io++;\n+ const u8 coinh = *jvs_io++;\n+ const u8 coinl = *jvs_io++;\n+\n+ m_coin[slot] -= (coinh << 8) | coinl;\n+\n+ message.AddData(StatusOkay);\n+ DEBUG_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: Command 0x30, CoinSubOutput: {}", slot);\n+ break;\n+ }\n+ case JVSIOCommand::GeneralDriverOutput:\n+ {\n+ if (!validate_jvs_io(1, "GeneralDriverOutput"))\n+ break;\n+ const u32 bytes = *jvs_io++;\n+\n+ if (bytes)\n+ {\n+ message.AddData(StatusOkay);\n+\n+ // The lamps are controlled via this\n+ if (AMMediaboard::GetGameType() == MarioKartGP)\n+ {\n+ if (!validate_jvs_io(1, "GeneralDriverOutput (MarioKartGP)"))\n+ break;\n+ const u32 status = *jvs_io++;\n+ if (status & 4)\n+ {\n+ DEBUG_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: Command 32, Item Button ON");\n+ }\n+ else\n+ {\n+ DEBUG_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: Command 32, Item Button OFF");\n+ }\n+ if (status & 8)\n+ {\n+ DEBUG_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: Command 32, Cancel Button ON");\n+ }\n+ else\n+ {\n+ DEBUG_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: Command 32, Cancel Button OFF");\n+ }\n+ break;\n+ }\n+\n+ if (!validate_jvs_io(bytes, "GeneralDriverOutput"))\n+ break;\n+ Common::UniqueBuffer<u8> buf(bytes);\n+\n+ for (u32 i = 0; i < bytes; ++i)\n+ {\n+ buf[i] = *jvs_io++;', 'path': 'Source/Core/Core/HW/SI/SI_DeviceAMBaseboard.cpp', 'position': 2326, 'original_position': 2326, 'commit_id': '5bf8fe93286ebe4f08fb469a44f259a8397c89e0', 'user': {'login': 'sepalani', 'id': 7890055, 'node_id': 'MDQ6VXNlcjc4OTAwNTU=', 'avatar_url': 'https://avatars.githubusercontent.com/u/7890055?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/sepalani', 'html_url': 'https://github.com/sepalani', 'followers_url': 'https://api.github.com/users/sepalani/followers', 'following_url': 'https://api.github.com/users/sepalani/following{/other_user}', 'gists_url': 'https://api.github.com/users/sepalani/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/sepalani/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/sepalani/subscriptions', 'organizations_url': 'https://api.github.com/users/sepalani/orgs', 'repos_url': 'https://api.github.com/users/sepalani/repos', 'events_url': 'https://api.github.com/users/sepalani/events{/privacy}', 'received_events_url': 'https://api.github.com/users/sepalani/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'body': "Fair point. Moreover, `jvs_io` is checked against `bytes` but we don't check that bytes is greater than 2 before accessing the buffer for the info log. Will be addressed.", 'created_at': '2026-02-01T19:49:19Z', 'updated_at': '2026-02-01T19:49:20Z', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/13844#discussion_r2751867162', 'pull_request_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13844', 'author_association': 'MEMBER', '_links': {'self': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2751867162'}, 'html': {'href': 'https://github.com/dolphin-emu/dolphin/pull/13844#discussion_r2751867162'}, 'pull_request': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13844'}}, 'original_commit_id': '5bf8fe93286ebe4f08fb469a44f259a8397c89e0', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2751867162/reactions', 'total_count': 0, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 0, 'confused': 0, 'heart': 0, 'rocket': 0, 'eyes': 0}, 'in_reply_to_id': 2751395550}], 'type': 'gh_pull_request_review'}
2026-02-01T15:40:28.820404 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'JosJuice', 'action': 'submitted', 'pr_id': 13844, 'pr_title': 'Core: Triforce support', 'state': 'commented', 'url': 'https://github.com/dolphin-emu/dolphin/pull/13844#pullrequestreview-3735806017', 'comments': [{'id': 2751294423, 'node_id': 'PRRC_kwDOALCn2M6j_WvX', 'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2751294423', 'pull_request_review_id': 3735806017, 'diff_hunk': '@@ -0,0 +1,2708 @@\n+// Copyright 2025 Dolphin Emulator Project\n+// SPDX-License-Identifier: GPL-2.0-or-later\n+\n+#include "Core/HW/SI/SI_DeviceAMBaseboard.h"\n+\n+#include <algorithm>\n+#include <numeric>\n+#include <string>\n+\n+#include <fmt/format.h>\n+\n+#include "Common/Buffer.h"\n+#include "Common/CommonTypes.h"\n+#include "Common/FileUtil.h"\n+#include "Common/IOFile.h"\n+#include "Common/Logging/Log.h"\n+#include "Common/MsgHandler.h"\n+#include "Common/Swap.h"\n+\n+#include "Core/Boot/Boot.h"\n+#include "Core/BootManager.h"\n+#include "Core/Config/MainSettings.h"\n+#include "Core/ConfigManager.h"\n+#include "Core/Core.h"\n+#include "Core/CoreTiming.h"\n+#include "Core/HW/DVD/AMMediaboard.h"\n+#include "Core/HW/DVD/DVDInterface.h"\n+#include "Core/HW/EXI/EXI.h"\n+#include "Core/HW/GCPad.h"\n+#include "Core/HW/MMIO.h"\n+#include "Core/HW/Memmap.h"\n+#include "Core/HW/ProcessorInterface.h"\n+#include "Core/HW/SI/SI.h"\n+#include "Core/HW/SI/SI_Device.h"\n+#include "Core/HW/SI/SI_DeviceGCController.h"\n+#include "Core/HW/SystemTimers.h"\n+#include "Core/Movie.h"\n+#include "Core/NetPlayProto.h"\n+#include "Core/System.h"\n+\n+#include "InputCommon/GCPadStatus.h"\n+\n+namespace SerialInterface\n+{\n+void JVSIOMessage::Start(int node)\n+{\n+ m_last_start = m_pointer;\n+ const u8 header[3] = {0xE0, (u8)node, 0};\n+ m_checksum = 0;\n+ AddData(header, 3, 1);\n+}\n+\n+void JVSIOMessage::AddData(const u8* dst, size_t len, int sync = 0)\n+{\n+ if (m_pointer + len >= sizeof(m_message))\n+ {\n+ PanicAlertFmt("JVSIOMessage overrun!");\n+ return;\n+ }\n+\n+ while (len--)\n+ {\n+ const u8 c = *dst++;\n+ if (!sync && ((c == 0xE0) || (c == 0xD0)))\n+ {\n+ if (m_pointer + 2 > sizeof(m_message))\n+ {\n+ PanicAlertFmt("JVSIOMessage overrun!");\n+ break;\n+ }\n+ m_message[m_pointer++] = 0xD0;\n+ m_message[m_pointer++] = c - 1;\n+ }\n+ else\n+ {\n+ if (m_pointer >= sizeof(m_message))\n+ {\n+ PanicAlertFmt("JVSIOMessage overrun!");\n+ break;\n+ }\n+ m_message[m_pointer++] = c;\n+ }\n+\n+ if (!sync)\n+ m_checksum += c;\n+ sync = 0;\n+ }\n+}\n+\n+void JVSIOMessage::AddData(const void* data, size_t len)\n+{\n+ AddData(static_cast<const u8*>(data), len);\n+}\n+\n+void JVSIOMessage::AddData(const char* data)\n+{\n+ AddData(data, strlen(data));\n+}\n+\n+void JVSIOMessage::AddData(u32 n)\n+{\n+ const u8 cs = n;\n+ AddData(&cs, 1);\n+}\n+\n+void JVSIOMessage::End()\n+{\n+ const u32 len = m_pointer - m_last_start;\n+ if (m_last_start + 2 < sizeof(m_message) && len >= 3)\n+ {\n+ m_message[m_last_start + 2] = len - 2; // assuming len <0xD0\n+ AddData(m_checksum + len - 2);\n+ }\n+ else\n+ {\n+ PanicAlertFmt("JVSIOMessage: Not enough space for checksum!");\n+ }\n+}\n+\n+static constexpr u8 CheckSumXOR(const u8* data, u32 length)\n+{\n+ return std::accumulate(data, data + length, u8{}, std::bit_xor());\n+}\n+\n+static constexpr char s_cdr_program_version[] = {" Version 1.22,2003/09/19,171-8213B"};\n+static constexpr char s_cdr_boot_version[] = {" Version 1.04,2003/06/17,171-8213B"};\n+static constexpr u8 s_cdr_card_data[] = {\n+ 0x00, 0x6E, 0x00, 0x00, 0x01, 0x00, 0x00, 0x06, 0x00, 0x00, 0x07, 0x00, 0x00, 0x0B,\n+ 0x00, 0x00, 0x0E, 0x00, 0x00, 0x10, 0x00, 0x00, 0x17, 0x00, 0x00, 0x19, 0x00, 0x00,\n+ 0x1A, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x20, 0x00,\n+ 0x00, 0x22, 0x00, 0x00, 0x23, 0x00, 0x00, 0x24, 0x00, 0x00, 0x27, 0x00, 0x00, 0x28,\n+ 0x00, 0x00, 0x2C, 0x00, 0x00, 0x2F, 0x00, 0x00, 0x34, 0x00, 0x00, 0x35, 0x00, 0x00,\n+ 0x37, 0x00, 0x00, 0x38, 0x00, 0x00, 0x39, 0x00, 0x00, 0x3D, 0x00};\n+\n+const constexpr u8 s_region_flags[] = "\\x00\\x00\\x30\\x00"\n+ // "\\x01\\xfe\\x00\\x00" // JAPAN\n+ "\\x02\\xfd\\x00\\x00" // USA\n+ //"\\x03\\xfc\\x00\\x00" // export\n+ "\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff";\n+// AM-Baseboard device on SI\n+CSIDevice_AMBaseboard::CSIDevice_AMBaseboard(Core::System& system, SIDevices device,\n+ int device_number)\n+ : ISIDevice(system, device, device_number)\n+{\n+ // Card ID\n+ m_ic_card_data[0x20] = 0x95;\n+ m_ic_card_data[0x21] = 0x71;\n+\n+ if (AMMediaboard::GetGameType() == KeyOfAvalon)\n+ {\n+ m_ic_card_data[0x22] = 0x26;\n+ m_ic_card_data[0x23] = 0x40;\n+ }\n+ else if (AMMediaboard::GetGameType() == VirtuaStriker4)\n+ {\n+ m_ic_card_data[0x22] = 0x44;\n+ m_ic_card_data[0x23] = 0x00;\n+ }\n+\n+ // Use count\n+ m_ic_card_data[0x28] = 0xFF;\n+ m_ic_card_data[0x29] = 0xFF;\n+}\n+\n+constexpr u32 SI_XFER_LENGTH_MASK = 0x7f;\n+\n+// Translate [0,1,2,...,126,127] to [128,1,2,...,126,127]\n+constexpr s32 ConvertSILengthField(u32 field)\n+{\n+ return ((field - 1) & SI_XFER_LENGTH_MASK) + 1;\n+}\n+\n+void CSIDevice_AMBaseboard::ICCardSendReply(ICCommand* iccommand, u8* buffer, u32* length)\n+{\n+ iccommand->status = Common::swap16(iccommand->status);\n+\n+ const auto iccommand_data = reinterpret_cast<const u8*>(iccommand);\n+ const u8 crc = CheckSumXOR(iccommand_data + 2, iccommand->pktlen - 1);\n+\n+ for (u32 i = 0; i < iccommand->pktlen + 1; ++i)\n+ {\n+ buffer[(*length)++] = iccommand_data[i];\n+ }\n+\n+ buffer[(*length)++] = crc;\n+}\n+\n+void CSIDevice_AMBaseboard::SwapBuffers(u8* buffer, u32* buffer_length)\n+{\n+ memcpy(m_last[1], buffer, 0x80); // Save current buffer\n+ memcpy(buffer, m_last[0], 0x80); // Load previous buffer\n+ memcpy(m_last[0], m_last[1], 0x80); // Update history\n+\n+ m_lastptr[1] = *buffer_length; // Swap lengths\n+ *buffer_length = m_lastptr[0];\n+ m_lastptr[0] = m_lastptr[1];\n+}\n+\n+int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length)\n+{\n+ const auto& serial_interface = m_system.GetSerialInterface();\n+ u32 buffer_length = ConvertSILengthField(serial_interface.GetInLength());\n+\n+ // Debug logging\n+ ISIDevice::RunBuffer(buffer, buffer_length);\n+\n+ u32 buffer_position = 0;\n+ while (buffer_position < buffer_length)\n+ {\n+ BaseBoardCommand command = static_cast<BaseBoardCommand>(buffer[buffer_position]);\n+ buffer_position++;\n+\n+ switch (command)\n+ {\n+ case BaseBoardCommand::GCAM_Reset: // Returns ID and dip switches\n+ {\n+ const u32 id = Common::swap32(SI_AM_BASEBOARD | 0x100);\n+ std::memcpy(buffer, &id, sizeof(id));\n+ return sizeof(id);\n+ }\n+ break;\n+ case BaseBoardCommand::GCAM_Command:\n+ {\n+ u32 checksum = 0;\n+ for (u32 i = 0; i < buffer_length; ++i)\n+ checksum += buffer[i];\n+\n+ std::array<u8, 0x80> data_out{};\n+ u32 data_offset = 0;\n+\n+ static u32 dip_switch_1 = 0xFE;\n+ static u32 dip_switch_0 = 0xFF;\n+\n+ data_out[data_offset++] = 1;\n+ data_out[data_offset++] = 1;\n+\n+ u8* data_in = buffer + 2;\n+ if (buffer_position >= buffer_length)\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: buffer overflow (position={}, length={})",\n+ buffer_position, buffer_length);\n+ buffer_position = buffer_length;\n+ break;\n+ }\n+\n+ const u32 requested_size = buffer[buffer_position] + 2;\n+ if (requested_size > buffer_length)\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: requested size ({}) bigger than buffer\'s ({})",\n+ requested_size, buffer_length);\n+ buffer_position = buffer_length;\n+ break;\n+ }\n+ u8* const data_in_end = buffer + requested_size;\n+\n+ // Helper to check that iterating over data n times is safe,\n+ // i.e. *data++ at most lead to data.end()\n+ auto validate_data_in_out = [&](u32 n_in, u32 n_out, std::string_view command) -> bool {\n+ if (data_in + n_in > data_in_end)\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: data_in overflow in {}", command);\n+ else if (u64{data_offset} + n_out > data_out.size())\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: data_out overflow in {}", command);\n+ else\n+ return true;\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "Overflow details:\\n"\n+ " - data_in(begin={}, current={}, end={}, n_in={})\\n"\n+ " - data_out(offset={}, size={}, n_out={})\\n"\n+ " - buffer(position={}, length={})",\n+ fmt::ptr(buffer + 2), fmt::ptr(data_in), fmt::ptr(data_in_end), n_in,\n+ data_offset, data_out.size(), n_out, buffer_position, buffer_length);\n+ data_in = data_in_end;\n+ return false;\n+ };\n+\n+ while (data_in < data_in_end)\n+ {\n+ const u32 gcam_command = *data_in++;\n+ switch (GCAMCommand(gcam_command))\n+ {\n+ case GCAMCommand::StatusSwitches:\n+ {\n+ if (!validate_data_in_out(1, 4, "StatusSwitches"))\n+ break;\n+\n+ const u8 status = *data_in++;\n+ DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x10, {:02x} (READ STATUS&SWITCHES)",\n+ status);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x2;\n+\n+ // We read Test/Service from the JVS-I/O SwitchesInput instead\n+ //\n+ // const GCPadStatus pad_status = Pad::GetStatus(ISIDevice::m_device_number);\n+ // baseboard test/service switches\n+ // if (pad_status.button & PAD_BUTTON_Y)\t// Test\n+ // dip_switch_0 &= ~0x80;\n+ // if (pad_status.button & PAD_BUTTON_X)\t// Service\n+ // dip_switch_0 &= ~0x40;\n+\n+ // Horizontal Scanning Frequency switch\n+ // Required for F-Zero AX booting via Sega Boot\n+ if (AMMediaboard::GetGameType() == FZeroAX ||\n+ AMMediaboard::GetGameType() == FZeroAXMonster)\n+ {\n+ dip_switch_0 &= ~0x20;\n+ }\n+\n+ // Disable camera in MKGP1/2\n+ if (AMMediaboard::GetGameType() == MarioKartGP ||\n+ AMMediaboard::GetGameType() == MarioKartGP2)\n+ {\n+ dip_switch_0 &= ~0x10;\n+ }\n+\n+ data_out[data_offset++] = dip_switch_0;\n+ data_out[data_offset++] = dip_switch_1;\n+ break;\n+ }\n+ case GCAMCommand::SerialNumber:\n+ {\n+ if (!validate_data_in_out(1, 18, "SerialNumber"))\n+ break;\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x11, {:02x} (READ SERIAL NR)",\n+ *data_in);\n+ data_in++;\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 16;\n+ memcpy(data_out.data() + data_offset, "AADE-01B98394904", 16);\n+\n+ data_offset += 16;\n+ break;\n+ }\n+ case GCAMCommand::Unknown_12:\n+ if (!validate_data_in_out(2, 2, "Unknown_12"))\n+ break;\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x12, {:02x} {:02x}", data_in[0],\n+ data_in[1]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x00;\n+\n+ data_in += 2;\n+ break;\n+ case GCAMCommand::Unknown_14:\n+ if (!validate_data_in_out(2, 2, "Unknown_14"))\n+ break;\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x14, {:02x} {:02x}", data_in[0],\n+ data_in[1]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x00;\n+\n+ data_in += 2;\n+ break;\n+ case GCAMCommand::FirmVersion:\n+ if (!validate_data_in_out(1, 4, "FirmVersion"))\n+ break;\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x15, {:02x} (READ FIRM VERSION)",\n+ *data_in);\n+ data_in++;\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x02;\n+ // 00.26\n+ data_out[data_offset++] = 0x00;\n+ data_out[data_offset++] = 0x26;\n+ break;\n+ case GCAMCommand::FPGAVersion:\n+ if (!validate_data_in_out(1, 4, "FPGAVersion"))\n+ break;\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x16, {:02x} (READ FPGA VERSION)",\n+ *data_in);\n+ data_in++;\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x02;\n+ // 07.06\n+ data_out[data_offset++] = 0x07;\n+ data_out[data_offset++] = 0x06;\n+ break;\n+ case GCAMCommand::RegionSettings:\n+ {\n+ if (!validate_data_in_out(5, 0x16, "RegionSettings"))\n+ break;\n+\n+ // Used by SegaBoot for region checks (dev mode skips this check)\n+ // In some games this also controls the displayed language\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x1F, {:02x} {:02x} {:02x} {:02x} {:02x} (REGION)",\n+ data_in[0], data_in[1], data_in[2], data_in[3], data_in[4]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x14;\n+\n+ for (int i = 0; i < 0x14; ++i)\n+ data_out[data_offset++] = s_region_flags[i];\n+\n+ data_in += 5;\n+ }\n+ break;\n+ // No reply\n+ // Note: Always sends three bytes even though size is set to two\n+ case GCAMCommand::Unknown_21:\n+ {\n+ if (!validate_data_in_out(4, 0, "Unknown_21"))\n+ break;\n+\n+ DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x21, {:02x}, {:02x}, {:02x}, {:02x}",\n+ data_in[0], data_in[1], data_in[2], data_in[3]);\n+ data_in += 4;\n+ }\n+ break;\n+ // No reply\n+ // Note: Always sends six bytes\n+ case GCAMCommand::Unknown_22:\n+ {\n+ if (!validate_data_in_out(7, 0, "Unknown_22"))\n+ break;\n+\n+ DEBUG_LOG_FMT(\n+ SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x22, {:02x}, {:02x}, {:02x}, {:02x}, {:02x}, {:02x}, {:02x}",\n+ data_in[0], data_in[1], data_in[2], data_in[3], data_in[4], data_in[5], data_in[6]);\n+\n+ const u32 in_size = data_in[0] + 1;\n+ if (!validate_data_in_out(in_size, 0, "Unknown_22"))\n+ break;\n+ data_in += in_size;\n+ }\n+ break;\n+ case GCAMCommand::Unknown_23:\n+ if (!validate_data_in_out(2, 2, "Unknown_23"))\n+ break;\n+\n+ DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x23, {:02x} {:02x}", data_in[0],\n+ data_in[1]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x00;\n+\n+ data_in += 2;\n+ break;\n+ case GCAMCommand::Unknown_24:\n+ if (!validate_data_in_out(2, 2, "Unknown_24"))\n+ break;\n+\n+ DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x24, {:02x} {:02x}", data_in[0],\n+ data_in[1]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x00;\n+\n+ data_in += 2;\n+ break;\n+ case GCAMCommand::SerialA:\n+ {\n+ if (!validate_data_in_out(1, 0, "SerialA"))\n+ break;\n+\n+ const u32 length = *data_in++;\n+ if (length)\n+ {\n+ if (!validate_data_in_out(length, 0, "SerialA"))\n+ break;\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x31, length=0x{:02x}, hexdump:\\n{}",\n+ length, HexDump(data_in, length));\n+\n+ // Serial - Wheel\n+ if (AMMediaboard::GetGameType() == MarioKartGP ||\n+ AMMediaboard::GetGameType() == MarioKartGP2)\n+ {\n+ if (!validate_data_in_out(10, 2, "SerialA (Wheel)"))\n+ break;\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x31, (WHEEL) {:02x}{:02x} {:02x}{:02x} {:02x} {:02x} "\n+ "{:02x} {:02x} {:02x} {:02x}",\n+ data_in[0], data_in[1], data_in[2], data_in[3], data_in[4], data_in[5],\n+ data_in[6], data_in[7], data_in[8], data_in[9]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x03;\n+\n+ switch (m_wheel_init)\n+ {\n+ case 0:\n+ if (!validate_data_in_out(0, 3, "SerialA (Wheel)"))\n+ break;\n+ data_out[data_offset++] = \'E\'; // Error\n+ data_out[data_offset++] = \'0\';\n+ data_out[data_offset++] = \'0\';\n+ m_wheel_init++;\n+ break;\n+ case 1:\n+ if (!validate_data_in_out(0, 3, "SerialA (Wheel)"))\n+ break;\n+ data_out[data_offset++] = \'C\'; // Power Off\n+ data_out[data_offset++] = \'0\';\n+ data_out[data_offset++] = \'6\';\n+ // Only turn on when a wheel is connected\n+ if (serial_interface.GetDeviceType(1) == SerialInterface::SIDEVICE_GC_STEERING)\n+ {\n+ m_wheel_init++;\n+ }\n+ break;\n+ case 2:\n+ if (!validate_data_in_out(0, 3, "SerialA (Wheel)"))\n+ break;\n+ data_out[data_offset++] = \'C\'; // Power On\n+ data_out[data_offset++] = \'0\';\n+ data_out[data_offset++] = \'1\';\n+ break;\n+ default:\n+ break;\n+ }\n+\n+ // u16 CenteringForce= ptr(6);\n+ // u16 FrictionForce = ptr(8);\n+ // u16 Roll = ptr(10);\n+ if (!validate_data_in_out(length, 0, "SerialA (Wheel)"))\n+ break;\n+ data_in += length;\n+ break;\n+ }\n+\n+ // Serial - Unknown\n+ if (AMMediaboard::GetGameType() == GekitouProYakyuu)\n+ {\n+ if (!validate_data_in_out(sizeof(u32), 0, "SerialA (Unknown)"))\n+ break;\n+ const u32 serial_command = Common::BitCastPtr<u32>(data_in);\n+\n+ if (serial_command == 0x00001000)\n+ {\n+ if (!validate_data_in_out(0, 5, "SerialA (Unknown)"))\n+ break;\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x03;\n+ data_out[data_offset++] = 1;\n+ data_out[data_offset++] = 2;\n+ data_out[data_offset++] = 3;\n+ }\n+\n+ if (!validate_data_in_out(length, 0, "SerialA (Unknown)"))\n+ break;\n+ data_in += length;\n+ break;\n+ }\n+\n+ // Serial IC-CARD / Serial Deck Reader\n+ if (AMMediaboard::GetGameType() == VirtuaStriker4 ||\n+ AMMediaboard::GetGameType() == VirtuaStriker4_2006 ||\n+ AMMediaboard::GetGameType() == KeyOfAvalon)\n+ {\n+ if (!validate_data_in_out(2, 0, "SerialA (IC-CARD)"))\n+ break;\n+ u32 serial_command = data_in[1];\n+\n+ ICCommand icco;\n+\n+ // Set default reply\n+ icco.pktcmd = gcam_command;\n+ icco.pktlen = 7;\n+ icco.fixed = 0x10;\n+ icco.command = serial_command;\n+ icco.flag = 0;\n+ icco.length = 2;\n+ icco.status = 0;\n+ icco.extlen = 0;\n+\n+ // Check for rest of data from the write pages command\n+ if (m_ic_write_size && m_ic_write_offset)\n+ {\n+ const u32 size = data_in[1];\n+\n+ if (!validate_data_in_out(size + 2, 0, "SerialA (IC-CARD)"))\n+ break;\n+ DEBUG_LOG_FMT(SERIALINTERFACE_CARD, "Command: {}", HexDump(data_in, size + 2));\n+\n+ INFO_LOG_FMT(\n+ SERIALINTERFACE_CARD,\n+ "GC-AM: Command 25 (IC-CARD) Write Pages: Off:{:x} Size:{:x} PSize:{:x}",\n+ m_ic_write_offset, m_ic_write_size, size);\n+\n+ if (u64{m_ic_write_offset} + size > sizeof(m_ic_write_buffer))\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 25 (IC-CARD) m_ic_write_buffer overflow:\\n"\n+ " - m_ic_write_buffer(offset={}, size={})\\n"\n+ " - size={}\\n",\n+ m_ic_write_offset, sizeof(m_ic_write_buffer), size);\n+ data_in = data_in_end;\n+ break;\n+ }\n+ memcpy(m_ic_write_buffer + m_ic_write_offset, data_in + 2, size);\n+\n+ m_ic_write_offset += size;\n+\n+ if (m_ic_write_offset > m_ic_write_size)\n+ {\n+ m_ic_write_offset = 0;\n+\n+ const u16 page = m_ic_write_buffer[5];\n+ const u16 count = m_ic_write_buffer[7];\n+\n+ if ((page * 8 + count * 8) > sizeof(m_ic_card_data) ||\n+ (10 + count * 8) > sizeof(m_ic_write_buffer))\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 25 (IC-CARD) Write Pages overflow:\\n"\n+ " - m_ic_card_data(offset={}, size={})\\n"\n+ " - m_ic_write_buffer(offset={}, size={})\\n"\n+ " - size={}, page={}, count={}\\n",\n+ page * 8, sizeof(m_ic_card_data), 10, sizeof(m_ic_write_buffer),\n+ count * 8, page, count);\n+ data_in = data_in_end;\n+ break;\n+ }\n+ memcpy(m_ic_card_data + page * 8, m_ic_write_buffer + 10, count * 8);\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 25 (IC-CARD) Write Pages:{} Count:{}({:x})", page,\n+ count, size);\n+\n+ icco.command = WritePages;\n+\n+ if (!validate_data_in_out(0, icco.pktlen + 2, "SerialA (IC-CARD)"))\n+ break;\n+ ICCardSendReply(&icco, data_out.data(), &data_offset);\n+ }\n+ if (!validate_data_in_out(length, 0, "SerialA (IC-CARD)"))\n+ break;\n+ data_in += length;\n+ break;\n+ }\n+\n+ switch (ICCARDCommand(serial_command))\n+ {\n+ case ICCARDCommand::GetStatus:\n+ icco.status = m_ic_card_state;\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (IC-CARD) Get Status:{:02x}", m_ic_card_state);\n+ break;\n+ case ICCARDCommand::SetBaudrate:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (IC-CARD) Set Baudrate");\n+ break;\n+ case ICCARDCommand::FieldOn:\n+ m_ic_card_state |= 0x10;\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (IC-CARD) Field On");\n+ break;\n+ case ICCARDCommand::InsertCheck:\n+ icco.status = m_ic_card_status;\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (IC-CARD) Insert Check:{:02x}", m_ic_card_status);\n+ break;\n+ case ICCARDCommand::AntiCollision:\n+ icco.extlen = 8;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ // Card ID\n+ icco.extdata[0] = 0x00;\n+ icco.extdata[1] = 0x00;\n+ icco.extdata[2] = 0x54;\n+ icco.extdata[3] = 0x4D;\n+ icco.extdata[4] = 0x50;\n+ icco.extdata[5] = 0x00;\n+ icco.extdata[6] = 0x00;\n+ icco.extdata[7] = 0x00;\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (IC-CARD) Anti Collision");\n+ break;\n+ case ICCARDCommand::SelectCard:\n+ icco.extlen = 8;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ // Session\n+ icco.extdata[0] = 0x00;\n+ icco.extdata[1] = m_ic_card_session;\n+ icco.extdata[2] = 0x00;\n+ icco.extdata[3] = 0x00;\n+ icco.extdata[4] = 0x00;\n+ icco.extdata[5] = 0x00;\n+ icco.extdata[6] = 0x00;\n+ icco.extdata[7] = 0x00;\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (IC-CARD) Select Card:{}",\n+ m_ic_card_session);\n+ break;\n+ case ICCARDCommand::ReadPage:\n+ case ICCARDCommand::ReadUseCount:\n+ {\n+ if (!validate_data_in_out(8, 0, "SerialA (IC-CARD)"))\n+ break;\n+ const u16 page = Common::swap16(data_in + 6) & 0xFF; // 255 is max page\n+\n+ icco.extlen = 8;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ memcpy(icco.extdata, m_ic_card_data + page * 8, 8);\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 31 (IC-CARD) Read Page:{}",\n+ page);\n+ break;\n+ }\n+ case ICCARDCommand::WritePage:\n+ {\n+ if (!validate_data_in_out(10, 0, "SerialA (IC-CARD)"))\n+ break;\n+ const u16 page = Common::swap16(data_in + 8) & 0xFF; // 255 is max page\n+\n+ // Write only one page\n+ if (page == 4)\n+ {\n+ icco.status = 0x80;\n+ }\n+ else\n+ {\n+ if (!validate_data_in_out(18, 0, "SerialA (IC-CARD)"))\n+ break;\n+ memcpy(m_ic_card_data + page * 8, data_in + 10, 8);\n+ }\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (IC-CARD) Write Page:{}",\n+ page);\n+ break;\n+ }\n+ case ICCARDCommand::DecreaseUseCount:\n+ {\n+ if (!validate_data_in_out(8, 0, "SerialA (IC-CARD)"))\n+ break;\n+ const u16 page = Common::swap16(data_in + 6) & 0xFF; // 255 is max page\n+\n+ icco.extlen = 2;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ auto ic_card_data = Common::BitCastPtr<u16>(m_ic_card_data + 0x28);\n+ ic_card_data = ic_card_data - 1;\n+\n+ // Counter\n+ icco.extdata[0] = m_ic_card_data[0x28];\n+ icco.extdata[1] = m_ic_card_data[0x29];\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 31 (IC-CARD) Decrease Use Count:{}", page);\n+ break;\n+ }\n+ case ICCARDCommand::ReadPages:\n+ {\n+ if (!validate_data_in_out(10, 0, "SerialA (IC-CARD)"))\n+ break;\n+ const u16 page = Common::swap16(data_in + 6) & 0xFF; // 255 is max page\n+ const u16 count = Common::swap16(data_in + 8);\n+\n+ const u32 offs = page * 8;\n+ u32 cnt = count * 8;\n+\n+ // Limit read size to not overwrite the reply buffer\n+ const std::size_t reply_buffer_size = sizeof(icco.extdata) - 1;\n+ if (data_offset > reply_buffer_size)\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 31 (IC-CARD) Read Pages overflow:"\n+ " offset={} > buffer_size={}",\n+ data_offset, reply_buffer_size);\n+ data_in = data_in_end;\n+ break;\n+ }\n+ if (cnt > reply_buffer_size - data_offset)\n+ {\n+ cnt = 5 * 8;\n+ }\n+\n+ icco.extlen = cnt;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ if (offs + cnt > sizeof(icco.extdata))\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 31 (IC-CARD) Read Pages overflow:"\n+ " offset={} + count={} > buffer_size={}",\n+ offs, cnt, sizeof(icco.extdata));\n+ data_in = data_in_end;\n+ break;\n+ }\n+ memcpy(icco.extdata, m_ic_card_data + offs, cnt);\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 31 (IC-CARD) Read Pages:{} Count:{}", page, count);\n+ break;\n+ }\n+ case ICCARDCommand::WritePages:\n+ {\n+ if (!validate_data_in_out(10, 0, "SerialA (IC-CARD)"))\n+ break;\n+ const u16 pksize = length;\n+ const u16 size = Common::swap16(data_in + 2);\n+ const u16 page = Common::swap16(data_in + 6) & 0xFF; // 255 is max page\n+ const u16 count = Common::swap16(data_in + 8);\n+\n+ // We got a complete packet\n+ if (pksize - 5 == size)\n+ {\n+ if (page == 4) // Read Only Page, must return error\n+ {\n+ icco.status = 0x80;\n+ }\n+ else\n+ {\n+ if (page * 8 + count * 8 > sizeof(m_ic_card_data))\n+ {\n+ ERROR_LOG_FMT(\n+ SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (IC-CARD) Data overflow: Pages:{} Count:{}({:x})",\n+ page, count, size);\n+ }\n+ else\n+ {\n+ if (!validate_data_in_out(13 + count * 8, 0, "SerialA (IC-CARD)"))\n+ break;\n+ memcpy(m_ic_card_data + page * 8, data_in + 13, count * 8);\n+ }\n+ }\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (IC-CARD) Write Pages:{} Count:{}({:x})", page,\n+ count, size);\n+ }\n+ // VirtuaStriker 4 splits the writes over multiple packets\n+ else\n+ {\n+ if (!validate_data_in_out(2 + pksize, 0, "SerialA (IC-CARD)"))\n+ break;\n+ memcpy(m_ic_write_buffer, data_in + 2, pksize);\n+ m_ic_write_offset += pksize;\n+ m_ic_write_size = size;\n+ }\n+ break;\n+ }\n+ default:\n+ // Handle Deck Reader commands\n+ if (!validate_data_in_out(1, 0, "SerialA (DECK READER)"))\n+ break;\n+ serial_command = data_in[0];\n+ icco.command = serial_command;\n+ icco.flag = 0;\n+ switch (CDReaderCommand(serial_command))\n+ {\n+ case CDReaderCommand::ProgramVersion:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Program Version");\n+\n+ icco.extlen = (u32)strlen(s_cdr_program_version);\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ memcpy(icco.extdata, s_cdr_program_version, icco.extlen);\n+ break;\n+ case CDReaderCommand::BootVersion:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Boot Version");\n+\n+ icco.extlen = (u32)strlen(s_cdr_boot_version);\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ memcpy(icco.extdata, s_cdr_boot_version, icco.extlen);\n+ break;\n+ case CDReaderCommand::ShutterGet:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Shutter Get");\n+\n+ icco.extlen = 4;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ icco.extdata[0] = 0;\n+ icco.extdata[1] = 0;\n+ icco.extdata[2] = 0;\n+ icco.extdata[3] = 0;\n+ break;\n+ case CDReaderCommand::CameraCheck:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Camera Check");\n+\n+ icco.extlen = 6;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ icco.extdata[0] = 0x23;\n+ icco.extdata[1] = 0x28;\n+ icco.extdata[2] = 0x45;\n+ icco.extdata[3] = 0x29;\n+ icco.extdata[4] = 0x45;\n+ icco.extdata[5] = 0x29;\n+ break;\n+ case CDReaderCommand::ProgramChecksum:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Program Checksum");\n+\n+ icco.extlen = 4;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ icco.extdata[0] = 0x23;\n+ icco.extdata[1] = 0x28;\n+ icco.extdata[2] = 0x45;\n+ icco.extdata[3] = 0x29;\n+ break;\n+ case CDReaderCommand::BootChecksum:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Boot Checksum");\n+\n+ icco.extlen = 4;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ icco.extdata[0] = 0x23;\n+ icco.extdata[1] = 0x28;\n+ icco.extdata[2] = 0x45;\n+ icco.extdata[3] = 0x29;\n+ break;\n+ case CDReaderCommand::SelfTest:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (DECK READER) Self Test");\n+ icco.flag = 0x00;\n+ break;\n+ case CDReaderCommand::SensLock:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (DECK READER) Sens Lock");\n+ icco.flag = 0x01;\n+ break;\n+ case CDReaderCommand::SensCard:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (DECK READER) Sens Card");\n+ break;\n+ case CDReaderCommand::ShutterCard:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Shutter Card");\n+ break;\n+ case CDReaderCommand::ReadCard:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (DECK READER) Read Card");\n+\n+ icco.fixed = 0xAA;\n+ icco.flag = 0xAA;\n+ icco.extlen = sizeof(s_cdr_card_data);\n+ icco.length = 0x72;\n+ icco.status = Common::swap16(icco.extlen);\n+\n+ icco.pktlen += icco.extlen;\n+\n+ memcpy(icco.extdata, s_cdr_card_data, sizeof(s_cdr_card_data));\n+\n+ break;\n+ default:\n+ if (!validate_data_in_out(14, 0, "SerialA (DECK READER)"))\n+ break;\n+ WARN_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (IC-Card) {:02x} {:02x} {:02x} {:02x} {:02x} "\n+ "{:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x}",\n+ data_in[2], data_in[3], data_in[4], data_in[5], data_in[6],\n+ data_in[7], data_in[8], data_in[9], data_in[10], data_in[11],\n+ data_in[12], data_in[13]);\n+ break;\n+ }\n+ break;\n+ }\n+\n+ if (!validate_data_in_out(0, icco.pktlen + 2, "SerialA (IC-CARD)"))\n+ break;\n+ ICCardSendReply(&icco, data_out.data(), &data_offset);\n+\n+ if (!validate_data_in_out(2, 0, "SerialA (IC-CARD)"))\n+ break;\n+ data_in += length;\n+ break;\n+ }\n+ }\n+\n+ u32 command_offset = 0;\n+ while (command_offset < length)\n+ {\n+ // All commands are OR\'d with 0x80\n+ // Last byte is checksum which we don\'t care about\n+ if (!validate_data_in_out(command_offset + 4, 0, "SerialA"))\n+ break;\n+ const u32 serial_command = Common::swap32(data_in + command_offset) ^ 0x80000000;\n+\n+ if (AMMediaboard::GetGameType() == FZeroAX ||\n+ AMMediaboard::GetGameType() == FZeroAXMonster)\n+ {\n+ INFO_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x31 (MOTOR) Length:{:02x} Command:{:04x}({:02x})",\n+ length, (serial_command >> 8) & 0xFFFF, serial_command >> 24);\n+ }\n+ else\n+ {\n+ INFO_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x31 (SERIAL) Command:{:06x}",\n+ serial_command);\n+\n+ if (serial_command == 0x801000)\n+ {\n+ if (!validate_data_in_out(0, 4, "SerialA"))\n+ break;\n+ data_out[data_offset++] = 0x31;\n+ data_out[data_offset++] = 0x02;\n+ data_out[data_offset++] = 0xFF;\n+ data_out[data_offset++] = 0x01;\n+ }\n+ }\n+\n+ command_offset += sizeof(u32);\n+\n+ if (AMMediaboard::GetGameType() == FZeroAX ||\n+ AMMediaboard::GetGameType() == FZeroAXMonster)\n+ {\n+ if (command_offset + 5 >= std::size(m_motor_reply))\n+ {\n+ ERROR_LOG_FMT(\n+ SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x31 (MOTOR) overflow: offset={} >= motor_reply_size={}",\n+ command_offset + 5, std::size(m_motor_reply));\n+ data_in = data_in_end;\n+ break;\n+ }\n+\n+ // Status\n+ m_motor_reply[command_offset + 2] = 0;\n+ m_motor_reply[command_offset + 3] = 0;\n+\n+ // Error\n+ m_motor_reply[command_offset + 4] = 0;\n+\n+ switch (serial_command >> 24)\n+ {\n+ case 0:\n+ case 1: // Set Maximum?\n+ case 2:\n+ break;\n+\n+ // 0x00-0x40: left\n+ // 0x40-0x80: right\n+ case 4: // Move Steering Wheel\n+ // Left\n+ if (serial_command & 0x010000)\n+ {\n+ m_motor_force_y = -((s16)serial_command & 0xFF00);\n+ }\n+ else // Right\n+ {\n+ m_motor_force_y = (serial_command - 0x4000) & 0xFF00;\n+ }\n+\n+ m_motor_force_y *= 2;\n+\n+ // FFB\n+ if (m_motor_init == 2)\n+ {\n+ if (serial_interface.GetDeviceType(1) == SerialInterface::SIDEVICE_GC_STEERING)\n+ {\n+ const GCPadStatus pad_status = Pad::GetStatus(1);\n+ if (pad_status.isConnected)\n+ {\n+ const ControlState mapped_strength = (double)(m_motor_force_y >> 8) / 127.f;\n+\n+ Pad::Rumble(1, mapped_strength);\n+ INFO_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x31 (MOTOR) mapped_strength:{}",\n+ mapped_strength);\n+ }\n+ }\n+ }\n+ break;\n+ case 6: // nice\n+ case 9:\n+ default:\n+ break;\n+ // Switch back to normal controls\n+ case 7:\n+ m_motor_init = 2;\n+ break;\n+ // Reset\n+ case 0x7F:\n+ m_motor_init = 1;\n+ memset(m_motor_reply, 0, sizeof(m_motor_reply));\n+ break;\n+ }\n+\n+ // Checksum\n+ m_motor_reply[command_offset + 5] = m_motor_reply[command_offset + 2] ^\n+ m_motor_reply[command_offset + 3] ^\n+ m_motor_reply[command_offset + 4];\n+ }\n+ }\n+\n+ if (length == 0)\n+ {\n+ if (!validate_data_in_out(length, 2, "SerialA"))\n+ break;\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x00;\n+ }\n+ else\n+ {\n+ if (m_motor_init)\n+ {\n+ // Motor\n+ m_motor_reply[0] = gcam_command;\n+ m_motor_reply[1] = length; // Same out as in size\n+\n+ const u32 reply_size = m_motor_reply[1] + 2;\n+ if (!validate_data_in_out(length, reply_size, "SerialA"))\n+ break;\n+\n+ if (reply_size > sizeof(m_motor_reply))\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command SerialA, reply_size={} too big for m_motor_reply",\n+ reply_size);\n+ data_in = data_in_end;\n+ break;\n+ }\n+ memcpy(data_out.data() + data_offset, m_motor_reply, reply_size);\n+ data_offset += reply_size;\n+ }\n+ }\n+\n+ if (!validate_data_in_out(length, 0, "SerialA"))\n+ {\n+ break;\n+ }\n+ data_in += length;\n+ break;\n+ }\n+ case GCAMCommand::SerialB:\n+ {\n+ DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 32 (CARD-Interface)");\n+ if (!validate_data_in_out(1, 0, "SerialB"))\n+ break;\n+ const u32 length = *data_in++;\n+ if (!validate_data_in_out(length, 0, "SerialB"))\n+ break;\n+ if (length)\n+ {\n+ // Send Card Reply\n+ if (length == 1 && data_in[0] == 0x05)\n+ {\n+ if (m_card_read_length)\n+ {\n+ if (!validate_data_in_out(0, 1, "SerialB"))\n+ break;\n+ data_out[data_offset++] = gcam_command;\n+ u32 read_length = m_card_read_length - m_card_read;\n+\n+ if (AMMediaboard::GetGameType() == FZeroAX)\n+ {\n+ read_length = std::min<u32>(read_length, 0x2F);\n+ }\n+\n+ if (!validate_data_in_out(0, 1, "SerialB"))\n+ break;\n+ data_out[data_offset++] = read_length; // 0x2F (max size per packet)\n+\n+ if (!validate_data_in_out(0, read_length, "SerialB"))\n+ break;\n+ if (u64{m_card_read} + read_length > sizeof(m_card_read_packet))\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command SerialB, m_card_read_packet overflow:\\n"\n+ " - m_card_read_packet = {}\\n"\n+ " - m_card_read = {}\\n"\n+ " - read_length = {}",\n+ fmt::ptr(m_card_read_packet), m_card_read, read_length);\n+ data_in = data_in_end;\n+ break;\n+ }\n+ memcpy(data_out.data() + data_offset, m_card_read_packet + m_card_read,\n+ read_length);\n+\n+ data_offset += read_length;\n+ m_card_read += read_length;\n+\n+ if (m_card_read >= m_card_read_length)\n+ m_card_read_length = 0;\n+\n+ data_in += length;\n+ break;\n+ }\n+\n+ if (!validate_data_in_out(0, 5, "SerialB"))\n+ break;\n+\n+ data_out[data_offset++] = gcam_command;\n+ const u32 command_length_offset = data_offset;\n+ data_out[data_offset++] = 0x00; // len\n+\n+ data_out[data_offset++] = 0x02;\n+ const u32 checksum_start = data_offset;\n+\n+ data_out[data_offset++] = 0x00; // 0x00 len\n+\n+ data_out[data_offset++] = m_card_command; // 0x01 command\n+\n+ switch (CARDCommand(m_card_command))\n+ {\n+ case CARDCommand::Init:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x00; // 0x02\n+ data_out[data_offset++] = 0x30; // 0x03\n+ break;\n+ case CARDCommand::GetState:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x20 | m_card_bit; // 0x02\n+\n+ // bit 0: Please take your card\n+ // bit 1: Endless waiting causes UNK_E to be called\n+ data_out[data_offset++] = 0x00; // 0x03\n+ break;\n+ case CARDCommand::Read:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x02; // 0x02\n+ data_out[data_offset++] = 0x53; // 0x03\n+ break;\n+ case CARDCommand::IsPresent:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x22; // 0x02\n+ data_out[data_offset++] = 0x30; // 0x03\n+ break;\n+ case CARDCommand::Write:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x02; // 0x02\n+ data_out[data_offset++] = 0x00; // 0x03\n+ break;\n+ case CARDCommand::SetPrintParam:\n+ case CARDCommand::RegisterFont:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x00; // 0x02\n+ data_out[data_offset++] = 0x00; // 0x03\n+ break;\n+ case CARDCommand::WriteInfo:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x02; // 0x02\n+ data_out[data_offset++] = 0x00; // 0x03\n+ break;\n+ case CARDCommand::Erase:\n+ // TODO: CARDCommand::Erase is not handled.\n+ break;\n+ case CARDCommand::Eject:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ if (AMMediaboard::GetGameType() == FZeroAX)\n+ {\n+ data_out[data_offset++] = 0x01; // 0x02\n+ }\n+ else\n+ {\n+ data_out[data_offset++] = 0x31; // 0x02\n+ }\n+ data_out[data_offset++] = 0x30; // 0x03\n+ break;\n+ case CARDCommand::Clean:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x02; // 0x02\n+ data_out[data_offset++] = 0x00; // 0x03\n+ break;\n+ case CARDCommand::Load:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x02; // 0x02\n+ data_out[data_offset++] = 0x30; // 0x03\n+ break;\n+ case CARDCommand::SetShutter:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x00; // 0x02\n+ data_out[data_offset++] = 0x00; // 0x03\n+ break;\n+ }\n+\n+ if (!validate_data_in_out(0, 3, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x30; // 0x04\n+ data_out[data_offset++] = 0x00; // 0x05\n+\n+ data_out[data_offset++] = 0x03; // 0x06\n+\n+ data_out[checksum_start] = data_offset - checksum_start; // 0x00 len\n+\n+ if (!validate_data_in_out(0, 1, "SerialB"))\n+ break;\n+ data_out[data_offset] = 0; // 0x07\n+ for (u32 i = 0; i < data_out[checksum_start]; ++i)\n+ data_out[data_offset] ^= data_out[checksum_start + i];\n+\n+ if (!validate_data_in_out(0, 1, "SerialB"))\n+ break;\n+ data_offset++;\n+\n+ data_out[command_length_offset] = data_out[checksum_start] + 2;\n+ }\n+ else\n+ {\n+ if (!validate_data_in_out(length, 0, "SerialB"))\n+ break;\n+ if (u64{m_card_offset} + length > std::size(m_card_buffer))\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command SerialB, m_card_buffer overflow:\\n"\n+ " - m_card_buffer = {}\\n"\n+ " - m_card_offset = {}\\n"\n+ " - length = {}",\n+ fmt::ptr(m_card_buffer), m_card_offset, length);\n+ data_in = data_in_end;\n+ break;\n+ }\n+ for (u32 i = 0; i < length; ++i)\n+ m_card_buffer[m_card_offset + i] = data_in[i];\n+\n+ m_card_offset += length;\n+\n+ // Check if we got a complete command\n+ if (m_card_buffer[0] == 0x02)\n+ {\n+ if (m_card_buffer[1] == m_card_offset - 2)\n+ {\n+ if (m_card_buffer[m_card_offset - 2] == 0x03)', 'path': 'Source/Core/Core/HW/SI/SI_DeviceAMBaseboard.cpp', 'position': 1345, 'original_position': 1345, 'commit_id': '5bf8fe93286ebe4f08fb469a44f259a8397c89e0', 'user': {'login': 'JosJuice', 'id': 6716818, 'node_id': 'MDQ6VXNlcjY3MTY4MTg=', 'avatar_url': 'https://avatars.githubusercontent.com/u/6716818?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/JosJuice', 'html_url': 'https://github.com/JosJuice', 'followers_url': 'https://api.github.com/users/JosJuice/followers', 'following_url': 'https://api.github.com/users/JosJuice/following{/other_user}', 'gists_url': 'https://api.github.com/users/JosJuice/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/JosJuice/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/JosJuice/subscriptions', 'organizations_url': 'https://api.github.com/users/JosJuice/orgs', 'repos_url': 'https://api.github.com/users/JosJuice/repos', 'events_url': 'https://api.github.com/users/JosJuice/events{/privacy}', 'received_events_url': 'https://api.github.com/users/JosJuice/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'body': "I think `m_card_buffer[0]` contains the same value as `length`. If that's the case, this array offset is safe, but this is hard to reason about. If my analysis is correct, could we do something like replacing `m_card_buffer[0] == 0x02` with `length == 0x02`, or replacing `m_card_offset - 2` with a copy of `m_card_offset` from before we incremented it by `length`?", 'created_at': '2026-02-01T14:18:38Z', 'updated_at': '2026-02-01T15:40:26Z', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/13844#discussion_r2751294423', 'pull_request_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13844', 'author_association': 'MEMBER', '_links': {'self': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2751294423'}, 'html': {'href': 'https://github.com/dolphin-emu/dolphin/pull/13844#discussion_r2751294423'}, 'pull_request': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13844'}}, 'original_commit_id': '5bf8fe93286ebe4f08fb469a44f259a8397c89e0', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2751294423/reactions', 'total_count': 0, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 0, 'confused': 0, 'heart': 0, 'rocket': 0, 'eyes': 0}}, {'id': 2751296052, 'node_id': 'PRRC_kwDOALCn2M6j_XI0', 'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2751296052', 'pull_request_review_id': 3735806017, 'diff_hunk': '@@ -0,0 +1,2708 @@\n+// Copyright 2025 Dolphin Emulator Project\n+// SPDX-License-Identifier: GPL-2.0-or-later\n+\n+#include "Core/HW/SI/SI_DeviceAMBaseboard.h"\n+\n+#include <algorithm>\n+#include <numeric>\n+#include <string>\n+\n+#include <fmt/format.h>\n+\n+#include "Common/Buffer.h"\n+#include "Common/CommonTypes.h"\n+#include "Common/FileUtil.h"\n+#include "Common/IOFile.h"\n+#include "Common/Logging/Log.h"\n+#include "Common/MsgHandler.h"\n+#include "Common/Swap.h"\n+\n+#include "Core/Boot/Boot.h"\n+#include "Core/BootManager.h"\n+#include "Core/Config/MainSettings.h"\n+#include "Core/ConfigManager.h"\n+#include "Core/Core.h"\n+#include "Core/CoreTiming.h"\n+#include "Core/HW/DVD/AMMediaboard.h"\n+#include "Core/HW/DVD/DVDInterface.h"\n+#include "Core/HW/EXI/EXI.h"\n+#include "Core/HW/GCPad.h"\n+#include "Core/HW/MMIO.h"\n+#include "Core/HW/Memmap.h"\n+#include "Core/HW/ProcessorInterface.h"\n+#include "Core/HW/SI/SI.h"\n+#include "Core/HW/SI/SI_Device.h"\n+#include "Core/HW/SI/SI_DeviceGCController.h"\n+#include "Core/HW/SystemTimers.h"\n+#include "Core/Movie.h"\n+#include "Core/NetPlayProto.h"\n+#include "Core/System.h"\n+\n+#include "InputCommon/GCPadStatus.h"\n+\n+namespace SerialInterface\n+{\n+void JVSIOMessage::Start(int node)\n+{\n+ m_last_start = m_pointer;\n+ const u8 header[3] = {0xE0, (u8)node, 0};\n+ m_checksum = 0;\n+ AddData(header, 3, 1);\n+}\n+\n+void JVSIOMessage::AddData(const u8* dst, size_t len, int sync = 0)\n+{\n+ if (m_pointer + len >= sizeof(m_message))\n+ {\n+ PanicAlertFmt("JVSIOMessage overrun!");\n+ return;\n+ }\n+\n+ while (len--)\n+ {\n+ const u8 c = *dst++;\n+ if (!sync && ((c == 0xE0) || (c == 0xD0)))\n+ {\n+ if (m_pointer + 2 > sizeof(m_message))\n+ {\n+ PanicAlertFmt("JVSIOMessage overrun!");\n+ break;\n+ }\n+ m_message[m_pointer++] = 0xD0;\n+ m_message[m_pointer++] = c - 1;\n+ }\n+ else\n+ {\n+ if (m_pointer >= sizeof(m_message))\n+ {\n+ PanicAlertFmt("JVSIOMessage overrun!");\n+ break;\n+ }\n+ m_message[m_pointer++] = c;\n+ }\n+\n+ if (!sync)\n+ m_checksum += c;\n+ sync = 0;\n+ }\n+}\n+\n+void JVSIOMessage::AddData(const void* data, size_t len)\n+{\n+ AddData(static_cast<const u8*>(data), len);\n+}\n+\n+void JVSIOMessage::AddData(const char* data)\n+{\n+ AddData(data, strlen(data));\n+}\n+\n+void JVSIOMessage::AddData(u32 n)\n+{\n+ const u8 cs = n;\n+ AddData(&cs, 1);\n+}\n+\n+void JVSIOMessage::End()\n+{\n+ const u32 len = m_pointer - m_last_start;\n+ if (m_last_start + 2 < sizeof(m_message) && len >= 3)\n+ {\n+ m_message[m_last_start + 2] = len - 2; // assuming len <0xD0\n+ AddData(m_checksum + len - 2);\n+ }\n+ else\n+ {\n+ PanicAlertFmt("JVSIOMessage: Not enough space for checksum!");\n+ }\n+}\n+\n+static constexpr u8 CheckSumXOR(const u8* data, u32 length)\n+{\n+ return std::accumulate(data, data + length, u8{}, std::bit_xor());\n+}\n+\n+static constexpr char s_cdr_program_version[] = {" Version 1.22,2003/09/19,171-8213B"};\n+static constexpr char s_cdr_boot_version[] = {" Version 1.04,2003/06/17,171-8213B"};\n+static constexpr u8 s_cdr_card_data[] = {\n+ 0x00, 0x6E, 0x00, 0x00, 0x01, 0x00, 0x00, 0x06, 0x00, 0x00, 0x07, 0x00, 0x00, 0x0B,\n+ 0x00, 0x00, 0x0E, 0x00, 0x00, 0x10, 0x00, 0x00, 0x17, 0x00, 0x00, 0x19, 0x00, 0x00,\n+ 0x1A, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x20, 0x00,\n+ 0x00, 0x22, 0x00, 0x00, 0x23, 0x00, 0x00, 0x24, 0x00, 0x00, 0x27, 0x00, 0x00, 0x28,\n+ 0x00, 0x00, 0x2C, 0x00, 0x00, 0x2F, 0x00, 0x00, 0x34, 0x00, 0x00, 0x35, 0x00, 0x00,\n+ 0x37, 0x00, 0x00, 0x38, 0x00, 0x00, 0x39, 0x00, 0x00, 0x3D, 0x00};\n+\n+const constexpr u8 s_region_flags[] = "\\x00\\x00\\x30\\x00"\n+ // "\\x01\\xfe\\x00\\x00" // JAPAN\n+ "\\x02\\xfd\\x00\\x00" // USA\n+ //"\\x03\\xfc\\x00\\x00" // export\n+ "\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff";\n+// AM-Baseboard device on SI\n+CSIDevice_AMBaseboard::CSIDevice_AMBaseboard(Core::System& system, SIDevices device,\n+ int device_number)\n+ : ISIDevice(system, device, device_number)\n+{\n+ // Card ID\n+ m_ic_card_data[0x20] = 0x95;\n+ m_ic_card_data[0x21] = 0x71;\n+\n+ if (AMMediaboard::GetGameType() == KeyOfAvalon)\n+ {\n+ m_ic_card_data[0x22] = 0x26;\n+ m_ic_card_data[0x23] = 0x40;\n+ }\n+ else if (AMMediaboard::GetGameType() == VirtuaStriker4)\n+ {\n+ m_ic_card_data[0x22] = 0x44;\n+ m_ic_card_data[0x23] = 0x00;\n+ }\n+\n+ // Use count\n+ m_ic_card_data[0x28] = 0xFF;\n+ m_ic_card_data[0x29] = 0xFF;\n+}\n+\n+constexpr u32 SI_XFER_LENGTH_MASK = 0x7f;\n+\n+// Translate [0,1,2,...,126,127] to [128,1,2,...,126,127]\n+constexpr s32 ConvertSILengthField(u32 field)\n+{\n+ return ((field - 1) & SI_XFER_LENGTH_MASK) + 1;\n+}\n+\n+void CSIDevice_AMBaseboard::ICCardSendReply(ICCommand* iccommand, u8* buffer, u32* length)\n+{\n+ iccommand->status = Common::swap16(iccommand->status);\n+\n+ const auto iccommand_data = reinterpret_cast<const u8*>(iccommand);\n+ const u8 crc = CheckSumXOR(iccommand_data + 2, iccommand->pktlen - 1);\n+\n+ for (u32 i = 0; i < iccommand->pktlen + 1; ++i)\n+ {\n+ buffer[(*length)++] = iccommand_data[i];\n+ }\n+\n+ buffer[(*length)++] = crc;\n+}\n+\n+void CSIDevice_AMBaseboard::SwapBuffers(u8* buffer, u32* buffer_length)\n+{\n+ memcpy(m_last[1], buffer, 0x80); // Save current buffer\n+ memcpy(buffer, m_last[0], 0x80); // Load previous buffer\n+ memcpy(m_last[0], m_last[1], 0x80); // Update history\n+\n+ m_lastptr[1] = *buffer_length; // Swap lengths\n+ *buffer_length = m_lastptr[0];\n+ m_lastptr[0] = m_lastptr[1];\n+}\n+\n+int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length)\n+{\n+ const auto& serial_interface = m_system.GetSerialInterface();\n+ u32 buffer_length = ConvertSILengthField(serial_interface.GetInLength());\n+\n+ // Debug logging\n+ ISIDevice::RunBuffer(buffer, buffer_length);\n+\n+ u32 buffer_position = 0;\n+ while (buffer_position < buffer_length)\n+ {\n+ BaseBoardCommand command = static_cast<BaseBoardCommand>(buffer[buffer_position]);\n+ buffer_position++;\n+\n+ switch (command)\n+ {\n+ case BaseBoardCommand::GCAM_Reset: // Returns ID and dip switches\n+ {\n+ const u32 id = Common::swap32(SI_AM_BASEBOARD | 0x100);\n+ std::memcpy(buffer, &id, sizeof(id));\n+ return sizeof(id);\n+ }\n+ break;\n+ case BaseBoardCommand::GCAM_Command:\n+ {\n+ u32 checksum = 0;\n+ for (u32 i = 0; i < buffer_length; ++i)\n+ checksum += buffer[i];\n+\n+ std::array<u8, 0x80> data_out{};\n+ u32 data_offset = 0;\n+\n+ static u32 dip_switch_1 = 0xFE;\n+ static u32 dip_switch_0 = 0xFF;\n+\n+ data_out[data_offset++] = 1;\n+ data_out[data_offset++] = 1;\n+\n+ u8* data_in = buffer + 2;\n+ if (buffer_position >= buffer_length)\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: buffer overflow (position={}, length={})",\n+ buffer_position, buffer_length);\n+ buffer_position = buffer_length;\n+ break;\n+ }\n+\n+ const u32 requested_size = buffer[buffer_position] + 2;\n+ if (requested_size > buffer_length)\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: requested size ({}) bigger than buffer\'s ({})",\n+ requested_size, buffer_length);\n+ buffer_position = buffer_length;\n+ break;\n+ }\n+ u8* const data_in_end = buffer + requested_size;\n+\n+ // Helper to check that iterating over data n times is safe,\n+ // i.e. *data++ at most lead to data.end()\n+ auto validate_data_in_out = [&](u32 n_in, u32 n_out, std::string_view command) -> bool {\n+ if (data_in + n_in > data_in_end)\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: data_in overflow in {}", command);\n+ else if (u64{data_offset} + n_out > data_out.size())\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: data_out overflow in {}", command);\n+ else\n+ return true;\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "Overflow details:\\n"\n+ " - data_in(begin={}, current={}, end={}, n_in={})\\n"\n+ " - data_out(offset={}, size={}, n_out={})\\n"\n+ " - buffer(position={}, length={})",\n+ fmt::ptr(buffer + 2), fmt::ptr(data_in), fmt::ptr(data_in_end), n_in,\n+ data_offset, data_out.size(), n_out, buffer_position, buffer_length);\n+ data_in = data_in_end;\n+ return false;\n+ };\n+\n+ while (data_in < data_in_end)\n+ {\n+ const u32 gcam_command = *data_in++;\n+ switch (GCAMCommand(gcam_command))\n+ {\n+ case GCAMCommand::StatusSwitches:\n+ {\n+ if (!validate_data_in_out(1, 4, "StatusSwitches"))\n+ break;\n+\n+ const u8 status = *data_in++;\n+ DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x10, {:02x} (READ STATUS&SWITCHES)",\n+ status);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x2;\n+\n+ // We read Test/Service from the JVS-I/O SwitchesInput instead\n+ //\n+ // const GCPadStatus pad_status = Pad::GetStatus(ISIDevice::m_device_number);\n+ // baseboard test/service switches\n+ // if (pad_status.button & PAD_BUTTON_Y)\t// Test\n+ // dip_switch_0 &= ~0x80;\n+ // if (pad_status.button & PAD_BUTTON_X)\t// Service\n+ // dip_switch_0 &= ~0x40;\n+\n+ // Horizontal Scanning Frequency switch\n+ // Required for F-Zero AX booting via Sega Boot\n+ if (AMMediaboard::GetGameType() == FZeroAX ||\n+ AMMediaboard::GetGameType() == FZeroAXMonster)\n+ {\n+ dip_switch_0 &= ~0x20;\n+ }\n+\n+ // Disable camera in MKGP1/2\n+ if (AMMediaboard::GetGameType() == MarioKartGP ||\n+ AMMediaboard::GetGameType() == MarioKartGP2)\n+ {\n+ dip_switch_0 &= ~0x10;\n+ }\n+\n+ data_out[data_offset++] = dip_switch_0;\n+ data_out[data_offset++] = dip_switch_1;\n+ break;\n+ }\n+ case GCAMCommand::SerialNumber:\n+ {\n+ if (!validate_data_in_out(1, 18, "SerialNumber"))\n+ break;\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x11, {:02x} (READ SERIAL NR)",\n+ *data_in);\n+ data_in++;\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 16;\n+ memcpy(data_out.data() + data_offset, "AADE-01B98394904", 16);\n+\n+ data_offset += 16;\n+ break;\n+ }\n+ case GCAMCommand::Unknown_12:\n+ if (!validate_data_in_out(2, 2, "Unknown_12"))\n+ break;\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x12, {:02x} {:02x}", data_in[0],\n+ data_in[1]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x00;\n+\n+ data_in += 2;\n+ break;\n+ case GCAMCommand::Unknown_14:\n+ if (!validate_data_in_out(2, 2, "Unknown_14"))\n+ break;\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x14, {:02x} {:02x}", data_in[0],\n+ data_in[1]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x00;\n+\n+ data_in += 2;\n+ break;\n+ case GCAMCommand::FirmVersion:\n+ if (!validate_data_in_out(1, 4, "FirmVersion"))\n+ break;\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x15, {:02x} (READ FIRM VERSION)",\n+ *data_in);\n+ data_in++;\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x02;\n+ // 00.26\n+ data_out[data_offset++] = 0x00;\n+ data_out[data_offset++] = 0x26;\n+ break;\n+ case GCAMCommand::FPGAVersion:\n+ if (!validate_data_in_out(1, 4, "FPGAVersion"))\n+ break;\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x16, {:02x} (READ FPGA VERSION)",\n+ *data_in);\n+ data_in++;\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x02;\n+ // 07.06\n+ data_out[data_offset++] = 0x07;\n+ data_out[data_offset++] = 0x06;\n+ break;\n+ case GCAMCommand::RegionSettings:\n+ {\n+ if (!validate_data_in_out(5, 0x16, "RegionSettings"))\n+ break;\n+\n+ // Used by SegaBoot for region checks (dev mode skips this check)\n+ // In some games this also controls the displayed language\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x1F, {:02x} {:02x} {:02x} {:02x} {:02x} (REGION)",\n+ data_in[0], data_in[1], data_in[2], data_in[3], data_in[4]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x14;\n+\n+ for (int i = 0; i < 0x14; ++i)\n+ data_out[data_offset++] = s_region_flags[i];\n+\n+ data_in += 5;\n+ }\n+ break;\n+ // No reply\n+ // Note: Always sends three bytes even though size is set to two\n+ case GCAMCommand::Unknown_21:\n+ {\n+ if (!validate_data_in_out(4, 0, "Unknown_21"))\n+ break;\n+\n+ DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x21, {:02x}, {:02x}, {:02x}, {:02x}",\n+ data_in[0], data_in[1], data_in[2], data_in[3]);\n+ data_in += 4;\n+ }\n+ break;\n+ // No reply\n+ // Note: Always sends six bytes\n+ case GCAMCommand::Unknown_22:\n+ {\n+ if (!validate_data_in_out(7, 0, "Unknown_22"))\n+ break;\n+\n+ DEBUG_LOG_FMT(\n+ SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x22, {:02x}, {:02x}, {:02x}, {:02x}, {:02x}, {:02x}, {:02x}",\n+ data_in[0], data_in[1], data_in[2], data_in[3], data_in[4], data_in[5], data_in[6]);\n+\n+ const u32 in_size = data_in[0] + 1;\n+ if (!validate_data_in_out(in_size, 0, "Unknown_22"))\n+ break;\n+ data_in += in_size;\n+ }\n+ break;\n+ case GCAMCommand::Unknown_23:\n+ if (!validate_data_in_out(2, 2, "Unknown_23"))\n+ break;\n+\n+ DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x23, {:02x} {:02x}", data_in[0],\n+ data_in[1]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x00;\n+\n+ data_in += 2;\n+ break;\n+ case GCAMCommand::Unknown_24:\n+ if (!validate_data_in_out(2, 2, "Unknown_24"))\n+ break;\n+\n+ DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x24, {:02x} {:02x}", data_in[0],\n+ data_in[1]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x00;\n+\n+ data_in += 2;\n+ break;\n+ case GCAMCommand::SerialA:\n+ {\n+ if (!validate_data_in_out(1, 0, "SerialA"))\n+ break;\n+\n+ const u32 length = *data_in++;\n+ if (length)\n+ {\n+ if (!validate_data_in_out(length, 0, "SerialA"))\n+ break;\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x31, length=0x{:02x}, hexdump:\\n{}",\n+ length, HexDump(data_in, length));\n+\n+ // Serial - Wheel\n+ if (AMMediaboard::GetGameType() == MarioKartGP ||\n+ AMMediaboard::GetGameType() == MarioKartGP2)\n+ {\n+ if (!validate_data_in_out(10, 2, "SerialA (Wheel)"))\n+ break;\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x31, (WHEEL) {:02x}{:02x} {:02x}{:02x} {:02x} {:02x} "\n+ "{:02x} {:02x} {:02x} {:02x}",\n+ data_in[0], data_in[1], data_in[2], data_in[3], data_in[4], data_in[5],\n+ data_in[6], data_in[7], data_in[8], data_in[9]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x03;\n+\n+ switch (m_wheel_init)\n+ {\n+ case 0:\n+ if (!validate_data_in_out(0, 3, "SerialA (Wheel)"))\n+ break;\n+ data_out[data_offset++] = \'E\'; // Error\n+ data_out[data_offset++] = \'0\';\n+ data_out[data_offset++] = \'0\';\n+ m_wheel_init++;\n+ break;\n+ case 1:\n+ if (!validate_data_in_out(0, 3, "SerialA (Wheel)"))\n+ break;\n+ data_out[data_offset++] = \'C\'; // Power Off\n+ data_out[data_offset++] = \'0\';\n+ data_out[data_offset++] = \'6\';\n+ // Only turn on when a wheel is connected\n+ if (serial_interface.GetDeviceType(1) == SerialInterface::SIDEVICE_GC_STEERING)\n+ {\n+ m_wheel_init++;\n+ }\n+ break;\n+ case 2:\n+ if (!validate_data_in_out(0, 3, "SerialA (Wheel)"))\n+ break;\n+ data_out[data_offset++] = \'C\'; // Power On\n+ data_out[data_offset++] = \'0\';\n+ data_out[data_offset++] = \'1\';\n+ break;\n+ default:\n+ break;\n+ }\n+\n+ // u16 CenteringForce= ptr(6);\n+ // u16 FrictionForce = ptr(8);\n+ // u16 Roll = ptr(10);\n+ if (!validate_data_in_out(length, 0, "SerialA (Wheel)"))\n+ break;\n+ data_in += length;\n+ break;\n+ }\n+\n+ // Serial - Unknown\n+ if (AMMediaboard::GetGameType() == GekitouProYakyuu)\n+ {\n+ if (!validate_data_in_out(sizeof(u32), 0, "SerialA (Unknown)"))\n+ break;\n+ const u32 serial_command = Common::BitCastPtr<u32>(data_in);\n+\n+ if (serial_command == 0x00001000)\n+ {\n+ if (!validate_data_in_out(0, 5, "SerialA (Unknown)"))\n+ break;\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x03;\n+ data_out[data_offset++] = 1;\n+ data_out[data_offset++] = 2;\n+ data_out[data_offset++] = 3;\n+ }\n+\n+ if (!validate_data_in_out(length, 0, "SerialA (Unknown)"))\n+ break;\n+ data_in += length;\n+ break;\n+ }\n+\n+ // Serial IC-CARD / Serial Deck Reader\n+ if (AMMediaboard::GetGameType() == VirtuaStriker4 ||\n+ AMMediaboard::GetGameType() == VirtuaStriker4_2006 ||\n+ AMMediaboard::GetGameType() == KeyOfAvalon)\n+ {\n+ if (!validate_data_in_out(2, 0, "SerialA (IC-CARD)"))\n+ break;\n+ u32 serial_command = data_in[1];\n+\n+ ICCommand icco;\n+\n+ // Set default reply\n+ icco.pktcmd = gcam_command;\n+ icco.pktlen = 7;\n+ icco.fixed = 0x10;\n+ icco.command = serial_command;\n+ icco.flag = 0;\n+ icco.length = 2;\n+ icco.status = 0;\n+ icco.extlen = 0;\n+\n+ // Check for rest of data from the write pages command\n+ if (m_ic_write_size && m_ic_write_offset)\n+ {\n+ const u32 size = data_in[1];\n+\n+ if (!validate_data_in_out(size + 2, 0, "SerialA (IC-CARD)"))\n+ break;\n+ DEBUG_LOG_FMT(SERIALINTERFACE_CARD, "Command: {}", HexDump(data_in, size + 2));\n+\n+ INFO_LOG_FMT(\n+ SERIALINTERFACE_CARD,\n+ "GC-AM: Command 25 (IC-CARD) Write Pages: Off:{:x} Size:{:x} PSize:{:x}",\n+ m_ic_write_offset, m_ic_write_size, size);\n+\n+ if (u64{m_ic_write_offset} + size > sizeof(m_ic_write_buffer))\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 25 (IC-CARD) m_ic_write_buffer overflow:\\n"\n+ " - m_ic_write_buffer(offset={}, size={})\\n"\n+ " - size={}\\n",\n+ m_ic_write_offset, sizeof(m_ic_write_buffer), size);\n+ data_in = data_in_end;\n+ break;\n+ }\n+ memcpy(m_ic_write_buffer + m_ic_write_offset, data_in + 2, size);\n+\n+ m_ic_write_offset += size;\n+\n+ if (m_ic_write_offset > m_ic_write_size)\n+ {\n+ m_ic_write_offset = 0;\n+\n+ const u16 page = m_ic_write_buffer[5];\n+ const u16 count = m_ic_write_buffer[7];\n+\n+ if ((page * 8 + count * 8) > sizeof(m_ic_card_data) ||\n+ (10 + count * 8) > sizeof(m_ic_write_buffer))\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 25 (IC-CARD) Write Pages overflow:\\n"\n+ " - m_ic_card_data(offset={}, size={})\\n"\n+ " - m_ic_write_buffer(offset={}, size={})\\n"\n+ " - size={}, page={}, count={}\\n",\n+ page * 8, sizeof(m_ic_card_data), 10, sizeof(m_ic_write_buffer),\n+ count * 8, page, count);\n+ data_in = data_in_end;\n+ break;\n+ }\n+ memcpy(m_ic_card_data + page * 8, m_ic_write_buffer + 10, count * 8);\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 25 (IC-CARD) Write Pages:{} Count:{}({:x})", page,\n+ count, size);\n+\n+ icco.command = WritePages;\n+\n+ if (!validate_data_in_out(0, icco.pktlen + 2, "SerialA (IC-CARD)"))\n+ break;\n+ ICCardSendReply(&icco, data_out.data(), &data_offset);\n+ }\n+ if (!validate_data_in_out(length, 0, "SerialA (IC-CARD)"))\n+ break;\n+ data_in += length;\n+ break;\n+ }\n+\n+ switch (ICCARDCommand(serial_command))\n+ {\n+ case ICCARDCommand::GetStatus:\n+ icco.status = m_ic_card_state;\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (IC-CARD) Get Status:{:02x}", m_ic_card_state);\n+ break;\n+ case ICCARDCommand::SetBaudrate:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (IC-CARD) Set Baudrate");\n+ break;\n+ case ICCARDCommand::FieldOn:\n+ m_ic_card_state |= 0x10;\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (IC-CARD) Field On");\n+ break;\n+ case ICCARDCommand::InsertCheck:\n+ icco.status = m_ic_card_status;\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (IC-CARD) Insert Check:{:02x}", m_ic_card_status);\n+ break;\n+ case ICCARDCommand::AntiCollision:\n+ icco.extlen = 8;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ // Card ID\n+ icco.extdata[0] = 0x00;\n+ icco.extdata[1] = 0x00;\n+ icco.extdata[2] = 0x54;\n+ icco.extdata[3] = 0x4D;\n+ icco.extdata[4] = 0x50;\n+ icco.extdata[5] = 0x00;\n+ icco.extdata[6] = 0x00;\n+ icco.extdata[7] = 0x00;\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (IC-CARD) Anti Collision");\n+ break;\n+ case ICCARDCommand::SelectCard:\n+ icco.extlen = 8;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ // Session\n+ icco.extdata[0] = 0x00;\n+ icco.extdata[1] = m_ic_card_session;\n+ icco.extdata[2] = 0x00;\n+ icco.extdata[3] = 0x00;\n+ icco.extdata[4] = 0x00;\n+ icco.extdata[5] = 0x00;\n+ icco.extdata[6] = 0x00;\n+ icco.extdata[7] = 0x00;\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (IC-CARD) Select Card:{}",\n+ m_ic_card_session);\n+ break;\n+ case ICCARDCommand::ReadPage:\n+ case ICCARDCommand::ReadUseCount:\n+ {\n+ if (!validate_data_in_out(8, 0, "SerialA (IC-CARD)"))\n+ break;\n+ const u16 page = Common::swap16(data_in + 6) & 0xFF; // 255 is max page\n+\n+ icco.extlen = 8;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ memcpy(icco.extdata, m_ic_card_data + page * 8, 8);\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 31 (IC-CARD) Read Page:{}",\n+ page);\n+ break;\n+ }\n+ case ICCARDCommand::WritePage:\n+ {\n+ if (!validate_data_in_out(10, 0, "SerialA (IC-CARD)"))\n+ break;\n+ const u16 page = Common::swap16(data_in + 8) & 0xFF; // 255 is max page\n+\n+ // Write only one page\n+ if (page == 4)\n+ {\n+ icco.status = 0x80;\n+ }\n+ else\n+ {\n+ if (!validate_data_in_out(18, 0, "SerialA (IC-CARD)"))\n+ break;\n+ memcpy(m_ic_card_data + page * 8, data_in + 10, 8);\n+ }\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (IC-CARD) Write Page:{}",\n+ page);\n+ break;\n+ }\n+ case ICCARDCommand::DecreaseUseCount:\n+ {\n+ if (!validate_data_in_out(8, 0, "SerialA (IC-CARD)"))\n+ break;\n+ const u16 page = Common::swap16(data_in + 6) & 0xFF; // 255 is max page\n+\n+ icco.extlen = 2;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ auto ic_card_data = Common::BitCastPtr<u16>(m_ic_card_data + 0x28);\n+ ic_card_data = ic_card_data - 1;\n+\n+ // Counter\n+ icco.extdata[0] = m_ic_card_data[0x28];\n+ icco.extdata[1] = m_ic_card_data[0x29];\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 31 (IC-CARD) Decrease Use Count:{}", page);\n+ break;\n+ }\n+ case ICCARDCommand::ReadPages:\n+ {\n+ if (!validate_data_in_out(10, 0, "SerialA (IC-CARD)"))\n+ break;\n+ const u16 page = Common::swap16(data_in + 6) & 0xFF; // 255 is max page\n+ const u16 count = Common::swap16(data_in + 8);\n+\n+ const u32 offs = page * 8;\n+ u32 cnt = count * 8;\n+\n+ // Limit read size to not overwrite the reply buffer\n+ const std::size_t reply_buffer_size = sizeof(icco.extdata) - 1;\n+ if (data_offset > reply_buffer_size)\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 31 (IC-CARD) Read Pages overflow:"\n+ " offset={} > buffer_size={}",\n+ data_offset, reply_buffer_size);\n+ data_in = data_in_end;\n+ break;\n+ }\n+ if (cnt > reply_buffer_size - data_offset)\n+ {\n+ cnt = 5 * 8;\n+ }\n+\n+ icco.extlen = cnt;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ if (offs + cnt > sizeof(icco.extdata))\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 31 (IC-CARD) Read Pages overflow:"\n+ " offset={} + count={} > buffer_size={}",\n+ offs, cnt, sizeof(icco.extdata));\n+ data_in = data_in_end;\n+ break;\n+ }\n+ memcpy(icco.extdata, m_ic_card_data + offs, cnt);\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 31 (IC-CARD) Read Pages:{} Count:{}", page, count);\n+ break;\n+ }\n+ case ICCARDCommand::WritePages:\n+ {\n+ if (!validate_data_in_out(10, 0, "SerialA (IC-CARD)"))\n+ break;\n+ const u16 pksize = length;\n+ const u16 size = Common::swap16(data_in + 2);\n+ const u16 page = Common::swap16(data_in + 6) & 0xFF; // 255 is max page\n+ const u16 count = Common::swap16(data_in + 8);\n+\n+ // We got a complete packet\n+ if (pksize - 5 == size)\n+ {\n+ if (page == 4) // Read Only Page, must return error\n+ {\n+ icco.status = 0x80;\n+ }\n+ else\n+ {\n+ if (page * 8 + count * 8 > sizeof(m_ic_card_data))\n+ {\n+ ERROR_LOG_FMT(\n+ SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (IC-CARD) Data overflow: Pages:{} Count:{}({:x})",\n+ page, count, size);\n+ }\n+ else\n+ {\n+ if (!validate_data_in_out(13 + count * 8, 0, "SerialA (IC-CARD)"))\n+ break;\n+ memcpy(m_ic_card_data + page * 8, data_in + 13, count * 8);\n+ }\n+ }\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (IC-CARD) Write Pages:{} Count:{}({:x})", page,\n+ count, size);\n+ }\n+ // VirtuaStriker 4 splits the writes over multiple packets\n+ else\n+ {\n+ if (!validate_data_in_out(2 + pksize, 0, "SerialA (IC-CARD)"))\n+ break;\n+ memcpy(m_ic_write_buffer, data_in + 2, pksize);\n+ m_ic_write_offset += pksize;\n+ m_ic_write_size = size;\n+ }\n+ break;\n+ }\n+ default:\n+ // Handle Deck Reader commands\n+ if (!validate_data_in_out(1, 0, "SerialA (DECK READER)"))\n+ break;\n+ serial_command = data_in[0];\n+ icco.command = serial_command;\n+ icco.flag = 0;\n+ switch (CDReaderCommand(serial_command))\n+ {\n+ case CDReaderCommand::ProgramVersion:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Program Version");\n+\n+ icco.extlen = (u32)strlen(s_cdr_program_version);\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ memcpy(icco.extdata, s_cdr_program_version, icco.extlen);\n+ break;\n+ case CDReaderCommand::BootVersion:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Boot Version");\n+\n+ icco.extlen = (u32)strlen(s_cdr_boot_version);\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ memcpy(icco.extdata, s_cdr_boot_version, icco.extlen);\n+ break;\n+ case CDReaderCommand::ShutterGet:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Shutter Get");\n+\n+ icco.extlen = 4;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ icco.extdata[0] = 0;\n+ icco.extdata[1] = 0;\n+ icco.extdata[2] = 0;\n+ icco.extdata[3] = 0;\n+ break;\n+ case CDReaderCommand::CameraCheck:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Camera Check");\n+\n+ icco.extlen = 6;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ icco.extdata[0] = 0x23;\n+ icco.extdata[1] = 0x28;\n+ icco.extdata[2] = 0x45;\n+ icco.extdata[3] = 0x29;\n+ icco.extdata[4] = 0x45;\n+ icco.extdata[5] = 0x29;\n+ break;\n+ case CDReaderCommand::ProgramChecksum:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Program Checksum");\n+\n+ icco.extlen = 4;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ icco.extdata[0] = 0x23;\n+ icco.extdata[1] = 0x28;\n+ icco.extdata[2] = 0x45;\n+ icco.extdata[3] = 0x29;\n+ break;\n+ case CDReaderCommand::BootChecksum:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Boot Checksum");\n+\n+ icco.extlen = 4;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ icco.extdata[0] = 0x23;\n+ icco.extdata[1] = 0x28;\n+ icco.extdata[2] = 0x45;\n+ icco.extdata[3] = 0x29;\n+ break;\n+ case CDReaderCommand::SelfTest:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (DECK READER) Self Test");\n+ icco.flag = 0x00;\n+ break;\n+ case CDReaderCommand::SensLock:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (DECK READER) Sens Lock");\n+ icco.flag = 0x01;\n+ break;\n+ case CDReaderCommand::SensCard:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (DECK READER) Sens Card");\n+ break;\n+ case CDReaderCommand::ShutterCard:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Shutter Card");\n+ break;\n+ case CDReaderCommand::ReadCard:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (DECK READER) Read Card");\n+\n+ icco.fixed = 0xAA;\n+ icco.flag = 0xAA;\n+ icco.extlen = sizeof(s_cdr_card_data);\n+ icco.length = 0x72;\n+ icco.status = Common::swap16(icco.extlen);\n+\n+ icco.pktlen += icco.extlen;\n+\n+ memcpy(icco.extdata, s_cdr_card_data, sizeof(s_cdr_card_data));\n+\n+ break;\n+ default:\n+ if (!validate_data_in_out(14, 0, "SerialA (DECK READER)"))\n+ break;\n+ WARN_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (IC-Card) {:02x} {:02x} {:02x} {:02x} {:02x} "\n+ "{:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x}",\n+ data_in[2], data_in[3], data_in[4], data_in[5], data_in[6],\n+ data_in[7], data_in[8], data_in[9], data_in[10], data_in[11],\n+ data_in[12], data_in[13]);\n+ break;\n+ }\n+ break;\n+ }\n+\n+ if (!validate_data_in_out(0, icco.pktlen + 2, "SerialA (IC-CARD)"))\n+ break;\n+ ICCardSendReply(&icco, data_out.data(), &data_offset);\n+\n+ if (!validate_data_in_out(2, 0, "SerialA (IC-CARD)"))\n+ break;\n+ data_in += length;\n+ break;\n+ }\n+ }\n+\n+ u32 command_offset = 0;\n+ while (command_offset < length)\n+ {\n+ // All commands are OR\'d with 0x80\n+ // Last byte is checksum which we don\'t care about\n+ if (!validate_data_in_out(command_offset + 4, 0, "SerialA"))\n+ break;\n+ const u32 serial_command = Common::swap32(data_in + command_offset) ^ 0x80000000;\n+\n+ if (AMMediaboard::GetGameType() == FZeroAX ||\n+ AMMediaboard::GetGameType() == FZeroAXMonster)\n+ {\n+ INFO_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x31 (MOTOR) Length:{:02x} Command:{:04x}({:02x})",\n+ length, (serial_command >> 8) & 0xFFFF, serial_command >> 24);\n+ }\n+ else\n+ {\n+ INFO_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x31 (SERIAL) Command:{:06x}",\n+ serial_command);\n+\n+ if (serial_command == 0x801000)\n+ {\n+ if (!validate_data_in_out(0, 4, "SerialA"))\n+ break;\n+ data_out[data_offset++] = 0x31;\n+ data_out[data_offset++] = 0x02;\n+ data_out[data_offset++] = 0xFF;\n+ data_out[data_offset++] = 0x01;\n+ }\n+ }\n+\n+ command_offset += sizeof(u32);\n+\n+ if (AMMediaboard::GetGameType() == FZeroAX ||\n+ AMMediaboard::GetGameType() == FZeroAXMonster)\n+ {\n+ if (command_offset + 5 >= std::size(m_motor_reply))\n+ {\n+ ERROR_LOG_FMT(\n+ SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x31 (MOTOR) overflow: offset={} >= motor_reply_size={}",\n+ command_offset + 5, std::size(m_motor_reply));\n+ data_in = data_in_end;\n+ break;\n+ }\n+\n+ // Status\n+ m_motor_reply[command_offset + 2] = 0;\n+ m_motor_reply[command_offset + 3] = 0;\n+\n+ // Error\n+ m_motor_reply[command_offset + 4] = 0;\n+\n+ switch (serial_command >> 24)\n+ {\n+ case 0:\n+ case 1: // Set Maximum?\n+ case 2:\n+ break;\n+\n+ // 0x00-0x40: left\n+ // 0x40-0x80: right\n+ case 4: // Move Steering Wheel\n+ // Left\n+ if (serial_command & 0x010000)\n+ {\n+ m_motor_force_y = -((s16)serial_command & 0xFF00);\n+ }\n+ else // Right\n+ {\n+ m_motor_force_y = (serial_command - 0x4000) & 0xFF00;\n+ }\n+\n+ m_motor_force_y *= 2;\n+\n+ // FFB\n+ if (m_motor_init == 2)\n+ {\n+ if (serial_interface.GetDeviceType(1) == SerialInterface::SIDEVICE_GC_STEERING)\n+ {\n+ const GCPadStatus pad_status = Pad::GetStatus(1);\n+ if (pad_status.isConnected)\n+ {\n+ const ControlState mapped_strength = (double)(m_motor_force_y >> 8) / 127.f;\n+\n+ Pad::Rumble(1, mapped_strength);\n+ INFO_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x31 (MOTOR) mapped_strength:{}",\n+ mapped_strength);\n+ }\n+ }\n+ }\n+ break;\n+ case 6: // nice\n+ case 9:\n+ default:\n+ break;\n+ // Switch back to normal controls\n+ case 7:\n+ m_motor_init = 2;\n+ break;\n+ // Reset\n+ case 0x7F:\n+ m_motor_init = 1;\n+ memset(m_motor_reply, 0, sizeof(m_motor_reply));\n+ break;\n+ }\n+\n+ // Checksum\n+ m_motor_reply[command_offset + 5] = m_motor_reply[command_offset + 2] ^\n+ m_motor_reply[command_offset + 3] ^\n+ m_motor_reply[command_offset + 4];\n+ }\n+ }\n+\n+ if (length == 0)\n+ {\n+ if (!validate_data_in_out(length, 2, "SerialA"))\n+ break;\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x00;\n+ }\n+ else\n+ {\n+ if (m_motor_init)\n+ {\n+ // Motor\n+ m_motor_reply[0] = gcam_command;\n+ m_motor_reply[1] = length; // Same out as in size\n+\n+ const u32 reply_size = m_motor_reply[1] + 2;\n+ if (!validate_data_in_out(length, reply_size, "SerialA"))\n+ break;\n+\n+ if (reply_size > sizeof(m_motor_reply))\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command SerialA, reply_size={} too big for m_motor_reply",\n+ reply_size);\n+ data_in = data_in_end;\n+ break;\n+ }\n+ memcpy(data_out.data() + data_offset, m_motor_reply, reply_size);\n+ data_offset += reply_size;\n+ }\n+ }\n+\n+ if (!validate_data_in_out(length, 0, "SerialA"))\n+ {\n+ break;\n+ }\n+ data_in += length;\n+ break;\n+ }\n+ case GCAMCommand::SerialB:\n+ {\n+ DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 32 (CARD-Interface)");\n+ if (!validate_data_in_out(1, 0, "SerialB"))\n+ break;\n+ const u32 length = *data_in++;\n+ if (!validate_data_in_out(length, 0, "SerialB"))\n+ break;\n+ if (length)\n+ {\n+ // Send Card Reply\n+ if (length == 1 && data_in[0] == 0x05)\n+ {\n+ if (m_card_read_length)\n+ {\n+ if (!validate_data_in_out(0, 1, "SerialB"))\n+ break;\n+ data_out[data_offset++] = gcam_command;\n+ u32 read_length = m_card_read_length - m_card_read;\n+\n+ if (AMMediaboard::GetGameType() == FZeroAX)\n+ {\n+ read_length = std::min<u32>(read_length, 0x2F);\n+ }\n+\n+ if (!validate_data_in_out(0, 1, "SerialB"))\n+ break;\n+ data_out[data_offset++] = read_length; // 0x2F (max size per packet)\n+\n+ if (!validate_data_in_out(0, read_length, "SerialB"))\n+ break;\n+ if (u64{m_card_read} + read_length > sizeof(m_card_read_packet))\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command SerialB, m_card_read_packet overflow:\\n"\n+ " - m_card_read_packet = {}\\n"\n+ " - m_card_read = {}\\n"\n+ " - read_length = {}",\n+ fmt::ptr(m_card_read_packet), m_card_read, read_length);\n+ data_in = data_in_end;\n+ break;\n+ }\n+ memcpy(data_out.data() + data_offset, m_card_read_packet + m_card_read,\n+ read_length);\n+\n+ data_offset += read_length;\n+ m_card_read += read_length;\n+\n+ if (m_card_read >= m_card_read_length)\n+ m_card_read_length = 0;\n+\n+ data_in += length;\n+ break;\n+ }\n+\n+ if (!validate_data_in_out(0, 5, "SerialB"))\n+ break;\n+\n+ data_out[data_offset++] = gcam_command;\n+ const u32 command_length_offset = data_offset;\n+ data_out[data_offset++] = 0x00; // len\n+\n+ data_out[data_offset++] = 0x02;\n+ const u32 checksum_start = data_offset;\n+\n+ data_out[data_offset++] = 0x00; // 0x00 len\n+\n+ data_out[data_offset++] = m_card_command; // 0x01 command\n+\n+ switch (CARDCommand(m_card_command))\n+ {\n+ case CARDCommand::Init:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x00; // 0x02\n+ data_out[data_offset++] = 0x30; // 0x03\n+ break;\n+ case CARDCommand::GetState:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x20 | m_card_bit; // 0x02\n+\n+ // bit 0: Please take your card\n+ // bit 1: Endless waiting causes UNK_E to be called\n+ data_out[data_offset++] = 0x00; // 0x03\n+ break;\n+ case CARDCommand::Read:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x02; // 0x02\n+ data_out[data_offset++] = 0x53; // 0x03\n+ break;\n+ case CARDCommand::IsPresent:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x22; // 0x02\n+ data_out[data_offset++] = 0x30; // 0x03\n+ break;\n+ case CARDCommand::Write:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x02; // 0x02\n+ data_out[data_offset++] = 0x00; // 0x03\n+ break;\n+ case CARDCommand::SetPrintParam:\n+ case CARDCommand::RegisterFont:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x00; // 0x02\n+ data_out[data_offset++] = 0x00; // 0x03\n+ break;\n+ case CARDCommand::WriteInfo:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x02; // 0x02\n+ data_out[data_offset++] = 0x00; // 0x03\n+ break;\n+ case CARDCommand::Erase:\n+ // TODO: CARDCommand::Erase is not handled.\n+ break;\n+ case CARDCommand::Eject:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ if (AMMediaboard::GetGameType() == FZeroAX)\n+ {\n+ data_out[data_offset++] = 0x01; // 0x02\n+ }\n+ else\n+ {\n+ data_out[data_offset++] = 0x31; // 0x02\n+ }\n+ data_out[data_offset++] = 0x30; // 0x03\n+ break;\n+ case CARDCommand::Clean:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x02; // 0x02\n+ data_out[data_offset++] = 0x00; // 0x03\n+ break;\n+ case CARDCommand::Load:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x02; // 0x02\n+ data_out[data_offset++] = 0x30; // 0x03\n+ break;\n+ case CARDCommand::SetShutter:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x00; // 0x02\n+ data_out[data_offset++] = 0x00; // 0x03\n+ break;\n+ }\n+\n+ if (!validate_data_in_out(0, 3, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x30; // 0x04\n+ data_out[data_offset++] = 0x00; // 0x05\n+\n+ data_out[data_offset++] = 0x03; // 0x06\n+\n+ data_out[checksum_start] = data_offset - checksum_start; // 0x00 len\n+\n+ if (!validate_data_in_out(0, 1, "SerialB"))\n+ break;\n+ data_out[data_offset] = 0; // 0x07\n+ for (u32 i = 0; i < data_out[checksum_start]; ++i)\n+ data_out[data_offset] ^= data_out[checksum_start + i];\n+\n+ if (!validate_data_in_out(0, 1, "SerialB"))\n+ break;\n+ data_offset++;\n+\n+ data_out[command_length_offset] = data_out[checksum_start] + 2;\n+ }\n+ else\n+ {\n+ if (!validate_data_in_out(length, 0, "SerialB"))\n+ break;\n+ if (u64{m_card_offset} + length > std::size(m_card_buffer))\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command SerialB, m_card_buffer overflow:\\n"\n+ " - m_card_buffer = {}\\n"\n+ " - m_card_offset = {}\\n"\n+ " - length = {}",\n+ fmt::ptr(m_card_buffer), m_card_offset, length);\n+ data_in = data_in_end;\n+ break;\n+ }\n+ for (u32 i = 0; i < length; ++i)\n+ m_card_buffer[m_card_offset + i] = data_in[i];\n+\n+ m_card_offset += length;\n+\n+ // Check if we got a complete command\n+ if (m_card_buffer[0] == 0x02)\n+ {\n+ if (m_card_buffer[1] == m_card_offset - 2)\n+ {\n+ if (m_card_buffer[m_card_offset - 2] == 0x03)', 'path': 'Source/Core/Core/HW/SI/SI_DeviceAMBaseboard.cpp', 'position': 1345, 'original_position': 1345, 'commit_id': '5bf8fe93286ebe4f08fb469a44f259a8397c89e0', 'user': {'login': 'JosJuice', 'id': 6716818, 'node_id': 'MDQ6VXNlcjY3MTY4MTg=', 'avatar_url': 'https://avatars.githubusercontent.com/u/6716818?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/JosJuice', 'html_url': 'https://github.com/JosJuice', 'followers_url': 'https://api.github.com/users/JosJuice/followers', 'following_url': 'https://api.github.com/users/JosJuice/following{/other_user}', 'gists_url': 'https://api.github.com/users/JosJuice/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/JosJuice/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/JosJuice/subscriptions', 'organizations_url': 'https://api.github.com/users/JosJuice/orgs', 'repos_url': 'https://api.github.com/users/JosJuice/repos', 'events_url': 'https://api.github.com/users/JosJuice/events{/privacy}', 'received_events_url': 'https://api.github.com/users/JosJuice/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'body': 'Also, we should probably use `&&` instead of three nested `if`s. The indentation is getting pretty big here.', 'created_at': '2026-02-01T14:20:34Z', 'updated_at': '2026-02-01T15:40:26Z', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/13844#discussion_r2751296052', 'pull_request_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13844', 'author_association': 'MEMBER', '_links': {'self': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2751296052'}, 'html': {'href': 'https://github.com/dolphin-emu/dolphin/pull/13844#discussion_r2751296052'}, 'pull_request': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13844'}}, 'original_commit_id': '5bf8fe93286ebe4f08fb469a44f259a8397c89e0', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2751296052/reactions', 'total_count': 0, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 0, 'confused': 0, 'heart': 0, 'rocket': 0, 'eyes': 0}, 'in_reply_to_id': 2751294423}, {'id': 2751374597, 'node_id': 'PRRC_kwDOALCn2M6j_qUF', 'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2751374597', 'pull_request_review_id': 3735806017, 'diff_hunk': '@@ -0,0 +1,2708 @@\n+// Copyright 2025 Dolphin Emulator Project\n+// SPDX-License-Identifier: GPL-2.0-or-later\n+\n+#include "Core/HW/SI/SI_DeviceAMBaseboard.h"\n+\n+#include <algorithm>\n+#include <numeric>\n+#include <string>\n+\n+#include <fmt/format.h>\n+\n+#include "Common/Buffer.h"\n+#include "Common/CommonTypes.h"\n+#include "Common/FileUtil.h"\n+#include "Common/IOFile.h"\n+#include "Common/Logging/Log.h"\n+#include "Common/MsgHandler.h"\n+#include "Common/Swap.h"\n+\n+#include "Core/Boot/Boot.h"\n+#include "Core/BootManager.h"\n+#include "Core/Config/MainSettings.h"\n+#include "Core/ConfigManager.h"\n+#include "Core/Core.h"\n+#include "Core/CoreTiming.h"\n+#include "Core/HW/DVD/AMMediaboard.h"\n+#include "Core/HW/DVD/DVDInterface.h"\n+#include "Core/HW/EXI/EXI.h"\n+#include "Core/HW/GCPad.h"\n+#include "Core/HW/MMIO.h"\n+#include "Core/HW/Memmap.h"\n+#include "Core/HW/ProcessorInterface.h"\n+#include "Core/HW/SI/SI.h"\n+#include "Core/HW/SI/SI_Device.h"\n+#include "Core/HW/SI/SI_DeviceGCController.h"\n+#include "Core/HW/SystemTimers.h"\n+#include "Core/Movie.h"\n+#include "Core/NetPlayProto.h"\n+#include "Core/System.h"\n+\n+#include "InputCommon/GCPadStatus.h"\n+\n+namespace SerialInterface\n+{\n+void JVSIOMessage::Start(int node)\n+{\n+ m_last_start = m_pointer;\n+ const u8 header[3] = {0xE0, (u8)node, 0};\n+ m_checksum = 0;\n+ AddData(header, 3, 1);\n+}\n+\n+void JVSIOMessage::AddData(const u8* dst, size_t len, int sync = 0)\n+{\n+ if (m_pointer + len >= sizeof(m_message))\n+ {\n+ PanicAlertFmt("JVSIOMessage overrun!");\n+ return;\n+ }\n+\n+ while (len--)\n+ {\n+ const u8 c = *dst++;\n+ if (!sync && ((c == 0xE0) || (c == 0xD0)))\n+ {\n+ if (m_pointer + 2 > sizeof(m_message))\n+ {\n+ PanicAlertFmt("JVSIOMessage overrun!");\n+ break;\n+ }\n+ m_message[m_pointer++] = 0xD0;\n+ m_message[m_pointer++] = c - 1;\n+ }\n+ else\n+ {\n+ if (m_pointer >= sizeof(m_message))\n+ {\n+ PanicAlertFmt("JVSIOMessage overrun!");\n+ break;\n+ }\n+ m_message[m_pointer++] = c;\n+ }\n+\n+ if (!sync)\n+ m_checksum += c;\n+ sync = 0;\n+ }\n+}\n+\n+void JVSIOMessage::AddData(const void* data, size_t len)\n+{\n+ AddData(static_cast<const u8*>(data), len);\n+}\n+\n+void JVSIOMessage::AddData(const char* data)\n+{\n+ AddData(data, strlen(data));\n+}\n+\n+void JVSIOMessage::AddData(u32 n)\n+{\n+ const u8 cs = n;\n+ AddData(&cs, 1);\n+}\n+\n+void JVSIOMessage::End()\n+{\n+ const u32 len = m_pointer - m_last_start;\n+ if (m_last_start + 2 < sizeof(m_message) && len >= 3)\n+ {\n+ m_message[m_last_start + 2] = len - 2; // assuming len <0xD0\n+ AddData(m_checksum + len - 2);\n+ }\n+ else\n+ {\n+ PanicAlertFmt("JVSIOMessage: Not enough space for checksum!");\n+ }\n+}\n+\n+static constexpr u8 CheckSumXOR(const u8* data, u32 length)\n+{\n+ return std::accumulate(data, data + length, u8{}, std::bit_xor());\n+}\n+\n+static constexpr char s_cdr_program_version[] = {" Version 1.22,2003/09/19,171-8213B"};\n+static constexpr char s_cdr_boot_version[] = {" Version 1.04,2003/06/17,171-8213B"};\n+static constexpr u8 s_cdr_card_data[] = {\n+ 0x00, 0x6E, 0x00, 0x00, 0x01, 0x00, 0x00, 0x06, 0x00, 0x00, 0x07, 0x00, 0x00, 0x0B,\n+ 0x00, 0x00, 0x0E, 0x00, 0x00, 0x10, 0x00, 0x00, 0x17, 0x00, 0x00, 0x19, 0x00, 0x00,\n+ 0x1A, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x20, 0x00,\n+ 0x00, 0x22, 0x00, 0x00, 0x23, 0x00, 0x00, 0x24, 0x00, 0x00, 0x27, 0x00, 0x00, 0x28,\n+ 0x00, 0x00, 0x2C, 0x00, 0x00, 0x2F, 0x00, 0x00, 0x34, 0x00, 0x00, 0x35, 0x00, 0x00,\n+ 0x37, 0x00, 0x00, 0x38, 0x00, 0x00, 0x39, 0x00, 0x00, 0x3D, 0x00};\n+\n+const constexpr u8 s_region_flags[] = "\\x00\\x00\\x30\\x00"\n+ // "\\x01\\xfe\\x00\\x00" // JAPAN\n+ "\\x02\\xfd\\x00\\x00" // USA\n+ //"\\x03\\xfc\\x00\\x00" // export\n+ "\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff";\n+// AM-Baseboard device on SI\n+CSIDevice_AMBaseboard::CSIDevice_AMBaseboard(Core::System& system, SIDevices device,\n+ int device_number)\n+ : ISIDevice(system, device, device_number)\n+{\n+ // Card ID\n+ m_ic_card_data[0x20] = 0x95;\n+ m_ic_card_data[0x21] = 0x71;\n+\n+ if (AMMediaboard::GetGameType() == KeyOfAvalon)\n+ {\n+ m_ic_card_data[0x22] = 0x26;\n+ m_ic_card_data[0x23] = 0x40;\n+ }\n+ else if (AMMediaboard::GetGameType() == VirtuaStriker4)\n+ {\n+ m_ic_card_data[0x22] = 0x44;\n+ m_ic_card_data[0x23] = 0x00;\n+ }\n+\n+ // Use count\n+ m_ic_card_data[0x28] = 0xFF;\n+ m_ic_card_data[0x29] = 0xFF;\n+}\n+\n+constexpr u32 SI_XFER_LENGTH_MASK = 0x7f;\n+\n+// Translate [0,1,2,...,126,127] to [128,1,2,...,126,127]\n+constexpr s32 ConvertSILengthField(u32 field)\n+{\n+ return ((field - 1) & SI_XFER_LENGTH_MASK) + 1;\n+}\n+\n+void CSIDevice_AMBaseboard::ICCardSendReply(ICCommand* iccommand, u8* buffer, u32* length)\n+{\n+ iccommand->status = Common::swap16(iccommand->status);\n+\n+ const auto iccommand_data = reinterpret_cast<const u8*>(iccommand);\n+ const u8 crc = CheckSumXOR(iccommand_data + 2, iccommand->pktlen - 1);\n+\n+ for (u32 i = 0; i < iccommand->pktlen + 1; ++i)\n+ {\n+ buffer[(*length)++] = iccommand_data[i];\n+ }\n+\n+ buffer[(*length)++] = crc;\n+}\n+\n+void CSIDevice_AMBaseboard::SwapBuffers(u8* buffer, u32* buffer_length)\n+{\n+ memcpy(m_last[1], buffer, 0x80); // Save current buffer\n+ memcpy(buffer, m_last[0], 0x80); // Load previous buffer\n+ memcpy(m_last[0], m_last[1], 0x80); // Update history\n+\n+ m_lastptr[1] = *buffer_length; // Swap lengths\n+ *buffer_length = m_lastptr[0];\n+ m_lastptr[0] = m_lastptr[1];\n+}\n+\n+int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length)\n+{\n+ const auto& serial_interface = m_system.GetSerialInterface();\n+ u32 buffer_length = ConvertSILengthField(serial_interface.GetInLength());\n+\n+ // Debug logging\n+ ISIDevice::RunBuffer(buffer, buffer_length);\n+\n+ u32 buffer_position = 0;\n+ while (buffer_position < buffer_length)\n+ {\n+ BaseBoardCommand command = static_cast<BaseBoardCommand>(buffer[buffer_position]);\n+ buffer_position++;\n+\n+ switch (command)\n+ {\n+ case BaseBoardCommand::GCAM_Reset: // Returns ID and dip switches\n+ {\n+ const u32 id = Common::swap32(SI_AM_BASEBOARD | 0x100);\n+ std::memcpy(buffer, &id, sizeof(id));\n+ return sizeof(id);\n+ }\n+ break;\n+ case BaseBoardCommand::GCAM_Command:\n+ {\n+ u32 checksum = 0;\n+ for (u32 i = 0; i < buffer_length; ++i)\n+ checksum += buffer[i];\n+\n+ std::array<u8, 0x80> data_out{};\n+ u32 data_offset = 0;\n+\n+ static u32 dip_switch_1 = 0xFE;\n+ static u32 dip_switch_0 = 0xFF;\n+\n+ data_out[data_offset++] = 1;\n+ data_out[data_offset++] = 1;\n+\n+ u8* data_in = buffer + 2;\n+ if (buffer_position >= buffer_length)\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: buffer overflow (position={}, length={})",\n+ buffer_position, buffer_length);\n+ buffer_position = buffer_length;\n+ break;\n+ }\n+\n+ const u32 requested_size = buffer[buffer_position] + 2;\n+ if (requested_size > buffer_length)\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: requested size ({}) bigger than buffer\'s ({})",\n+ requested_size, buffer_length);\n+ buffer_position = buffer_length;\n+ break;\n+ }\n+ u8* const data_in_end = buffer + requested_size;\n+\n+ // Helper to check that iterating over data n times is safe,\n+ // i.e. *data++ at most lead to data.end()\n+ auto validate_data_in_out = [&](u32 n_in, u32 n_out, std::string_view command) -> bool {\n+ if (data_in + n_in > data_in_end)\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: data_in overflow in {}", command);\n+ else if (u64{data_offset} + n_out > data_out.size())\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: data_out overflow in {}", command);\n+ else\n+ return true;\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "Overflow details:\\n"\n+ " - data_in(begin={}, current={}, end={}, n_in={})\\n"\n+ " - data_out(offset={}, size={}, n_out={})\\n"\n+ " - buffer(position={}, length={})",\n+ fmt::ptr(buffer + 2), fmt::ptr(data_in), fmt::ptr(data_in_end), n_in,\n+ data_offset, data_out.size(), n_out, buffer_position, buffer_length);\n+ data_in = data_in_end;\n+ return false;\n+ };\n+\n+ while (data_in < data_in_end)\n+ {\n+ const u32 gcam_command = *data_in++;\n+ switch (GCAMCommand(gcam_command))\n+ {\n+ case GCAMCommand::StatusSwitches:\n+ {\n+ if (!validate_data_in_out(1, 4, "StatusSwitches"))\n+ break;\n+\n+ const u8 status = *data_in++;\n+ DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x10, {:02x} (READ STATUS&SWITCHES)",\n+ status);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x2;\n+\n+ // We read Test/Service from the JVS-I/O SwitchesInput instead\n+ //\n+ // const GCPadStatus pad_status = Pad::GetStatus(ISIDevice::m_device_number);\n+ // baseboard test/service switches\n+ // if (pad_status.button & PAD_BUTTON_Y)\t// Test\n+ // dip_switch_0 &= ~0x80;\n+ // if (pad_status.button & PAD_BUTTON_X)\t// Service\n+ // dip_switch_0 &= ~0x40;\n+\n+ // Horizontal Scanning Frequency switch\n+ // Required for F-Zero AX booting via Sega Boot\n+ if (AMMediaboard::GetGameType() == FZeroAX ||\n+ AMMediaboard::GetGameType() == FZeroAXMonster)\n+ {\n+ dip_switch_0 &= ~0x20;\n+ }\n+\n+ // Disable camera in MKGP1/2\n+ if (AMMediaboard::GetGameType() == MarioKartGP ||\n+ AMMediaboard::GetGameType() == MarioKartGP2)\n+ {\n+ dip_switch_0 &= ~0x10;\n+ }\n+\n+ data_out[data_offset++] = dip_switch_0;\n+ data_out[data_offset++] = dip_switch_1;\n+ break;\n+ }\n+ case GCAMCommand::SerialNumber:\n+ {\n+ if (!validate_data_in_out(1, 18, "SerialNumber"))\n+ break;\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x11, {:02x} (READ SERIAL NR)",\n+ *data_in);\n+ data_in++;\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 16;\n+ memcpy(data_out.data() + data_offset, "AADE-01B98394904", 16);\n+\n+ data_offset += 16;\n+ break;\n+ }\n+ case GCAMCommand::Unknown_12:\n+ if (!validate_data_in_out(2, 2, "Unknown_12"))\n+ break;\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x12, {:02x} {:02x}", data_in[0],\n+ data_in[1]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x00;\n+\n+ data_in += 2;\n+ break;\n+ case GCAMCommand::Unknown_14:\n+ if (!validate_data_in_out(2, 2, "Unknown_14"))\n+ break;\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x14, {:02x} {:02x}", data_in[0],\n+ data_in[1]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x00;\n+\n+ data_in += 2;\n+ break;\n+ case GCAMCommand::FirmVersion:\n+ if (!validate_data_in_out(1, 4, "FirmVersion"))\n+ break;\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x15, {:02x} (READ FIRM VERSION)",\n+ *data_in);\n+ data_in++;\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x02;\n+ // 00.26\n+ data_out[data_offset++] = 0x00;\n+ data_out[data_offset++] = 0x26;\n+ break;\n+ case GCAMCommand::FPGAVersion:\n+ if (!validate_data_in_out(1, 4, "FPGAVersion"))\n+ break;\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x16, {:02x} (READ FPGA VERSION)",\n+ *data_in);\n+ data_in++;\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x02;\n+ // 07.06\n+ data_out[data_offset++] = 0x07;\n+ data_out[data_offset++] = 0x06;\n+ break;\n+ case GCAMCommand::RegionSettings:\n+ {\n+ if (!validate_data_in_out(5, 0x16, "RegionSettings"))\n+ break;\n+\n+ // Used by SegaBoot for region checks (dev mode skips this check)\n+ // In some games this also controls the displayed language\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x1F, {:02x} {:02x} {:02x} {:02x} {:02x} (REGION)",\n+ data_in[0], data_in[1], data_in[2], data_in[3], data_in[4]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x14;\n+\n+ for (int i = 0; i < 0x14; ++i)\n+ data_out[data_offset++] = s_region_flags[i];\n+\n+ data_in += 5;\n+ }\n+ break;\n+ // No reply\n+ // Note: Always sends three bytes even though size is set to two\n+ case GCAMCommand::Unknown_21:\n+ {\n+ if (!validate_data_in_out(4, 0, "Unknown_21"))\n+ break;\n+\n+ DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x21, {:02x}, {:02x}, {:02x}, {:02x}",\n+ data_in[0], data_in[1], data_in[2], data_in[3]);\n+ data_in += 4;\n+ }\n+ break;\n+ // No reply\n+ // Note: Always sends six bytes\n+ case GCAMCommand::Unknown_22:\n+ {\n+ if (!validate_data_in_out(7, 0, "Unknown_22"))\n+ break;\n+\n+ DEBUG_LOG_FMT(\n+ SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x22, {:02x}, {:02x}, {:02x}, {:02x}, {:02x}, {:02x}, {:02x}",\n+ data_in[0], data_in[1], data_in[2], data_in[3], data_in[4], data_in[5], data_in[6]);\n+\n+ const u32 in_size = data_in[0] + 1;\n+ if (!validate_data_in_out(in_size, 0, "Unknown_22"))\n+ break;\n+ data_in += in_size;\n+ }\n+ break;\n+ case GCAMCommand::Unknown_23:\n+ if (!validate_data_in_out(2, 2, "Unknown_23"))\n+ break;\n+\n+ DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x23, {:02x} {:02x}", data_in[0],\n+ data_in[1]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x00;\n+\n+ data_in += 2;\n+ break;\n+ case GCAMCommand::Unknown_24:\n+ if (!validate_data_in_out(2, 2, "Unknown_24"))\n+ break;\n+\n+ DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x24, {:02x} {:02x}", data_in[0],\n+ data_in[1]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x00;\n+\n+ data_in += 2;\n+ break;\n+ case GCAMCommand::SerialA:\n+ {\n+ if (!validate_data_in_out(1, 0, "SerialA"))\n+ break;\n+\n+ const u32 length = *data_in++;\n+ if (length)\n+ {\n+ if (!validate_data_in_out(length, 0, "SerialA"))\n+ break;\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x31, length=0x{:02x}, hexdump:\\n{}",\n+ length, HexDump(data_in, length));\n+\n+ // Serial - Wheel\n+ if (AMMediaboard::GetGameType() == MarioKartGP ||\n+ AMMediaboard::GetGameType() == MarioKartGP2)\n+ {\n+ if (!validate_data_in_out(10, 2, "SerialA (Wheel)"))\n+ break;\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x31, (WHEEL) {:02x}{:02x} {:02x}{:02x} {:02x} {:02x} "\n+ "{:02x} {:02x} {:02x} {:02x}",\n+ data_in[0], data_in[1], data_in[2], data_in[3], data_in[4], data_in[5],\n+ data_in[6], data_in[7], data_in[8], data_in[9]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x03;\n+\n+ switch (m_wheel_init)\n+ {\n+ case 0:\n+ if (!validate_data_in_out(0, 3, "SerialA (Wheel)"))\n+ break;\n+ data_out[data_offset++] = \'E\'; // Error\n+ data_out[data_offset++] = \'0\';\n+ data_out[data_offset++] = \'0\';\n+ m_wheel_init++;\n+ break;\n+ case 1:\n+ if (!validate_data_in_out(0, 3, "SerialA (Wheel)"))\n+ break;\n+ data_out[data_offset++] = \'C\'; // Power Off\n+ data_out[data_offset++] = \'0\';\n+ data_out[data_offset++] = \'6\';\n+ // Only turn on when a wheel is connected\n+ if (serial_interface.GetDeviceType(1) == SerialInterface::SIDEVICE_GC_STEERING)\n+ {\n+ m_wheel_init++;\n+ }\n+ break;\n+ case 2:\n+ if (!validate_data_in_out(0, 3, "SerialA (Wheel)"))\n+ break;\n+ data_out[data_offset++] = \'C\'; // Power On\n+ data_out[data_offset++] = \'0\';\n+ data_out[data_offset++] = \'1\';\n+ break;\n+ default:\n+ break;\n+ }\n+\n+ // u16 CenteringForce= ptr(6);\n+ // u16 FrictionForce = ptr(8);\n+ // u16 Roll = ptr(10);\n+ if (!validate_data_in_out(length, 0, "SerialA (Wheel)"))\n+ break;\n+ data_in += length;\n+ break;\n+ }\n+\n+ // Serial - Unknown\n+ if (AMMediaboard::GetGameType() == GekitouProYakyuu)\n+ {\n+ if (!validate_data_in_out(sizeof(u32), 0, "SerialA (Unknown)"))\n+ break;\n+ const u32 serial_command = Common::BitCastPtr<u32>(data_in);\n+\n+ if (serial_command == 0x00001000)\n+ {\n+ if (!validate_data_in_out(0, 5, "SerialA (Unknown)"))\n+ break;\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x03;\n+ data_out[data_offset++] = 1;\n+ data_out[data_offset++] = 2;\n+ data_out[data_offset++] = 3;\n+ }\n+\n+ if (!validate_data_in_out(length, 0, "SerialA (Unknown)"))\n+ break;\n+ data_in += length;\n+ break;\n+ }\n+\n+ // Serial IC-CARD / Serial Deck Reader\n+ if (AMMediaboard::GetGameType() == VirtuaStriker4 ||\n+ AMMediaboard::GetGameType() == VirtuaStriker4_2006 ||\n+ AMMediaboard::GetGameType() == KeyOfAvalon)\n+ {\n+ if (!validate_data_in_out(2, 0, "SerialA (IC-CARD)"))\n+ break;\n+ u32 serial_command = data_in[1];\n+\n+ ICCommand icco;\n+\n+ // Set default reply\n+ icco.pktcmd = gcam_command;\n+ icco.pktlen = 7;\n+ icco.fixed = 0x10;\n+ icco.command = serial_command;\n+ icco.flag = 0;\n+ icco.length = 2;\n+ icco.status = 0;\n+ icco.extlen = 0;\n+\n+ // Check for rest of data from the write pages command\n+ if (m_ic_write_size && m_ic_write_offset)\n+ {\n+ const u32 size = data_in[1];\n+\n+ if (!validate_data_in_out(size + 2, 0, "SerialA (IC-CARD)"))\n+ break;\n+ DEBUG_LOG_FMT(SERIALINTERFACE_CARD, "Command: {}", HexDump(data_in, size + 2));\n+\n+ INFO_LOG_FMT(\n+ SERIALINTERFACE_CARD,\n+ "GC-AM: Command 25 (IC-CARD) Write Pages: Off:{:x} Size:{:x} PSize:{:x}",\n+ m_ic_write_offset, m_ic_write_size, size);\n+\n+ if (u64{m_ic_write_offset} + size > sizeof(m_ic_write_buffer))\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 25 (IC-CARD) m_ic_write_buffer overflow:\\n"\n+ " - m_ic_write_buffer(offset={}, size={})\\n"\n+ " - size={}\\n",\n+ m_ic_write_offset, sizeof(m_ic_write_buffer), size);\n+ data_in = data_in_end;\n+ break;\n+ }\n+ memcpy(m_ic_write_buffer + m_ic_write_offset, data_in + 2, size);\n+\n+ m_ic_write_offset += size;\n+\n+ if (m_ic_write_offset > m_ic_write_size)\n+ {\n+ m_ic_write_offset = 0;\n+\n+ const u16 page = m_ic_write_buffer[5];\n+ const u16 count = m_ic_write_buffer[7];\n+\n+ if ((page * 8 + count * 8) > sizeof(m_ic_card_data) ||\n+ (10 + count * 8) > sizeof(m_ic_write_buffer))\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 25 (IC-CARD) Write Pages overflow:\\n"\n+ " - m_ic_card_data(offset={}, size={})\\n"\n+ " - m_ic_write_buffer(offset={}, size={})\\n"\n+ " - size={}, page={}, count={}\\n",\n+ page * 8, sizeof(m_ic_card_data), 10, sizeof(m_ic_write_buffer),\n+ count * 8, page, count);\n+ data_in = data_in_end;\n+ break;\n+ }\n+ memcpy(m_ic_card_data + page * 8, m_ic_write_buffer + 10, count * 8);\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 25 (IC-CARD) Write Pages:{} Count:{}({:x})", page,\n+ count, size);\n+\n+ icco.command = WritePages;\n+\n+ if (!validate_data_in_out(0, icco.pktlen + 2, "SerialA (IC-CARD)"))\n+ break;\n+ ICCardSendReply(&icco, data_out.data(), &data_offset);\n+ }\n+ if (!validate_data_in_out(length, 0, "SerialA (IC-CARD)"))\n+ break;\n+ data_in += length;\n+ break;\n+ }\n+\n+ switch (ICCARDCommand(serial_command))\n+ {\n+ case ICCARDCommand::GetStatus:\n+ icco.status = m_ic_card_state;\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (IC-CARD) Get Status:{:02x}", m_ic_card_state);\n+ break;\n+ case ICCARDCommand::SetBaudrate:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (IC-CARD) Set Baudrate");\n+ break;\n+ case ICCARDCommand::FieldOn:\n+ m_ic_card_state |= 0x10;\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (IC-CARD) Field On");\n+ break;\n+ case ICCARDCommand::InsertCheck:\n+ icco.status = m_ic_card_status;\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (IC-CARD) Insert Check:{:02x}", m_ic_card_status);\n+ break;\n+ case ICCARDCommand::AntiCollision:\n+ icco.extlen = 8;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ // Card ID\n+ icco.extdata[0] = 0x00;\n+ icco.extdata[1] = 0x00;\n+ icco.extdata[2] = 0x54;\n+ icco.extdata[3] = 0x4D;\n+ icco.extdata[4] = 0x50;\n+ icco.extdata[5] = 0x00;\n+ icco.extdata[6] = 0x00;\n+ icco.extdata[7] = 0x00;\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (IC-CARD) Anti Collision");\n+ break;\n+ case ICCARDCommand::SelectCard:\n+ icco.extlen = 8;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ // Session\n+ icco.extdata[0] = 0x00;\n+ icco.extdata[1] = m_ic_card_session;\n+ icco.extdata[2] = 0x00;\n+ icco.extdata[3] = 0x00;\n+ icco.extdata[4] = 0x00;\n+ icco.extdata[5] = 0x00;\n+ icco.extdata[6] = 0x00;\n+ icco.extdata[7] = 0x00;\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (IC-CARD) Select Card:{}",\n+ m_ic_card_session);\n+ break;\n+ case ICCARDCommand::ReadPage:\n+ case ICCARDCommand::ReadUseCount:\n+ {\n+ if (!validate_data_in_out(8, 0, "SerialA (IC-CARD)"))\n+ break;\n+ const u16 page = Common::swap16(data_in + 6) & 0xFF; // 255 is max page\n+\n+ icco.extlen = 8;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ memcpy(icco.extdata, m_ic_card_data + page * 8, 8);\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 31 (IC-CARD) Read Page:{}",\n+ page);\n+ break;\n+ }\n+ case ICCARDCommand::WritePage:\n+ {\n+ if (!validate_data_in_out(10, 0, "SerialA (IC-CARD)"))\n+ break;\n+ const u16 page = Common::swap16(data_in + 8) & 0xFF; // 255 is max page\n+\n+ // Write only one page\n+ if (page == 4)\n+ {\n+ icco.status = 0x80;\n+ }\n+ else\n+ {\n+ if (!validate_data_in_out(18, 0, "SerialA (IC-CARD)"))\n+ break;\n+ memcpy(m_ic_card_data + page * 8, data_in + 10, 8);\n+ }\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (IC-CARD) Write Page:{}",\n+ page);\n+ break;\n+ }\n+ case ICCARDCommand::DecreaseUseCount:\n+ {\n+ if (!validate_data_in_out(8, 0, "SerialA (IC-CARD)"))\n+ break;\n+ const u16 page = Common::swap16(data_in + 6) & 0xFF; // 255 is max page\n+\n+ icco.extlen = 2;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ auto ic_card_data = Common::BitCastPtr<u16>(m_ic_card_data + 0x28);\n+ ic_card_data = ic_card_data - 1;\n+\n+ // Counter\n+ icco.extdata[0] = m_ic_card_data[0x28];\n+ icco.extdata[1] = m_ic_card_data[0x29];\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 31 (IC-CARD) Decrease Use Count:{}", page);\n+ break;\n+ }\n+ case ICCARDCommand::ReadPages:\n+ {\n+ if (!validate_data_in_out(10, 0, "SerialA (IC-CARD)"))\n+ break;\n+ const u16 page = Common::swap16(data_in + 6) & 0xFF; // 255 is max page\n+ const u16 count = Common::swap16(data_in + 8);\n+\n+ const u32 offs = page * 8;\n+ u32 cnt = count * 8;\n+\n+ // Limit read size to not overwrite the reply buffer\n+ const std::size_t reply_buffer_size = sizeof(icco.extdata) - 1;\n+ if (data_offset > reply_buffer_size)\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 31 (IC-CARD) Read Pages overflow:"\n+ " offset={} > buffer_size={}",\n+ data_offset, reply_buffer_size);\n+ data_in = data_in_end;\n+ break;\n+ }\n+ if (cnt > reply_buffer_size - data_offset)\n+ {\n+ cnt = 5 * 8;\n+ }\n+\n+ icco.extlen = cnt;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ if (offs + cnt > sizeof(icco.extdata))\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 31 (IC-CARD) Read Pages overflow:"\n+ " offset={} + count={} > buffer_size={}",\n+ offs, cnt, sizeof(icco.extdata));\n+ data_in = data_in_end;\n+ break;\n+ }\n+ memcpy(icco.extdata, m_ic_card_data + offs, cnt);\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 31 (IC-CARD) Read Pages:{} Count:{}", page, count);\n+ break;\n+ }\n+ case ICCARDCommand::WritePages:\n+ {\n+ if (!validate_data_in_out(10, 0, "SerialA (IC-CARD)"))\n+ break;\n+ const u16 pksize = length;\n+ const u16 size = Common::swap16(data_in + 2);\n+ const u16 page = Common::swap16(data_in + 6) & 0xFF; // 255 is max page\n+ const u16 count = Common::swap16(data_in + 8);\n+\n+ // We got a complete packet\n+ if (pksize - 5 == size)\n+ {\n+ if (page == 4) // Read Only Page, must return error\n+ {\n+ icco.status = 0x80;\n+ }\n+ else\n+ {\n+ if (page * 8 + count * 8 > sizeof(m_ic_card_data))\n+ {\n+ ERROR_LOG_FMT(\n+ SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (IC-CARD) Data overflow: Pages:{} Count:{}({:x})",\n+ page, count, size);\n+ }\n+ else\n+ {\n+ if (!validate_data_in_out(13 + count * 8, 0, "SerialA (IC-CARD)"))\n+ break;\n+ memcpy(m_ic_card_data + page * 8, data_in + 13, count * 8);\n+ }\n+ }\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (IC-CARD) Write Pages:{} Count:{}({:x})", page,\n+ count, size);\n+ }\n+ // VirtuaStriker 4 splits the writes over multiple packets\n+ else\n+ {\n+ if (!validate_data_in_out(2 + pksize, 0, "SerialA (IC-CARD)"))\n+ break;\n+ memcpy(m_ic_write_buffer, data_in + 2, pksize);\n+ m_ic_write_offset += pksize;\n+ m_ic_write_size = size;\n+ }\n+ break;\n+ }\n+ default:\n+ // Handle Deck Reader commands\n+ if (!validate_data_in_out(1, 0, "SerialA (DECK READER)"))\n+ break;\n+ serial_command = data_in[0];\n+ icco.command = serial_command;\n+ icco.flag = 0;\n+ switch (CDReaderCommand(serial_command))\n+ {\n+ case CDReaderCommand::ProgramVersion:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Program Version");\n+\n+ icco.extlen = (u32)strlen(s_cdr_program_version);\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ memcpy(icco.extdata, s_cdr_program_version, icco.extlen);\n+ break;\n+ case CDReaderCommand::BootVersion:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Boot Version");\n+\n+ icco.extlen = (u32)strlen(s_cdr_boot_version);\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ memcpy(icco.extdata, s_cdr_boot_version, icco.extlen);\n+ break;\n+ case CDReaderCommand::ShutterGet:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Shutter Get");\n+\n+ icco.extlen = 4;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ icco.extdata[0] = 0;\n+ icco.extdata[1] = 0;\n+ icco.extdata[2] = 0;\n+ icco.extdata[3] = 0;\n+ break;\n+ case CDReaderCommand::CameraCheck:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Camera Check");\n+\n+ icco.extlen = 6;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ icco.extdata[0] = 0x23;\n+ icco.extdata[1] = 0x28;\n+ icco.extdata[2] = 0x45;\n+ icco.extdata[3] = 0x29;\n+ icco.extdata[4] = 0x45;\n+ icco.extdata[5] = 0x29;\n+ break;\n+ case CDReaderCommand::ProgramChecksum:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Program Checksum");\n+\n+ icco.extlen = 4;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ icco.extdata[0] = 0x23;\n+ icco.extdata[1] = 0x28;\n+ icco.extdata[2] = 0x45;\n+ icco.extdata[3] = 0x29;\n+ break;\n+ case CDReaderCommand::BootChecksum:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Boot Checksum");\n+\n+ icco.extlen = 4;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ icco.extdata[0] = 0x23;\n+ icco.extdata[1] = 0x28;\n+ icco.extdata[2] = 0x45;\n+ icco.extdata[3] = 0x29;\n+ break;\n+ case CDReaderCommand::SelfTest:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (DECK READER) Self Test");\n+ icco.flag = 0x00;\n+ break;\n+ case CDReaderCommand::SensLock:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (DECK READER) Sens Lock");\n+ icco.flag = 0x01;\n+ break;\n+ case CDReaderCommand::SensCard:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (DECK READER) Sens Card");\n+ break;\n+ case CDReaderCommand::ShutterCard:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Shutter Card");\n+ break;\n+ case CDReaderCommand::ReadCard:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (DECK READER) Read Card");\n+\n+ icco.fixed = 0xAA;\n+ icco.flag = 0xAA;\n+ icco.extlen = sizeof(s_cdr_card_data);\n+ icco.length = 0x72;\n+ icco.status = Common::swap16(icco.extlen);\n+\n+ icco.pktlen += icco.extlen;\n+\n+ memcpy(icco.extdata, s_cdr_card_data, sizeof(s_cdr_card_data));\n+\n+ break;\n+ default:\n+ if (!validate_data_in_out(14, 0, "SerialA (DECK READER)"))\n+ break;\n+ WARN_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (IC-Card) {:02x} {:02x} {:02x} {:02x} {:02x} "\n+ "{:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x}",\n+ data_in[2], data_in[3], data_in[4], data_in[5], data_in[6],\n+ data_in[7], data_in[8], data_in[9], data_in[10], data_in[11],\n+ data_in[12], data_in[13]);\n+ break;\n+ }\n+ break;\n+ }\n+\n+ if (!validate_data_in_out(0, icco.pktlen + 2, "SerialA (IC-CARD)"))\n+ break;\n+ ICCardSendReply(&icco, data_out.data(), &data_offset);\n+\n+ if (!validate_data_in_out(2, 0, "SerialA (IC-CARD)"))\n+ break;\n+ data_in += length;\n+ break;\n+ }\n+ }\n+\n+ u32 command_offset = 0;\n+ while (command_offset < length)\n+ {\n+ // All commands are OR\'d with 0x80\n+ // Last byte is checksum which we don\'t care about\n+ if (!validate_data_in_out(command_offset + 4, 0, "SerialA"))\n+ break;\n+ const u32 serial_command = Common::swap32(data_in + command_offset) ^ 0x80000000;\n+\n+ if (AMMediaboard::GetGameType() == FZeroAX ||\n+ AMMediaboard::GetGameType() == FZeroAXMonster)\n+ {\n+ INFO_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x31 (MOTOR) Length:{:02x} Command:{:04x}({:02x})",\n+ length, (serial_command >> 8) & 0xFFFF, serial_command >> 24);\n+ }\n+ else\n+ {\n+ INFO_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x31 (SERIAL) Command:{:06x}",\n+ serial_command);\n+\n+ if (serial_command == 0x801000)\n+ {\n+ if (!validate_data_in_out(0, 4, "SerialA"))\n+ break;\n+ data_out[data_offset++] = 0x31;\n+ data_out[data_offset++] = 0x02;\n+ data_out[data_offset++] = 0xFF;\n+ data_out[data_offset++] = 0x01;\n+ }\n+ }\n+\n+ command_offset += sizeof(u32);\n+\n+ if (AMMediaboard::GetGameType() == FZeroAX ||\n+ AMMediaboard::GetGameType() == FZeroAXMonster)\n+ {\n+ if (command_offset + 5 >= std::size(m_motor_reply))\n+ {\n+ ERROR_LOG_FMT(\n+ SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x31 (MOTOR) overflow: offset={} >= motor_reply_size={}",\n+ command_offset + 5, std::size(m_motor_reply));\n+ data_in = data_in_end;\n+ break;\n+ }\n+\n+ // Status\n+ m_motor_reply[command_offset + 2] = 0;\n+ m_motor_reply[command_offset + 3] = 0;\n+\n+ // Error\n+ m_motor_reply[command_offset + 4] = 0;\n+\n+ switch (serial_command >> 24)\n+ {\n+ case 0:\n+ case 1: // Set Maximum?\n+ case 2:\n+ break;\n+\n+ // 0x00-0x40: left\n+ // 0x40-0x80: right\n+ case 4: // Move Steering Wheel\n+ // Left\n+ if (serial_command & 0x010000)\n+ {\n+ m_motor_force_y = -((s16)serial_command & 0xFF00);\n+ }\n+ else // Right\n+ {\n+ m_motor_force_y = (serial_command - 0x4000) & 0xFF00;\n+ }\n+\n+ m_motor_force_y *= 2;\n+\n+ // FFB\n+ if (m_motor_init == 2)\n+ {\n+ if (serial_interface.GetDeviceType(1) == SerialInterface::SIDEVICE_GC_STEERING)\n+ {\n+ const GCPadStatus pad_status = Pad::GetStatus(1);\n+ if (pad_status.isConnected)\n+ {\n+ const ControlState mapped_strength = (double)(m_motor_force_y >> 8) / 127.f;\n+\n+ Pad::Rumble(1, mapped_strength);\n+ INFO_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x31 (MOTOR) mapped_strength:{}",\n+ mapped_strength);\n+ }\n+ }\n+ }\n+ break;\n+ case 6: // nice\n+ case 9:\n+ default:\n+ break;\n+ // Switch back to normal controls\n+ case 7:\n+ m_motor_init = 2;\n+ break;\n+ // Reset\n+ case 0x7F:\n+ m_motor_init = 1;\n+ memset(m_motor_reply, 0, sizeof(m_motor_reply));\n+ break;\n+ }\n+\n+ // Checksum\n+ m_motor_reply[command_offset + 5] = m_motor_reply[command_offset + 2] ^\n+ m_motor_reply[command_offset + 3] ^\n+ m_motor_reply[command_offset + 4];\n+ }\n+ }\n+\n+ if (length == 0)\n+ {\n+ if (!validate_data_in_out(length, 2, "SerialA"))\n+ break;\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x00;\n+ }\n+ else\n+ {\n+ if (m_motor_init)\n+ {\n+ // Motor\n+ m_motor_reply[0] = gcam_command;\n+ m_motor_reply[1] = length; // Same out as in size\n+\n+ const u32 reply_size = m_motor_reply[1] + 2;\n+ if (!validate_data_in_out(length, reply_size, "SerialA"))\n+ break;\n+\n+ if (reply_size > sizeof(m_motor_reply))\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command SerialA, reply_size={} too big for m_motor_reply",\n+ reply_size);\n+ data_in = data_in_end;\n+ break;\n+ }\n+ memcpy(data_out.data() + data_offset, m_motor_reply, reply_size);\n+ data_offset += reply_size;\n+ }\n+ }\n+\n+ if (!validate_data_in_out(length, 0, "SerialA"))\n+ {\n+ break;\n+ }\n+ data_in += length;\n+ break;\n+ }\n+ case GCAMCommand::SerialB:\n+ {\n+ DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 32 (CARD-Interface)");\n+ if (!validate_data_in_out(1, 0, "SerialB"))\n+ break;\n+ const u32 length = *data_in++;\n+ if (!validate_data_in_out(length, 0, "SerialB"))\n+ break;\n+ if (length)\n+ {\n+ // Send Card Reply\n+ if (length == 1 && data_in[0] == 0x05)\n+ {\n+ if (m_card_read_length)\n+ {\n+ if (!validate_data_in_out(0, 1, "SerialB"))\n+ break;\n+ data_out[data_offset++] = gcam_command;\n+ u32 read_length = m_card_read_length - m_card_read;\n+\n+ if (AMMediaboard::GetGameType() == FZeroAX)\n+ {\n+ read_length = std::min<u32>(read_length, 0x2F);\n+ }\n+\n+ if (!validate_data_in_out(0, 1, "SerialB"))\n+ break;\n+ data_out[data_offset++] = read_length; // 0x2F (max size per packet)\n+\n+ if (!validate_data_in_out(0, read_length, "SerialB"))\n+ break;\n+ if (u64{m_card_read} + read_length > sizeof(m_card_read_packet))\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command SerialB, m_card_read_packet overflow:\\n"\n+ " - m_card_read_packet = {}\\n"\n+ " - m_card_read = {}\\n"\n+ " - read_length = {}",\n+ fmt::ptr(m_card_read_packet), m_card_read, read_length);\n+ data_in = data_in_end;\n+ break;\n+ }\n+ memcpy(data_out.data() + data_offset, m_card_read_packet + m_card_read,\n+ read_length);\n+\n+ data_offset += read_length;\n+ m_card_read += read_length;\n+\n+ if (m_card_read >= m_card_read_length)\n+ m_card_read_length = 0;\n+\n+ data_in += length;\n+ break;\n+ }\n+\n+ if (!validate_data_in_out(0, 5, "SerialB"))\n+ break;\n+\n+ data_out[data_offset++] = gcam_command;\n+ const u32 command_length_offset = data_offset;\n+ data_out[data_offset++] = 0x00; // len\n+\n+ data_out[data_offset++] = 0x02;\n+ const u32 checksum_start = data_offset;\n+\n+ data_out[data_offset++] = 0x00; // 0x00 len\n+\n+ data_out[data_offset++] = m_card_command; // 0x01 command\n+\n+ switch (CARDCommand(m_card_command))\n+ {\n+ case CARDCommand::Init:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x00; // 0x02\n+ data_out[data_offset++] = 0x30; // 0x03\n+ break;\n+ case CARDCommand::GetState:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x20 | m_card_bit; // 0x02\n+\n+ // bit 0: Please take your card\n+ // bit 1: Endless waiting causes UNK_E to be called\n+ data_out[data_offset++] = 0x00; // 0x03\n+ break;\n+ case CARDCommand::Read:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x02; // 0x02\n+ data_out[data_offset++] = 0x53; // 0x03\n+ break;\n+ case CARDCommand::IsPresent:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x22; // 0x02\n+ data_out[data_offset++] = 0x30; // 0x03\n+ break;\n+ case CARDCommand::Write:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x02; // 0x02\n+ data_out[data_offset++] = 0x00; // 0x03\n+ break;\n+ case CARDCommand::SetPrintParam:\n+ case CARDCommand::RegisterFont:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x00; // 0x02\n+ data_out[data_offset++] = 0x00; // 0x03\n+ break;\n+ case CARDCommand::WriteInfo:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x02; // 0x02\n+ data_out[data_offset++] = 0x00; // 0x03\n+ break;\n+ case CARDCommand::Erase:\n+ // TODO: CARDCommand::Erase is not handled.\n+ break;\n+ case CARDCommand::Eject:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ if (AMMediaboard::GetGameType() == FZeroAX)\n+ {\n+ data_out[data_offset++] = 0x01; // 0x02\n+ }\n+ else\n+ {\n+ data_out[data_offset++] = 0x31; // 0x02\n+ }\n+ data_out[data_offset++] = 0x30; // 0x03\n+ break;\n+ case CARDCommand::Clean:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x02; // 0x02\n+ data_out[data_offset++] = 0x00; // 0x03\n+ break;\n+ case CARDCommand::Load:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x02; // 0x02\n+ data_out[data_offset++] = 0x30; // 0x03\n+ break;\n+ case CARDCommand::SetShutter:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x00; // 0x02\n+ data_out[data_offset++] = 0x00; // 0x03\n+ break;\n+ }\n+\n+ if (!validate_data_in_out(0, 3, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x30; // 0x04\n+ data_out[data_offset++] = 0x00; // 0x05\n+\n+ data_out[data_offset++] = 0x03; // 0x06\n+\n+ data_out[checksum_start] = data_offset - checksum_start; // 0x00 len\n+\n+ if (!validate_data_in_out(0, 1, "SerialB"))\n+ break;\n+ data_out[data_offset] = 0; // 0x07\n+ for (u32 i = 0; i < data_out[checksum_start]; ++i)\n+ data_out[data_offset] ^= data_out[checksum_start + i];\n+\n+ if (!validate_data_in_out(0, 1, "SerialB"))\n+ break;\n+ data_offset++;\n+\n+ data_out[command_length_offset] = data_out[checksum_start] + 2;\n+ }\n+ else\n+ {\n+ if (!validate_data_in_out(length, 0, "SerialB"))\n+ break;\n+ if (u64{m_card_offset} + length > std::size(m_card_buffer))\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command SerialB, m_card_buffer overflow:\\n"\n+ " - m_card_buffer = {}\\n"\n+ " - m_card_offset = {}\\n"\n+ " - length = {}",\n+ fmt::ptr(m_card_buffer), m_card_offset, length);\n+ data_in = data_in_end;\n+ break;\n+ }\n+ for (u32 i = 0; i < length; ++i)\n+ m_card_buffer[m_card_offset + i] = data_in[i];\n+\n+ m_card_offset += length;\n+\n+ // Check if we got a complete command\n+ if (m_card_buffer[0] == 0x02)\n+ {\n+ if (m_card_buffer[1] == m_card_offset - 2)\n+ {\n+ if (m_card_buffer[m_card_offset - 2] == 0x03)\n+ {\n+ m_card_command = m_card_buffer[2];\n+\n+ switch (CARDCommand(m_card_command))\n+ {\n+ case CARDCommand::Init:\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD Init");\n+\n+ m_card_write_length = 0;\n+ m_card_bit = 0;\n+ m_card_memory_size = 0;\n+ m_card_state_call_count = 0;\n+ break;\n+ case CARDCommand::GetState:\n+ {\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD GetState({:02X})",\n+ m_card_bit);\n+\n+ if (m_card_memory_size == 0)\n+ {\n+ const std::string card_filename(\n+ fmt::format("{}tricard_{}.bin", File::GetUserPath(D_TRIUSER_IDX),\n+ SConfig::GetInstance().GetGameID()));\n+\n+ if (File::Exists(card_filename))\n+ {\n+ File::IOFile card(card_filename, "rb+");\n+ m_card_memory_size = static_cast<u32>(card.GetSize());\n+ if (m_card_memory_size > sizeof(m_card_memory))\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command CARD GetState overflow:\\n"\n+ " - file name = {}\\n"\n+ " - file size = {}\\n"\n+ " - card size = {}",\n+ card_filename, m_card_memory_size, sizeof(m_card_memory));\n+ data_in = data_in_end;\n+ break;\n+ }\n+ card.ReadBytes(m_card_memory, m_card_memory_size);\n+ card.Close();\n+\n+ m_card_is_inserted = true;\n+ }\n+ }\n+\n+ if (AMMediaboard::GetGameType() == FZeroAX && m_card_memory_size)\n+ {\n+ m_card_state_call_count++;\n+ if (m_card_state_call_count > 10)\n+ {\n+ if (m_card_bit & 2)\n+ m_card_bit &= ~2u;\n+ else\n+ m_card_bit |= 2;\n+\n+ m_card_state_call_count = 0;\n+ }\n+ }\n+\n+ if (m_card_clean == 1)\n+ {\n+ m_card_clean = 2;\n+ }\n+ else if (m_card_clean == 2)\n+ {\n+ const std::string card_filename(\n+ fmt::format("{}tricard_{}.bin", File::GetUserPath(D_TRIUSER_IDX),\n+ SConfig::GetInstance().GetGameID()));\n+\n+ if (File::Exists(card_filename))\n+ {\n+ m_card_memory_size = static_cast<u32>(File::GetSize(card_filename));\n+ if (m_card_memory_size)\n+ {\n+ if (AMMediaboard::GetGameType() == FZeroAX)\n+ {\n+ m_card_bit = 2;\n+ }\n+ else\n+ {\n+ m_card_bit = 1;\n+ }\n+ }\n+ }\n+ m_card_clean = 0;\n+ }\n+ break;\n+ }\n+ case CARDCommand::IsPresent:\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD IsPresent");\n+ break;\n+ case CARDCommand::RegisterFont:\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD RegisterFont");\n+ break;\n+ case CARDCommand::Load:\n+ {\n+ const u8 mode = m_card_buffer[6];\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD Load({:02X})",\n+ mode);\n+ break;\n+ }\n+ case CARDCommand::Clean:\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD Clean");\n+ m_card_clean = 1;\n+ break;\n+ case CARDCommand::Read:\n+ {\n+ const u8 mode = m_card_buffer[6];\n+ const u8 bitmode = m_card_buffer[7];\n+ const u8 track = m_card_buffer[8];\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command CARD Read({:02X},{:02X},{:02X})", mode,\n+ bitmode, track);\n+\n+ // Prepare read packet\n+ memset(m_card_read_packet, 0, 0xDB);\n+\n+ const std::string card_filename(\n+ fmt::format("{}tricard_{}.bin", File::GetUserPath(D_TRIUSER_IDX),\n+ SConfig::GetInstance().GetGameID()));\n+\n+ if (File::Exists(card_filename))\n+ {\n+ File::IOFile card(card_filename, "rb+");\n+ if (m_card_memory_size == 0)\n+ {\n+ m_card_memory_size = static_cast<u32>(card.GetSize());\n+ }\n+\n+ if (m_card_memory_size > sizeof(m_card_memory))\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command CARD Read overflow:\\n"\n+ " - file name = {}\\n"\n+ " - file size = {}\\n"\n+ " - card size = {}",\n+ card_filename, m_card_memory_size, sizeof(m_card_memory));\n+ data_in = data_in_end;\n+ break;\n+ }\n+ card.ReadBytes(m_card_memory, m_card_memory_size);\n+ card.Close();\n+\n+ m_card_is_inserted = true;\n+ }\n+ else if (m_card_memory_size > sizeof(m_card_memory))\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command CARD Read overflow:\\n"\n+ " - requested size = {}\\n"\n+ " - card size = {}",\n+ m_card_memory_size, sizeof(m_card_memory));\n+ data_in = data_in_end;\n+ break;\n+ }\n+\n+ m_card_read_packet[0] = 0x02; // SUB CMD\n+ m_card_read_packet[1] = 0x00; // SUB CMDLen\n+\n+ m_card_read_packet[2] = 0x33; // CARD CMD\n+\n+ if (m_card_is_inserted) // CARD Status\n+ {\n+ m_card_read_packet[3] = 0x31;\n+ }\n+ else\n+ {\n+ m_card_read_packet[3] = 0x30;\n+ }\n+\n+ m_card_read_packet[4] = 0x30;\n+ m_card_read_packet[5] = 0x30;\n+\n+ u32 packet_offset = 6;\n+ // Data reply\n+ static_assert(sizeof(m_card_read_packet) >= sizeof(m_card_memory) + 6);\n+ memcpy(m_card_read_packet + packet_offset, m_card_memory, m_card_memory_size);\n+ packet_offset += m_card_memory_size;\n+\n+ static_assert(sizeof(m_card_read_packet) >= sizeof(m_card_memory) + 7);\n+ m_card_read_packet[packet_offset++] = 0x03;\n+\n+ m_card_read_packet[1] = packet_offset - 1; // SUB CMDLen\n+\n+ static_assert(sizeof(m_card_read_packet) >= sizeof(m_card_memory) + 8);\n+ for (u32 i = 0; i < packet_offset - 1; ++i)\n+ m_card_read_packet[packet_offset] ^= m_card_read_packet[1 + i];\n+\n+ static_assert(sizeof(m_card_read_packet) >= sizeof(m_card_memory) + 9);\n+ packet_offset++;\n+\n+ m_card_read_length = packet_offset;\n+ m_card_read = 0;\n+ break;\n+ }\n+ case CARDCommand::Write:\n+ {\n+ const u8 mode = m_card_buffer[6];\n+ const u8 bitmode = m_card_buffer[7];\n+ const u8 track = m_card_buffer[8];\n+\n+ m_card_memory_size = m_card_buffer[1] - 9;\n+ if (m_card_memory_size > sizeof(m_card_memory))\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command CARD Write overflow:\\n"\n+ " - write size = {}\\n"\n+ " - card size = {}",\n+ m_card_memory_size, sizeof(m_card_memory));\n+ data_in = data_in_end;\n+ break;\n+ }\n+\n+ static_assert(sizeof(m_card_buffer) >= sizeof(m_card_memory) + 9);\n+ memcpy(m_card_memory, m_card_buffer + 9, m_card_memory_size);\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command CARD Write: {:02X} {:02X} {:02X} {}", mode,\n+ bitmode, track, m_card_memory_size);\n+\n+ const std::string card_filename(File::GetUserPath(D_TRIUSER_IDX) +\n+ "tricard_" +\n+ SConfig::GetInstance().GetGameID() + ".bin");\n+\n+ File::IOFile card(card_filename, "wb+");\n+ card.WriteBytes(m_card_memory, m_card_memory_size);\n+ card.Close();\n+\n+ m_card_bit = 2;\n+\n+ m_card_state_call_count = 0;\n+ break;\n+ }\n+ case CARDCommand::SetPrintParam:\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD SetPrintParam");\n+ break;\n+ case CARDCommand::WriteInfo:\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD WriteInfo");\n+ break;\n+ case CARDCommand::Erase:\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD Erase");\n+ break;\n+ case CARDCommand::Eject:\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD Eject");\n+ if (AMMediaboard::GetGameType() != FZeroAX)\n+ {\n+ m_card_bit = 0;\n+ }\n+ break;\n+ case CARDCommand::SetShutter:\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD SetShutter");\n+ if (AMMediaboard::GetGameType() != FZeroAX)\n+ {\n+ m_card_bit = 0;\n+ }\n+ // Close\n+ if (m_card_buffer[6] == 0x30)\n+ {\n+ m_card_shutter = false;\n+ }\n+ // Open\n+ else if (m_card_buffer[6] == 0x31)\n+ {\n+ m_card_shutter = true;\n+ }\n+ break;\n+ default:\n+ ERROR_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: CARD:Unhandled command!");\n+ ERROR_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: CARD:[{:08X}]", m_card_command);\n+ // hexdump( m_card_buffer, m_card_offset );\n+ break;\n+ }\n+ m_card_offset = 0;\n+ }\n+ }\n+ }\n+\n+ if (!validate_data_in_out(0, 3, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x32;\n+ data_out[data_offset++] = 0x01; // len\n+ data_out[data_offset++] = 0x06; // OK\n+ }\n+ }\n+ else\n+ {\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x00; // len\n+ }\n+ data_in += length;\n+ break;\n+ }\n+ case GCAMCommand::JVSIOA:\n+ case GCAMCommand::JVSIOB:\n+ {\n+ if (!validate_data_in_out(4, 0, "JVSIO"))\n+ break;\n+\n+ JVSIOMessage message;\n+\n+ static int delay = 0;\n+\n+ const u8* const frame = &data_in[0];\n+ const u8 nr_bytes = frame[3]; // Byte after E0 xx\n+ u32 frame_len = nr_bytes + 3; // Header(2) + length byte + payload + checksum\n+\n+ u8 jvs_buf[0x80];\n+\n+ frame_len = std::min<u32>(frame_len, sizeof(jvs_buf));\n+\n+ if (!validate_data_in_out(frame_len, 0, "JVSIO"))\n+ break;\n+ DEBUG_LOG_FMT(SERIALINTERFACE_JVSIO, "GC-AM: Command {:02x} (JVS IO), hexdump:\\n{}",\n+ gcam_command, HexDump(data_in, frame_len));\n+\n+ memcpy(jvs_buf, frame, frame_len);\n+\n+ // Extract node and payload pointers\n+ u8 node = jvs_buf[2];\n+ u8* jvs_io = jvs_buf + 4; // First payload byte\n+ u8* const jvs_end = jvs_buf + frame_len; // One byte before checksum\n+ u8* const jvs_begin = jvs_io;\n+\n+ message.Start(0);\n+ message.AddData(1);\n+\n+ // Helper to check that iterating over jvs_io n times is safe,\n+ // i.e. *jvs_io++ at most lead to jvs_end\n+ auto validate_jvs_io = [&](u32 n, std::string_view command) -> bool {\n+ if (jvs_io + n > jvs_end)\n+ ERROR_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: overflow in {}", command);\n+ else\n+ return true;\n+ ERROR_LOG_FMT(SERIALINTERFACE_JVSIO,\n+ "Overflow details:\\n"\n+ " - jvs_io(begin={}, current={}, end={}, n={})\\n"\n+ " - delay={}, node={}\\n"\n+ " - frame(begin={}, len={})",\n+ fmt::ptr(jvs_begin), fmt::ptr(jvs_io), fmt::ptr(jvs_end), n, delay, node,\n+ fmt::ptr(frame), frame_len);\n+ jvs_io = jvs_end;\n+ return false;\n+ };\n+\n+ // Now iterate over the payload\n+ while (jvs_io < jvs_end)\n+ {\n+ const u8 jvsio_command = *jvs_io++;\n+ DEBUG_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO:node={}, command={:02x}", node,\n+ jvsio_command);\n+\n+ switch (JVSIOCommand(jvsio_command))\n+ {\n+ case JVSIOCommand::IOID:\n+ message.AddData(StatusOkay);\n+ switch (AMMediaboard::GetGameType())\n+ {\n+ case FZeroAX:\n+ // Specific version that enables DX mode on AX machines, all this does is enable the\n+ // motion of a chair\n+ message.AddData("SEGA ENTERPRISES,LTD.;837-13844-01 I/O CNTL BD2 ;");\n+ break;\n+ case FZeroAXMonster:\n+ case MarioKartGP:\n+ case MarioKartGP2:\n+ default:\n+ message.AddData("namco ltd.;FCA-1;Ver1.01;JPN,Multipurpose + Rotary Encoder");\n+ break;\n+ case VirtuaStriker3:\n+ case VirtuaStriker4:\n+ case VirtuaStriker4_2006:\n+ message.AddData("SEGA ENTERPRISES,LTD.;I/O BD JVS;837-13551;Ver1.00");\n+ break;\n+ }\n+ NOTICE_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: Command 0x10, BoardID");\n+ message.AddData((u32)0);\n+ break;\n+ case JVSIOCommand::CommandRevision:\n+ message.AddData(StatusOkay);\n+ message.AddData(0x11);\n+ NOTICE_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: Command 0x11, CommandRevision");\n+ break;\n+ case JVSIOCommand::JVRevision:\n+ message.AddData(StatusOkay);\n+ message.AddData(0x20);\n+ NOTICE_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: Command 0x12, JVRevision");\n+ break;\n+ case JVSIOCommand::CommunicationVersion:\n+ message.AddData(StatusOkay);\n+ message.AddData(0x10);\n+ NOTICE_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: Command 0x13, CommunicationVersion");\n+ break;\n+\n+ // Slave features:\n+ //\n+ // Inputs:\n+ // 0x01: Switch input: players, buttons\n+ // 0x02: Coin input: slots\n+ // 0x03: Analog input: channels, bits\n+ // 0x04: Rotary input: channels\n+ // 0x05: Keycode input: 0,0,0 ?\n+ // 0x06: Screen position input: X bits, Y bits, channels\n+ //\n+ // Outputs:\n+ // 0x10: Card system: slots\n+ // 0x11: Medal hopper: channels\n+ // 0x12: GPO-out: slots\n+ // 0x13: Analog output: channels\n+ // 0x14: Character output: width, height, type\n+ // 0x15: Backup\n+ case JVSIOCommand::CheckFunctionality:\n+ message.AddData(StatusOkay);\n+ switch (AMMediaboard::GetGameType())\n+ {\n+ case FZeroAX:\n+ case FZeroAXMonster:\n+ // 2 Player (12bit) (p2=paddles), 1 Coin slot, 6 Analog-in\n+ // message.AddData((void *)"\\x01\\x02\\x0C\\x00", 4);\n+ // message.AddData((void *)"\\x02\\x01\\x00\\x00", 4);\n+ // message.AddData((void *)"\\x03\\x06\\x00\\x00", 4);\n+ // message.AddData((void *)"\\x00\\x00\\x00\\x00", 4);\n+ //\n+ // DX Version: 2 Player (22bit) (p2=paddles), 2 Coin slot, 8 Analog-in,\n+ // 22 Driver-out\n+ message.AddData("\\x01\\x02\\x12\\x00", 4);\n+ message.AddData("\\x02\\x02\\x00\\x00", 4);\n+ message.AddData("\\x03\\x08\\x0A\\x00", 4);\n+ message.AddData("\\x12\\x16\\x00\\x00", 4);\n+ message.AddData("\\x00\\x00\\x00\\x00", 4);\n+ break;\n+ case VirtuaStriker3:\n+ // 2 Player (13bit), 2 Coin slot, 4 Analog-in, 1 CARD, 8 Driver-out\n+ message.AddData("\\x01\\x02\\x0D\\x00", 4);\n+ message.AddData("\\x02\\x02\\x00\\x00", 4);\n+ message.AddData("\\x10\\x01\\x00\\x00", 4);\n+ message.AddData("\\x12\\x08\\x00\\x00", 4);\n+ message.AddData("\\x00\\x00\\x00\\x00", 4);\n+ break;\n+ case GekitouProYakyuu:\n+ // 2 Player (13bit), 2 Coin slot, 4 Analog-in, 1 CARD, 8 Driver-out\n+ message.AddData("\\x01\\x02\\x0D\\x00", 4);\n+ message.AddData("\\x02\\x02\\x00\\x00", 4);\n+ message.AddData("\\x03\\x04\\x00\\x00", 4);\n+ message.AddData("\\x10\\x01\\x00\\x00", 4);\n+ message.AddData("\\x12\\x08\\x00\\x00", 4);\n+ message.AddData("\\x00\\x00\\x00\\x00", 4);\n+ break;\n+ case VirtuaStriker4:\n+ case VirtuaStriker4_2006:\n+ // 2 Player (13bit), 1 Coin slot, 4 Analog-in, 1 CARD\n+ message.AddData("\\x01\\x02\\x0D\\x00", 4);\n+ message.AddData("\\x02\\x01\\x00\\x00", 4);\n+ message.AddData("\\x03\\x04\\x00\\x00", 4);\n+ message.AddData("\\x10\\x01\\x00\\x00", 4);\n+ message.AddData("\\x00\\x00\\x00\\x00", 4);\n+ break;\n+ case KeyOfAvalon:\n+ // 1 Player (15bit), 1 Coin slot, 3 Analog-in, Touch, 1 CARD, 1 Driver-out\n+ // (Unconfirmed)\n+ message.AddData("\\x01\\x01\\x0F\\x00", 4);\n+ message.AddData("\\x02\\x01\\x00\\x00", 4);\n+ message.AddData("\\x03\\x03\\x00\\x00", 4);\n+ message.AddData("\\x06\\x10\\x10\\x01", 4);\n+ message.AddData("\\x10\\x01\\x00\\x00", 4);\n+ message.AddData("\\x12\\x01\\x00\\x00", 4);\n+ message.AddData("\\x00\\x00\\x00\\x00", 4);\n+ break;\n+ case MarioKartGP:\n+ case MarioKartGP2:\n+ default:\n+ // 1 Player (15bit), 1 Coin slot, 3 Analog-in, 1 CARD, 1 Driver-out\n+ message.AddData("\\x01\\x01\\x0F\\x00", 4);\n+ message.AddData("\\x02\\x01\\x00\\x00", 4);\n+ message.AddData("\\x03\\x03\\x00\\x00", 4);\n+ message.AddData("\\x10\\x01\\x00\\x00", 4);\n+ message.AddData("\\x12\\x01\\x00\\x00", 4);\n+ message.AddData("\\x00\\x00\\x00\\x00", 4);\n+ break;\n+ }\n+ NOTICE_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: Command 0x14, CheckFunctionality");\n+ break;\n+ case JVSIOCommand::MainID:\n+ {\n+ const u8* const main_id = jvs_io;\n+ while (jvs_io < jvs_end && *jvs_io++)\n+ {\n+ }\n+ if (main_id < jvs_io)\n+ {\n+ DEBUG_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: Command MainId:\\n{}",\n+ HexDump(main_id, jvs_io - main_id));\n+ }\n+ message.AddData(StatusOkay);\n+ break;\n+ }\n+ case JVSIOCommand::SwitchesInput:\n+ {\n+ if (!validate_jvs_io(2, "SwitchesInput"))\n+ break;\n+ const u32 player_count = *jvs_io++;\n+ const u32 player_byte_count = *jvs_io++;\n+\n+ DEBUG_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: Command 0x20, SwitchInputs: {} {}",\n+ player_count, player_byte_count);\n+\n+ message.AddData(StatusOkay);\n+\n+ GCPadStatus pad_status = Pad::GetStatus(0);\n+\n+ // Test button\n+ if (pad_status.switches & SWITCH_TEST)\n+ {\n+ // Trying to access the test menu without SegaBoot present will cause a crash\n+ if (AMMediaboard::GetTestMenu())\n+ {\n+ message.AddData(0x80);\n+ }\n+ else\n+ {\n+ PanicAlertFmt("Test menu is disabled due missing SegaBoot");\n+ }\n+ }\n+ else\n+ {\n+ message.AddData((u32)0x00);\n+ }\n+\n+ for (u32 i = 0; i < player_count; ++i)\n+ {\n+ u8 player_data[3]{};\n+\n+ // Service button\n+ if (pad_status.switches & SWITCH_SERVICE)\n+ player_data[0] |= 0x40;\n+\n+ switch (AMMediaboard::GetGameType())\n+ {\n+ // Controller configuration for F-Zero AX (DX)\n+ case FZeroAX:\n+ if (i == 0)\n+ {\n+ if (m_fzdx_seatbelt)\n+ {\n+ player_data[0] |= 0x01;\n+ }\n+\n+ // Start\n+ if (pad_status.button & PAD_BUTTON_START)\n+ player_data[0] |= 0x80;\n+ // Boost\n+ if (pad_status.button & PAD_BUTTON_A)\n+ player_data[0] |= 0x02;\n+ // View Change 1\n+ if (pad_status.button & PAD_BUTTON_RIGHT)\n+ player_data[0] |= 0x20;\n+ // View Change 2\n+ if (pad_status.button & PAD_BUTTON_LEFT)\n+ player_data[0] |= 0x10;\n+ // View Change 3\n+ if (pad_status.button & PAD_BUTTON_UP)\n+ player_data[0] |= 0x08;\n+ // View Change 4\n+ if (pad_status.button & PAD_BUTTON_DOWN)\n+ player_data[0] |= 0x04;\n+ player_data[1] = m_rx_reply & 0xF0;\n+ }\n+ else if (i == 1)\n+ {\n+ // Paddle left\n+ if (pad_status.button & PAD_BUTTON_X)\n+ player_data[0] |= 0x20;\n+ // Paddle right\n+ if (pad_status.button & PAD_BUTTON_Y)\n+ player_data[0] |= 0x10;\n+\n+ if (m_fzdx_motion_stop)\n+ {\n+ player_data[0] |= 2;\n+ }\n+ if (m_fzdx_sensor_right)\n+ {\n+ player_data[0] |= 4;\n+ }\n+ if (m_fzdx_sensor_left)\n+ {\n+ player_data[0] |= 8;\n+ }\n+\n+ player_data[1] = m_rx_reply << 4;\n+ }\n+ break;\n+ // Controller configuration for F-Zero AX MonsterRide\n+ case FZeroAXMonster:\n+ if (i == 0)\n+ {\n+ if (m_fzcc_sensor)\n+ {\n+ player_data[0] |= 0x01;\n+ }\n+\n+ // Start\n+ if (pad_status.button & PAD_BUTTON_START)\n+ player_data[0] |= 0x80;\n+ // Boost\n+ if (pad_status.button & PAD_BUTTON_A)\n+ player_data[0] |= 0x02;\n+ // View Change 1\n+ if (pad_status.button & PAD_BUTTON_RIGHT)\n+ player_data[0] |= 0x20;\n+ // View Change 2\n+ if (pad_status.button & PAD_BUTTON_LEFT)\n+ player_data[0] |= 0x10;\n+ // View Change 3\n+ if (pad_status.button & PAD_BUTTON_UP)\n+ player_data[0] |= 0x08;\n+ // View Change 4\n+ if (pad_status.button & PAD_BUTTON_DOWN)\n+ player_data[0] |= 0x04;\n+\n+ player_data[1] = m_rx_reply & 0xF0;\n+ }\n+ else if (i == 1)\n+ {\n+ // Paddle left\n+ if (pad_status.button & PAD_BUTTON_X)\n+ player_data[0] |= 0x20;\n+ // Paddle right\n+ if (pad_status.button & PAD_BUTTON_Y)\n+ player_data[0] |= 0x10;\n+\n+ if (m_fzcc_seatbelt)\n+ {\n+ player_data[0] |= 2;\n+ }\n+ if (m_fzcc_service)\n+ {\n+ player_data[0] |= 4;\n+ }\n+ if (m_fzcc_emergency)\n+ {\n+ player_data[0] |= 8;\n+ }\n+ }\n+ break;\n+ // Controller configuration for Virtua Striker 3 games\n+ case VirtuaStriker3:\n+ pad_status = Pad::GetStatus(i);\n+ // Start\n+ if (pad_status.button & PAD_BUTTON_START)\n+ player_data[0] |= 0x80;\n+ // Shoot\n+ if (pad_status.button & PAD_BUTTON_B)\n+ player_data[0] |= 0x01;\n+ // Short Pass\n+ if (pad_status.button & PAD_BUTTON_A)\n+ player_data[1] |= 0x80;\n+ // Long Pass\n+ if (pad_status.button & PAD_BUTTON_X)\n+ player_data[0] |= 0x02;\n+ // Left\n+ if (pad_status.button & PAD_BUTTON_LEFT)\n+ player_data[0] |= 0x08;\n+ // Up\n+ if (pad_status.button & PAD_BUTTON_UP)\n+ player_data[0] |= 0x20;\n+ // Right\n+ if (pad_status.button & PAD_BUTTON_RIGHT)\n+ player_data[0] |= 0x04;\n+ // Down\n+ if (pad_status.button & PAD_BUTTON_DOWN)\n+ player_data[0] |= 0x10;\n+ break;\n+ // Controller configuration for Virtua Striker 4 games\n+ case VirtuaStriker4:\n+ case VirtuaStriker4_2006:\n+ {\n+ pad_status = Pad::GetStatus(i);\n+ // Start\n+ if (pad_status.button & PAD_BUTTON_START)\n+ player_data[0] |= 0x80;\n+ // Long Pass\n+ if (pad_status.button & PAD_BUTTON_X)\n+ player_data[0] |= 0x01;\n+ // Short Pass\n+ if (pad_status.button & PAD_BUTTON_A)\n+ player_data[0] |= 0x02;\n+ // Shoot\n+ if (pad_status.button & PAD_BUTTON_B)\n+ player_data[1] |= 0x80;\n+ // Dash\n+ if (pad_status.button & PAD_BUTTON_Y)\n+ player_data[1] |= 0x40;\n+ // Tactics (U)\n+ if (pad_status.button & PAD_BUTTON_LEFT)\n+ player_data[0] |= 0x20;\n+ // Tactics (M)\n+ if (pad_status.button & PAD_BUTTON_UP)\n+ player_data[0] |= 0x08;\n+ // Tactics (D)\n+ if (pad_status.button & PAD_BUTTON_RIGHT)\n+ player_data[0] |= 0x04;\n+\n+ if (i == 0)\n+ {\n+ player_data[0] |= 0x10; // IC-Card Switch ON\n+\n+ // IC-Card Lock\n+ if (pad_status.button & PAD_BUTTON_DOWN)\n+ player_data[1] |= 0x20;\n+ }\n+ }\n+ break;\n+ // Controller configuration for Gekitou Pro Yakyuu\n+ case GekitouProYakyuu:\n+ pad_status = Pad::GetStatus(i);\n+ // Start\n+ if (pad_status.button & PAD_BUTTON_START)\n+ player_data[0] |= 0x80;\n+ // A\n+ if (pad_status.button & PAD_BUTTON_B)\n+ player_data[0] |= 0x01;\n+ // B\n+ if (pad_status.button & PAD_BUTTON_A)\n+ player_data[0] |= 0x02;\n+ // Gekitou\n+ if (pad_status.button & PAD_TRIGGER_L)\n+ player_data[1] |= 0x80;\n+ // Left\n+ if (pad_status.button & PAD_BUTTON_LEFT)\n+ player_data[0] |= 0x08;\n+ // Up\n+ if (pad_status.button & PAD_BUTTON_UP)\n+ player_data[0] |= 0x20;\n+ // Right\n+ if (pad_status.button & PAD_BUTTON_RIGHT)\n+ player_data[0] |= 0x04;\n+ // Down\n+ if (pad_status.button & PAD_BUTTON_DOWN)\n+ player_data[0] |= 0x10;\n+ break;\n+ // Controller configuration for Mario Kart and other games\n+ default:\n+ case MarioKartGP:\n+ case MarioKartGP2:\n+ {\n+ // Start\n+ if (pad_status.button & PAD_BUTTON_START)\n+ player_data[0] |= 0x80;\n+ // Item button\n+ if (pad_status.button & PAD_BUTTON_A)\n+ player_data[1] |= 0x20;\n+ // VS-Cancel button\n+ if (pad_status.button & PAD_BUTTON_B)\n+ player_data[1] |= 0x02;\n+ }\n+ break;\n+ case KeyOfAvalon:\n+ {\n+ // Debug On\n+ if (pad_status.button & PAD_BUTTON_START)\n+ player_data[0] |= 0x80;\n+ // Switch 1\n+ if (pad_status.button & PAD_BUTTON_A)\n+ player_data[0] |= 0x04;\n+ // Switch 2\n+ if (pad_status.button & PAD_BUTTON_B)\n+ player_data[0] |= 0x08;\n+ // Toggle inserted card\n+ if (pad_status.button & PAD_TRIGGER_L)\n+ {\n+ m_ic_card_status ^= ICCARDStatus::NoCard;\n+ }\n+ }\n+ break;\n+ }\n+\n+ for (u32 j = 0; j < player_byte_count; ++j)\n+ message.AddData(player_data[j]);', 'path': 'Source/Core/Core/HW/SI/SI_DeviceAMBaseboard.cpp', 'position': 2127, 'original_position': 2127, 'commit_id': '5bf8fe93286ebe4f08fb469a44f259a8397c89e0', 'user': {'login': 'JosJuice', 'id': 6716818, 'node_id': 'MDQ6VXNlcjY3MTY4MTg=', 'avatar_url': 'https://avatars.githubusercontent.com/u/6716818?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/JosJuice', 'html_url': 'https://github.com/JosJuice', 'followers_url': 'https://api.github.com/users/JosJuice/followers', 'following_url': 'https://api.github.com/users/JosJuice/following{/other_user}', 'gists_url': 'https://api.github.com/users/JosJuice/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/JosJuice/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/JosJuice/subscriptions', 'organizations_url': 'https://api.github.com/users/JosJuice/orgs', 'repos_url': 'https://api.github.com/users/JosJuice/repos', 'events_url': 'https://api.github.com/users/JosJuice/events{/privacy}', 'received_events_url': 'https://api.github.com/users/JosJuice/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'body': '`player_byte_count` might be larger than the size of `player_data`.', 'created_at': '2026-02-01T15:19:06Z', 'updated_at': '2026-02-01T15:40:26Z', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/13844#discussion_r2751374597', 'pull_request_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13844', 'author_association': 'MEMBER', '_links': {'self': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2751374597'}, 'html': {'href': 'https://github.com/dolphin-emu/dolphin/pull/13844#discussion_r2751374597'}, 'pull_request': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13844'}}, 'original_commit_id': '5bf8fe93286ebe4f08fb469a44f259a8397c89e0', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2751374597/reactions', 'total_count': 0, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 0, 'confused': 0, 'heart': 0, 'rocket': 0, 'eyes': 0}}, {'id': 2751386784, 'node_id': 'PRRC_kwDOALCn2M6j_tSg', 'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2751386784', 'pull_request_review_id': 3735806017, 'diff_hunk': '@@ -0,0 +1,2708 @@\n+// Copyright 2025 Dolphin Emulator Project\n+// SPDX-License-Identifier: GPL-2.0-or-later\n+\n+#include "Core/HW/SI/SI_DeviceAMBaseboard.h"\n+\n+#include <algorithm>\n+#include <numeric>\n+#include <string>\n+\n+#include <fmt/format.h>\n+\n+#include "Common/Buffer.h"\n+#include "Common/CommonTypes.h"\n+#include "Common/FileUtil.h"\n+#include "Common/IOFile.h"\n+#include "Common/Logging/Log.h"\n+#include "Common/MsgHandler.h"\n+#include "Common/Swap.h"\n+\n+#include "Core/Boot/Boot.h"\n+#include "Core/BootManager.h"\n+#include "Core/Config/MainSettings.h"\n+#include "Core/ConfigManager.h"\n+#include "Core/Core.h"\n+#include "Core/CoreTiming.h"\n+#include "Core/HW/DVD/AMMediaboard.h"\n+#include "Core/HW/DVD/DVDInterface.h"\n+#include "Core/HW/EXI/EXI.h"\n+#include "Core/HW/GCPad.h"\n+#include "Core/HW/MMIO.h"\n+#include "Core/HW/Memmap.h"\n+#include "Core/HW/ProcessorInterface.h"\n+#include "Core/HW/SI/SI.h"\n+#include "Core/HW/SI/SI_Device.h"\n+#include "Core/HW/SI/SI_DeviceGCController.h"\n+#include "Core/HW/SystemTimers.h"\n+#include "Core/Movie.h"\n+#include "Core/NetPlayProto.h"\n+#include "Core/System.h"\n+\n+#include "InputCommon/GCPadStatus.h"\n+\n+namespace SerialInterface\n+{\n+void JVSIOMessage::Start(int node)\n+{\n+ m_last_start = m_pointer;\n+ const u8 header[3] = {0xE0, (u8)node, 0};\n+ m_checksum = 0;\n+ AddData(header, 3, 1);\n+}\n+\n+void JVSIOMessage::AddData(const u8* dst, size_t len, int sync = 0)\n+{\n+ if (m_pointer + len >= sizeof(m_message))\n+ {\n+ PanicAlertFmt("JVSIOMessage overrun!");\n+ return;\n+ }\n+\n+ while (len--)\n+ {\n+ const u8 c = *dst++;\n+ if (!sync && ((c == 0xE0) || (c == 0xD0)))\n+ {\n+ if (m_pointer + 2 > sizeof(m_message))\n+ {\n+ PanicAlertFmt("JVSIOMessage overrun!");\n+ break;\n+ }\n+ m_message[m_pointer++] = 0xD0;\n+ m_message[m_pointer++] = c - 1;\n+ }\n+ else\n+ {\n+ if (m_pointer >= sizeof(m_message))\n+ {\n+ PanicAlertFmt("JVSIOMessage overrun!");\n+ break;\n+ }\n+ m_message[m_pointer++] = c;\n+ }\n+\n+ if (!sync)\n+ m_checksum += c;\n+ sync = 0;\n+ }\n+}\n+\n+void JVSIOMessage::AddData(const void* data, size_t len)\n+{\n+ AddData(static_cast<const u8*>(data), len);\n+}\n+\n+void JVSIOMessage::AddData(const char* data)\n+{\n+ AddData(data, strlen(data));\n+}\n+\n+void JVSIOMessage::AddData(u32 n)\n+{\n+ const u8 cs = n;\n+ AddData(&cs, 1);\n+}\n+\n+void JVSIOMessage::End()\n+{\n+ const u32 len = m_pointer - m_last_start;\n+ if (m_last_start + 2 < sizeof(m_message) && len >= 3)\n+ {\n+ m_message[m_last_start + 2] = len - 2; // assuming len <0xD0\n+ AddData(m_checksum + len - 2);\n+ }\n+ else\n+ {\n+ PanicAlertFmt("JVSIOMessage: Not enough space for checksum!");\n+ }\n+}\n+\n+static constexpr u8 CheckSumXOR(const u8* data, u32 length)\n+{\n+ return std::accumulate(data, data + length, u8{}, std::bit_xor());\n+}\n+\n+static constexpr char s_cdr_program_version[] = {" Version 1.22,2003/09/19,171-8213B"};\n+static constexpr char s_cdr_boot_version[] = {" Version 1.04,2003/06/17,171-8213B"};\n+static constexpr u8 s_cdr_card_data[] = {\n+ 0x00, 0x6E, 0x00, 0x00, 0x01, 0x00, 0x00, 0x06, 0x00, 0x00, 0x07, 0x00, 0x00, 0x0B,\n+ 0x00, 0x00, 0x0E, 0x00, 0x00, 0x10, 0x00, 0x00, 0x17, 0x00, 0x00, 0x19, 0x00, 0x00,\n+ 0x1A, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x20, 0x00,\n+ 0x00, 0x22, 0x00, 0x00, 0x23, 0x00, 0x00, 0x24, 0x00, 0x00, 0x27, 0x00, 0x00, 0x28,\n+ 0x00, 0x00, 0x2C, 0x00, 0x00, 0x2F, 0x00, 0x00, 0x34, 0x00, 0x00, 0x35, 0x00, 0x00,\n+ 0x37, 0x00, 0x00, 0x38, 0x00, 0x00, 0x39, 0x00, 0x00, 0x3D, 0x00};\n+\n+const constexpr u8 s_region_flags[] = "\\x00\\x00\\x30\\x00"\n+ // "\\x01\\xfe\\x00\\x00" // JAPAN\n+ "\\x02\\xfd\\x00\\x00" // USA\n+ //"\\x03\\xfc\\x00\\x00" // export\n+ "\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff";\n+// AM-Baseboard device on SI\n+CSIDevice_AMBaseboard::CSIDevice_AMBaseboard(Core::System& system, SIDevices device,\n+ int device_number)\n+ : ISIDevice(system, device, device_number)\n+{\n+ // Card ID\n+ m_ic_card_data[0x20] = 0x95;\n+ m_ic_card_data[0x21] = 0x71;\n+\n+ if (AMMediaboard::GetGameType() == KeyOfAvalon)\n+ {\n+ m_ic_card_data[0x22] = 0x26;\n+ m_ic_card_data[0x23] = 0x40;\n+ }\n+ else if (AMMediaboard::GetGameType() == VirtuaStriker4)\n+ {\n+ m_ic_card_data[0x22] = 0x44;\n+ m_ic_card_data[0x23] = 0x00;\n+ }\n+\n+ // Use count\n+ m_ic_card_data[0x28] = 0xFF;\n+ m_ic_card_data[0x29] = 0xFF;\n+}\n+\n+constexpr u32 SI_XFER_LENGTH_MASK = 0x7f;\n+\n+// Translate [0,1,2,...,126,127] to [128,1,2,...,126,127]\n+constexpr s32 ConvertSILengthField(u32 field)\n+{\n+ return ((field - 1) & SI_XFER_LENGTH_MASK) + 1;\n+}\n+\n+void CSIDevice_AMBaseboard::ICCardSendReply(ICCommand* iccommand, u8* buffer, u32* length)\n+{\n+ iccommand->status = Common::swap16(iccommand->status);\n+\n+ const auto iccommand_data = reinterpret_cast<const u8*>(iccommand);\n+ const u8 crc = CheckSumXOR(iccommand_data + 2, iccommand->pktlen - 1);\n+\n+ for (u32 i = 0; i < iccommand->pktlen + 1; ++i)\n+ {\n+ buffer[(*length)++] = iccommand_data[i];\n+ }\n+\n+ buffer[(*length)++] = crc;\n+}\n+\n+void CSIDevice_AMBaseboard::SwapBuffers(u8* buffer, u32* buffer_length)\n+{\n+ memcpy(m_last[1], buffer, 0x80); // Save current buffer\n+ memcpy(buffer, m_last[0], 0x80); // Load previous buffer\n+ memcpy(m_last[0], m_last[1], 0x80); // Update history\n+\n+ m_lastptr[1] = *buffer_length; // Swap lengths\n+ *buffer_length = m_lastptr[0];\n+ m_lastptr[0] = m_lastptr[1];\n+}\n+\n+int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length)\n+{\n+ const auto& serial_interface = m_system.GetSerialInterface();\n+ u32 buffer_length = ConvertSILengthField(serial_interface.GetInLength());\n+\n+ // Debug logging\n+ ISIDevice::RunBuffer(buffer, buffer_length);\n+\n+ u32 buffer_position = 0;\n+ while (buffer_position < buffer_length)\n+ {\n+ BaseBoardCommand command = static_cast<BaseBoardCommand>(buffer[buffer_position]);\n+ buffer_position++;\n+\n+ switch (command)\n+ {\n+ case BaseBoardCommand::GCAM_Reset: // Returns ID and dip switches\n+ {\n+ const u32 id = Common::swap32(SI_AM_BASEBOARD | 0x100);\n+ std::memcpy(buffer, &id, sizeof(id));\n+ return sizeof(id);\n+ }\n+ break;\n+ case BaseBoardCommand::GCAM_Command:\n+ {\n+ u32 checksum = 0;\n+ for (u32 i = 0; i < buffer_length; ++i)\n+ checksum += buffer[i];\n+\n+ std::array<u8, 0x80> data_out{};\n+ u32 data_offset = 0;\n+\n+ static u32 dip_switch_1 = 0xFE;\n+ static u32 dip_switch_0 = 0xFF;\n+\n+ data_out[data_offset++] = 1;\n+ data_out[data_offset++] = 1;\n+\n+ u8* data_in = buffer + 2;\n+ if (buffer_position >= buffer_length)\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: buffer overflow (position={}, length={})",\n+ buffer_position, buffer_length);\n+ buffer_position = buffer_length;\n+ break;\n+ }\n+\n+ const u32 requested_size = buffer[buffer_position] + 2;\n+ if (requested_size > buffer_length)\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: requested size ({}) bigger than buffer\'s ({})",\n+ requested_size, buffer_length);\n+ buffer_position = buffer_length;\n+ break;\n+ }\n+ u8* const data_in_end = buffer + requested_size;\n+\n+ // Helper to check that iterating over data n times is safe,\n+ // i.e. *data++ at most lead to data.end()\n+ auto validate_data_in_out = [&](u32 n_in, u32 n_out, std::string_view command) -> bool {\n+ if (data_in + n_in > data_in_end)\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: data_in overflow in {}", command);\n+ else if (u64{data_offset} + n_out > data_out.size())\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: data_out overflow in {}", command);\n+ else\n+ return true;\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "Overflow details:\\n"\n+ " - data_in(begin={}, current={}, end={}, n_in={})\\n"\n+ " - data_out(offset={}, size={}, n_out={})\\n"\n+ " - buffer(position={}, length={})",\n+ fmt::ptr(buffer + 2), fmt::ptr(data_in), fmt::ptr(data_in_end), n_in,\n+ data_offset, data_out.size(), n_out, buffer_position, buffer_length);\n+ data_in = data_in_end;\n+ return false;\n+ };\n+\n+ while (data_in < data_in_end)\n+ {\n+ const u32 gcam_command = *data_in++;\n+ switch (GCAMCommand(gcam_command))\n+ {\n+ case GCAMCommand::StatusSwitches:\n+ {\n+ if (!validate_data_in_out(1, 4, "StatusSwitches"))\n+ break;\n+\n+ const u8 status = *data_in++;\n+ DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x10, {:02x} (READ STATUS&SWITCHES)",\n+ status);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x2;\n+\n+ // We read Test/Service from the JVS-I/O SwitchesInput instead\n+ //\n+ // const GCPadStatus pad_status = Pad::GetStatus(ISIDevice::m_device_number);\n+ // baseboard test/service switches\n+ // if (pad_status.button & PAD_BUTTON_Y)\t// Test\n+ // dip_switch_0 &= ~0x80;\n+ // if (pad_status.button & PAD_BUTTON_X)\t// Service\n+ // dip_switch_0 &= ~0x40;\n+\n+ // Horizontal Scanning Frequency switch\n+ // Required for F-Zero AX booting via Sega Boot\n+ if (AMMediaboard::GetGameType() == FZeroAX ||\n+ AMMediaboard::GetGameType() == FZeroAXMonster)\n+ {\n+ dip_switch_0 &= ~0x20;\n+ }\n+\n+ // Disable camera in MKGP1/2\n+ if (AMMediaboard::GetGameType() == MarioKartGP ||\n+ AMMediaboard::GetGameType() == MarioKartGP2)\n+ {\n+ dip_switch_0 &= ~0x10;\n+ }\n+\n+ data_out[data_offset++] = dip_switch_0;\n+ data_out[data_offset++] = dip_switch_1;\n+ break;\n+ }\n+ case GCAMCommand::SerialNumber:\n+ {\n+ if (!validate_data_in_out(1, 18, "SerialNumber"))\n+ break;\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x11, {:02x} (READ SERIAL NR)",\n+ *data_in);\n+ data_in++;\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 16;\n+ memcpy(data_out.data() + data_offset, "AADE-01B98394904", 16);\n+\n+ data_offset += 16;\n+ break;\n+ }\n+ case GCAMCommand::Unknown_12:\n+ if (!validate_data_in_out(2, 2, "Unknown_12"))\n+ break;\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x12, {:02x} {:02x}", data_in[0],\n+ data_in[1]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x00;\n+\n+ data_in += 2;\n+ break;\n+ case GCAMCommand::Unknown_14:\n+ if (!validate_data_in_out(2, 2, "Unknown_14"))\n+ break;\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x14, {:02x} {:02x}", data_in[0],\n+ data_in[1]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x00;\n+\n+ data_in += 2;\n+ break;\n+ case GCAMCommand::FirmVersion:\n+ if (!validate_data_in_out(1, 4, "FirmVersion"))\n+ break;\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x15, {:02x} (READ FIRM VERSION)",\n+ *data_in);\n+ data_in++;\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x02;\n+ // 00.26\n+ data_out[data_offset++] = 0x00;\n+ data_out[data_offset++] = 0x26;\n+ break;\n+ case GCAMCommand::FPGAVersion:\n+ if (!validate_data_in_out(1, 4, "FPGAVersion"))\n+ break;\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x16, {:02x} (READ FPGA VERSION)",\n+ *data_in);\n+ data_in++;\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x02;\n+ // 07.06\n+ data_out[data_offset++] = 0x07;\n+ data_out[data_offset++] = 0x06;\n+ break;\n+ case GCAMCommand::RegionSettings:\n+ {\n+ if (!validate_data_in_out(5, 0x16, "RegionSettings"))\n+ break;\n+\n+ // Used by SegaBoot for region checks (dev mode skips this check)\n+ // In some games this also controls the displayed language\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x1F, {:02x} {:02x} {:02x} {:02x} {:02x} (REGION)",\n+ data_in[0], data_in[1], data_in[2], data_in[3], data_in[4]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x14;\n+\n+ for (int i = 0; i < 0x14; ++i)\n+ data_out[data_offset++] = s_region_flags[i];\n+\n+ data_in += 5;\n+ }\n+ break;\n+ // No reply\n+ // Note: Always sends three bytes even though size is set to two\n+ case GCAMCommand::Unknown_21:\n+ {\n+ if (!validate_data_in_out(4, 0, "Unknown_21"))\n+ break;\n+\n+ DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x21, {:02x}, {:02x}, {:02x}, {:02x}",\n+ data_in[0], data_in[1], data_in[2], data_in[3]);\n+ data_in += 4;\n+ }\n+ break;\n+ // No reply\n+ // Note: Always sends six bytes\n+ case GCAMCommand::Unknown_22:\n+ {\n+ if (!validate_data_in_out(7, 0, "Unknown_22"))\n+ break;\n+\n+ DEBUG_LOG_FMT(\n+ SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x22, {:02x}, {:02x}, {:02x}, {:02x}, {:02x}, {:02x}, {:02x}",\n+ data_in[0], data_in[1], data_in[2], data_in[3], data_in[4], data_in[5], data_in[6]);\n+\n+ const u32 in_size = data_in[0] + 1;\n+ if (!validate_data_in_out(in_size, 0, "Unknown_22"))\n+ break;\n+ data_in += in_size;\n+ }\n+ break;\n+ case GCAMCommand::Unknown_23:\n+ if (!validate_data_in_out(2, 2, "Unknown_23"))\n+ break;\n+\n+ DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x23, {:02x} {:02x}", data_in[0],\n+ data_in[1]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x00;\n+\n+ data_in += 2;\n+ break;\n+ case GCAMCommand::Unknown_24:\n+ if (!validate_data_in_out(2, 2, "Unknown_24"))\n+ break;\n+\n+ DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x24, {:02x} {:02x}", data_in[0],\n+ data_in[1]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x00;\n+\n+ data_in += 2;\n+ break;\n+ case GCAMCommand::SerialA:\n+ {\n+ if (!validate_data_in_out(1, 0, "SerialA"))\n+ break;\n+\n+ const u32 length = *data_in++;\n+ if (length)\n+ {\n+ if (!validate_data_in_out(length, 0, "SerialA"))\n+ break;\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x31, length=0x{:02x}, hexdump:\\n{}",\n+ length, HexDump(data_in, length));\n+\n+ // Serial - Wheel\n+ if (AMMediaboard::GetGameType() == MarioKartGP ||\n+ AMMediaboard::GetGameType() == MarioKartGP2)\n+ {\n+ if (!validate_data_in_out(10, 2, "SerialA (Wheel)"))\n+ break;\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x31, (WHEEL) {:02x}{:02x} {:02x}{:02x} {:02x} {:02x} "\n+ "{:02x} {:02x} {:02x} {:02x}",\n+ data_in[0], data_in[1], data_in[2], data_in[3], data_in[4], data_in[5],\n+ data_in[6], data_in[7], data_in[8], data_in[9]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x03;\n+\n+ switch (m_wheel_init)\n+ {\n+ case 0:\n+ if (!validate_data_in_out(0, 3, "SerialA (Wheel)"))\n+ break;\n+ data_out[data_offset++] = \'E\'; // Error\n+ data_out[data_offset++] = \'0\';\n+ data_out[data_offset++] = \'0\';\n+ m_wheel_init++;\n+ break;\n+ case 1:\n+ if (!validate_data_in_out(0, 3, "SerialA (Wheel)"))\n+ break;\n+ data_out[data_offset++] = \'C\'; // Power Off\n+ data_out[data_offset++] = \'0\';\n+ data_out[data_offset++] = \'6\';\n+ // Only turn on when a wheel is connected\n+ if (serial_interface.GetDeviceType(1) == SerialInterface::SIDEVICE_GC_STEERING)\n+ {\n+ m_wheel_init++;\n+ }\n+ break;\n+ case 2:\n+ if (!validate_data_in_out(0, 3, "SerialA (Wheel)"))\n+ break;\n+ data_out[data_offset++] = \'C\'; // Power On\n+ data_out[data_offset++] = \'0\';\n+ data_out[data_offset++] = \'1\';\n+ break;\n+ default:\n+ break;\n+ }\n+\n+ // u16 CenteringForce= ptr(6);\n+ // u16 FrictionForce = ptr(8);\n+ // u16 Roll = ptr(10);\n+ if (!validate_data_in_out(length, 0, "SerialA (Wheel)"))\n+ break;\n+ data_in += length;\n+ break;\n+ }\n+\n+ // Serial - Unknown\n+ if (AMMediaboard::GetGameType() == GekitouProYakyuu)\n+ {\n+ if (!validate_data_in_out(sizeof(u32), 0, "SerialA (Unknown)"))\n+ break;\n+ const u32 serial_command = Common::BitCastPtr<u32>(data_in);\n+\n+ if (serial_command == 0x00001000)\n+ {\n+ if (!validate_data_in_out(0, 5, "SerialA (Unknown)"))\n+ break;\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x03;\n+ data_out[data_offset++] = 1;\n+ data_out[data_offset++] = 2;\n+ data_out[data_offset++] = 3;\n+ }\n+\n+ if (!validate_data_in_out(length, 0, "SerialA (Unknown)"))\n+ break;\n+ data_in += length;\n+ break;\n+ }\n+\n+ // Serial IC-CARD / Serial Deck Reader\n+ if (AMMediaboard::GetGameType() == VirtuaStriker4 ||\n+ AMMediaboard::GetGameType() == VirtuaStriker4_2006 ||\n+ AMMediaboard::GetGameType() == KeyOfAvalon)\n+ {\n+ if (!validate_data_in_out(2, 0, "SerialA (IC-CARD)"))\n+ break;\n+ u32 serial_command = data_in[1];\n+\n+ ICCommand icco;\n+\n+ // Set default reply\n+ icco.pktcmd = gcam_command;\n+ icco.pktlen = 7;\n+ icco.fixed = 0x10;\n+ icco.command = serial_command;\n+ icco.flag = 0;\n+ icco.length = 2;\n+ icco.status = 0;\n+ icco.extlen = 0;\n+\n+ // Check for rest of data from the write pages command\n+ if (m_ic_write_size && m_ic_write_offset)\n+ {\n+ const u32 size = data_in[1];\n+\n+ if (!validate_data_in_out(size + 2, 0, "SerialA (IC-CARD)"))\n+ break;\n+ DEBUG_LOG_FMT(SERIALINTERFACE_CARD, "Command: {}", HexDump(data_in, size + 2));\n+\n+ INFO_LOG_FMT(\n+ SERIALINTERFACE_CARD,\n+ "GC-AM: Command 25 (IC-CARD) Write Pages: Off:{:x} Size:{:x} PSize:{:x}",\n+ m_ic_write_offset, m_ic_write_size, size);\n+\n+ if (u64{m_ic_write_offset} + size > sizeof(m_ic_write_buffer))\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 25 (IC-CARD) m_ic_write_buffer overflow:\\n"\n+ " - m_ic_write_buffer(offset={}, size={})\\n"\n+ " - size={}\\n",\n+ m_ic_write_offset, sizeof(m_ic_write_buffer), size);\n+ data_in = data_in_end;\n+ break;\n+ }\n+ memcpy(m_ic_write_buffer + m_ic_write_offset, data_in + 2, size);\n+\n+ m_ic_write_offset += size;\n+\n+ if (m_ic_write_offset > m_ic_write_size)\n+ {\n+ m_ic_write_offset = 0;\n+\n+ const u16 page = m_ic_write_buffer[5];\n+ const u16 count = m_ic_write_buffer[7];\n+\n+ if ((page * 8 + count * 8) > sizeof(m_ic_card_data) ||\n+ (10 + count * 8) > sizeof(m_ic_write_buffer))\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 25 (IC-CARD) Write Pages overflow:\\n"\n+ " - m_ic_card_data(offset={}, size={})\\n"\n+ " - m_ic_write_buffer(offset={}, size={})\\n"\n+ " - size={}, page={}, count={}\\n",\n+ page * 8, sizeof(m_ic_card_data), 10, sizeof(m_ic_write_buffer),\n+ count * 8, page, count);\n+ data_in = data_in_end;\n+ break;\n+ }\n+ memcpy(m_ic_card_data + page * 8, m_ic_write_buffer + 10, count * 8);\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 25 (IC-CARD) Write Pages:{} Count:{}({:x})", page,\n+ count, size);\n+\n+ icco.command = WritePages;\n+\n+ if (!validate_data_in_out(0, icco.pktlen + 2, "SerialA (IC-CARD)"))\n+ break;\n+ ICCardSendReply(&icco, data_out.data(), &data_offset);\n+ }\n+ if (!validate_data_in_out(length, 0, "SerialA (IC-CARD)"))\n+ break;\n+ data_in += length;\n+ break;\n+ }\n+\n+ switch (ICCARDCommand(serial_command))\n+ {\n+ case ICCARDCommand::GetStatus:\n+ icco.status = m_ic_card_state;\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (IC-CARD) Get Status:{:02x}", m_ic_card_state);\n+ break;\n+ case ICCARDCommand::SetBaudrate:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (IC-CARD) Set Baudrate");\n+ break;\n+ case ICCARDCommand::FieldOn:\n+ m_ic_card_state |= 0x10;\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (IC-CARD) Field On");\n+ break;\n+ case ICCARDCommand::InsertCheck:\n+ icco.status = m_ic_card_status;\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (IC-CARD) Insert Check:{:02x}", m_ic_card_status);\n+ break;\n+ case ICCARDCommand::AntiCollision:\n+ icco.extlen = 8;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ // Card ID\n+ icco.extdata[0] = 0x00;\n+ icco.extdata[1] = 0x00;\n+ icco.extdata[2] = 0x54;\n+ icco.extdata[3] = 0x4D;\n+ icco.extdata[4] = 0x50;\n+ icco.extdata[5] = 0x00;\n+ icco.extdata[6] = 0x00;\n+ icco.extdata[7] = 0x00;\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (IC-CARD) Anti Collision");\n+ break;\n+ case ICCARDCommand::SelectCard:\n+ icco.extlen = 8;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ // Session\n+ icco.extdata[0] = 0x00;\n+ icco.extdata[1] = m_ic_card_session;\n+ icco.extdata[2] = 0x00;\n+ icco.extdata[3] = 0x00;\n+ icco.extdata[4] = 0x00;\n+ icco.extdata[5] = 0x00;\n+ icco.extdata[6] = 0x00;\n+ icco.extdata[7] = 0x00;\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (IC-CARD) Select Card:{}",\n+ m_ic_card_session);\n+ break;\n+ case ICCARDCommand::ReadPage:\n+ case ICCARDCommand::ReadUseCount:\n+ {\n+ if (!validate_data_in_out(8, 0, "SerialA (IC-CARD)"))\n+ break;\n+ const u16 page = Common::swap16(data_in + 6) & 0xFF; // 255 is max page\n+\n+ icco.extlen = 8;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ memcpy(icco.extdata, m_ic_card_data + page * 8, 8);\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 31 (IC-CARD) Read Page:{}",\n+ page);\n+ break;\n+ }\n+ case ICCARDCommand::WritePage:\n+ {\n+ if (!validate_data_in_out(10, 0, "SerialA (IC-CARD)"))\n+ break;\n+ const u16 page = Common::swap16(data_in + 8) & 0xFF; // 255 is max page\n+\n+ // Write only one page\n+ if (page == 4)\n+ {\n+ icco.status = 0x80;\n+ }\n+ else\n+ {\n+ if (!validate_data_in_out(18, 0, "SerialA (IC-CARD)"))\n+ break;\n+ memcpy(m_ic_card_data + page * 8, data_in + 10, 8);\n+ }\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (IC-CARD) Write Page:{}",\n+ page);\n+ break;\n+ }\n+ case ICCARDCommand::DecreaseUseCount:\n+ {\n+ if (!validate_data_in_out(8, 0, "SerialA (IC-CARD)"))\n+ break;\n+ const u16 page = Common::swap16(data_in + 6) & 0xFF; // 255 is max page\n+\n+ icco.extlen = 2;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ auto ic_card_data = Common::BitCastPtr<u16>(m_ic_card_data + 0x28);\n+ ic_card_data = ic_card_data - 1;\n+\n+ // Counter\n+ icco.extdata[0] = m_ic_card_data[0x28];\n+ icco.extdata[1] = m_ic_card_data[0x29];\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 31 (IC-CARD) Decrease Use Count:{}", page);\n+ break;\n+ }\n+ case ICCARDCommand::ReadPages:\n+ {\n+ if (!validate_data_in_out(10, 0, "SerialA (IC-CARD)"))\n+ break;\n+ const u16 page = Common::swap16(data_in + 6) & 0xFF; // 255 is max page\n+ const u16 count = Common::swap16(data_in + 8);\n+\n+ const u32 offs = page * 8;\n+ u32 cnt = count * 8;\n+\n+ // Limit read size to not overwrite the reply buffer\n+ const std::size_t reply_buffer_size = sizeof(icco.extdata) - 1;\n+ if (data_offset > reply_buffer_size)\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 31 (IC-CARD) Read Pages overflow:"\n+ " offset={} > buffer_size={}",\n+ data_offset, reply_buffer_size);\n+ data_in = data_in_end;\n+ break;\n+ }\n+ if (cnt > reply_buffer_size - data_offset)\n+ {\n+ cnt = 5 * 8;\n+ }\n+\n+ icco.extlen = cnt;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ if (offs + cnt > sizeof(icco.extdata))\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 31 (IC-CARD) Read Pages overflow:"\n+ " offset={} + count={} > buffer_size={}",\n+ offs, cnt, sizeof(icco.extdata));\n+ data_in = data_in_end;\n+ break;\n+ }\n+ memcpy(icco.extdata, m_ic_card_data + offs, cnt);\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 31 (IC-CARD) Read Pages:{} Count:{}", page, count);\n+ break;\n+ }\n+ case ICCARDCommand::WritePages:\n+ {\n+ if (!validate_data_in_out(10, 0, "SerialA (IC-CARD)"))\n+ break;\n+ const u16 pksize = length;\n+ const u16 size = Common::swap16(data_in + 2);\n+ const u16 page = Common::swap16(data_in + 6) & 0xFF; // 255 is max page\n+ const u16 count = Common::swap16(data_in + 8);\n+\n+ // We got a complete packet\n+ if (pksize - 5 == size)\n+ {\n+ if (page == 4) // Read Only Page, must return error\n+ {\n+ icco.status = 0x80;\n+ }\n+ else\n+ {\n+ if (page * 8 + count * 8 > sizeof(m_ic_card_data))\n+ {\n+ ERROR_LOG_FMT(\n+ SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (IC-CARD) Data overflow: Pages:{} Count:{}({:x})",\n+ page, count, size);\n+ }\n+ else\n+ {\n+ if (!validate_data_in_out(13 + count * 8, 0, "SerialA (IC-CARD)"))\n+ break;\n+ memcpy(m_ic_card_data + page * 8, data_in + 13, count * 8);\n+ }\n+ }\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (IC-CARD) Write Pages:{} Count:{}({:x})", page,\n+ count, size);\n+ }\n+ // VirtuaStriker 4 splits the writes over multiple packets\n+ else\n+ {\n+ if (!validate_data_in_out(2 + pksize, 0, "SerialA (IC-CARD)"))\n+ break;\n+ memcpy(m_ic_write_buffer, data_in + 2, pksize);\n+ m_ic_write_offset += pksize;\n+ m_ic_write_size = size;\n+ }\n+ break;\n+ }\n+ default:\n+ // Handle Deck Reader commands\n+ if (!validate_data_in_out(1, 0, "SerialA (DECK READER)"))\n+ break;\n+ serial_command = data_in[0];\n+ icco.command = serial_command;\n+ icco.flag = 0;\n+ switch (CDReaderCommand(serial_command))\n+ {\n+ case CDReaderCommand::ProgramVersion:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Program Version");\n+\n+ icco.extlen = (u32)strlen(s_cdr_program_version);\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ memcpy(icco.extdata, s_cdr_program_version, icco.extlen);\n+ break;\n+ case CDReaderCommand::BootVersion:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Boot Version");\n+\n+ icco.extlen = (u32)strlen(s_cdr_boot_version);\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ memcpy(icco.extdata, s_cdr_boot_version, icco.extlen);\n+ break;\n+ case CDReaderCommand::ShutterGet:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Shutter Get");\n+\n+ icco.extlen = 4;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ icco.extdata[0] = 0;\n+ icco.extdata[1] = 0;\n+ icco.extdata[2] = 0;\n+ icco.extdata[3] = 0;\n+ break;\n+ case CDReaderCommand::CameraCheck:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Camera Check");\n+\n+ icco.extlen = 6;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ icco.extdata[0] = 0x23;\n+ icco.extdata[1] = 0x28;\n+ icco.extdata[2] = 0x45;\n+ icco.extdata[3] = 0x29;\n+ icco.extdata[4] = 0x45;\n+ icco.extdata[5] = 0x29;\n+ break;\n+ case CDReaderCommand::ProgramChecksum:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Program Checksum");\n+\n+ icco.extlen = 4;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ icco.extdata[0] = 0x23;\n+ icco.extdata[1] = 0x28;\n+ icco.extdata[2] = 0x45;\n+ icco.extdata[3] = 0x29;\n+ break;\n+ case CDReaderCommand::BootChecksum:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Boot Checksum");\n+\n+ icco.extlen = 4;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ icco.extdata[0] = 0x23;\n+ icco.extdata[1] = 0x28;\n+ icco.extdata[2] = 0x45;\n+ icco.extdata[3] = 0x29;\n+ break;\n+ case CDReaderCommand::SelfTest:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (DECK READER) Self Test");\n+ icco.flag = 0x00;\n+ break;\n+ case CDReaderCommand::SensLock:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (DECK READER) Sens Lock");\n+ icco.flag = 0x01;\n+ break;\n+ case CDReaderCommand::SensCard:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (DECK READER) Sens Card");\n+ break;\n+ case CDReaderCommand::ShutterCard:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Shutter Card");\n+ break;\n+ case CDReaderCommand::ReadCard:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (DECK READER) Read Card");\n+\n+ icco.fixed = 0xAA;\n+ icco.flag = 0xAA;\n+ icco.extlen = sizeof(s_cdr_card_data);\n+ icco.length = 0x72;\n+ icco.status = Common::swap16(icco.extlen);\n+\n+ icco.pktlen += icco.extlen;\n+\n+ memcpy(icco.extdata, s_cdr_card_data, sizeof(s_cdr_card_data));\n+\n+ break;\n+ default:\n+ if (!validate_data_in_out(14, 0, "SerialA (DECK READER)"))\n+ break;\n+ WARN_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (IC-Card) {:02x} {:02x} {:02x} {:02x} {:02x} "\n+ "{:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x}",\n+ data_in[2], data_in[3], data_in[4], data_in[5], data_in[6],\n+ data_in[7], data_in[8], data_in[9], data_in[10], data_in[11],\n+ data_in[12], data_in[13]);\n+ break;\n+ }\n+ break;\n+ }\n+\n+ if (!validate_data_in_out(0, icco.pktlen + 2, "SerialA (IC-CARD)"))\n+ break;\n+ ICCardSendReply(&icco, data_out.data(), &data_offset);\n+\n+ if (!validate_data_in_out(2, 0, "SerialA (IC-CARD)"))\n+ break;\n+ data_in += length;\n+ break;\n+ }\n+ }\n+\n+ u32 command_offset = 0;\n+ while (command_offset < length)\n+ {\n+ // All commands are OR\'d with 0x80\n+ // Last byte is checksum which we don\'t care about\n+ if (!validate_data_in_out(command_offset + 4, 0, "SerialA"))\n+ break;\n+ const u32 serial_command = Common::swap32(data_in + command_offset) ^ 0x80000000;\n+\n+ if (AMMediaboard::GetGameType() == FZeroAX ||\n+ AMMediaboard::GetGameType() == FZeroAXMonster)\n+ {\n+ INFO_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x31 (MOTOR) Length:{:02x} Command:{:04x}({:02x})",\n+ length, (serial_command >> 8) & 0xFFFF, serial_command >> 24);\n+ }\n+ else\n+ {\n+ INFO_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x31 (SERIAL) Command:{:06x}",\n+ serial_command);\n+\n+ if (serial_command == 0x801000)\n+ {\n+ if (!validate_data_in_out(0, 4, "SerialA"))\n+ break;\n+ data_out[data_offset++] = 0x31;\n+ data_out[data_offset++] = 0x02;\n+ data_out[data_offset++] = 0xFF;\n+ data_out[data_offset++] = 0x01;\n+ }\n+ }\n+\n+ command_offset += sizeof(u32);\n+\n+ if (AMMediaboard::GetGameType() == FZeroAX ||\n+ AMMediaboard::GetGameType() == FZeroAXMonster)\n+ {\n+ if (command_offset + 5 >= std::size(m_motor_reply))\n+ {\n+ ERROR_LOG_FMT(\n+ SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x31 (MOTOR) overflow: offset={} >= motor_reply_size={}",\n+ command_offset + 5, std::size(m_motor_reply));\n+ data_in = data_in_end;\n+ break;\n+ }\n+\n+ // Status\n+ m_motor_reply[command_offset + 2] = 0;\n+ m_motor_reply[command_offset + 3] = 0;\n+\n+ // Error\n+ m_motor_reply[command_offset + 4] = 0;\n+\n+ switch (serial_command >> 24)\n+ {\n+ case 0:\n+ case 1: // Set Maximum?\n+ case 2:\n+ break;\n+\n+ // 0x00-0x40: left\n+ // 0x40-0x80: right\n+ case 4: // Move Steering Wheel\n+ // Left\n+ if (serial_command & 0x010000)\n+ {\n+ m_motor_force_y = -((s16)serial_command & 0xFF00);\n+ }\n+ else // Right\n+ {\n+ m_motor_force_y = (serial_command - 0x4000) & 0xFF00;\n+ }\n+\n+ m_motor_force_y *= 2;\n+\n+ // FFB\n+ if (m_motor_init == 2)\n+ {\n+ if (serial_interface.GetDeviceType(1) == SerialInterface::SIDEVICE_GC_STEERING)\n+ {\n+ const GCPadStatus pad_status = Pad::GetStatus(1);\n+ if (pad_status.isConnected)\n+ {\n+ const ControlState mapped_strength = (double)(m_motor_force_y >> 8) / 127.f;\n+\n+ Pad::Rumble(1, mapped_strength);\n+ INFO_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x31 (MOTOR) mapped_strength:{}",\n+ mapped_strength);\n+ }\n+ }\n+ }\n+ break;\n+ case 6: // nice\n+ case 9:\n+ default:\n+ break;\n+ // Switch back to normal controls\n+ case 7:\n+ m_motor_init = 2;\n+ break;\n+ // Reset\n+ case 0x7F:\n+ m_motor_init = 1;\n+ memset(m_motor_reply, 0, sizeof(m_motor_reply));\n+ break;\n+ }\n+\n+ // Checksum\n+ m_motor_reply[command_offset + 5] = m_motor_reply[command_offset + 2] ^\n+ m_motor_reply[command_offset + 3] ^\n+ m_motor_reply[command_offset + 4];\n+ }\n+ }\n+\n+ if (length == 0)\n+ {\n+ if (!validate_data_in_out(length, 2, "SerialA"))\n+ break;\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x00;\n+ }\n+ else\n+ {\n+ if (m_motor_init)\n+ {\n+ // Motor\n+ m_motor_reply[0] = gcam_command;\n+ m_motor_reply[1] = length; // Same out as in size\n+\n+ const u32 reply_size = m_motor_reply[1] + 2;\n+ if (!validate_data_in_out(length, reply_size, "SerialA"))\n+ break;\n+\n+ if (reply_size > sizeof(m_motor_reply))\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command SerialA, reply_size={} too big for m_motor_reply",\n+ reply_size);\n+ data_in = data_in_end;\n+ break;\n+ }\n+ memcpy(data_out.data() + data_offset, m_motor_reply, reply_size);\n+ data_offset += reply_size;\n+ }\n+ }\n+\n+ if (!validate_data_in_out(length, 0, "SerialA"))\n+ {\n+ break;\n+ }\n+ data_in += length;\n+ break;\n+ }\n+ case GCAMCommand::SerialB:\n+ {\n+ DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 32 (CARD-Interface)");\n+ if (!validate_data_in_out(1, 0, "SerialB"))\n+ break;\n+ const u32 length = *data_in++;\n+ if (!validate_data_in_out(length, 0, "SerialB"))\n+ break;\n+ if (length)\n+ {\n+ // Send Card Reply\n+ if (length == 1 && data_in[0] == 0x05)\n+ {\n+ if (m_card_read_length)\n+ {\n+ if (!validate_data_in_out(0, 1, "SerialB"))\n+ break;\n+ data_out[data_offset++] = gcam_command;\n+ u32 read_length = m_card_read_length - m_card_read;\n+\n+ if (AMMediaboard::GetGameType() == FZeroAX)\n+ {\n+ read_length = std::min<u32>(read_length, 0x2F);\n+ }\n+\n+ if (!validate_data_in_out(0, 1, "SerialB"))\n+ break;\n+ data_out[data_offset++] = read_length; // 0x2F (max size per packet)\n+\n+ if (!validate_data_in_out(0, read_length, "SerialB"))\n+ break;\n+ if (u64{m_card_read} + read_length > sizeof(m_card_read_packet))\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command SerialB, m_card_read_packet overflow:\\n"\n+ " - m_card_read_packet = {}\\n"\n+ " - m_card_read = {}\\n"\n+ " - read_length = {}",\n+ fmt::ptr(m_card_read_packet), m_card_read, read_length);\n+ data_in = data_in_end;\n+ break;\n+ }\n+ memcpy(data_out.data() + data_offset, m_card_read_packet + m_card_read,\n+ read_length);\n+\n+ data_offset += read_length;\n+ m_card_read += read_length;\n+\n+ if (m_card_read >= m_card_read_length)\n+ m_card_read_length = 0;\n+\n+ data_in += length;\n+ break;\n+ }\n+\n+ if (!validate_data_in_out(0, 5, "SerialB"))\n+ break;\n+\n+ data_out[data_offset++] = gcam_command;\n+ const u32 command_length_offset = data_offset;\n+ data_out[data_offset++] = 0x00; // len\n+\n+ data_out[data_offset++] = 0x02;\n+ const u32 checksum_start = data_offset;\n+\n+ data_out[data_offset++] = 0x00; // 0x00 len\n+\n+ data_out[data_offset++] = m_card_command; // 0x01 command\n+\n+ switch (CARDCommand(m_card_command))\n+ {\n+ case CARDCommand::Init:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x00; // 0x02\n+ data_out[data_offset++] = 0x30; // 0x03\n+ break;\n+ case CARDCommand::GetState:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x20 | m_card_bit; // 0x02\n+\n+ // bit 0: Please take your card\n+ // bit 1: Endless waiting causes UNK_E to be called\n+ data_out[data_offset++] = 0x00; // 0x03\n+ break;\n+ case CARDCommand::Read:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x02; // 0x02\n+ data_out[data_offset++] = 0x53; // 0x03\n+ break;\n+ case CARDCommand::IsPresent:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x22; // 0x02\n+ data_out[data_offset++] = 0x30; // 0x03\n+ break;\n+ case CARDCommand::Write:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x02; // 0x02\n+ data_out[data_offset++] = 0x00; // 0x03\n+ break;\n+ case CARDCommand::SetPrintParam:\n+ case CARDCommand::RegisterFont:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x00; // 0x02\n+ data_out[data_offset++] = 0x00; // 0x03\n+ break;\n+ case CARDCommand::WriteInfo:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x02; // 0x02\n+ data_out[data_offset++] = 0x00; // 0x03\n+ break;\n+ case CARDCommand::Erase:\n+ // TODO: CARDCommand::Erase is not handled.\n+ break;\n+ case CARDCommand::Eject:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ if (AMMediaboard::GetGameType() == FZeroAX)\n+ {\n+ data_out[data_offset++] = 0x01; // 0x02\n+ }\n+ else\n+ {\n+ data_out[data_offset++] = 0x31; // 0x02\n+ }\n+ data_out[data_offset++] = 0x30; // 0x03\n+ break;\n+ case CARDCommand::Clean:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x02; // 0x02\n+ data_out[data_offset++] = 0x00; // 0x03\n+ break;\n+ case CARDCommand::Load:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x02; // 0x02\n+ data_out[data_offset++] = 0x30; // 0x03\n+ break;\n+ case CARDCommand::SetShutter:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x00; // 0x02\n+ data_out[data_offset++] = 0x00; // 0x03\n+ break;\n+ }\n+\n+ if (!validate_data_in_out(0, 3, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x30; // 0x04\n+ data_out[data_offset++] = 0x00; // 0x05\n+\n+ data_out[data_offset++] = 0x03; // 0x06\n+\n+ data_out[checksum_start] = data_offset - checksum_start; // 0x00 len\n+\n+ if (!validate_data_in_out(0, 1, "SerialB"))\n+ break;\n+ data_out[data_offset] = 0; // 0x07\n+ for (u32 i = 0; i < data_out[checksum_start]; ++i)\n+ data_out[data_offset] ^= data_out[checksum_start + i];\n+\n+ if (!validate_data_in_out(0, 1, "SerialB"))\n+ break;\n+ data_offset++;\n+\n+ data_out[command_length_offset] = data_out[checksum_start] + 2;\n+ }\n+ else\n+ {\n+ if (!validate_data_in_out(length, 0, "SerialB"))\n+ break;\n+ if (u64{m_card_offset} + length > std::size(m_card_buffer))\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command SerialB, m_card_buffer overflow:\\n"\n+ " - m_card_buffer = {}\\n"\n+ " - m_card_offset = {}\\n"\n+ " - length = {}",\n+ fmt::ptr(m_card_buffer), m_card_offset, length);\n+ data_in = data_in_end;\n+ break;\n+ }\n+ for (u32 i = 0; i < length; ++i)\n+ m_card_buffer[m_card_offset + i] = data_in[i];\n+\n+ m_card_offset += length;\n+\n+ // Check if we got a complete command\n+ if (m_card_buffer[0] == 0x02)\n+ {\n+ if (m_card_buffer[1] == m_card_offset - 2)\n+ {\n+ if (m_card_buffer[m_card_offset - 2] == 0x03)\n+ {\n+ m_card_command = m_card_buffer[2];\n+\n+ switch (CARDCommand(m_card_command))\n+ {\n+ case CARDCommand::Init:\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD Init");\n+\n+ m_card_write_length = 0;\n+ m_card_bit = 0;\n+ m_card_memory_size = 0;\n+ m_card_state_call_count = 0;\n+ break;\n+ case CARDCommand::GetState:\n+ {\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD GetState({:02X})",\n+ m_card_bit);\n+\n+ if (m_card_memory_size == 0)\n+ {\n+ const std::string card_filename(\n+ fmt::format("{}tricard_{}.bin", File::GetUserPath(D_TRIUSER_IDX),\n+ SConfig::GetInstance().GetGameID()));\n+\n+ if (File::Exists(card_filename))\n+ {\n+ File::IOFile card(card_filename, "rb+");\n+ m_card_memory_size = static_cast<u32>(card.GetSize());\n+ if (m_card_memory_size > sizeof(m_card_memory))\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command CARD GetState overflow:\\n"\n+ " - file name = {}\\n"\n+ " - file size = {}\\n"\n+ " - card size = {}",\n+ card_filename, m_card_memory_size, sizeof(m_card_memory));\n+ data_in = data_in_end;\n+ break;\n+ }\n+ card.ReadBytes(m_card_memory, m_card_memory_size);\n+ card.Close();\n+\n+ m_card_is_inserted = true;\n+ }\n+ }\n+\n+ if (AMMediaboard::GetGameType() == FZeroAX && m_card_memory_size)\n+ {\n+ m_card_state_call_count++;\n+ if (m_card_state_call_count > 10)\n+ {\n+ if (m_card_bit & 2)\n+ m_card_bit &= ~2u;\n+ else\n+ m_card_bit |= 2;\n+\n+ m_card_state_call_count = 0;\n+ }\n+ }\n+\n+ if (m_card_clean == 1)\n+ {\n+ m_card_clean = 2;\n+ }\n+ else if (m_card_clean == 2)\n+ {\n+ const std::string card_filename(\n+ fmt::format("{}tricard_{}.bin", File::GetUserPath(D_TRIUSER_IDX),\n+ SConfig::GetInstance().GetGameID()));\n+\n+ if (File::Exists(card_filename))\n+ {\n+ m_card_memory_size = static_cast<u32>(File::GetSize(card_filename));\n+ if (m_card_memory_size)\n+ {\n+ if (AMMediaboard::GetGameType() == FZeroAX)\n+ {\n+ m_card_bit = 2;\n+ }\n+ else\n+ {\n+ m_card_bit = 1;\n+ }\n+ }\n+ }\n+ m_card_clean = 0;\n+ }\n+ break;\n+ }\n+ case CARDCommand::IsPresent:\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD IsPresent");\n+ break;\n+ case CARDCommand::RegisterFont:\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD RegisterFont");\n+ break;\n+ case CARDCommand::Load:\n+ {\n+ const u8 mode = m_card_buffer[6];\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD Load({:02X})",\n+ mode);\n+ break;\n+ }\n+ case CARDCommand::Clean:\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD Clean");\n+ m_card_clean = 1;\n+ break;\n+ case CARDCommand::Read:\n+ {\n+ const u8 mode = m_card_buffer[6];\n+ const u8 bitmode = m_card_buffer[7];\n+ const u8 track = m_card_buffer[8];\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command CARD Read({:02X},{:02X},{:02X})", mode,\n+ bitmode, track);\n+\n+ // Prepare read packet\n+ memset(m_card_read_packet, 0, 0xDB);\n+\n+ const std::string card_filename(\n+ fmt::format("{}tricard_{}.bin", File::GetUserPath(D_TRIUSER_IDX),\n+ SConfig::GetInstance().GetGameID()));\n+\n+ if (File::Exists(card_filename))\n+ {\n+ File::IOFile card(card_filename, "rb+");\n+ if (m_card_memory_size == 0)\n+ {\n+ m_card_memory_size = static_cast<u32>(card.GetSize());\n+ }\n+\n+ if (m_card_memory_size > sizeof(m_card_memory))\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command CARD Read overflow:\\n"\n+ " - file name = {}\\n"\n+ " - file size = {}\\n"\n+ " - card size = {}",\n+ card_filename, m_card_memory_size, sizeof(m_card_memory));\n+ data_in = data_in_end;\n+ break;\n+ }\n+ card.ReadBytes(m_card_memory, m_card_memory_size);\n+ card.Close();\n+\n+ m_card_is_inserted = true;\n+ }\n+ else if (m_card_memory_size > sizeof(m_card_memory))\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command CARD Read overflow:\\n"\n+ " - requested size = {}\\n"\n+ " - card size = {}",\n+ m_card_memory_size, sizeof(m_card_memory));\n+ data_in = data_in_end;\n+ break;\n+ }\n+\n+ m_card_read_packet[0] = 0x02; // SUB CMD\n+ m_card_read_packet[1] = 0x00; // SUB CMDLen\n+\n+ m_card_read_packet[2] = 0x33; // CARD CMD\n+\n+ if (m_card_is_inserted) // CARD Status\n+ {\n+ m_card_read_packet[3] = 0x31;\n+ }\n+ else\n+ {\n+ m_card_read_packet[3] = 0x30;\n+ }\n+\n+ m_card_read_packet[4] = 0x30;\n+ m_card_read_packet[5] = 0x30;\n+\n+ u32 packet_offset = 6;\n+ // Data reply\n+ static_assert(sizeof(m_card_read_packet) >= sizeof(m_card_memory) + 6);\n+ memcpy(m_card_read_packet + packet_offset, m_card_memory, m_card_memory_size);\n+ packet_offset += m_card_memory_size;\n+\n+ static_assert(sizeof(m_card_read_packet) >= sizeof(m_card_memory) + 7);\n+ m_card_read_packet[packet_offset++] = 0x03;\n+\n+ m_card_read_packet[1] = packet_offset - 1; // SUB CMDLen\n+\n+ static_assert(sizeof(m_card_read_packet) >= sizeof(m_card_memory) + 8);\n+ for (u32 i = 0; i < packet_offset - 1; ++i)\n+ m_card_read_packet[packet_offset] ^= m_card_read_packet[1 + i];\n+\n+ static_assert(sizeof(m_card_read_packet) >= sizeof(m_card_memory) + 9);\n+ packet_offset++;\n+\n+ m_card_read_length = packet_offset;\n+ m_card_read = 0;\n+ break;\n+ }\n+ case CARDCommand::Write:\n+ {\n+ const u8 mode = m_card_buffer[6];\n+ const u8 bitmode = m_card_buffer[7];\n+ const u8 track = m_card_buffer[8];\n+\n+ m_card_memory_size = m_card_buffer[1] - 9;\n+ if (m_card_memory_size > sizeof(m_card_memory))\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command CARD Write overflow:\\n"\n+ " - write size = {}\\n"\n+ " - card size = {}",\n+ m_card_memory_size, sizeof(m_card_memory));\n+ data_in = data_in_end;\n+ break;\n+ }\n+\n+ static_assert(sizeof(m_card_buffer) >= sizeof(m_card_memory) + 9);\n+ memcpy(m_card_memory, m_card_buffer + 9, m_card_memory_size);\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command CARD Write: {:02X} {:02X} {:02X} {}", mode,\n+ bitmode, track, m_card_memory_size);\n+\n+ const std::string card_filename(File::GetUserPath(D_TRIUSER_IDX) +\n+ "tricard_" +\n+ SConfig::GetInstance().GetGameID() + ".bin");\n+\n+ File::IOFile card(card_filename, "wb+");\n+ card.WriteBytes(m_card_memory, m_card_memory_size);\n+ card.Close();\n+\n+ m_card_bit = 2;\n+\n+ m_card_state_call_count = 0;\n+ break;\n+ }\n+ case CARDCommand::SetPrintParam:\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD SetPrintParam");\n+ break;\n+ case CARDCommand::WriteInfo:\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD WriteInfo");\n+ break;\n+ case CARDCommand::Erase:\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD Erase");\n+ break;\n+ case CARDCommand::Eject:\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD Eject");\n+ if (AMMediaboard::GetGameType() != FZeroAX)\n+ {\n+ m_card_bit = 0;\n+ }\n+ break;\n+ case CARDCommand::SetShutter:\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD SetShutter");\n+ if (AMMediaboard::GetGameType() != FZeroAX)\n+ {\n+ m_card_bit = 0;\n+ }\n+ // Close\n+ if (m_card_buffer[6] == 0x30)\n+ {\n+ m_card_shutter = false;\n+ }\n+ // Open\n+ else if (m_card_buffer[6] == 0x31)\n+ {\n+ m_card_shutter = true;\n+ }\n+ break;\n+ default:\n+ ERROR_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: CARD:Unhandled command!");\n+ ERROR_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: CARD:[{:08X}]", m_card_command);\n+ // hexdump( m_card_buffer, m_card_offset );\n+ break;\n+ }\n+ m_card_offset = 0;\n+ }\n+ }\n+ }\n+\n+ if (!validate_data_in_out(0, 3, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x32;\n+ data_out[data_offset++] = 0x01; // len\n+ data_out[data_offset++] = 0x06; // OK\n+ }\n+ }\n+ else\n+ {\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x00; // len\n+ }\n+ data_in += length;\n+ break;\n+ }\n+ case GCAMCommand::JVSIOA:\n+ case GCAMCommand::JVSIOB:\n+ {\n+ if (!validate_data_in_out(4, 0, "JVSIO"))\n+ break;\n+\n+ JVSIOMessage message;\n+\n+ static int delay = 0;\n+\n+ const u8* const frame = &data_in[0];\n+ const u8 nr_bytes = frame[3]; // Byte after E0 xx\n+ u32 frame_len = nr_bytes + 3; // Header(2) + length byte + payload + checksum\n+\n+ u8 jvs_buf[0x80];\n+\n+ frame_len = std::min<u32>(frame_len, sizeof(jvs_buf));\n+\n+ if (!validate_data_in_out(frame_len, 0, "JVSIO"))\n+ break;\n+ DEBUG_LOG_FMT(SERIALINTERFACE_JVSIO, "GC-AM: Command {:02x} (JVS IO), hexdump:\\n{}",\n+ gcam_command, HexDump(data_in, frame_len));\n+\n+ memcpy(jvs_buf, frame, frame_len);\n+\n+ // Extract node and payload pointers\n+ u8 node = jvs_buf[2];\n+ u8* jvs_io = jvs_buf + 4; // First payload byte\n+ u8* const jvs_end = jvs_buf + frame_len; // One byte before checksum\n+ u8* const jvs_begin = jvs_io;\n+\n+ message.Start(0);\n+ message.AddData(1);\n+\n+ // Helper to check that iterating over jvs_io n times is safe,\n+ // i.e. *jvs_io++ at most lead to jvs_end\n+ auto validate_jvs_io = [&](u32 n, std::string_view command) -> bool {\n+ if (jvs_io + n > jvs_end)\n+ ERROR_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: overflow in {}", command);\n+ else\n+ return true;\n+ ERROR_LOG_FMT(SERIALINTERFACE_JVSIO,\n+ "Overflow details:\\n"\n+ " - jvs_io(begin={}, current={}, end={}, n={})\\n"\n+ " - delay={}, node={}\\n"\n+ " - frame(begin={}, len={})",\n+ fmt::ptr(jvs_begin), fmt::ptr(jvs_io), fmt::ptr(jvs_end), n, delay, node,\n+ fmt::ptr(frame), frame_len);\n+ jvs_io = jvs_end;\n+ return false;\n+ };\n+\n+ // Now iterate over the payload\n+ while (jvs_io < jvs_end)\n+ {\n+ const u8 jvsio_command = *jvs_io++;\n+ DEBUG_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO:node={}, command={:02x}", node,\n+ jvsio_command);\n+\n+ switch (JVSIOCommand(jvsio_command))\n+ {\n+ case JVSIOCommand::IOID:\n+ message.AddData(StatusOkay);\n+ switch (AMMediaboard::GetGameType())\n+ {\n+ case FZeroAX:\n+ // Specific version that enables DX mode on AX machines, all this does is enable the\n+ // motion of a chair\n+ message.AddData("SEGA ENTERPRISES,LTD.;837-13844-01 I/O CNTL BD2 ;");\n+ break;\n+ case FZeroAXMonster:\n+ case MarioKartGP:\n+ case MarioKartGP2:\n+ default:\n+ message.AddData("namco ltd.;FCA-1;Ver1.01;JPN,Multipurpose + Rotary Encoder");\n+ break;\n+ case VirtuaStriker3:\n+ case VirtuaStriker4:\n+ case VirtuaStriker4_2006:\n+ message.AddData("SEGA ENTERPRISES,LTD.;I/O BD JVS;837-13551;Ver1.00");\n+ break;\n+ }\n+ NOTICE_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: Command 0x10, BoardID");\n+ message.AddData((u32)0);\n+ break;\n+ case JVSIOCommand::CommandRevision:\n+ message.AddData(StatusOkay);\n+ message.AddData(0x11);\n+ NOTICE_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: Command 0x11, CommandRevision");\n+ break;\n+ case JVSIOCommand::JVRevision:\n+ message.AddData(StatusOkay);\n+ message.AddData(0x20);\n+ NOTICE_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: Command 0x12, JVRevision");\n+ break;\n+ case JVSIOCommand::CommunicationVersion:\n+ message.AddData(StatusOkay);\n+ message.AddData(0x10);\n+ NOTICE_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: Command 0x13, CommunicationVersion");\n+ break;\n+\n+ // Slave features:\n+ //\n+ // Inputs:\n+ // 0x01: Switch input: players, buttons\n+ // 0x02: Coin input: slots\n+ // 0x03: Analog input: channels, bits\n+ // 0x04: Rotary input: channels\n+ // 0x05: Keycode input: 0,0,0 ?\n+ // 0x06: Screen position input: X bits, Y bits, channels\n+ //\n+ // Outputs:\n+ // 0x10: Card system: slots\n+ // 0x11: Medal hopper: channels\n+ // 0x12: GPO-out: slots\n+ // 0x13: Analog output: channels\n+ // 0x14: Character output: width, height, type\n+ // 0x15: Backup\n+ case JVSIOCommand::CheckFunctionality:\n+ message.AddData(StatusOkay);\n+ switch (AMMediaboard::GetGameType())\n+ {\n+ case FZeroAX:\n+ case FZeroAXMonster:\n+ // 2 Player (12bit) (p2=paddles), 1 Coin slot, 6 Analog-in\n+ // message.AddData((void *)"\\x01\\x02\\x0C\\x00", 4);\n+ // message.AddData((void *)"\\x02\\x01\\x00\\x00", 4);\n+ // message.AddData((void *)"\\x03\\x06\\x00\\x00", 4);\n+ // message.AddData((void *)"\\x00\\x00\\x00\\x00", 4);\n+ //\n+ // DX Version: 2 Player (22bit) (p2=paddles), 2 Coin slot, 8 Analog-in,\n+ // 22 Driver-out\n+ message.AddData("\\x01\\x02\\x12\\x00", 4);\n+ message.AddData("\\x02\\x02\\x00\\x00", 4);\n+ message.AddData("\\x03\\x08\\x0A\\x00", 4);\n+ message.AddData("\\x12\\x16\\x00\\x00", 4);\n+ message.AddData("\\x00\\x00\\x00\\x00", 4);\n+ break;\n+ case VirtuaStriker3:\n+ // 2 Player (13bit), 2 Coin slot, 4 Analog-in, 1 CARD, 8 Driver-out\n+ message.AddData("\\x01\\x02\\x0D\\x00", 4);\n+ message.AddData("\\x02\\x02\\x00\\x00", 4);\n+ message.AddData("\\x10\\x01\\x00\\x00", 4);\n+ message.AddData("\\x12\\x08\\x00\\x00", 4);\n+ message.AddData("\\x00\\x00\\x00\\x00", 4);\n+ break;\n+ case GekitouProYakyuu:\n+ // 2 Player (13bit), 2 Coin slot, 4 Analog-in, 1 CARD, 8 Driver-out\n+ message.AddData("\\x01\\x02\\x0D\\x00", 4);\n+ message.AddData("\\x02\\x02\\x00\\x00", 4);\n+ message.AddData("\\x03\\x04\\x00\\x00", 4);\n+ message.AddData("\\x10\\x01\\x00\\x00", 4);\n+ message.AddData("\\x12\\x08\\x00\\x00", 4);\n+ message.AddData("\\x00\\x00\\x00\\x00", 4);\n+ break;\n+ case VirtuaStriker4:\n+ case VirtuaStriker4_2006:\n+ // 2 Player (13bit), 1 Coin slot, 4 Analog-in, 1 CARD\n+ message.AddData("\\x01\\x02\\x0D\\x00", 4);\n+ message.AddData("\\x02\\x01\\x00\\x00", 4);\n+ message.AddData("\\x03\\x04\\x00\\x00", 4);\n+ message.AddData("\\x10\\x01\\x00\\x00", 4);\n+ message.AddData("\\x00\\x00\\x00\\x00", 4);\n+ break;\n+ case KeyOfAvalon:\n+ // 1 Player (15bit), 1 Coin slot, 3 Analog-in, Touch, 1 CARD, 1 Driver-out\n+ // (Unconfirmed)\n+ message.AddData("\\x01\\x01\\x0F\\x00", 4);\n+ message.AddData("\\x02\\x01\\x00\\x00", 4);\n+ message.AddData("\\x03\\x03\\x00\\x00", 4);\n+ message.AddData("\\x06\\x10\\x10\\x01", 4);\n+ message.AddData("\\x10\\x01\\x00\\x00", 4);\n+ message.AddData("\\x12\\x01\\x00\\x00", 4);\n+ message.AddData("\\x00\\x00\\x00\\x00", 4);\n+ break;\n+ case MarioKartGP:\n+ case MarioKartGP2:\n+ default:\n+ // 1 Player (15bit), 1 Coin slot, 3 Analog-in, 1 CARD, 1 Driver-out\n+ message.AddData("\\x01\\x01\\x0F\\x00", 4);\n+ message.AddData("\\x02\\x01\\x00\\x00", 4);\n+ message.AddData("\\x03\\x03\\x00\\x00", 4);\n+ message.AddData("\\x10\\x01\\x00\\x00", 4);\n+ message.AddData("\\x12\\x01\\x00\\x00", 4);\n+ message.AddData("\\x00\\x00\\x00\\x00", 4);\n+ break;\n+ }\n+ NOTICE_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: Command 0x14, CheckFunctionality");\n+ break;\n+ case JVSIOCommand::MainID:\n+ {\n+ const u8* const main_id = jvs_io;\n+ while (jvs_io < jvs_end && *jvs_io++)\n+ {\n+ }\n+ if (main_id < jvs_io)\n+ {\n+ DEBUG_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: Command MainId:\\n{}",\n+ HexDump(main_id, jvs_io - main_id));\n+ }\n+ message.AddData(StatusOkay);\n+ break;\n+ }\n+ case JVSIOCommand::SwitchesInput:\n+ {\n+ if (!validate_jvs_io(2, "SwitchesInput"))\n+ break;\n+ const u32 player_count = *jvs_io++;\n+ const u32 player_byte_count = *jvs_io++;\n+\n+ DEBUG_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: Command 0x20, SwitchInputs: {} {}",\n+ player_count, player_byte_count);\n+\n+ message.AddData(StatusOkay);\n+\n+ GCPadStatus pad_status = Pad::GetStatus(0);\n+\n+ // Test button\n+ if (pad_status.switches & SWITCH_TEST)\n+ {\n+ // Trying to access the test menu without SegaBoot present will cause a crash\n+ if (AMMediaboard::GetTestMenu())\n+ {\n+ message.AddData(0x80);\n+ }\n+ else\n+ {\n+ PanicAlertFmt("Test menu is disabled due missing SegaBoot");\n+ }\n+ }\n+ else\n+ {\n+ message.AddData((u32)0x00);\n+ }\n+\n+ for (u32 i = 0; i < player_count; ++i)\n+ {\n+ u8 player_data[3]{};\n+\n+ // Service button\n+ if (pad_status.switches & SWITCH_SERVICE)\n+ player_data[0] |= 0x40;\n+\n+ switch (AMMediaboard::GetGameType())\n+ {\n+ // Controller configuration for F-Zero AX (DX)\n+ case FZeroAX:\n+ if (i == 0)\n+ {\n+ if (m_fzdx_seatbelt)\n+ {\n+ player_data[0] |= 0x01;\n+ }\n+\n+ // Start\n+ if (pad_status.button & PAD_BUTTON_START)\n+ player_data[0] |= 0x80;\n+ // Boost\n+ if (pad_status.button & PAD_BUTTON_A)\n+ player_data[0] |= 0x02;\n+ // View Change 1\n+ if (pad_status.button & PAD_BUTTON_RIGHT)\n+ player_data[0] |= 0x20;\n+ // View Change 2\n+ if (pad_status.button & PAD_BUTTON_LEFT)\n+ player_data[0] |= 0x10;\n+ // View Change 3\n+ if (pad_status.button & PAD_BUTTON_UP)\n+ player_data[0] |= 0x08;\n+ // View Change 4\n+ if (pad_status.button & PAD_BUTTON_DOWN)\n+ player_data[0] |= 0x04;\n+ player_data[1] = m_rx_reply & 0xF0;\n+ }\n+ else if (i == 1)\n+ {\n+ // Paddle left\n+ if (pad_status.button & PAD_BUTTON_X)\n+ player_data[0] |= 0x20;\n+ // Paddle right\n+ if (pad_status.button & PAD_BUTTON_Y)\n+ player_data[0] |= 0x10;\n+\n+ if (m_fzdx_motion_stop)\n+ {\n+ player_data[0] |= 2;\n+ }\n+ if (m_fzdx_sensor_right)\n+ {\n+ player_data[0] |= 4;\n+ }\n+ if (m_fzdx_sensor_left)\n+ {\n+ player_data[0] |= 8;\n+ }\n+\n+ player_data[1] = m_rx_reply << 4;\n+ }\n+ break;\n+ // Controller configuration for F-Zero AX MonsterRide\n+ case FZeroAXMonster:\n+ if (i == 0)\n+ {\n+ if (m_fzcc_sensor)\n+ {\n+ player_data[0] |= 0x01;\n+ }\n+\n+ // Start\n+ if (pad_status.button & PAD_BUTTON_START)\n+ player_data[0] |= 0x80;\n+ // Boost\n+ if (pad_status.button & PAD_BUTTON_A)\n+ player_data[0] |= 0x02;\n+ // View Change 1\n+ if (pad_status.button & PAD_BUTTON_RIGHT)\n+ player_data[0] |= 0x20;\n+ // View Change 2\n+ if (pad_status.button & PAD_BUTTON_LEFT)\n+ player_data[0] |= 0x10;\n+ // View Change 3\n+ if (pad_status.button & PAD_BUTTON_UP)\n+ player_data[0] |= 0x08;\n+ // View Change 4\n+ if (pad_status.button & PAD_BUTTON_DOWN)\n+ player_data[0] |= 0x04;\n+\n+ player_data[1] = m_rx_reply & 0xF0;\n+ }\n+ else if (i == 1)\n+ {\n+ // Paddle left\n+ if (pad_status.button & PAD_BUTTON_X)\n+ player_data[0] |= 0x20;\n+ // Paddle right\n+ if (pad_status.button & PAD_BUTTON_Y)\n+ player_data[0] |= 0x10;\n+\n+ if (m_fzcc_seatbelt)\n+ {\n+ player_data[0] |= 2;\n+ }\n+ if (m_fzcc_service)\n+ {\n+ player_data[0] |= 4;\n+ }\n+ if (m_fzcc_emergency)\n+ {\n+ player_data[0] |= 8;\n+ }\n+ }\n+ break;\n+ // Controller configuration for Virtua Striker 3 games\n+ case VirtuaStriker3:\n+ pad_status = Pad::GetStatus(i);\n+ // Start\n+ if (pad_status.button & PAD_BUTTON_START)\n+ player_data[0] |= 0x80;\n+ // Shoot\n+ if (pad_status.button & PAD_BUTTON_B)\n+ player_data[0] |= 0x01;\n+ // Short Pass\n+ if (pad_status.button & PAD_BUTTON_A)\n+ player_data[1] |= 0x80;\n+ // Long Pass\n+ if (pad_status.button & PAD_BUTTON_X)\n+ player_data[0] |= 0x02;\n+ // Left\n+ if (pad_status.button & PAD_BUTTON_LEFT)\n+ player_data[0] |= 0x08;\n+ // Up\n+ if (pad_status.button & PAD_BUTTON_UP)\n+ player_data[0] |= 0x20;\n+ // Right\n+ if (pad_status.button & PAD_BUTTON_RIGHT)\n+ player_data[0] |= 0x04;\n+ // Down\n+ if (pad_status.button & PAD_BUTTON_DOWN)\n+ player_data[0] |= 0x10;\n+ break;\n+ // Controller configuration for Virtua Striker 4 games\n+ case VirtuaStriker4:\n+ case VirtuaStriker4_2006:\n+ {\n+ pad_status = Pad::GetStatus(i);\n+ // Start\n+ if (pad_status.button & PAD_BUTTON_START)\n+ player_data[0] |= 0x80;\n+ // Long Pass\n+ if (pad_status.button & PAD_BUTTON_X)\n+ player_data[0] |= 0x01;\n+ // Short Pass\n+ if (pad_status.button & PAD_BUTTON_A)\n+ player_data[0] |= 0x02;\n+ // Shoot\n+ if (pad_status.button & PAD_BUTTON_B)\n+ player_data[1] |= 0x80;\n+ // Dash\n+ if (pad_status.button & PAD_BUTTON_Y)\n+ player_data[1] |= 0x40;\n+ // Tactics (U)\n+ if (pad_status.button & PAD_BUTTON_LEFT)\n+ player_data[0] |= 0x20;\n+ // Tactics (M)\n+ if (pad_status.button & PAD_BUTTON_UP)\n+ player_data[0] |= 0x08;\n+ // Tactics (D)\n+ if (pad_status.button & PAD_BUTTON_RIGHT)\n+ player_data[0] |= 0x04;\n+\n+ if (i == 0)\n+ {\n+ player_data[0] |= 0x10; // IC-Card Switch ON\n+\n+ // IC-Card Lock\n+ if (pad_status.button & PAD_BUTTON_DOWN)\n+ player_data[1] |= 0x20;\n+ }\n+ }\n+ break;\n+ // Controller configuration for Gekitou Pro Yakyuu\n+ case GekitouProYakyuu:\n+ pad_status = Pad::GetStatus(i);\n+ // Start\n+ if (pad_status.button & PAD_BUTTON_START)\n+ player_data[0] |= 0x80;\n+ // A\n+ if (pad_status.button & PAD_BUTTON_B)\n+ player_data[0] |= 0x01;\n+ // B\n+ if (pad_status.button & PAD_BUTTON_A)\n+ player_data[0] |= 0x02;\n+ // Gekitou\n+ if (pad_status.button & PAD_TRIGGER_L)\n+ player_data[1] |= 0x80;\n+ // Left\n+ if (pad_status.button & PAD_BUTTON_LEFT)\n+ player_data[0] |= 0x08;\n+ // Up\n+ if (pad_status.button & PAD_BUTTON_UP)\n+ player_data[0] |= 0x20;\n+ // Right\n+ if (pad_status.button & PAD_BUTTON_RIGHT)\n+ player_data[0] |= 0x04;\n+ // Down\n+ if (pad_status.button & PAD_BUTTON_DOWN)\n+ player_data[0] |= 0x10;\n+ break;\n+ // Controller configuration for Mario Kart and other games\n+ default:\n+ case MarioKartGP:\n+ case MarioKartGP2:\n+ {\n+ // Start\n+ if (pad_status.button & PAD_BUTTON_START)\n+ player_data[0] |= 0x80;\n+ // Item button\n+ if (pad_status.button & PAD_BUTTON_A)\n+ player_data[1] |= 0x20;\n+ // VS-Cancel button\n+ if (pad_status.button & PAD_BUTTON_B)\n+ player_data[1] |= 0x02;\n+ }\n+ break;\n+ case KeyOfAvalon:\n+ {\n+ // Debug On\n+ if (pad_status.button & PAD_BUTTON_START)\n+ player_data[0] |= 0x80;\n+ // Switch 1\n+ if (pad_status.button & PAD_BUTTON_A)\n+ player_data[0] |= 0x04;\n+ // Switch 2\n+ if (pad_status.button & PAD_BUTTON_B)\n+ player_data[0] |= 0x08;\n+ // Toggle inserted card\n+ if (pad_status.button & PAD_TRIGGER_L)\n+ {\n+ m_ic_card_status ^= ICCARDStatus::NoCard;\n+ }\n+ }\n+ break;\n+ }\n+\n+ for (u32 j = 0; j < player_byte_count; ++j)\n+ message.AddData(player_data[j]);\n+ }\n+ break;\n+ }\n+ case JVSIOCommand::CoinInput:\n+ {\n+ if (!validate_jvs_io(1, "CoinInput"))\n+ break;\n+ const u32 slots = *jvs_io++;\n+ message.AddData(StatusOkay);\n+ for (u32 i = 0; i < slots; i++)\n+ {\n+ GCPadStatus pad_status = Pad::GetStatus(i);\n+ if ((pad_status.switches & SWITCH_COIN) && !m_coin_pressed[i])\n+ {\n+ m_coin[i]++;', 'path': 'Source/Core/Core/HW/SI/SI_DeviceAMBaseboard.cpp', 'position': 2142, 'original_position': 2142, 'commit_id': '5bf8fe93286ebe4f08fb469a44f259a8397c89e0', 'user': {'login': 'JosJuice', 'id': 6716818, 'node_id': 'MDQ6VXNlcjY3MTY4MTg=', 'avatar_url': 'https://avatars.githubusercontent.com/u/6716818?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/JosJuice', 'html_url': 'https://github.com/JosJuice', 'followers_url': 'https://api.github.com/users/JosJuice/followers', 'following_url': 'https://api.github.com/users/JosJuice/following{/other_user}', 'gists_url': 'https://api.github.com/users/JosJuice/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/JosJuice/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/JosJuice/subscriptions', 'organizations_url': 'https://api.github.com/users/JosJuice/orgs', 'repos_url': 'https://api.github.com/users/JosJuice/repos', 'events_url': 'https://api.github.com/users/JosJuice/events{/privacy}', 'received_events_url': 'https://api.github.com/users/JosJuice/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'body': '`slots` might be larger than 2, which is the number of elements in `m_coin` and `m_coin_pressed`.', 'created_at': '2026-02-01T15:23:06Z', 'updated_at': '2026-02-01T15:40:26Z', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/13844#discussion_r2751386784', 'pull_request_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13844', 'author_association': 'MEMBER', '_links': {'self': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2751386784'}, 'html': {'href': 'https://github.com/dolphin-emu/dolphin/pull/13844#discussion_r2751386784'}, 'pull_request': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13844'}}, 'original_commit_id': '5bf8fe93286ebe4f08fb469a44f259a8397c89e0', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2751386784/reactions', 'total_count': 0, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 0, 'confused': 0, 'heart': 0, 'rocket': 0, 'eyes': 0}}, {'id': 2751389783, 'node_id': 'PRRC_kwDOALCn2M6j_uBX', 'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2751389783', 'pull_request_review_id': 3735806017, 'diff_hunk': '@@ -0,0 +1,2708 @@\n+// Copyright 2025 Dolphin Emulator Project\n+// SPDX-License-Identifier: GPL-2.0-or-later\n+\n+#include "Core/HW/SI/SI_DeviceAMBaseboard.h"\n+\n+#include <algorithm>\n+#include <numeric>\n+#include <string>\n+\n+#include <fmt/format.h>\n+\n+#include "Common/Buffer.h"\n+#include "Common/CommonTypes.h"\n+#include "Common/FileUtil.h"\n+#include "Common/IOFile.h"\n+#include "Common/Logging/Log.h"\n+#include "Common/MsgHandler.h"\n+#include "Common/Swap.h"\n+\n+#include "Core/Boot/Boot.h"\n+#include "Core/BootManager.h"\n+#include "Core/Config/MainSettings.h"\n+#include "Core/ConfigManager.h"\n+#include "Core/Core.h"\n+#include "Core/CoreTiming.h"\n+#include "Core/HW/DVD/AMMediaboard.h"\n+#include "Core/HW/DVD/DVDInterface.h"\n+#include "Core/HW/EXI/EXI.h"\n+#include "Core/HW/GCPad.h"\n+#include "Core/HW/MMIO.h"\n+#include "Core/HW/Memmap.h"\n+#include "Core/HW/ProcessorInterface.h"\n+#include "Core/HW/SI/SI.h"\n+#include "Core/HW/SI/SI_Device.h"\n+#include "Core/HW/SI/SI_DeviceGCController.h"\n+#include "Core/HW/SystemTimers.h"\n+#include "Core/Movie.h"\n+#include "Core/NetPlayProto.h"\n+#include "Core/System.h"\n+\n+#include "InputCommon/GCPadStatus.h"\n+\n+namespace SerialInterface\n+{\n+void JVSIOMessage::Start(int node)\n+{\n+ m_last_start = m_pointer;\n+ const u8 header[3] = {0xE0, (u8)node, 0};\n+ m_checksum = 0;\n+ AddData(header, 3, 1);\n+}\n+\n+void JVSIOMessage::AddData(const u8* dst, size_t len, int sync = 0)\n+{\n+ if (m_pointer + len >= sizeof(m_message))\n+ {\n+ PanicAlertFmt("JVSIOMessage overrun!");\n+ return;\n+ }\n+\n+ while (len--)\n+ {\n+ const u8 c = *dst++;\n+ if (!sync && ((c == 0xE0) || (c == 0xD0)))\n+ {\n+ if (m_pointer + 2 > sizeof(m_message))\n+ {\n+ PanicAlertFmt("JVSIOMessage overrun!");\n+ break;\n+ }\n+ m_message[m_pointer++] = 0xD0;\n+ m_message[m_pointer++] = c - 1;\n+ }\n+ else\n+ {\n+ if (m_pointer >= sizeof(m_message))\n+ {\n+ PanicAlertFmt("JVSIOMessage overrun!");\n+ break;\n+ }\n+ m_message[m_pointer++] = c;\n+ }\n+\n+ if (!sync)\n+ m_checksum += c;\n+ sync = 0;\n+ }\n+}\n+\n+void JVSIOMessage::AddData(const void* data, size_t len)\n+{\n+ AddData(static_cast<const u8*>(data), len);\n+}\n+\n+void JVSIOMessage::AddData(const char* data)\n+{\n+ AddData(data, strlen(data));\n+}\n+\n+void JVSIOMessage::AddData(u32 n)\n+{\n+ const u8 cs = n;\n+ AddData(&cs, 1);\n+}\n+\n+void JVSIOMessage::End()\n+{\n+ const u32 len = m_pointer - m_last_start;\n+ if (m_last_start + 2 < sizeof(m_message) && len >= 3)\n+ {\n+ m_message[m_last_start + 2] = len - 2; // assuming len <0xD0\n+ AddData(m_checksum + len - 2);\n+ }\n+ else\n+ {\n+ PanicAlertFmt("JVSIOMessage: Not enough space for checksum!");\n+ }\n+}\n+\n+static constexpr u8 CheckSumXOR(const u8* data, u32 length)\n+{\n+ return std::accumulate(data, data + length, u8{}, std::bit_xor());\n+}\n+\n+static constexpr char s_cdr_program_version[] = {" Version 1.22,2003/09/19,171-8213B"};\n+static constexpr char s_cdr_boot_version[] = {" Version 1.04,2003/06/17,171-8213B"};\n+static constexpr u8 s_cdr_card_data[] = {\n+ 0x00, 0x6E, 0x00, 0x00, 0x01, 0x00, 0x00, 0x06, 0x00, 0x00, 0x07, 0x00, 0x00, 0x0B,\n+ 0x00, 0x00, 0x0E, 0x00, 0x00, 0x10, 0x00, 0x00, 0x17, 0x00, 0x00, 0x19, 0x00, 0x00,\n+ 0x1A, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x20, 0x00,\n+ 0x00, 0x22, 0x00, 0x00, 0x23, 0x00, 0x00, 0x24, 0x00, 0x00, 0x27, 0x00, 0x00, 0x28,\n+ 0x00, 0x00, 0x2C, 0x00, 0x00, 0x2F, 0x00, 0x00, 0x34, 0x00, 0x00, 0x35, 0x00, 0x00,\n+ 0x37, 0x00, 0x00, 0x38, 0x00, 0x00, 0x39, 0x00, 0x00, 0x3D, 0x00};\n+\n+const constexpr u8 s_region_flags[] = "\\x00\\x00\\x30\\x00"\n+ // "\\x01\\xfe\\x00\\x00" // JAPAN\n+ "\\x02\\xfd\\x00\\x00" // USA\n+ //"\\x03\\xfc\\x00\\x00" // export\n+ "\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff";\n+// AM-Baseboard device on SI\n+CSIDevice_AMBaseboard::CSIDevice_AMBaseboard(Core::System& system, SIDevices device,\n+ int device_number)\n+ : ISIDevice(system, device, device_number)\n+{\n+ // Card ID\n+ m_ic_card_data[0x20] = 0x95;\n+ m_ic_card_data[0x21] = 0x71;\n+\n+ if (AMMediaboard::GetGameType() == KeyOfAvalon)\n+ {\n+ m_ic_card_data[0x22] = 0x26;\n+ m_ic_card_data[0x23] = 0x40;\n+ }\n+ else if (AMMediaboard::GetGameType() == VirtuaStriker4)\n+ {\n+ m_ic_card_data[0x22] = 0x44;\n+ m_ic_card_data[0x23] = 0x00;\n+ }\n+\n+ // Use count\n+ m_ic_card_data[0x28] = 0xFF;\n+ m_ic_card_data[0x29] = 0xFF;\n+}\n+\n+constexpr u32 SI_XFER_LENGTH_MASK = 0x7f;\n+\n+// Translate [0,1,2,...,126,127] to [128,1,2,...,126,127]\n+constexpr s32 ConvertSILengthField(u32 field)\n+{\n+ return ((field - 1) & SI_XFER_LENGTH_MASK) + 1;\n+}\n+\n+void CSIDevice_AMBaseboard::ICCardSendReply(ICCommand* iccommand, u8* buffer, u32* length)\n+{\n+ iccommand->status = Common::swap16(iccommand->status);\n+\n+ const auto iccommand_data = reinterpret_cast<const u8*>(iccommand);\n+ const u8 crc = CheckSumXOR(iccommand_data + 2, iccommand->pktlen - 1);\n+\n+ for (u32 i = 0; i < iccommand->pktlen + 1; ++i)\n+ {\n+ buffer[(*length)++] = iccommand_data[i];\n+ }\n+\n+ buffer[(*length)++] = crc;\n+}\n+\n+void CSIDevice_AMBaseboard::SwapBuffers(u8* buffer, u32* buffer_length)\n+{\n+ memcpy(m_last[1], buffer, 0x80); // Save current buffer\n+ memcpy(buffer, m_last[0], 0x80); // Load previous buffer\n+ memcpy(m_last[0], m_last[1], 0x80); // Update history\n+\n+ m_lastptr[1] = *buffer_length; // Swap lengths\n+ *buffer_length = m_lastptr[0];\n+ m_lastptr[0] = m_lastptr[1];\n+}\n+\n+int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length)\n+{\n+ const auto& serial_interface = m_system.GetSerialInterface();\n+ u32 buffer_length = ConvertSILengthField(serial_interface.GetInLength());\n+\n+ // Debug logging\n+ ISIDevice::RunBuffer(buffer, buffer_length);\n+\n+ u32 buffer_position = 0;\n+ while (buffer_position < buffer_length)\n+ {\n+ BaseBoardCommand command = static_cast<BaseBoardCommand>(buffer[buffer_position]);\n+ buffer_position++;\n+\n+ switch (command)\n+ {\n+ case BaseBoardCommand::GCAM_Reset: // Returns ID and dip switches\n+ {\n+ const u32 id = Common::swap32(SI_AM_BASEBOARD | 0x100);\n+ std::memcpy(buffer, &id, sizeof(id));\n+ return sizeof(id);\n+ }\n+ break;\n+ case BaseBoardCommand::GCAM_Command:\n+ {\n+ u32 checksum = 0;\n+ for (u32 i = 0; i < buffer_length; ++i)\n+ checksum += buffer[i];\n+\n+ std::array<u8, 0x80> data_out{};\n+ u32 data_offset = 0;\n+\n+ static u32 dip_switch_1 = 0xFE;\n+ static u32 dip_switch_0 = 0xFF;\n+\n+ data_out[data_offset++] = 1;\n+ data_out[data_offset++] = 1;\n+\n+ u8* data_in = buffer + 2;\n+ if (buffer_position >= buffer_length)\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: buffer overflow (position={}, length={})",\n+ buffer_position, buffer_length);\n+ buffer_position = buffer_length;\n+ break;\n+ }\n+\n+ const u32 requested_size = buffer[buffer_position] + 2;\n+ if (requested_size > buffer_length)\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: requested size ({}) bigger than buffer\'s ({})",\n+ requested_size, buffer_length);\n+ buffer_position = buffer_length;\n+ break;\n+ }\n+ u8* const data_in_end = buffer + requested_size;\n+\n+ // Helper to check that iterating over data n times is safe,\n+ // i.e. *data++ at most lead to data.end()\n+ auto validate_data_in_out = [&](u32 n_in, u32 n_out, std::string_view command) -> bool {\n+ if (data_in + n_in > data_in_end)\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: data_in overflow in {}", command);\n+ else if (u64{data_offset} + n_out > data_out.size())\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: data_out overflow in {}", command);\n+ else\n+ return true;\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "Overflow details:\\n"\n+ " - data_in(begin={}, current={}, end={}, n_in={})\\n"\n+ " - data_out(offset={}, size={}, n_out={})\\n"\n+ " - buffer(position={}, length={})",\n+ fmt::ptr(buffer + 2), fmt::ptr(data_in), fmt::ptr(data_in_end), n_in,\n+ data_offset, data_out.size(), n_out, buffer_position, buffer_length);\n+ data_in = data_in_end;\n+ return false;\n+ };\n+\n+ while (data_in < data_in_end)\n+ {\n+ const u32 gcam_command = *data_in++;\n+ switch (GCAMCommand(gcam_command))\n+ {\n+ case GCAMCommand::StatusSwitches:\n+ {\n+ if (!validate_data_in_out(1, 4, "StatusSwitches"))\n+ break;\n+\n+ const u8 status = *data_in++;\n+ DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x10, {:02x} (READ STATUS&SWITCHES)",\n+ status);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x2;\n+\n+ // We read Test/Service from the JVS-I/O SwitchesInput instead\n+ //\n+ // const GCPadStatus pad_status = Pad::GetStatus(ISIDevice::m_device_number);\n+ // baseboard test/service switches\n+ // if (pad_status.button & PAD_BUTTON_Y)\t// Test\n+ // dip_switch_0 &= ~0x80;\n+ // if (pad_status.button & PAD_BUTTON_X)\t// Service\n+ // dip_switch_0 &= ~0x40;\n+\n+ // Horizontal Scanning Frequency switch\n+ // Required for F-Zero AX booting via Sega Boot\n+ if (AMMediaboard::GetGameType() == FZeroAX ||\n+ AMMediaboard::GetGameType() == FZeroAXMonster)\n+ {\n+ dip_switch_0 &= ~0x20;\n+ }\n+\n+ // Disable camera in MKGP1/2\n+ if (AMMediaboard::GetGameType() == MarioKartGP ||\n+ AMMediaboard::GetGameType() == MarioKartGP2)\n+ {\n+ dip_switch_0 &= ~0x10;\n+ }\n+\n+ data_out[data_offset++] = dip_switch_0;\n+ data_out[data_offset++] = dip_switch_1;\n+ break;\n+ }\n+ case GCAMCommand::SerialNumber:\n+ {\n+ if (!validate_data_in_out(1, 18, "SerialNumber"))\n+ break;\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x11, {:02x} (READ SERIAL NR)",\n+ *data_in);\n+ data_in++;\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 16;\n+ memcpy(data_out.data() + data_offset, "AADE-01B98394904", 16);\n+\n+ data_offset += 16;\n+ break;\n+ }\n+ case GCAMCommand::Unknown_12:\n+ if (!validate_data_in_out(2, 2, "Unknown_12"))\n+ break;\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x12, {:02x} {:02x}", data_in[0],\n+ data_in[1]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x00;\n+\n+ data_in += 2;\n+ break;\n+ case GCAMCommand::Unknown_14:\n+ if (!validate_data_in_out(2, 2, "Unknown_14"))\n+ break;\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x14, {:02x} {:02x}", data_in[0],\n+ data_in[1]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x00;\n+\n+ data_in += 2;\n+ break;\n+ case GCAMCommand::FirmVersion:\n+ if (!validate_data_in_out(1, 4, "FirmVersion"))\n+ break;\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x15, {:02x} (READ FIRM VERSION)",\n+ *data_in);\n+ data_in++;\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x02;\n+ // 00.26\n+ data_out[data_offset++] = 0x00;\n+ data_out[data_offset++] = 0x26;\n+ break;\n+ case GCAMCommand::FPGAVersion:\n+ if (!validate_data_in_out(1, 4, "FPGAVersion"))\n+ break;\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x16, {:02x} (READ FPGA VERSION)",\n+ *data_in);\n+ data_in++;\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x02;\n+ // 07.06\n+ data_out[data_offset++] = 0x07;\n+ data_out[data_offset++] = 0x06;\n+ break;\n+ case GCAMCommand::RegionSettings:\n+ {\n+ if (!validate_data_in_out(5, 0x16, "RegionSettings"))\n+ break;\n+\n+ // Used by SegaBoot for region checks (dev mode skips this check)\n+ // In some games this also controls the displayed language\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x1F, {:02x} {:02x} {:02x} {:02x} {:02x} (REGION)",\n+ data_in[0], data_in[1], data_in[2], data_in[3], data_in[4]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x14;\n+\n+ for (int i = 0; i < 0x14; ++i)\n+ data_out[data_offset++] = s_region_flags[i];\n+\n+ data_in += 5;\n+ }\n+ break;\n+ // No reply\n+ // Note: Always sends three bytes even though size is set to two\n+ case GCAMCommand::Unknown_21:\n+ {\n+ if (!validate_data_in_out(4, 0, "Unknown_21"))\n+ break;\n+\n+ DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x21, {:02x}, {:02x}, {:02x}, {:02x}",\n+ data_in[0], data_in[1], data_in[2], data_in[3]);\n+ data_in += 4;\n+ }\n+ break;\n+ // No reply\n+ // Note: Always sends six bytes\n+ case GCAMCommand::Unknown_22:\n+ {\n+ if (!validate_data_in_out(7, 0, "Unknown_22"))\n+ break;\n+\n+ DEBUG_LOG_FMT(\n+ SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x22, {:02x}, {:02x}, {:02x}, {:02x}, {:02x}, {:02x}, {:02x}",\n+ data_in[0], data_in[1], data_in[2], data_in[3], data_in[4], data_in[5], data_in[6]);\n+\n+ const u32 in_size = data_in[0] + 1;\n+ if (!validate_data_in_out(in_size, 0, "Unknown_22"))\n+ break;\n+ data_in += in_size;\n+ }\n+ break;\n+ case GCAMCommand::Unknown_23:\n+ if (!validate_data_in_out(2, 2, "Unknown_23"))\n+ break;\n+\n+ DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x23, {:02x} {:02x}", data_in[0],\n+ data_in[1]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x00;\n+\n+ data_in += 2;\n+ break;\n+ case GCAMCommand::Unknown_24:\n+ if (!validate_data_in_out(2, 2, "Unknown_24"))\n+ break;\n+\n+ DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x24, {:02x} {:02x}", data_in[0],\n+ data_in[1]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x00;\n+\n+ data_in += 2;\n+ break;\n+ case GCAMCommand::SerialA:\n+ {\n+ if (!validate_data_in_out(1, 0, "SerialA"))\n+ break;\n+\n+ const u32 length = *data_in++;\n+ if (length)\n+ {\n+ if (!validate_data_in_out(length, 0, "SerialA"))\n+ break;\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x31, length=0x{:02x}, hexdump:\\n{}",\n+ length, HexDump(data_in, length));\n+\n+ // Serial - Wheel\n+ if (AMMediaboard::GetGameType() == MarioKartGP ||\n+ AMMediaboard::GetGameType() == MarioKartGP2)\n+ {\n+ if (!validate_data_in_out(10, 2, "SerialA (Wheel)"))\n+ break;\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x31, (WHEEL) {:02x}{:02x} {:02x}{:02x} {:02x} {:02x} "\n+ "{:02x} {:02x} {:02x} {:02x}",\n+ data_in[0], data_in[1], data_in[2], data_in[3], data_in[4], data_in[5],\n+ data_in[6], data_in[7], data_in[8], data_in[9]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x03;\n+\n+ switch (m_wheel_init)\n+ {\n+ case 0:\n+ if (!validate_data_in_out(0, 3, "SerialA (Wheel)"))\n+ break;\n+ data_out[data_offset++] = \'E\'; // Error\n+ data_out[data_offset++] = \'0\';\n+ data_out[data_offset++] = \'0\';\n+ m_wheel_init++;\n+ break;\n+ case 1:\n+ if (!validate_data_in_out(0, 3, "SerialA (Wheel)"))\n+ break;\n+ data_out[data_offset++] = \'C\'; // Power Off\n+ data_out[data_offset++] = \'0\';\n+ data_out[data_offset++] = \'6\';\n+ // Only turn on when a wheel is connected\n+ if (serial_interface.GetDeviceType(1) == SerialInterface::SIDEVICE_GC_STEERING)\n+ {\n+ m_wheel_init++;\n+ }\n+ break;\n+ case 2:\n+ if (!validate_data_in_out(0, 3, "SerialA (Wheel)"))\n+ break;\n+ data_out[data_offset++] = \'C\'; // Power On\n+ data_out[data_offset++] = \'0\';\n+ data_out[data_offset++] = \'1\';\n+ break;\n+ default:\n+ break;\n+ }\n+\n+ // u16 CenteringForce= ptr(6);\n+ // u16 FrictionForce = ptr(8);\n+ // u16 Roll = ptr(10);\n+ if (!validate_data_in_out(length, 0, "SerialA (Wheel)"))\n+ break;\n+ data_in += length;\n+ break;\n+ }\n+\n+ // Serial - Unknown\n+ if (AMMediaboard::GetGameType() == GekitouProYakyuu)\n+ {\n+ if (!validate_data_in_out(sizeof(u32), 0, "SerialA (Unknown)"))\n+ break;\n+ const u32 serial_command = Common::BitCastPtr<u32>(data_in);\n+\n+ if (serial_command == 0x00001000)\n+ {\n+ if (!validate_data_in_out(0, 5, "SerialA (Unknown)"))\n+ break;\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x03;\n+ data_out[data_offset++] = 1;\n+ data_out[data_offset++] = 2;\n+ data_out[data_offset++] = 3;\n+ }\n+\n+ if (!validate_data_in_out(length, 0, "SerialA (Unknown)"))\n+ break;\n+ data_in += length;\n+ break;\n+ }\n+\n+ // Serial IC-CARD / Serial Deck Reader\n+ if (AMMediaboard::GetGameType() == VirtuaStriker4 ||\n+ AMMediaboard::GetGameType() == VirtuaStriker4_2006 ||\n+ AMMediaboard::GetGameType() == KeyOfAvalon)\n+ {\n+ if (!validate_data_in_out(2, 0, "SerialA (IC-CARD)"))\n+ break;\n+ u32 serial_command = data_in[1];\n+\n+ ICCommand icco;\n+\n+ // Set default reply\n+ icco.pktcmd = gcam_command;\n+ icco.pktlen = 7;\n+ icco.fixed = 0x10;\n+ icco.command = serial_command;\n+ icco.flag = 0;\n+ icco.length = 2;\n+ icco.status = 0;\n+ icco.extlen = 0;\n+\n+ // Check for rest of data from the write pages command\n+ if (m_ic_write_size && m_ic_write_offset)\n+ {\n+ const u32 size = data_in[1];\n+\n+ if (!validate_data_in_out(size + 2, 0, "SerialA (IC-CARD)"))\n+ break;\n+ DEBUG_LOG_FMT(SERIALINTERFACE_CARD, "Command: {}", HexDump(data_in, size + 2));\n+\n+ INFO_LOG_FMT(\n+ SERIALINTERFACE_CARD,\n+ "GC-AM: Command 25 (IC-CARD) Write Pages: Off:{:x} Size:{:x} PSize:{:x}",\n+ m_ic_write_offset, m_ic_write_size, size);\n+\n+ if (u64{m_ic_write_offset} + size > sizeof(m_ic_write_buffer))\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 25 (IC-CARD) m_ic_write_buffer overflow:\\n"\n+ " - m_ic_write_buffer(offset={}, size={})\\n"\n+ " - size={}\\n",\n+ m_ic_write_offset, sizeof(m_ic_write_buffer), size);\n+ data_in = data_in_end;\n+ break;\n+ }\n+ memcpy(m_ic_write_buffer + m_ic_write_offset, data_in + 2, size);\n+\n+ m_ic_write_offset += size;\n+\n+ if (m_ic_write_offset > m_ic_write_size)\n+ {\n+ m_ic_write_offset = 0;\n+\n+ const u16 page = m_ic_write_buffer[5];\n+ const u16 count = m_ic_write_buffer[7];\n+\n+ if ((page * 8 + count * 8) > sizeof(m_ic_card_data) ||\n+ (10 + count * 8) > sizeof(m_ic_write_buffer))\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 25 (IC-CARD) Write Pages overflow:\\n"\n+ " - m_ic_card_data(offset={}, size={})\\n"\n+ " - m_ic_write_buffer(offset={}, size={})\\n"\n+ " - size={}, page={}, count={}\\n",\n+ page * 8, sizeof(m_ic_card_data), 10, sizeof(m_ic_write_buffer),\n+ count * 8, page, count);\n+ data_in = data_in_end;\n+ break;\n+ }\n+ memcpy(m_ic_card_data + page * 8, m_ic_write_buffer + 10, count * 8);\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 25 (IC-CARD) Write Pages:{} Count:{}({:x})", page,\n+ count, size);\n+\n+ icco.command = WritePages;\n+\n+ if (!validate_data_in_out(0, icco.pktlen + 2, "SerialA (IC-CARD)"))\n+ break;\n+ ICCardSendReply(&icco, data_out.data(), &data_offset);\n+ }\n+ if (!validate_data_in_out(length, 0, "SerialA (IC-CARD)"))\n+ break;\n+ data_in += length;\n+ break;\n+ }\n+\n+ switch (ICCARDCommand(serial_command))\n+ {\n+ case ICCARDCommand::GetStatus:\n+ icco.status = m_ic_card_state;\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (IC-CARD) Get Status:{:02x}", m_ic_card_state);\n+ break;\n+ case ICCARDCommand::SetBaudrate:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (IC-CARD) Set Baudrate");\n+ break;\n+ case ICCARDCommand::FieldOn:\n+ m_ic_card_state |= 0x10;\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (IC-CARD) Field On");\n+ break;\n+ case ICCARDCommand::InsertCheck:\n+ icco.status = m_ic_card_status;\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (IC-CARD) Insert Check:{:02x}", m_ic_card_status);\n+ break;\n+ case ICCARDCommand::AntiCollision:\n+ icco.extlen = 8;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ // Card ID\n+ icco.extdata[0] = 0x00;\n+ icco.extdata[1] = 0x00;\n+ icco.extdata[2] = 0x54;\n+ icco.extdata[3] = 0x4D;\n+ icco.extdata[4] = 0x50;\n+ icco.extdata[5] = 0x00;\n+ icco.extdata[6] = 0x00;\n+ icco.extdata[7] = 0x00;\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (IC-CARD) Anti Collision");\n+ break;\n+ case ICCARDCommand::SelectCard:\n+ icco.extlen = 8;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ // Session\n+ icco.extdata[0] = 0x00;\n+ icco.extdata[1] = m_ic_card_session;\n+ icco.extdata[2] = 0x00;\n+ icco.extdata[3] = 0x00;\n+ icco.extdata[4] = 0x00;\n+ icco.extdata[5] = 0x00;\n+ icco.extdata[6] = 0x00;\n+ icco.extdata[7] = 0x00;\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (IC-CARD) Select Card:{}",\n+ m_ic_card_session);\n+ break;\n+ case ICCARDCommand::ReadPage:\n+ case ICCARDCommand::ReadUseCount:\n+ {\n+ if (!validate_data_in_out(8, 0, "SerialA (IC-CARD)"))\n+ break;\n+ const u16 page = Common::swap16(data_in + 6) & 0xFF; // 255 is max page\n+\n+ icco.extlen = 8;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ memcpy(icco.extdata, m_ic_card_data + page * 8, 8);\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 31 (IC-CARD) Read Page:{}",\n+ page);\n+ break;\n+ }\n+ case ICCARDCommand::WritePage:\n+ {\n+ if (!validate_data_in_out(10, 0, "SerialA (IC-CARD)"))\n+ break;\n+ const u16 page = Common::swap16(data_in + 8) & 0xFF; // 255 is max page\n+\n+ // Write only one page\n+ if (page == 4)\n+ {\n+ icco.status = 0x80;\n+ }\n+ else\n+ {\n+ if (!validate_data_in_out(18, 0, "SerialA (IC-CARD)"))\n+ break;\n+ memcpy(m_ic_card_data + page * 8, data_in + 10, 8);\n+ }\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (IC-CARD) Write Page:{}",\n+ page);\n+ break;\n+ }\n+ case ICCARDCommand::DecreaseUseCount:\n+ {\n+ if (!validate_data_in_out(8, 0, "SerialA (IC-CARD)"))\n+ break;\n+ const u16 page = Common::swap16(data_in + 6) & 0xFF; // 255 is max page\n+\n+ icco.extlen = 2;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ auto ic_card_data = Common::BitCastPtr<u16>(m_ic_card_data + 0x28);\n+ ic_card_data = ic_card_data - 1;\n+\n+ // Counter\n+ icco.extdata[0] = m_ic_card_data[0x28];\n+ icco.extdata[1] = m_ic_card_data[0x29];\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 31 (IC-CARD) Decrease Use Count:{}", page);\n+ break;\n+ }\n+ case ICCARDCommand::ReadPages:\n+ {\n+ if (!validate_data_in_out(10, 0, "SerialA (IC-CARD)"))\n+ break;\n+ const u16 page = Common::swap16(data_in + 6) & 0xFF; // 255 is max page\n+ const u16 count = Common::swap16(data_in + 8);\n+\n+ const u32 offs = page * 8;\n+ u32 cnt = count * 8;\n+\n+ // Limit read size to not overwrite the reply buffer\n+ const std::size_t reply_buffer_size = sizeof(icco.extdata) - 1;\n+ if (data_offset > reply_buffer_size)\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 31 (IC-CARD) Read Pages overflow:"\n+ " offset={} > buffer_size={}",\n+ data_offset, reply_buffer_size);\n+ data_in = data_in_end;\n+ break;\n+ }\n+ if (cnt > reply_buffer_size - data_offset)\n+ {\n+ cnt = 5 * 8;\n+ }\n+\n+ icco.extlen = cnt;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ if (offs + cnt > sizeof(icco.extdata))\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 31 (IC-CARD) Read Pages overflow:"\n+ " offset={} + count={} > buffer_size={}",\n+ offs, cnt, sizeof(icco.extdata));\n+ data_in = data_in_end;\n+ break;\n+ }\n+ memcpy(icco.extdata, m_ic_card_data + offs, cnt);\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 31 (IC-CARD) Read Pages:{} Count:{}", page, count);\n+ break;\n+ }\n+ case ICCARDCommand::WritePages:\n+ {\n+ if (!validate_data_in_out(10, 0, "SerialA (IC-CARD)"))\n+ break;\n+ const u16 pksize = length;\n+ const u16 size = Common::swap16(data_in + 2);\n+ const u16 page = Common::swap16(data_in + 6) & 0xFF; // 255 is max page\n+ const u16 count = Common::swap16(data_in + 8);\n+\n+ // We got a complete packet\n+ if (pksize - 5 == size)\n+ {\n+ if (page == 4) // Read Only Page, must return error\n+ {\n+ icco.status = 0x80;\n+ }\n+ else\n+ {\n+ if (page * 8 + count * 8 > sizeof(m_ic_card_data))\n+ {\n+ ERROR_LOG_FMT(\n+ SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (IC-CARD) Data overflow: Pages:{} Count:{}({:x})",\n+ page, count, size);\n+ }\n+ else\n+ {\n+ if (!validate_data_in_out(13 + count * 8, 0, "SerialA (IC-CARD)"))\n+ break;\n+ memcpy(m_ic_card_data + page * 8, data_in + 13, count * 8);\n+ }\n+ }\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (IC-CARD) Write Pages:{} Count:{}({:x})", page,\n+ count, size);\n+ }\n+ // VirtuaStriker 4 splits the writes over multiple packets\n+ else\n+ {\n+ if (!validate_data_in_out(2 + pksize, 0, "SerialA (IC-CARD)"))\n+ break;\n+ memcpy(m_ic_write_buffer, data_in + 2, pksize);\n+ m_ic_write_offset += pksize;\n+ m_ic_write_size = size;\n+ }\n+ break;\n+ }\n+ default:\n+ // Handle Deck Reader commands\n+ if (!validate_data_in_out(1, 0, "SerialA (DECK READER)"))\n+ break;\n+ serial_command = data_in[0];\n+ icco.command = serial_command;\n+ icco.flag = 0;\n+ switch (CDReaderCommand(serial_command))\n+ {\n+ case CDReaderCommand::ProgramVersion:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Program Version");\n+\n+ icco.extlen = (u32)strlen(s_cdr_program_version);\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ memcpy(icco.extdata, s_cdr_program_version, icco.extlen);\n+ break;\n+ case CDReaderCommand::BootVersion:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Boot Version");\n+\n+ icco.extlen = (u32)strlen(s_cdr_boot_version);\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ memcpy(icco.extdata, s_cdr_boot_version, icco.extlen);\n+ break;\n+ case CDReaderCommand::ShutterGet:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Shutter Get");\n+\n+ icco.extlen = 4;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ icco.extdata[0] = 0;\n+ icco.extdata[1] = 0;\n+ icco.extdata[2] = 0;\n+ icco.extdata[3] = 0;\n+ break;\n+ case CDReaderCommand::CameraCheck:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Camera Check");\n+\n+ icco.extlen = 6;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ icco.extdata[0] = 0x23;\n+ icco.extdata[1] = 0x28;\n+ icco.extdata[2] = 0x45;\n+ icco.extdata[3] = 0x29;\n+ icco.extdata[4] = 0x45;\n+ icco.extdata[5] = 0x29;\n+ break;\n+ case CDReaderCommand::ProgramChecksum:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Program Checksum");\n+\n+ icco.extlen = 4;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ icco.extdata[0] = 0x23;\n+ icco.extdata[1] = 0x28;\n+ icco.extdata[2] = 0x45;\n+ icco.extdata[3] = 0x29;\n+ break;\n+ case CDReaderCommand::BootChecksum:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Boot Checksum");\n+\n+ icco.extlen = 4;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ icco.extdata[0] = 0x23;\n+ icco.extdata[1] = 0x28;\n+ icco.extdata[2] = 0x45;\n+ icco.extdata[3] = 0x29;\n+ break;\n+ case CDReaderCommand::SelfTest:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (DECK READER) Self Test");\n+ icco.flag = 0x00;\n+ break;\n+ case CDReaderCommand::SensLock:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (DECK READER) Sens Lock");\n+ icco.flag = 0x01;\n+ break;\n+ case CDReaderCommand::SensCard:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (DECK READER) Sens Card");\n+ break;\n+ case CDReaderCommand::ShutterCard:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Shutter Card");\n+ break;\n+ case CDReaderCommand::ReadCard:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (DECK READER) Read Card");\n+\n+ icco.fixed = 0xAA;\n+ icco.flag = 0xAA;\n+ icco.extlen = sizeof(s_cdr_card_data);\n+ icco.length = 0x72;\n+ icco.status = Common::swap16(icco.extlen);\n+\n+ icco.pktlen += icco.extlen;\n+\n+ memcpy(icco.extdata, s_cdr_card_data, sizeof(s_cdr_card_data));\n+\n+ break;\n+ default:\n+ if (!validate_data_in_out(14, 0, "SerialA (DECK READER)"))\n+ break;\n+ WARN_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (IC-Card) {:02x} {:02x} {:02x} {:02x} {:02x} "\n+ "{:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x}",\n+ data_in[2], data_in[3], data_in[4], data_in[5], data_in[6],\n+ data_in[7], data_in[8], data_in[9], data_in[10], data_in[11],\n+ data_in[12], data_in[13]);\n+ break;\n+ }\n+ break;\n+ }\n+\n+ if (!validate_data_in_out(0, icco.pktlen + 2, "SerialA (IC-CARD)"))\n+ break;\n+ ICCardSendReply(&icco, data_out.data(), &data_offset);\n+\n+ if (!validate_data_in_out(2, 0, "SerialA (IC-CARD)"))\n+ break;\n+ data_in += length;\n+ break;\n+ }\n+ }\n+\n+ u32 command_offset = 0;\n+ while (command_offset < length)\n+ {\n+ // All commands are OR\'d with 0x80\n+ // Last byte is checksum which we don\'t care about\n+ if (!validate_data_in_out(command_offset + 4, 0, "SerialA"))\n+ break;\n+ const u32 serial_command = Common::swap32(data_in + command_offset) ^ 0x80000000;\n+\n+ if (AMMediaboard::GetGameType() == FZeroAX ||\n+ AMMediaboard::GetGameType() == FZeroAXMonster)\n+ {\n+ INFO_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x31 (MOTOR) Length:{:02x} Command:{:04x}({:02x})",\n+ length, (serial_command >> 8) & 0xFFFF, serial_command >> 24);\n+ }\n+ else\n+ {\n+ INFO_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x31 (SERIAL) Command:{:06x}",\n+ serial_command);\n+\n+ if (serial_command == 0x801000)\n+ {\n+ if (!validate_data_in_out(0, 4, "SerialA"))\n+ break;\n+ data_out[data_offset++] = 0x31;\n+ data_out[data_offset++] = 0x02;\n+ data_out[data_offset++] = 0xFF;\n+ data_out[data_offset++] = 0x01;\n+ }\n+ }\n+\n+ command_offset += sizeof(u32);\n+\n+ if (AMMediaboard::GetGameType() == FZeroAX ||\n+ AMMediaboard::GetGameType() == FZeroAXMonster)\n+ {\n+ if (command_offset + 5 >= std::size(m_motor_reply))\n+ {\n+ ERROR_LOG_FMT(\n+ SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x31 (MOTOR) overflow: offset={} >= motor_reply_size={}",\n+ command_offset + 5, std::size(m_motor_reply));\n+ data_in = data_in_end;\n+ break;\n+ }\n+\n+ // Status\n+ m_motor_reply[command_offset + 2] = 0;\n+ m_motor_reply[command_offset + 3] = 0;\n+\n+ // Error\n+ m_motor_reply[command_offset + 4] = 0;\n+\n+ switch (serial_command >> 24)\n+ {\n+ case 0:\n+ case 1: // Set Maximum?\n+ case 2:\n+ break;\n+\n+ // 0x00-0x40: left\n+ // 0x40-0x80: right\n+ case 4: // Move Steering Wheel\n+ // Left\n+ if (serial_command & 0x010000)\n+ {\n+ m_motor_force_y = -((s16)serial_command & 0xFF00);\n+ }\n+ else // Right\n+ {\n+ m_motor_force_y = (serial_command - 0x4000) & 0xFF00;\n+ }\n+\n+ m_motor_force_y *= 2;\n+\n+ // FFB\n+ if (m_motor_init == 2)\n+ {\n+ if (serial_interface.GetDeviceType(1) == SerialInterface::SIDEVICE_GC_STEERING)\n+ {\n+ const GCPadStatus pad_status = Pad::GetStatus(1);\n+ if (pad_status.isConnected)\n+ {\n+ const ControlState mapped_strength = (double)(m_motor_force_y >> 8) / 127.f;\n+\n+ Pad::Rumble(1, mapped_strength);\n+ INFO_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x31 (MOTOR) mapped_strength:{}",\n+ mapped_strength);\n+ }\n+ }\n+ }\n+ break;\n+ case 6: // nice\n+ case 9:\n+ default:\n+ break;\n+ // Switch back to normal controls\n+ case 7:\n+ m_motor_init = 2;\n+ break;\n+ // Reset\n+ case 0x7F:\n+ m_motor_init = 1;\n+ memset(m_motor_reply, 0, sizeof(m_motor_reply));\n+ break;\n+ }\n+\n+ // Checksum\n+ m_motor_reply[command_offset + 5] = m_motor_reply[command_offset + 2] ^\n+ m_motor_reply[command_offset + 3] ^\n+ m_motor_reply[command_offset + 4];\n+ }\n+ }\n+\n+ if (length == 0)\n+ {\n+ if (!validate_data_in_out(length, 2, "SerialA"))\n+ break;\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x00;\n+ }\n+ else\n+ {\n+ if (m_motor_init)\n+ {\n+ // Motor\n+ m_motor_reply[0] = gcam_command;\n+ m_motor_reply[1] = length; // Same out as in size\n+\n+ const u32 reply_size = m_motor_reply[1] + 2;\n+ if (!validate_data_in_out(length, reply_size, "SerialA"))\n+ break;\n+\n+ if (reply_size > sizeof(m_motor_reply))\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command SerialA, reply_size={} too big for m_motor_reply",\n+ reply_size);\n+ data_in = data_in_end;\n+ break;\n+ }\n+ memcpy(data_out.data() + data_offset, m_motor_reply, reply_size);\n+ data_offset += reply_size;\n+ }\n+ }\n+\n+ if (!validate_data_in_out(length, 0, "SerialA"))\n+ {\n+ break;\n+ }\n+ data_in += length;\n+ break;\n+ }\n+ case GCAMCommand::SerialB:\n+ {\n+ DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 32 (CARD-Interface)");\n+ if (!validate_data_in_out(1, 0, "SerialB"))\n+ break;\n+ const u32 length = *data_in++;\n+ if (!validate_data_in_out(length, 0, "SerialB"))\n+ break;\n+ if (length)\n+ {\n+ // Send Card Reply\n+ if (length == 1 && data_in[0] == 0x05)\n+ {\n+ if (m_card_read_length)\n+ {\n+ if (!validate_data_in_out(0, 1, "SerialB"))\n+ break;\n+ data_out[data_offset++] = gcam_command;\n+ u32 read_length = m_card_read_length - m_card_read;\n+\n+ if (AMMediaboard::GetGameType() == FZeroAX)\n+ {\n+ read_length = std::min<u32>(read_length, 0x2F);\n+ }\n+\n+ if (!validate_data_in_out(0, 1, "SerialB"))\n+ break;\n+ data_out[data_offset++] = read_length; // 0x2F (max size per packet)\n+\n+ if (!validate_data_in_out(0, read_length, "SerialB"))\n+ break;\n+ if (u64{m_card_read} + read_length > sizeof(m_card_read_packet))\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command SerialB, m_card_read_packet overflow:\\n"\n+ " - m_card_read_packet = {}\\n"\n+ " - m_card_read = {}\\n"\n+ " - read_length = {}",\n+ fmt::ptr(m_card_read_packet), m_card_read, read_length);\n+ data_in = data_in_end;\n+ break;\n+ }\n+ memcpy(data_out.data() + data_offset, m_card_read_packet + m_card_read,\n+ read_length);\n+\n+ data_offset += read_length;\n+ m_card_read += read_length;\n+\n+ if (m_card_read >= m_card_read_length)\n+ m_card_read_length = 0;\n+\n+ data_in += length;\n+ break;\n+ }\n+\n+ if (!validate_data_in_out(0, 5, "SerialB"))\n+ break;\n+\n+ data_out[data_offset++] = gcam_command;\n+ const u32 command_length_offset = data_offset;\n+ data_out[data_offset++] = 0x00; // len\n+\n+ data_out[data_offset++] = 0x02;\n+ const u32 checksum_start = data_offset;\n+\n+ data_out[data_offset++] = 0x00; // 0x00 len\n+\n+ data_out[data_offset++] = m_card_command; // 0x01 command\n+\n+ switch (CARDCommand(m_card_command))\n+ {\n+ case CARDCommand::Init:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x00; // 0x02\n+ data_out[data_offset++] = 0x30; // 0x03\n+ break;\n+ case CARDCommand::GetState:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x20 | m_card_bit; // 0x02\n+\n+ // bit 0: Please take your card\n+ // bit 1: Endless waiting causes UNK_E to be called\n+ data_out[data_offset++] = 0x00; // 0x03\n+ break;\n+ case CARDCommand::Read:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x02; // 0x02\n+ data_out[data_offset++] = 0x53; // 0x03\n+ break;\n+ case CARDCommand::IsPresent:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x22; // 0x02\n+ data_out[data_offset++] = 0x30; // 0x03\n+ break;\n+ case CARDCommand::Write:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x02; // 0x02\n+ data_out[data_offset++] = 0x00; // 0x03\n+ break;\n+ case CARDCommand::SetPrintParam:\n+ case CARDCommand::RegisterFont:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x00; // 0x02\n+ data_out[data_offset++] = 0x00; // 0x03\n+ break;\n+ case CARDCommand::WriteInfo:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x02; // 0x02\n+ data_out[data_offset++] = 0x00; // 0x03\n+ break;\n+ case CARDCommand::Erase:\n+ // TODO: CARDCommand::Erase is not handled.\n+ break;\n+ case CARDCommand::Eject:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ if (AMMediaboard::GetGameType() == FZeroAX)\n+ {\n+ data_out[data_offset++] = 0x01; // 0x02\n+ }\n+ else\n+ {\n+ data_out[data_offset++] = 0x31; // 0x02\n+ }\n+ data_out[data_offset++] = 0x30; // 0x03\n+ break;\n+ case CARDCommand::Clean:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x02; // 0x02\n+ data_out[data_offset++] = 0x00; // 0x03\n+ break;\n+ case CARDCommand::Load:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x02; // 0x02\n+ data_out[data_offset++] = 0x30; // 0x03\n+ break;\n+ case CARDCommand::SetShutter:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x00; // 0x02\n+ data_out[data_offset++] = 0x00; // 0x03\n+ break;\n+ }\n+\n+ if (!validate_data_in_out(0, 3, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x30; // 0x04\n+ data_out[data_offset++] = 0x00; // 0x05\n+\n+ data_out[data_offset++] = 0x03; // 0x06\n+\n+ data_out[checksum_start] = data_offset - checksum_start; // 0x00 len\n+\n+ if (!validate_data_in_out(0, 1, "SerialB"))\n+ break;\n+ data_out[data_offset] = 0; // 0x07\n+ for (u32 i = 0; i < data_out[checksum_start]; ++i)\n+ data_out[data_offset] ^= data_out[checksum_start + i];\n+\n+ if (!validate_data_in_out(0, 1, "SerialB"))\n+ break;\n+ data_offset++;\n+\n+ data_out[command_length_offset] = data_out[checksum_start] + 2;\n+ }\n+ else\n+ {\n+ if (!validate_data_in_out(length, 0, "SerialB"))\n+ break;\n+ if (u64{m_card_offset} + length > std::size(m_card_buffer))\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command SerialB, m_card_buffer overflow:\\n"\n+ " - m_card_buffer = {}\\n"\n+ " - m_card_offset = {}\\n"\n+ " - length = {}",\n+ fmt::ptr(m_card_buffer), m_card_offset, length);\n+ data_in = data_in_end;\n+ break;\n+ }\n+ for (u32 i = 0; i < length; ++i)\n+ m_card_buffer[m_card_offset + i] = data_in[i];\n+\n+ m_card_offset += length;\n+\n+ // Check if we got a complete command\n+ if (m_card_buffer[0] == 0x02)\n+ {\n+ if (m_card_buffer[1] == m_card_offset - 2)\n+ {\n+ if (m_card_buffer[m_card_offset - 2] == 0x03)\n+ {\n+ m_card_command = m_card_buffer[2];\n+\n+ switch (CARDCommand(m_card_command))\n+ {\n+ case CARDCommand::Init:\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD Init");\n+\n+ m_card_write_length = 0;\n+ m_card_bit = 0;\n+ m_card_memory_size = 0;\n+ m_card_state_call_count = 0;\n+ break;\n+ case CARDCommand::GetState:\n+ {\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD GetState({:02X})",\n+ m_card_bit);\n+\n+ if (m_card_memory_size == 0)\n+ {\n+ const std::string card_filename(\n+ fmt::format("{}tricard_{}.bin", File::GetUserPath(D_TRIUSER_IDX),\n+ SConfig::GetInstance().GetGameID()));\n+\n+ if (File::Exists(card_filename))\n+ {\n+ File::IOFile card(card_filename, "rb+");\n+ m_card_memory_size = static_cast<u32>(card.GetSize());\n+ if (m_card_memory_size > sizeof(m_card_memory))\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command CARD GetState overflow:\\n"\n+ " - file name = {}\\n"\n+ " - file size = {}\\n"\n+ " - card size = {}",\n+ card_filename, m_card_memory_size, sizeof(m_card_memory));\n+ data_in = data_in_end;\n+ break;\n+ }\n+ card.ReadBytes(m_card_memory, m_card_memory_size);\n+ card.Close();\n+\n+ m_card_is_inserted = true;\n+ }\n+ }\n+\n+ if (AMMediaboard::GetGameType() == FZeroAX && m_card_memory_size)\n+ {\n+ m_card_state_call_count++;\n+ if (m_card_state_call_count > 10)\n+ {\n+ if (m_card_bit & 2)\n+ m_card_bit &= ~2u;\n+ else\n+ m_card_bit |= 2;\n+\n+ m_card_state_call_count = 0;\n+ }\n+ }\n+\n+ if (m_card_clean == 1)\n+ {\n+ m_card_clean = 2;\n+ }\n+ else if (m_card_clean == 2)\n+ {\n+ const std::string card_filename(\n+ fmt::format("{}tricard_{}.bin", File::GetUserPath(D_TRIUSER_IDX),\n+ SConfig::GetInstance().GetGameID()));\n+\n+ if (File::Exists(card_filename))\n+ {\n+ m_card_memory_size = static_cast<u32>(File::GetSize(card_filename));\n+ if (m_card_memory_size)\n+ {\n+ if (AMMediaboard::GetGameType() == FZeroAX)\n+ {\n+ m_card_bit = 2;\n+ }\n+ else\n+ {\n+ m_card_bit = 1;\n+ }\n+ }\n+ }\n+ m_card_clean = 0;\n+ }\n+ break;\n+ }\n+ case CARDCommand::IsPresent:\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD IsPresent");\n+ break;\n+ case CARDCommand::RegisterFont:\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD RegisterFont");\n+ break;\n+ case CARDCommand::Load:\n+ {\n+ const u8 mode = m_card_buffer[6];\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD Load({:02X})",\n+ mode);\n+ break;\n+ }\n+ case CARDCommand::Clean:\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD Clean");\n+ m_card_clean = 1;\n+ break;\n+ case CARDCommand::Read:\n+ {\n+ const u8 mode = m_card_buffer[6];\n+ const u8 bitmode = m_card_buffer[7];\n+ const u8 track = m_card_buffer[8];\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command CARD Read({:02X},{:02X},{:02X})", mode,\n+ bitmode, track);\n+\n+ // Prepare read packet\n+ memset(m_card_read_packet, 0, 0xDB);\n+\n+ const std::string card_filename(\n+ fmt::format("{}tricard_{}.bin", File::GetUserPath(D_TRIUSER_IDX),\n+ SConfig::GetInstance().GetGameID()));\n+\n+ if (File::Exists(card_filename))\n+ {\n+ File::IOFile card(card_filename, "rb+");\n+ if (m_card_memory_size == 0)\n+ {\n+ m_card_memory_size = static_cast<u32>(card.GetSize());\n+ }\n+\n+ if (m_card_memory_size > sizeof(m_card_memory))\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command CARD Read overflow:\\n"\n+ " - file name = {}\\n"\n+ " - file size = {}\\n"\n+ " - card size = {}",\n+ card_filename, m_card_memory_size, sizeof(m_card_memory));\n+ data_in = data_in_end;\n+ break;\n+ }\n+ card.ReadBytes(m_card_memory, m_card_memory_size);\n+ card.Close();\n+\n+ m_card_is_inserted = true;\n+ }\n+ else if (m_card_memory_size > sizeof(m_card_memory))\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command CARD Read overflow:\\n"\n+ " - requested size = {}\\n"\n+ " - card size = {}",\n+ m_card_memory_size, sizeof(m_card_memory));\n+ data_in = data_in_end;\n+ break;\n+ }\n+\n+ m_card_read_packet[0] = 0x02; // SUB CMD\n+ m_card_read_packet[1] = 0x00; // SUB CMDLen\n+\n+ m_card_read_packet[2] = 0x33; // CARD CMD\n+\n+ if (m_card_is_inserted) // CARD Status\n+ {\n+ m_card_read_packet[3] = 0x31;\n+ }\n+ else\n+ {\n+ m_card_read_packet[3] = 0x30;\n+ }\n+\n+ m_card_read_packet[4] = 0x30;\n+ m_card_read_packet[5] = 0x30;\n+\n+ u32 packet_offset = 6;\n+ // Data reply\n+ static_assert(sizeof(m_card_read_packet) >= sizeof(m_card_memory) + 6);\n+ memcpy(m_card_read_packet + packet_offset, m_card_memory, m_card_memory_size);\n+ packet_offset += m_card_memory_size;\n+\n+ static_assert(sizeof(m_card_read_packet) >= sizeof(m_card_memory) + 7);\n+ m_card_read_packet[packet_offset++] = 0x03;\n+\n+ m_card_read_packet[1] = packet_offset - 1; // SUB CMDLen\n+\n+ static_assert(sizeof(m_card_read_packet) >= sizeof(m_card_memory) + 8);\n+ for (u32 i = 0; i < packet_offset - 1; ++i)\n+ m_card_read_packet[packet_offset] ^= m_card_read_packet[1 + i];\n+\n+ static_assert(sizeof(m_card_read_packet) >= sizeof(m_card_memory) + 9);\n+ packet_offset++;\n+\n+ m_card_read_length = packet_offset;\n+ m_card_read = 0;\n+ break;\n+ }\n+ case CARDCommand::Write:\n+ {\n+ const u8 mode = m_card_buffer[6];\n+ const u8 bitmode = m_card_buffer[7];\n+ const u8 track = m_card_buffer[8];\n+\n+ m_card_memory_size = m_card_buffer[1] - 9;\n+ if (m_card_memory_size > sizeof(m_card_memory))\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command CARD Write overflow:\\n"\n+ " - write size = {}\\n"\n+ " - card size = {}",\n+ m_card_memory_size, sizeof(m_card_memory));\n+ data_in = data_in_end;\n+ break;\n+ }\n+\n+ static_assert(sizeof(m_card_buffer) >= sizeof(m_card_memory) + 9);\n+ memcpy(m_card_memory, m_card_buffer + 9, m_card_memory_size);\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command CARD Write: {:02X} {:02X} {:02X} {}", mode,\n+ bitmode, track, m_card_memory_size);\n+\n+ const std::string card_filename(File::GetUserPath(D_TRIUSER_IDX) +\n+ "tricard_" +\n+ SConfig::GetInstance().GetGameID() + ".bin");\n+\n+ File::IOFile card(card_filename, "wb+");\n+ card.WriteBytes(m_card_memory, m_card_memory_size);\n+ card.Close();\n+\n+ m_card_bit = 2;\n+\n+ m_card_state_call_count = 0;\n+ break;\n+ }\n+ case CARDCommand::SetPrintParam:\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD SetPrintParam");\n+ break;\n+ case CARDCommand::WriteInfo:\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD WriteInfo");\n+ break;\n+ case CARDCommand::Erase:\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD Erase");\n+ break;\n+ case CARDCommand::Eject:\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD Eject");\n+ if (AMMediaboard::GetGameType() != FZeroAX)\n+ {\n+ m_card_bit = 0;\n+ }\n+ break;\n+ case CARDCommand::SetShutter:\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD SetShutter");\n+ if (AMMediaboard::GetGameType() != FZeroAX)\n+ {\n+ m_card_bit = 0;\n+ }\n+ // Close\n+ if (m_card_buffer[6] == 0x30)\n+ {\n+ m_card_shutter = false;\n+ }\n+ // Open\n+ else if (m_card_buffer[6] == 0x31)\n+ {\n+ m_card_shutter = true;\n+ }\n+ break;\n+ default:\n+ ERROR_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: CARD:Unhandled command!");\n+ ERROR_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: CARD:[{:08X}]", m_card_command);\n+ // hexdump( m_card_buffer, m_card_offset );\n+ break;\n+ }\n+ m_card_offset = 0;\n+ }\n+ }\n+ }\n+\n+ if (!validate_data_in_out(0, 3, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x32;\n+ data_out[data_offset++] = 0x01; // len\n+ data_out[data_offset++] = 0x06; // OK\n+ }\n+ }\n+ else\n+ {\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x00; // len\n+ }\n+ data_in += length;\n+ break;\n+ }\n+ case GCAMCommand::JVSIOA:\n+ case GCAMCommand::JVSIOB:\n+ {\n+ if (!validate_data_in_out(4, 0, "JVSIO"))\n+ break;\n+\n+ JVSIOMessage message;\n+\n+ static int delay = 0;\n+\n+ const u8* const frame = &data_in[0];\n+ const u8 nr_bytes = frame[3]; // Byte after E0 xx\n+ u32 frame_len = nr_bytes + 3; // Header(2) + length byte + payload + checksum\n+\n+ u8 jvs_buf[0x80];\n+\n+ frame_len = std::min<u32>(frame_len, sizeof(jvs_buf));\n+\n+ if (!validate_data_in_out(frame_len, 0, "JVSIO"))\n+ break;\n+ DEBUG_LOG_FMT(SERIALINTERFACE_JVSIO, "GC-AM: Command {:02x} (JVS IO), hexdump:\\n{}",\n+ gcam_command, HexDump(data_in, frame_len));\n+\n+ memcpy(jvs_buf, frame, frame_len);\n+\n+ // Extract node and payload pointers\n+ u8 node = jvs_buf[2];\n+ u8* jvs_io = jvs_buf + 4; // First payload byte\n+ u8* const jvs_end = jvs_buf + frame_len; // One byte before checksum\n+ u8* const jvs_begin = jvs_io;\n+\n+ message.Start(0);\n+ message.AddData(1);\n+\n+ // Helper to check that iterating over jvs_io n times is safe,\n+ // i.e. *jvs_io++ at most lead to jvs_end\n+ auto validate_jvs_io = [&](u32 n, std::string_view command) -> bool {\n+ if (jvs_io + n > jvs_end)\n+ ERROR_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: overflow in {}", command);\n+ else\n+ return true;\n+ ERROR_LOG_FMT(SERIALINTERFACE_JVSIO,\n+ "Overflow details:\\n"\n+ " - jvs_io(begin={}, current={}, end={}, n={})\\n"\n+ " - delay={}, node={}\\n"\n+ " - frame(begin={}, len={})",\n+ fmt::ptr(jvs_begin), fmt::ptr(jvs_io), fmt::ptr(jvs_end), n, delay, node,\n+ fmt::ptr(frame), frame_len);\n+ jvs_io = jvs_end;\n+ return false;\n+ };\n+\n+ // Now iterate over the payload\n+ while (jvs_io < jvs_end)\n+ {\n+ const u8 jvsio_command = *jvs_io++;\n+ DEBUG_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO:node={}, command={:02x}", node,\n+ jvsio_command);\n+\n+ switch (JVSIOCommand(jvsio_command))\n+ {\n+ case JVSIOCommand::IOID:\n+ message.AddData(StatusOkay);\n+ switch (AMMediaboard::GetGameType())\n+ {\n+ case FZeroAX:\n+ // Specific version that enables DX mode on AX machines, all this does is enable the\n+ // motion of a chair\n+ message.AddData("SEGA ENTERPRISES,LTD.;837-13844-01 I/O CNTL BD2 ;");\n+ break;\n+ case FZeroAXMonster:\n+ case MarioKartGP:\n+ case MarioKartGP2:\n+ default:\n+ message.AddData("namco ltd.;FCA-1;Ver1.01;JPN,Multipurpose + Rotary Encoder");\n+ break;\n+ case VirtuaStriker3:\n+ case VirtuaStriker4:\n+ case VirtuaStriker4_2006:\n+ message.AddData("SEGA ENTERPRISES,LTD.;I/O BD JVS;837-13551;Ver1.00");\n+ break;\n+ }\n+ NOTICE_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: Command 0x10, BoardID");\n+ message.AddData((u32)0);\n+ break;\n+ case JVSIOCommand::CommandRevision:\n+ message.AddData(StatusOkay);\n+ message.AddData(0x11);\n+ NOTICE_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: Command 0x11, CommandRevision");\n+ break;\n+ case JVSIOCommand::JVRevision:\n+ message.AddData(StatusOkay);\n+ message.AddData(0x20);\n+ NOTICE_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: Command 0x12, JVRevision");\n+ break;\n+ case JVSIOCommand::CommunicationVersion:\n+ message.AddData(StatusOkay);\n+ message.AddData(0x10);\n+ NOTICE_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: Command 0x13, CommunicationVersion");\n+ break;\n+\n+ // Slave features:\n+ //\n+ // Inputs:\n+ // 0x01: Switch input: players, buttons\n+ // 0x02: Coin input: slots\n+ // 0x03: Analog input: channels, bits\n+ // 0x04: Rotary input: channels\n+ // 0x05: Keycode input: 0,0,0 ?\n+ // 0x06: Screen position input: X bits, Y bits, channels\n+ //\n+ // Outputs:\n+ // 0x10: Card system: slots\n+ // 0x11: Medal hopper: channels\n+ // 0x12: GPO-out: slots\n+ // 0x13: Analog output: channels\n+ // 0x14: Character output: width, height, type\n+ // 0x15: Backup\n+ case JVSIOCommand::CheckFunctionality:\n+ message.AddData(StatusOkay);\n+ switch (AMMediaboard::GetGameType())\n+ {\n+ case FZeroAX:\n+ case FZeroAXMonster:\n+ // 2 Player (12bit) (p2=paddles), 1 Coin slot, 6 Analog-in\n+ // message.AddData((void *)"\\x01\\x02\\x0C\\x00", 4);\n+ // message.AddData((void *)"\\x02\\x01\\x00\\x00", 4);\n+ // message.AddData((void *)"\\x03\\x06\\x00\\x00", 4);\n+ // message.AddData((void *)"\\x00\\x00\\x00\\x00", 4);\n+ //\n+ // DX Version: 2 Player (22bit) (p2=paddles), 2 Coin slot, 8 Analog-in,\n+ // 22 Driver-out\n+ message.AddData("\\x01\\x02\\x12\\x00", 4);\n+ message.AddData("\\x02\\x02\\x00\\x00", 4);\n+ message.AddData("\\x03\\x08\\x0A\\x00", 4);\n+ message.AddData("\\x12\\x16\\x00\\x00", 4);\n+ message.AddData("\\x00\\x00\\x00\\x00", 4);\n+ break;\n+ case VirtuaStriker3:\n+ // 2 Player (13bit), 2 Coin slot, 4 Analog-in, 1 CARD, 8 Driver-out\n+ message.AddData("\\x01\\x02\\x0D\\x00", 4);\n+ message.AddData("\\x02\\x02\\x00\\x00", 4);\n+ message.AddData("\\x10\\x01\\x00\\x00", 4);\n+ message.AddData("\\x12\\x08\\x00\\x00", 4);\n+ message.AddData("\\x00\\x00\\x00\\x00", 4);\n+ break;\n+ case GekitouProYakyuu:\n+ // 2 Player (13bit), 2 Coin slot, 4 Analog-in, 1 CARD, 8 Driver-out\n+ message.AddData("\\x01\\x02\\x0D\\x00", 4);\n+ message.AddData("\\x02\\x02\\x00\\x00", 4);\n+ message.AddData("\\x03\\x04\\x00\\x00", 4);\n+ message.AddData("\\x10\\x01\\x00\\x00", 4);\n+ message.AddData("\\x12\\x08\\x00\\x00", 4);\n+ message.AddData("\\x00\\x00\\x00\\x00", 4);\n+ break;\n+ case VirtuaStriker4:\n+ case VirtuaStriker4_2006:\n+ // 2 Player (13bit), 1 Coin slot, 4 Analog-in, 1 CARD\n+ message.AddData("\\x01\\x02\\x0D\\x00", 4);\n+ message.AddData("\\x02\\x01\\x00\\x00", 4);\n+ message.AddData("\\x03\\x04\\x00\\x00", 4);\n+ message.AddData("\\x10\\x01\\x00\\x00", 4);\n+ message.AddData("\\x00\\x00\\x00\\x00", 4);\n+ break;\n+ case KeyOfAvalon:\n+ // 1 Player (15bit), 1 Coin slot, 3 Analog-in, Touch, 1 CARD, 1 Driver-out\n+ // (Unconfirmed)\n+ message.AddData("\\x01\\x01\\x0F\\x00", 4);\n+ message.AddData("\\x02\\x01\\x00\\x00", 4);\n+ message.AddData("\\x03\\x03\\x00\\x00", 4);\n+ message.AddData("\\x06\\x10\\x10\\x01", 4);\n+ message.AddData("\\x10\\x01\\x00\\x00", 4);\n+ message.AddData("\\x12\\x01\\x00\\x00", 4);\n+ message.AddData("\\x00\\x00\\x00\\x00", 4);\n+ break;\n+ case MarioKartGP:\n+ case MarioKartGP2:\n+ default:\n+ // 1 Player (15bit), 1 Coin slot, 3 Analog-in, 1 CARD, 1 Driver-out\n+ message.AddData("\\x01\\x01\\x0F\\x00", 4);\n+ message.AddData("\\x02\\x01\\x00\\x00", 4);\n+ message.AddData("\\x03\\x03\\x00\\x00", 4);\n+ message.AddData("\\x10\\x01\\x00\\x00", 4);\n+ message.AddData("\\x12\\x01\\x00\\x00", 4);\n+ message.AddData("\\x00\\x00\\x00\\x00", 4);\n+ break;\n+ }\n+ NOTICE_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: Command 0x14, CheckFunctionality");\n+ break;\n+ case JVSIOCommand::MainID:\n+ {\n+ const u8* const main_id = jvs_io;\n+ while (jvs_io < jvs_end && *jvs_io++)\n+ {\n+ }\n+ if (main_id < jvs_io)\n+ {\n+ DEBUG_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: Command MainId:\\n{}",\n+ HexDump(main_id, jvs_io - main_id));\n+ }\n+ message.AddData(StatusOkay);\n+ break;\n+ }\n+ case JVSIOCommand::SwitchesInput:\n+ {\n+ if (!validate_jvs_io(2, "SwitchesInput"))\n+ break;\n+ const u32 player_count = *jvs_io++;\n+ const u32 player_byte_count = *jvs_io++;\n+\n+ DEBUG_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: Command 0x20, SwitchInputs: {} {}",\n+ player_count, player_byte_count);\n+\n+ message.AddData(StatusOkay);\n+\n+ GCPadStatus pad_status = Pad::GetStatus(0);\n+\n+ // Test button\n+ if (pad_status.switches & SWITCH_TEST)\n+ {\n+ // Trying to access the test menu without SegaBoot present will cause a crash\n+ if (AMMediaboard::GetTestMenu())\n+ {\n+ message.AddData(0x80);\n+ }\n+ else\n+ {\n+ PanicAlertFmt("Test menu is disabled due missing SegaBoot");\n+ }\n+ }\n+ else\n+ {\n+ message.AddData((u32)0x00);\n+ }\n+\n+ for (u32 i = 0; i < player_count; ++i)\n+ {\n+ u8 player_data[3]{};\n+\n+ // Service button\n+ if (pad_status.switches & SWITCH_SERVICE)\n+ player_data[0] |= 0x40;\n+\n+ switch (AMMediaboard::GetGameType())\n+ {\n+ // Controller configuration for F-Zero AX (DX)\n+ case FZeroAX:\n+ if (i == 0)\n+ {\n+ if (m_fzdx_seatbelt)\n+ {\n+ player_data[0] |= 0x01;\n+ }\n+\n+ // Start\n+ if (pad_status.button & PAD_BUTTON_START)\n+ player_data[0] |= 0x80;\n+ // Boost\n+ if (pad_status.button & PAD_BUTTON_A)\n+ player_data[0] |= 0x02;\n+ // View Change 1\n+ if (pad_status.button & PAD_BUTTON_RIGHT)\n+ player_data[0] |= 0x20;\n+ // View Change 2\n+ if (pad_status.button & PAD_BUTTON_LEFT)\n+ player_data[0] |= 0x10;\n+ // View Change 3\n+ if (pad_status.button & PAD_BUTTON_UP)\n+ player_data[0] |= 0x08;\n+ // View Change 4\n+ if (pad_status.button & PAD_BUTTON_DOWN)\n+ player_data[0] |= 0x04;\n+ player_data[1] = m_rx_reply & 0xF0;\n+ }\n+ else if (i == 1)\n+ {\n+ // Paddle left\n+ if (pad_status.button & PAD_BUTTON_X)\n+ player_data[0] |= 0x20;\n+ // Paddle right\n+ if (pad_status.button & PAD_BUTTON_Y)\n+ player_data[0] |= 0x10;\n+\n+ if (m_fzdx_motion_stop)\n+ {\n+ player_data[0] |= 2;\n+ }\n+ if (m_fzdx_sensor_right)\n+ {\n+ player_data[0] |= 4;\n+ }\n+ if (m_fzdx_sensor_left)\n+ {\n+ player_data[0] |= 8;\n+ }\n+\n+ player_data[1] = m_rx_reply << 4;\n+ }\n+ break;\n+ // Controller configuration for F-Zero AX MonsterRide\n+ case FZeroAXMonster:\n+ if (i == 0)\n+ {\n+ if (m_fzcc_sensor)\n+ {\n+ player_data[0] |= 0x01;\n+ }\n+\n+ // Start\n+ if (pad_status.button & PAD_BUTTON_START)\n+ player_data[0] |= 0x80;\n+ // Boost\n+ if (pad_status.button & PAD_BUTTON_A)\n+ player_data[0] |= 0x02;\n+ // View Change 1\n+ if (pad_status.button & PAD_BUTTON_RIGHT)\n+ player_data[0] |= 0x20;\n+ // View Change 2\n+ if (pad_status.button & PAD_BUTTON_LEFT)\n+ player_data[0] |= 0x10;\n+ // View Change 3\n+ if (pad_status.button & PAD_BUTTON_UP)\n+ player_data[0] |= 0x08;\n+ // View Change 4\n+ if (pad_status.button & PAD_BUTTON_DOWN)\n+ player_data[0] |= 0x04;\n+\n+ player_data[1] = m_rx_reply & 0xF0;\n+ }\n+ else if (i == 1)\n+ {\n+ // Paddle left\n+ if (pad_status.button & PAD_BUTTON_X)\n+ player_data[0] |= 0x20;\n+ // Paddle right\n+ if (pad_status.button & PAD_BUTTON_Y)\n+ player_data[0] |= 0x10;\n+\n+ if (m_fzcc_seatbelt)\n+ {\n+ player_data[0] |= 2;\n+ }\n+ if (m_fzcc_service)\n+ {\n+ player_data[0] |= 4;\n+ }\n+ if (m_fzcc_emergency)\n+ {\n+ player_data[0] |= 8;\n+ }\n+ }\n+ break;\n+ // Controller configuration for Virtua Striker 3 games\n+ case VirtuaStriker3:\n+ pad_status = Pad::GetStatus(i);\n+ // Start\n+ if (pad_status.button & PAD_BUTTON_START)\n+ player_data[0] |= 0x80;\n+ // Shoot\n+ if (pad_status.button & PAD_BUTTON_B)\n+ player_data[0] |= 0x01;\n+ // Short Pass\n+ if (pad_status.button & PAD_BUTTON_A)\n+ player_data[1] |= 0x80;\n+ // Long Pass\n+ if (pad_status.button & PAD_BUTTON_X)\n+ player_data[0] |= 0x02;\n+ // Left\n+ if (pad_status.button & PAD_BUTTON_LEFT)\n+ player_data[0] |= 0x08;\n+ // Up\n+ if (pad_status.button & PAD_BUTTON_UP)\n+ player_data[0] |= 0x20;\n+ // Right\n+ if (pad_status.button & PAD_BUTTON_RIGHT)\n+ player_data[0] |= 0x04;\n+ // Down\n+ if (pad_status.button & PAD_BUTTON_DOWN)\n+ player_data[0] |= 0x10;\n+ break;\n+ // Controller configuration for Virtua Striker 4 games\n+ case VirtuaStriker4:\n+ case VirtuaStriker4_2006:\n+ {\n+ pad_status = Pad::GetStatus(i);\n+ // Start\n+ if (pad_status.button & PAD_BUTTON_START)\n+ player_data[0] |= 0x80;\n+ // Long Pass\n+ if (pad_status.button & PAD_BUTTON_X)\n+ player_data[0] |= 0x01;\n+ // Short Pass\n+ if (pad_status.button & PAD_BUTTON_A)\n+ player_data[0] |= 0x02;\n+ // Shoot\n+ if (pad_status.button & PAD_BUTTON_B)\n+ player_data[1] |= 0x80;\n+ // Dash\n+ if (pad_status.button & PAD_BUTTON_Y)\n+ player_data[1] |= 0x40;\n+ // Tactics (U)\n+ if (pad_status.button & PAD_BUTTON_LEFT)\n+ player_data[0] |= 0x20;\n+ // Tactics (M)\n+ if (pad_status.button & PAD_BUTTON_UP)\n+ player_data[0] |= 0x08;\n+ // Tactics (D)\n+ if (pad_status.button & PAD_BUTTON_RIGHT)\n+ player_data[0] |= 0x04;\n+\n+ if (i == 0)\n+ {\n+ player_data[0] |= 0x10; // IC-Card Switch ON\n+\n+ // IC-Card Lock\n+ if (pad_status.button & PAD_BUTTON_DOWN)\n+ player_data[1] |= 0x20;\n+ }\n+ }\n+ break;\n+ // Controller configuration for Gekitou Pro Yakyuu\n+ case GekitouProYakyuu:\n+ pad_status = Pad::GetStatus(i);\n+ // Start\n+ if (pad_status.button & PAD_BUTTON_START)\n+ player_data[0] |= 0x80;\n+ // A\n+ if (pad_status.button & PAD_BUTTON_B)\n+ player_data[0] |= 0x01;\n+ // B\n+ if (pad_status.button & PAD_BUTTON_A)\n+ player_data[0] |= 0x02;\n+ // Gekitou\n+ if (pad_status.button & PAD_TRIGGER_L)\n+ player_data[1] |= 0x80;\n+ // Left\n+ if (pad_status.button & PAD_BUTTON_LEFT)\n+ player_data[0] |= 0x08;\n+ // Up\n+ if (pad_status.button & PAD_BUTTON_UP)\n+ player_data[0] |= 0x20;\n+ // Right\n+ if (pad_status.button & PAD_BUTTON_RIGHT)\n+ player_data[0] |= 0x04;\n+ // Down\n+ if (pad_status.button & PAD_BUTTON_DOWN)\n+ player_data[0] |= 0x10;\n+ break;\n+ // Controller configuration for Mario Kart and other games\n+ default:\n+ case MarioKartGP:\n+ case MarioKartGP2:\n+ {\n+ // Start\n+ if (pad_status.button & PAD_BUTTON_START)\n+ player_data[0] |= 0x80;\n+ // Item button\n+ if (pad_status.button & PAD_BUTTON_A)\n+ player_data[1] |= 0x20;\n+ // VS-Cancel button\n+ if (pad_status.button & PAD_BUTTON_B)\n+ player_data[1] |= 0x02;\n+ }\n+ break;\n+ case KeyOfAvalon:\n+ {\n+ // Debug On\n+ if (pad_status.button & PAD_BUTTON_START)\n+ player_data[0] |= 0x80;\n+ // Switch 1\n+ if (pad_status.button & PAD_BUTTON_A)\n+ player_data[0] |= 0x04;\n+ // Switch 2\n+ if (pad_status.button & PAD_BUTTON_B)\n+ player_data[0] |= 0x08;\n+ // Toggle inserted card\n+ if (pad_status.button & PAD_TRIGGER_L)\n+ {\n+ m_ic_card_status ^= ICCARDStatus::NoCard;\n+ }\n+ }\n+ break;\n+ }\n+\n+ for (u32 j = 0; j < player_byte_count; ++j)\n+ message.AddData(player_data[j]);\n+ }\n+ break;\n+ }\n+ case JVSIOCommand::CoinInput:\n+ {\n+ if (!validate_jvs_io(1, "CoinInput"))\n+ break;\n+ const u32 slots = *jvs_io++;\n+ message.AddData(StatusOkay);\n+ for (u32 i = 0; i < slots; i++)\n+ {\n+ GCPadStatus pad_status = Pad::GetStatus(i);\n+ if ((pad_status.switches & SWITCH_COIN) && !m_coin_pressed[i])\n+ {\n+ m_coin[i]++;\n+ }\n+ m_coin_pressed[i] = pad_status.switches & SWITCH_COIN;\n+ message.AddData((m_coin[i] >> 8) & 0x3f);\n+ message.AddData(m_coin[i] & 0xff);\n+ }\n+ DEBUG_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: Command 0x21, CoinInput: {}", slots);\n+ break;\n+ }\n+ case JVSIOCommand::AnalogInput:\n+ {\n+ if (!validate_jvs_io(1, "AnalogInput"))\n+ break;\n+ message.AddData(StatusOkay);\n+\n+ const u32 analogs = *jvs_io++;\n+ GCPadStatus pad_status = Pad::GetStatus(0);\n+\n+ DEBUG_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: Command 0x22, AnalogInput: {}",\n+ analogs);\n+\n+ switch (AMMediaboard::GetGameType())\n+ {\n+ case FZeroAX:\n+ case FZeroAXMonster:\n+ // Steering\n+ if (m_motor_init == 1)\n+ {\n+ if (m_motor_force_y > 0)\n+ {\n+ message.AddData(0x80 - (m_motor_force_y >> 8));\n+ }\n+ else\n+ {\n+ message.AddData((m_motor_force_y >> 8));\n+ }\n+ message.AddData((u8)0);\n+\n+ message.AddData(pad_status.stickY);\n+ message.AddData((u8)0);\n+ }\n+ else\n+ {\n+ // The center for the Y axis is expected to be 78h this adjusts that\n+ message.AddData(pad_status.stickX - 12);\n+ message.AddData((u8)0);\n+\n+ message.AddData(pad_status.stickY);\n+ message.AddData((u8)0);\n+ }\n+\n+ // Unused\n+ message.AddData((u8)0);\n+ message.AddData((u8)0);\n+ message.AddData((u8)0);\n+ message.AddData((u8)0);\n+\n+ // Gas\n+ message.AddData(pad_status.triggerRight);\n+ message.AddData((u8)0);\n+\n+ // Brake\n+ message.AddData(pad_status.triggerLeft);\n+ message.AddData((u8)0);\n+\n+ message.AddData((u8)0x80); // Motion Stop\n+ message.AddData((u8)0);\n+\n+ message.AddData((u8)0);\n+ message.AddData((u8)0);\n+\n+ break;\n+ case VirtuaStriker4:\n+ case VirtuaStriker4_2006:\n+ {\n+ message.AddData(-pad_status.stickY);\n+ message.AddData((u8)0);\n+ message.AddData(pad_status.stickX);\n+ message.AddData((u8)0);\n+\n+ pad_status = Pad::GetStatus(1);\n+\n+ message.AddData(-pad_status.stickY);\n+ message.AddData((u8)0);\n+ message.AddData(pad_status.stickX);\n+ message.AddData((u8)0);\n+ }\n+ break;\n+ default:\n+ case MarioKartGP:\n+ case MarioKartGP2:\n+ // Steering\n+ message.AddData(pad_status.stickX);\n+ message.AddData((u8)0);\n+\n+ // Gas\n+ message.AddData(pad_status.triggerRight);\n+ message.AddData((u8)0);\n+\n+ // Brake\n+ message.AddData(pad_status.triggerLeft);\n+ message.AddData((u8)0);\n+ break;\n+ }\n+ break;\n+ }\n+ case JVSIOCommand::PositionInput:\n+ {\n+ if (!validate_jvs_io(1, "PositionInput"))\n+ break;\n+ const u32 channel = *jvs_io++;\n+\n+ const GCPadStatus pad_status = Pad::GetStatus(0);\n+\n+ if (pad_status.button & PAD_TRIGGER_R)\n+ {\n+ // Tap at center of screen (~320,240)\n+ message.AddData("\\x01\\x00\\x8C\\x01\\x95",\n+ 5); // X=320 (0x0140), Y=240 (0x00F0)\n+ }\n+ else\n+ {\n+ message.AddData("\\x01\\xFF\\xFF\\xFF\\xFF", 5);\n+ }\n+\n+ DEBUG_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: Command 0x25, PositionInput:{}",\n+ channel);\n+ break;\n+ }\n+ case JVSIOCommand::CoinSubOutput:\n+ {\n+ if (!validate_jvs_io(3, "CoinSubOutput"))\n+ break;\n+ const u32 slot = *jvs_io++;\n+ const u8 coinh = *jvs_io++;\n+ const u8 coinl = *jvs_io++;\n+\n+ m_coin[slot] -= (coinh << 8) | coinl;', 'path': 'Source/Core/Core/HW/SI/SI_DeviceAMBaseboard.cpp', 'position': 2279, 'original_position': 2279, 'commit_id': '5bf8fe93286ebe4f08fb469a44f259a8397c89e0', 'user': {'login': 'JosJuice', 'id': 6716818, 'node_id': 'MDQ6VXNlcjY3MTY4MTg=', 'avatar_url': 'https://avatars.githubusercontent.com/u/6716818?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/JosJuice', 'html_url': 'https://github.com/JosJuice', 'followers_url': 'https://api.github.com/users/JosJuice/followers', 'following_url': 'https://api.github.com/users/JosJuice/following{/other_user}', 'gists_url': 'https://api.github.com/users/JosJuice/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/JosJuice/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/JosJuice/subscriptions', 'organizations_url': 'https://api.github.com/users/JosJuice/orgs', 'repos_url': 'https://api.github.com/users/JosJuice/repos', 'events_url': 'https://api.github.com/users/JosJuice/events{/privacy}', 'received_events_url': 'https://api.github.com/users/JosJuice/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'body': 'Unchecked `slot`.', 'created_at': '2026-02-01T15:24:01Z', 'updated_at': '2026-02-01T15:40:26Z', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/13844#discussion_r2751389783', 'pull_request_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13844', 'author_association': 'MEMBER', '_links': {'self': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2751389783'}, 'html': {'href': 'https://github.com/dolphin-emu/dolphin/pull/13844#discussion_r2751389783'}, 'pull_request': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13844'}}, 'original_commit_id': '5bf8fe93286ebe4f08fb469a44f259a8397c89e0', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2751389783/reactions', 'total_count': 0, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 0, 'confused': 0, 'heart': 0, 'rocket': 0, 'eyes': 0}}, {'id': 2751395550, 'node_id': 'PRRC_kwDOALCn2M6j_vbe', 'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2751395550', 'pull_request_review_id': 3735806017, 'diff_hunk': '@@ -0,0 +1,2708 @@\n+// Copyright 2025 Dolphin Emulator Project\n+// SPDX-License-Identifier: GPL-2.0-or-later\n+\n+#include "Core/HW/SI/SI_DeviceAMBaseboard.h"\n+\n+#include <algorithm>\n+#include <numeric>\n+#include <string>\n+\n+#include <fmt/format.h>\n+\n+#include "Common/Buffer.h"\n+#include "Common/CommonTypes.h"\n+#include "Common/FileUtil.h"\n+#include "Common/IOFile.h"\n+#include "Common/Logging/Log.h"\n+#include "Common/MsgHandler.h"\n+#include "Common/Swap.h"\n+\n+#include "Core/Boot/Boot.h"\n+#include "Core/BootManager.h"\n+#include "Core/Config/MainSettings.h"\n+#include "Core/ConfigManager.h"\n+#include "Core/Core.h"\n+#include "Core/CoreTiming.h"\n+#include "Core/HW/DVD/AMMediaboard.h"\n+#include "Core/HW/DVD/DVDInterface.h"\n+#include "Core/HW/EXI/EXI.h"\n+#include "Core/HW/GCPad.h"\n+#include "Core/HW/MMIO.h"\n+#include "Core/HW/Memmap.h"\n+#include "Core/HW/ProcessorInterface.h"\n+#include "Core/HW/SI/SI.h"\n+#include "Core/HW/SI/SI_Device.h"\n+#include "Core/HW/SI/SI_DeviceGCController.h"\n+#include "Core/HW/SystemTimers.h"\n+#include "Core/Movie.h"\n+#include "Core/NetPlayProto.h"\n+#include "Core/System.h"\n+\n+#include "InputCommon/GCPadStatus.h"\n+\n+namespace SerialInterface\n+{\n+void JVSIOMessage::Start(int node)\n+{\n+ m_last_start = m_pointer;\n+ const u8 header[3] = {0xE0, (u8)node, 0};\n+ m_checksum = 0;\n+ AddData(header, 3, 1);\n+}\n+\n+void JVSIOMessage::AddData(const u8* dst, size_t len, int sync = 0)\n+{\n+ if (m_pointer + len >= sizeof(m_message))\n+ {\n+ PanicAlertFmt("JVSIOMessage overrun!");\n+ return;\n+ }\n+\n+ while (len--)\n+ {\n+ const u8 c = *dst++;\n+ if (!sync && ((c == 0xE0) || (c == 0xD0)))\n+ {\n+ if (m_pointer + 2 > sizeof(m_message))\n+ {\n+ PanicAlertFmt("JVSIOMessage overrun!");\n+ break;\n+ }\n+ m_message[m_pointer++] = 0xD0;\n+ m_message[m_pointer++] = c - 1;\n+ }\n+ else\n+ {\n+ if (m_pointer >= sizeof(m_message))\n+ {\n+ PanicAlertFmt("JVSIOMessage overrun!");\n+ break;\n+ }\n+ m_message[m_pointer++] = c;\n+ }\n+\n+ if (!sync)\n+ m_checksum += c;\n+ sync = 0;\n+ }\n+}\n+\n+void JVSIOMessage::AddData(const void* data, size_t len)\n+{\n+ AddData(static_cast<const u8*>(data), len);\n+}\n+\n+void JVSIOMessage::AddData(const char* data)\n+{\n+ AddData(data, strlen(data));\n+}\n+\n+void JVSIOMessage::AddData(u32 n)\n+{\n+ const u8 cs = n;\n+ AddData(&cs, 1);\n+}\n+\n+void JVSIOMessage::End()\n+{\n+ const u32 len = m_pointer - m_last_start;\n+ if (m_last_start + 2 < sizeof(m_message) && len >= 3)\n+ {\n+ m_message[m_last_start + 2] = len - 2; // assuming len <0xD0\n+ AddData(m_checksum + len - 2);\n+ }\n+ else\n+ {\n+ PanicAlertFmt("JVSIOMessage: Not enough space for checksum!");\n+ }\n+}\n+\n+static constexpr u8 CheckSumXOR(const u8* data, u32 length)\n+{\n+ return std::accumulate(data, data + length, u8{}, std::bit_xor());\n+}\n+\n+static constexpr char s_cdr_program_version[] = {" Version 1.22,2003/09/19,171-8213B"};\n+static constexpr char s_cdr_boot_version[] = {" Version 1.04,2003/06/17,171-8213B"};\n+static constexpr u8 s_cdr_card_data[] = {\n+ 0x00, 0x6E, 0x00, 0x00, 0x01, 0x00, 0x00, 0x06, 0x00, 0x00, 0x07, 0x00, 0x00, 0x0B,\n+ 0x00, 0x00, 0x0E, 0x00, 0x00, 0x10, 0x00, 0x00, 0x17, 0x00, 0x00, 0x19, 0x00, 0x00,\n+ 0x1A, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x20, 0x00,\n+ 0x00, 0x22, 0x00, 0x00, 0x23, 0x00, 0x00, 0x24, 0x00, 0x00, 0x27, 0x00, 0x00, 0x28,\n+ 0x00, 0x00, 0x2C, 0x00, 0x00, 0x2F, 0x00, 0x00, 0x34, 0x00, 0x00, 0x35, 0x00, 0x00,\n+ 0x37, 0x00, 0x00, 0x38, 0x00, 0x00, 0x39, 0x00, 0x00, 0x3D, 0x00};\n+\n+const constexpr u8 s_region_flags[] = "\\x00\\x00\\x30\\x00"\n+ // "\\x01\\xfe\\x00\\x00" // JAPAN\n+ "\\x02\\xfd\\x00\\x00" // USA\n+ //"\\x03\\xfc\\x00\\x00" // export\n+ "\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff";\n+// AM-Baseboard device on SI\n+CSIDevice_AMBaseboard::CSIDevice_AMBaseboard(Core::System& system, SIDevices device,\n+ int device_number)\n+ : ISIDevice(system, device, device_number)\n+{\n+ // Card ID\n+ m_ic_card_data[0x20] = 0x95;\n+ m_ic_card_data[0x21] = 0x71;\n+\n+ if (AMMediaboard::GetGameType() == KeyOfAvalon)\n+ {\n+ m_ic_card_data[0x22] = 0x26;\n+ m_ic_card_data[0x23] = 0x40;\n+ }\n+ else if (AMMediaboard::GetGameType() == VirtuaStriker4)\n+ {\n+ m_ic_card_data[0x22] = 0x44;\n+ m_ic_card_data[0x23] = 0x00;\n+ }\n+\n+ // Use count\n+ m_ic_card_data[0x28] = 0xFF;\n+ m_ic_card_data[0x29] = 0xFF;\n+}\n+\n+constexpr u32 SI_XFER_LENGTH_MASK = 0x7f;\n+\n+// Translate [0,1,2,...,126,127] to [128,1,2,...,126,127]\n+constexpr s32 ConvertSILengthField(u32 field)\n+{\n+ return ((field - 1) & SI_XFER_LENGTH_MASK) + 1;\n+}\n+\n+void CSIDevice_AMBaseboard::ICCardSendReply(ICCommand* iccommand, u8* buffer, u32* length)\n+{\n+ iccommand->status = Common::swap16(iccommand->status);\n+\n+ const auto iccommand_data = reinterpret_cast<const u8*>(iccommand);\n+ const u8 crc = CheckSumXOR(iccommand_data + 2, iccommand->pktlen - 1);\n+\n+ for (u32 i = 0; i < iccommand->pktlen + 1; ++i)\n+ {\n+ buffer[(*length)++] = iccommand_data[i];\n+ }\n+\n+ buffer[(*length)++] = crc;\n+}\n+\n+void CSIDevice_AMBaseboard::SwapBuffers(u8* buffer, u32* buffer_length)\n+{\n+ memcpy(m_last[1], buffer, 0x80); // Save current buffer\n+ memcpy(buffer, m_last[0], 0x80); // Load previous buffer\n+ memcpy(m_last[0], m_last[1], 0x80); // Update history\n+\n+ m_lastptr[1] = *buffer_length; // Swap lengths\n+ *buffer_length = m_lastptr[0];\n+ m_lastptr[0] = m_lastptr[1];\n+}\n+\n+int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length)\n+{\n+ const auto& serial_interface = m_system.GetSerialInterface();\n+ u32 buffer_length = ConvertSILengthField(serial_interface.GetInLength());\n+\n+ // Debug logging\n+ ISIDevice::RunBuffer(buffer, buffer_length);\n+\n+ u32 buffer_position = 0;\n+ while (buffer_position < buffer_length)\n+ {\n+ BaseBoardCommand command = static_cast<BaseBoardCommand>(buffer[buffer_position]);\n+ buffer_position++;\n+\n+ switch (command)\n+ {\n+ case BaseBoardCommand::GCAM_Reset: // Returns ID and dip switches\n+ {\n+ const u32 id = Common::swap32(SI_AM_BASEBOARD | 0x100);\n+ std::memcpy(buffer, &id, sizeof(id));\n+ return sizeof(id);\n+ }\n+ break;\n+ case BaseBoardCommand::GCAM_Command:\n+ {\n+ u32 checksum = 0;\n+ for (u32 i = 0; i < buffer_length; ++i)\n+ checksum += buffer[i];\n+\n+ std::array<u8, 0x80> data_out{};\n+ u32 data_offset = 0;\n+\n+ static u32 dip_switch_1 = 0xFE;\n+ static u32 dip_switch_0 = 0xFF;\n+\n+ data_out[data_offset++] = 1;\n+ data_out[data_offset++] = 1;\n+\n+ u8* data_in = buffer + 2;\n+ if (buffer_position >= buffer_length)\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: buffer overflow (position={}, length={})",\n+ buffer_position, buffer_length);\n+ buffer_position = buffer_length;\n+ break;\n+ }\n+\n+ const u32 requested_size = buffer[buffer_position] + 2;\n+ if (requested_size > buffer_length)\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: requested size ({}) bigger than buffer\'s ({})",\n+ requested_size, buffer_length);\n+ buffer_position = buffer_length;\n+ break;\n+ }\n+ u8* const data_in_end = buffer + requested_size;\n+\n+ // Helper to check that iterating over data n times is safe,\n+ // i.e. *data++ at most lead to data.end()\n+ auto validate_data_in_out = [&](u32 n_in, u32 n_out, std::string_view command) -> bool {\n+ if (data_in + n_in > data_in_end)\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: data_in overflow in {}", command);\n+ else if (u64{data_offset} + n_out > data_out.size())\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: data_out overflow in {}", command);\n+ else\n+ return true;\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "Overflow details:\\n"\n+ " - data_in(begin={}, current={}, end={}, n_in={})\\n"\n+ " - data_out(offset={}, size={}, n_out={})\\n"\n+ " - buffer(position={}, length={})",\n+ fmt::ptr(buffer + 2), fmt::ptr(data_in), fmt::ptr(data_in_end), n_in,\n+ data_offset, data_out.size(), n_out, buffer_position, buffer_length);\n+ data_in = data_in_end;\n+ return false;\n+ };\n+\n+ while (data_in < data_in_end)\n+ {\n+ const u32 gcam_command = *data_in++;\n+ switch (GCAMCommand(gcam_command))\n+ {\n+ case GCAMCommand::StatusSwitches:\n+ {\n+ if (!validate_data_in_out(1, 4, "StatusSwitches"))\n+ break;\n+\n+ const u8 status = *data_in++;\n+ DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x10, {:02x} (READ STATUS&SWITCHES)",\n+ status);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x2;\n+\n+ // We read Test/Service from the JVS-I/O SwitchesInput instead\n+ //\n+ // const GCPadStatus pad_status = Pad::GetStatus(ISIDevice::m_device_number);\n+ // baseboard test/service switches\n+ // if (pad_status.button & PAD_BUTTON_Y)\t// Test\n+ // dip_switch_0 &= ~0x80;\n+ // if (pad_status.button & PAD_BUTTON_X)\t// Service\n+ // dip_switch_0 &= ~0x40;\n+\n+ // Horizontal Scanning Frequency switch\n+ // Required for F-Zero AX booting via Sega Boot\n+ if (AMMediaboard::GetGameType() == FZeroAX ||\n+ AMMediaboard::GetGameType() == FZeroAXMonster)\n+ {\n+ dip_switch_0 &= ~0x20;\n+ }\n+\n+ // Disable camera in MKGP1/2\n+ if (AMMediaboard::GetGameType() == MarioKartGP ||\n+ AMMediaboard::GetGameType() == MarioKartGP2)\n+ {\n+ dip_switch_0 &= ~0x10;\n+ }\n+\n+ data_out[data_offset++] = dip_switch_0;\n+ data_out[data_offset++] = dip_switch_1;\n+ break;\n+ }\n+ case GCAMCommand::SerialNumber:\n+ {\n+ if (!validate_data_in_out(1, 18, "SerialNumber"))\n+ break;\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x11, {:02x} (READ SERIAL NR)",\n+ *data_in);\n+ data_in++;\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 16;\n+ memcpy(data_out.data() + data_offset, "AADE-01B98394904", 16);\n+\n+ data_offset += 16;\n+ break;\n+ }\n+ case GCAMCommand::Unknown_12:\n+ if (!validate_data_in_out(2, 2, "Unknown_12"))\n+ break;\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x12, {:02x} {:02x}", data_in[0],\n+ data_in[1]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x00;\n+\n+ data_in += 2;\n+ break;\n+ case GCAMCommand::Unknown_14:\n+ if (!validate_data_in_out(2, 2, "Unknown_14"))\n+ break;\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x14, {:02x} {:02x}", data_in[0],\n+ data_in[1]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x00;\n+\n+ data_in += 2;\n+ break;\n+ case GCAMCommand::FirmVersion:\n+ if (!validate_data_in_out(1, 4, "FirmVersion"))\n+ break;\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x15, {:02x} (READ FIRM VERSION)",\n+ *data_in);\n+ data_in++;\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x02;\n+ // 00.26\n+ data_out[data_offset++] = 0x00;\n+ data_out[data_offset++] = 0x26;\n+ break;\n+ case GCAMCommand::FPGAVersion:\n+ if (!validate_data_in_out(1, 4, "FPGAVersion"))\n+ break;\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x16, {:02x} (READ FPGA VERSION)",\n+ *data_in);\n+ data_in++;\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x02;\n+ // 07.06\n+ data_out[data_offset++] = 0x07;\n+ data_out[data_offset++] = 0x06;\n+ break;\n+ case GCAMCommand::RegionSettings:\n+ {\n+ if (!validate_data_in_out(5, 0x16, "RegionSettings"))\n+ break;\n+\n+ // Used by SegaBoot for region checks (dev mode skips this check)\n+ // In some games this also controls the displayed language\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x1F, {:02x} {:02x} {:02x} {:02x} {:02x} (REGION)",\n+ data_in[0], data_in[1], data_in[2], data_in[3], data_in[4]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x14;\n+\n+ for (int i = 0; i < 0x14; ++i)\n+ data_out[data_offset++] = s_region_flags[i];\n+\n+ data_in += 5;\n+ }\n+ break;\n+ // No reply\n+ // Note: Always sends three bytes even though size is set to two\n+ case GCAMCommand::Unknown_21:\n+ {\n+ if (!validate_data_in_out(4, 0, "Unknown_21"))\n+ break;\n+\n+ DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x21, {:02x}, {:02x}, {:02x}, {:02x}",\n+ data_in[0], data_in[1], data_in[2], data_in[3]);\n+ data_in += 4;\n+ }\n+ break;\n+ // No reply\n+ // Note: Always sends six bytes\n+ case GCAMCommand::Unknown_22:\n+ {\n+ if (!validate_data_in_out(7, 0, "Unknown_22"))\n+ break;\n+\n+ DEBUG_LOG_FMT(\n+ SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x22, {:02x}, {:02x}, {:02x}, {:02x}, {:02x}, {:02x}, {:02x}",\n+ data_in[0], data_in[1], data_in[2], data_in[3], data_in[4], data_in[5], data_in[6]);\n+\n+ const u32 in_size = data_in[0] + 1;\n+ if (!validate_data_in_out(in_size, 0, "Unknown_22"))\n+ break;\n+ data_in += in_size;\n+ }\n+ break;\n+ case GCAMCommand::Unknown_23:\n+ if (!validate_data_in_out(2, 2, "Unknown_23"))\n+ break;\n+\n+ DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x23, {:02x} {:02x}", data_in[0],\n+ data_in[1]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x00;\n+\n+ data_in += 2;\n+ break;\n+ case GCAMCommand::Unknown_24:\n+ if (!validate_data_in_out(2, 2, "Unknown_24"))\n+ break;\n+\n+ DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x24, {:02x} {:02x}", data_in[0],\n+ data_in[1]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x00;\n+\n+ data_in += 2;\n+ break;\n+ case GCAMCommand::SerialA:\n+ {\n+ if (!validate_data_in_out(1, 0, "SerialA"))\n+ break;\n+\n+ const u32 length = *data_in++;\n+ if (length)\n+ {\n+ if (!validate_data_in_out(length, 0, "SerialA"))\n+ break;\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x31, length=0x{:02x}, hexdump:\\n{}",\n+ length, HexDump(data_in, length));\n+\n+ // Serial - Wheel\n+ if (AMMediaboard::GetGameType() == MarioKartGP ||\n+ AMMediaboard::GetGameType() == MarioKartGP2)\n+ {\n+ if (!validate_data_in_out(10, 2, "SerialA (Wheel)"))\n+ break;\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x31, (WHEEL) {:02x}{:02x} {:02x}{:02x} {:02x} {:02x} "\n+ "{:02x} {:02x} {:02x} {:02x}",\n+ data_in[0], data_in[1], data_in[2], data_in[3], data_in[4], data_in[5],\n+ data_in[6], data_in[7], data_in[8], data_in[9]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x03;\n+\n+ switch (m_wheel_init)\n+ {\n+ case 0:\n+ if (!validate_data_in_out(0, 3, "SerialA (Wheel)"))\n+ break;\n+ data_out[data_offset++] = \'E\'; // Error\n+ data_out[data_offset++] = \'0\';\n+ data_out[data_offset++] = \'0\';\n+ m_wheel_init++;\n+ break;\n+ case 1:\n+ if (!validate_data_in_out(0, 3, "SerialA (Wheel)"))\n+ break;\n+ data_out[data_offset++] = \'C\'; // Power Off\n+ data_out[data_offset++] = \'0\';\n+ data_out[data_offset++] = \'6\';\n+ // Only turn on when a wheel is connected\n+ if (serial_interface.GetDeviceType(1) == SerialInterface::SIDEVICE_GC_STEERING)\n+ {\n+ m_wheel_init++;\n+ }\n+ break;\n+ case 2:\n+ if (!validate_data_in_out(0, 3, "SerialA (Wheel)"))\n+ break;\n+ data_out[data_offset++] = \'C\'; // Power On\n+ data_out[data_offset++] = \'0\';\n+ data_out[data_offset++] = \'1\';\n+ break;\n+ default:\n+ break;\n+ }\n+\n+ // u16 CenteringForce= ptr(6);\n+ // u16 FrictionForce = ptr(8);\n+ // u16 Roll = ptr(10);\n+ if (!validate_data_in_out(length, 0, "SerialA (Wheel)"))\n+ break;\n+ data_in += length;\n+ break;\n+ }\n+\n+ // Serial - Unknown\n+ if (AMMediaboard::GetGameType() == GekitouProYakyuu)\n+ {\n+ if (!validate_data_in_out(sizeof(u32), 0, "SerialA (Unknown)"))\n+ break;\n+ const u32 serial_command = Common::BitCastPtr<u32>(data_in);\n+\n+ if (serial_command == 0x00001000)\n+ {\n+ if (!validate_data_in_out(0, 5, "SerialA (Unknown)"))\n+ break;\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x03;\n+ data_out[data_offset++] = 1;\n+ data_out[data_offset++] = 2;\n+ data_out[data_offset++] = 3;\n+ }\n+\n+ if (!validate_data_in_out(length, 0, "SerialA (Unknown)"))\n+ break;\n+ data_in += length;\n+ break;\n+ }\n+\n+ // Serial IC-CARD / Serial Deck Reader\n+ if (AMMediaboard::GetGameType() == VirtuaStriker4 ||\n+ AMMediaboard::GetGameType() == VirtuaStriker4_2006 ||\n+ AMMediaboard::GetGameType() == KeyOfAvalon)\n+ {\n+ if (!validate_data_in_out(2, 0, "SerialA (IC-CARD)"))\n+ break;\n+ u32 serial_command = data_in[1];\n+\n+ ICCommand icco;\n+\n+ // Set default reply\n+ icco.pktcmd = gcam_command;\n+ icco.pktlen = 7;\n+ icco.fixed = 0x10;\n+ icco.command = serial_command;\n+ icco.flag = 0;\n+ icco.length = 2;\n+ icco.status = 0;\n+ icco.extlen = 0;\n+\n+ // Check for rest of data from the write pages command\n+ if (m_ic_write_size && m_ic_write_offset)\n+ {\n+ const u32 size = data_in[1];\n+\n+ if (!validate_data_in_out(size + 2, 0, "SerialA (IC-CARD)"))\n+ break;\n+ DEBUG_LOG_FMT(SERIALINTERFACE_CARD, "Command: {}", HexDump(data_in, size + 2));\n+\n+ INFO_LOG_FMT(\n+ SERIALINTERFACE_CARD,\n+ "GC-AM: Command 25 (IC-CARD) Write Pages: Off:{:x} Size:{:x} PSize:{:x}",\n+ m_ic_write_offset, m_ic_write_size, size);\n+\n+ if (u64{m_ic_write_offset} + size > sizeof(m_ic_write_buffer))\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 25 (IC-CARD) m_ic_write_buffer overflow:\\n"\n+ " - m_ic_write_buffer(offset={}, size={})\\n"\n+ " - size={}\\n",\n+ m_ic_write_offset, sizeof(m_ic_write_buffer), size);\n+ data_in = data_in_end;\n+ break;\n+ }\n+ memcpy(m_ic_write_buffer + m_ic_write_offset, data_in + 2, size);\n+\n+ m_ic_write_offset += size;\n+\n+ if (m_ic_write_offset > m_ic_write_size)\n+ {\n+ m_ic_write_offset = 0;\n+\n+ const u16 page = m_ic_write_buffer[5];\n+ const u16 count = m_ic_write_buffer[7];\n+\n+ if ((page * 8 + count * 8) > sizeof(m_ic_card_data) ||\n+ (10 + count * 8) > sizeof(m_ic_write_buffer))\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 25 (IC-CARD) Write Pages overflow:\\n"\n+ " - m_ic_card_data(offset={}, size={})\\n"\n+ " - m_ic_write_buffer(offset={}, size={})\\n"\n+ " - size={}, page={}, count={}\\n",\n+ page * 8, sizeof(m_ic_card_data), 10, sizeof(m_ic_write_buffer),\n+ count * 8, page, count);\n+ data_in = data_in_end;\n+ break;\n+ }\n+ memcpy(m_ic_card_data + page * 8, m_ic_write_buffer + 10, count * 8);\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 25 (IC-CARD) Write Pages:{} Count:{}({:x})", page,\n+ count, size);\n+\n+ icco.command = WritePages;\n+\n+ if (!validate_data_in_out(0, icco.pktlen + 2, "SerialA (IC-CARD)"))\n+ break;\n+ ICCardSendReply(&icco, data_out.data(), &data_offset);\n+ }\n+ if (!validate_data_in_out(length, 0, "SerialA (IC-CARD)"))\n+ break;\n+ data_in += length;\n+ break;\n+ }\n+\n+ switch (ICCARDCommand(serial_command))\n+ {\n+ case ICCARDCommand::GetStatus:\n+ icco.status = m_ic_card_state;\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (IC-CARD) Get Status:{:02x}", m_ic_card_state);\n+ break;\n+ case ICCARDCommand::SetBaudrate:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (IC-CARD) Set Baudrate");\n+ break;\n+ case ICCARDCommand::FieldOn:\n+ m_ic_card_state |= 0x10;\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (IC-CARD) Field On");\n+ break;\n+ case ICCARDCommand::InsertCheck:\n+ icco.status = m_ic_card_status;\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (IC-CARD) Insert Check:{:02x}", m_ic_card_status);\n+ break;\n+ case ICCARDCommand::AntiCollision:\n+ icco.extlen = 8;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ // Card ID\n+ icco.extdata[0] = 0x00;\n+ icco.extdata[1] = 0x00;\n+ icco.extdata[2] = 0x54;\n+ icco.extdata[3] = 0x4D;\n+ icco.extdata[4] = 0x50;\n+ icco.extdata[5] = 0x00;\n+ icco.extdata[6] = 0x00;\n+ icco.extdata[7] = 0x00;\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (IC-CARD) Anti Collision");\n+ break;\n+ case ICCARDCommand::SelectCard:\n+ icco.extlen = 8;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ // Session\n+ icco.extdata[0] = 0x00;\n+ icco.extdata[1] = m_ic_card_session;\n+ icco.extdata[2] = 0x00;\n+ icco.extdata[3] = 0x00;\n+ icco.extdata[4] = 0x00;\n+ icco.extdata[5] = 0x00;\n+ icco.extdata[6] = 0x00;\n+ icco.extdata[7] = 0x00;\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (IC-CARD) Select Card:{}",\n+ m_ic_card_session);\n+ break;\n+ case ICCARDCommand::ReadPage:\n+ case ICCARDCommand::ReadUseCount:\n+ {\n+ if (!validate_data_in_out(8, 0, "SerialA (IC-CARD)"))\n+ break;\n+ const u16 page = Common::swap16(data_in + 6) & 0xFF; // 255 is max page\n+\n+ icco.extlen = 8;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ memcpy(icco.extdata, m_ic_card_data + page * 8, 8);\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 31 (IC-CARD) Read Page:{}",\n+ page);\n+ break;\n+ }\n+ case ICCARDCommand::WritePage:\n+ {\n+ if (!validate_data_in_out(10, 0, "SerialA (IC-CARD)"))\n+ break;\n+ const u16 page = Common::swap16(data_in + 8) & 0xFF; // 255 is max page\n+\n+ // Write only one page\n+ if (page == 4)\n+ {\n+ icco.status = 0x80;\n+ }\n+ else\n+ {\n+ if (!validate_data_in_out(18, 0, "SerialA (IC-CARD)"))\n+ break;\n+ memcpy(m_ic_card_data + page * 8, data_in + 10, 8);\n+ }\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (IC-CARD) Write Page:{}",\n+ page);\n+ break;\n+ }\n+ case ICCARDCommand::DecreaseUseCount:\n+ {\n+ if (!validate_data_in_out(8, 0, "SerialA (IC-CARD)"))\n+ break;\n+ const u16 page = Common::swap16(data_in + 6) & 0xFF; // 255 is max page\n+\n+ icco.extlen = 2;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ auto ic_card_data = Common::BitCastPtr<u16>(m_ic_card_data + 0x28);\n+ ic_card_data = ic_card_data - 1;\n+\n+ // Counter\n+ icco.extdata[0] = m_ic_card_data[0x28];\n+ icco.extdata[1] = m_ic_card_data[0x29];\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 31 (IC-CARD) Decrease Use Count:{}", page);\n+ break;\n+ }\n+ case ICCARDCommand::ReadPages:\n+ {\n+ if (!validate_data_in_out(10, 0, "SerialA (IC-CARD)"))\n+ break;\n+ const u16 page = Common::swap16(data_in + 6) & 0xFF; // 255 is max page\n+ const u16 count = Common::swap16(data_in + 8);\n+\n+ const u32 offs = page * 8;\n+ u32 cnt = count * 8;\n+\n+ // Limit read size to not overwrite the reply buffer\n+ const std::size_t reply_buffer_size = sizeof(icco.extdata) - 1;\n+ if (data_offset > reply_buffer_size)\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 31 (IC-CARD) Read Pages overflow:"\n+ " offset={} > buffer_size={}",\n+ data_offset, reply_buffer_size);\n+ data_in = data_in_end;\n+ break;\n+ }\n+ if (cnt > reply_buffer_size - data_offset)\n+ {\n+ cnt = 5 * 8;\n+ }\n+\n+ icco.extlen = cnt;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ if (offs + cnt > sizeof(icco.extdata))\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 31 (IC-CARD) Read Pages overflow:"\n+ " offset={} + count={} > buffer_size={}",\n+ offs, cnt, sizeof(icco.extdata));\n+ data_in = data_in_end;\n+ break;\n+ }\n+ memcpy(icco.extdata, m_ic_card_data + offs, cnt);\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 31 (IC-CARD) Read Pages:{} Count:{}", page, count);\n+ break;\n+ }\n+ case ICCARDCommand::WritePages:\n+ {\n+ if (!validate_data_in_out(10, 0, "SerialA (IC-CARD)"))\n+ break;\n+ const u16 pksize = length;\n+ const u16 size = Common::swap16(data_in + 2);\n+ const u16 page = Common::swap16(data_in + 6) & 0xFF; // 255 is max page\n+ const u16 count = Common::swap16(data_in + 8);\n+\n+ // We got a complete packet\n+ if (pksize - 5 == size)\n+ {\n+ if (page == 4) // Read Only Page, must return error\n+ {\n+ icco.status = 0x80;\n+ }\n+ else\n+ {\n+ if (page * 8 + count * 8 > sizeof(m_ic_card_data))\n+ {\n+ ERROR_LOG_FMT(\n+ SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (IC-CARD) Data overflow: Pages:{} Count:{}({:x})",\n+ page, count, size);\n+ }\n+ else\n+ {\n+ if (!validate_data_in_out(13 + count * 8, 0, "SerialA (IC-CARD)"))\n+ break;\n+ memcpy(m_ic_card_data + page * 8, data_in + 13, count * 8);\n+ }\n+ }\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (IC-CARD) Write Pages:{} Count:{}({:x})", page,\n+ count, size);\n+ }\n+ // VirtuaStriker 4 splits the writes over multiple packets\n+ else\n+ {\n+ if (!validate_data_in_out(2 + pksize, 0, "SerialA (IC-CARD)"))\n+ break;\n+ memcpy(m_ic_write_buffer, data_in + 2, pksize);\n+ m_ic_write_offset += pksize;\n+ m_ic_write_size = size;\n+ }\n+ break;\n+ }\n+ default:\n+ // Handle Deck Reader commands\n+ if (!validate_data_in_out(1, 0, "SerialA (DECK READER)"))\n+ break;\n+ serial_command = data_in[0];\n+ icco.command = serial_command;\n+ icco.flag = 0;\n+ switch (CDReaderCommand(serial_command))\n+ {\n+ case CDReaderCommand::ProgramVersion:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Program Version");\n+\n+ icco.extlen = (u32)strlen(s_cdr_program_version);\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ memcpy(icco.extdata, s_cdr_program_version, icco.extlen);\n+ break;\n+ case CDReaderCommand::BootVersion:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Boot Version");\n+\n+ icco.extlen = (u32)strlen(s_cdr_boot_version);\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ memcpy(icco.extdata, s_cdr_boot_version, icco.extlen);\n+ break;\n+ case CDReaderCommand::ShutterGet:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Shutter Get");\n+\n+ icco.extlen = 4;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ icco.extdata[0] = 0;\n+ icco.extdata[1] = 0;\n+ icco.extdata[2] = 0;\n+ icco.extdata[3] = 0;\n+ break;\n+ case CDReaderCommand::CameraCheck:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Camera Check");\n+\n+ icco.extlen = 6;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ icco.extdata[0] = 0x23;\n+ icco.extdata[1] = 0x28;\n+ icco.extdata[2] = 0x45;\n+ icco.extdata[3] = 0x29;\n+ icco.extdata[4] = 0x45;\n+ icco.extdata[5] = 0x29;\n+ break;\n+ case CDReaderCommand::ProgramChecksum:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Program Checksum");\n+\n+ icco.extlen = 4;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ icco.extdata[0] = 0x23;\n+ icco.extdata[1] = 0x28;\n+ icco.extdata[2] = 0x45;\n+ icco.extdata[3] = 0x29;\n+ break;\n+ case CDReaderCommand::BootChecksum:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Boot Checksum");\n+\n+ icco.extlen = 4;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ icco.extdata[0] = 0x23;\n+ icco.extdata[1] = 0x28;\n+ icco.extdata[2] = 0x45;\n+ icco.extdata[3] = 0x29;\n+ break;\n+ case CDReaderCommand::SelfTest:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (DECK READER) Self Test");\n+ icco.flag = 0x00;\n+ break;\n+ case CDReaderCommand::SensLock:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (DECK READER) Sens Lock");\n+ icco.flag = 0x01;\n+ break;\n+ case CDReaderCommand::SensCard:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (DECK READER) Sens Card");\n+ break;\n+ case CDReaderCommand::ShutterCard:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Shutter Card");\n+ break;\n+ case CDReaderCommand::ReadCard:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (DECK READER) Read Card");\n+\n+ icco.fixed = 0xAA;\n+ icco.flag = 0xAA;\n+ icco.extlen = sizeof(s_cdr_card_data);\n+ icco.length = 0x72;\n+ icco.status = Common::swap16(icco.extlen);\n+\n+ icco.pktlen += icco.extlen;\n+\n+ memcpy(icco.extdata, s_cdr_card_data, sizeof(s_cdr_card_data));\n+\n+ break;\n+ default:\n+ if (!validate_data_in_out(14, 0, "SerialA (DECK READER)"))\n+ break;\n+ WARN_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (IC-Card) {:02x} {:02x} {:02x} {:02x} {:02x} "\n+ "{:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x}",\n+ data_in[2], data_in[3], data_in[4], data_in[5], data_in[6],\n+ data_in[7], data_in[8], data_in[9], data_in[10], data_in[11],\n+ data_in[12], data_in[13]);\n+ break;\n+ }\n+ break;\n+ }\n+\n+ if (!validate_data_in_out(0, icco.pktlen + 2, "SerialA (IC-CARD)"))\n+ break;\n+ ICCardSendReply(&icco, data_out.data(), &data_offset);\n+\n+ if (!validate_data_in_out(2, 0, "SerialA (IC-CARD)"))\n+ break;\n+ data_in += length;\n+ break;\n+ }\n+ }\n+\n+ u32 command_offset = 0;\n+ while (command_offset < length)\n+ {\n+ // All commands are OR\'d with 0x80\n+ // Last byte is checksum which we don\'t care about\n+ if (!validate_data_in_out(command_offset + 4, 0, "SerialA"))\n+ break;\n+ const u32 serial_command = Common::swap32(data_in + command_offset) ^ 0x80000000;\n+\n+ if (AMMediaboard::GetGameType() == FZeroAX ||\n+ AMMediaboard::GetGameType() == FZeroAXMonster)\n+ {\n+ INFO_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x31 (MOTOR) Length:{:02x} Command:{:04x}({:02x})",\n+ length, (serial_command >> 8) & 0xFFFF, serial_command >> 24);\n+ }\n+ else\n+ {\n+ INFO_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x31 (SERIAL) Command:{:06x}",\n+ serial_command);\n+\n+ if (serial_command == 0x801000)\n+ {\n+ if (!validate_data_in_out(0, 4, "SerialA"))\n+ break;\n+ data_out[data_offset++] = 0x31;\n+ data_out[data_offset++] = 0x02;\n+ data_out[data_offset++] = 0xFF;\n+ data_out[data_offset++] = 0x01;\n+ }\n+ }\n+\n+ command_offset += sizeof(u32);\n+\n+ if (AMMediaboard::GetGameType() == FZeroAX ||\n+ AMMediaboard::GetGameType() == FZeroAXMonster)\n+ {\n+ if (command_offset + 5 >= std::size(m_motor_reply))\n+ {\n+ ERROR_LOG_FMT(\n+ SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x31 (MOTOR) overflow: offset={} >= motor_reply_size={}",\n+ command_offset + 5, std::size(m_motor_reply));\n+ data_in = data_in_end;\n+ break;\n+ }\n+\n+ // Status\n+ m_motor_reply[command_offset + 2] = 0;\n+ m_motor_reply[command_offset + 3] = 0;\n+\n+ // Error\n+ m_motor_reply[command_offset + 4] = 0;\n+\n+ switch (serial_command >> 24)\n+ {\n+ case 0:\n+ case 1: // Set Maximum?\n+ case 2:\n+ break;\n+\n+ // 0x00-0x40: left\n+ // 0x40-0x80: right\n+ case 4: // Move Steering Wheel\n+ // Left\n+ if (serial_command & 0x010000)\n+ {\n+ m_motor_force_y = -((s16)serial_command & 0xFF00);\n+ }\n+ else // Right\n+ {\n+ m_motor_force_y = (serial_command - 0x4000) & 0xFF00;\n+ }\n+\n+ m_motor_force_y *= 2;\n+\n+ // FFB\n+ if (m_motor_init == 2)\n+ {\n+ if (serial_interface.GetDeviceType(1) == SerialInterface::SIDEVICE_GC_STEERING)\n+ {\n+ const GCPadStatus pad_status = Pad::GetStatus(1);\n+ if (pad_status.isConnected)\n+ {\n+ const ControlState mapped_strength = (double)(m_motor_force_y >> 8) / 127.f;\n+\n+ Pad::Rumble(1, mapped_strength);\n+ INFO_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x31 (MOTOR) mapped_strength:{}",\n+ mapped_strength);\n+ }\n+ }\n+ }\n+ break;\n+ case 6: // nice\n+ case 9:\n+ default:\n+ break;\n+ // Switch back to normal controls\n+ case 7:\n+ m_motor_init = 2;\n+ break;\n+ // Reset\n+ case 0x7F:\n+ m_motor_init = 1;\n+ memset(m_motor_reply, 0, sizeof(m_motor_reply));\n+ break;\n+ }\n+\n+ // Checksum\n+ m_motor_reply[command_offset + 5] = m_motor_reply[command_offset + 2] ^\n+ m_motor_reply[command_offset + 3] ^\n+ m_motor_reply[command_offset + 4];\n+ }\n+ }\n+\n+ if (length == 0)\n+ {\n+ if (!validate_data_in_out(length, 2, "SerialA"))\n+ break;\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x00;\n+ }\n+ else\n+ {\n+ if (m_motor_init)\n+ {\n+ // Motor\n+ m_motor_reply[0] = gcam_command;\n+ m_motor_reply[1] = length; // Same out as in size\n+\n+ const u32 reply_size = m_motor_reply[1] + 2;\n+ if (!validate_data_in_out(length, reply_size, "SerialA"))\n+ break;\n+\n+ if (reply_size > sizeof(m_motor_reply))\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command SerialA, reply_size={} too big for m_motor_reply",\n+ reply_size);\n+ data_in = data_in_end;\n+ break;\n+ }\n+ memcpy(data_out.data() + data_offset, m_motor_reply, reply_size);\n+ data_offset += reply_size;\n+ }\n+ }\n+\n+ if (!validate_data_in_out(length, 0, "SerialA"))\n+ {\n+ break;\n+ }\n+ data_in += length;\n+ break;\n+ }\n+ case GCAMCommand::SerialB:\n+ {\n+ DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 32 (CARD-Interface)");\n+ if (!validate_data_in_out(1, 0, "SerialB"))\n+ break;\n+ const u32 length = *data_in++;\n+ if (!validate_data_in_out(length, 0, "SerialB"))\n+ break;\n+ if (length)\n+ {\n+ // Send Card Reply\n+ if (length == 1 && data_in[0] == 0x05)\n+ {\n+ if (m_card_read_length)\n+ {\n+ if (!validate_data_in_out(0, 1, "SerialB"))\n+ break;\n+ data_out[data_offset++] = gcam_command;\n+ u32 read_length = m_card_read_length - m_card_read;\n+\n+ if (AMMediaboard::GetGameType() == FZeroAX)\n+ {\n+ read_length = std::min<u32>(read_length, 0x2F);\n+ }\n+\n+ if (!validate_data_in_out(0, 1, "SerialB"))\n+ break;\n+ data_out[data_offset++] = read_length; // 0x2F (max size per packet)\n+\n+ if (!validate_data_in_out(0, read_length, "SerialB"))\n+ break;\n+ if (u64{m_card_read} + read_length > sizeof(m_card_read_packet))\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command SerialB, m_card_read_packet overflow:\\n"\n+ " - m_card_read_packet = {}\\n"\n+ " - m_card_read = {}\\n"\n+ " - read_length = {}",\n+ fmt::ptr(m_card_read_packet), m_card_read, read_length);\n+ data_in = data_in_end;\n+ break;\n+ }\n+ memcpy(data_out.data() + data_offset, m_card_read_packet + m_card_read,\n+ read_length);\n+\n+ data_offset += read_length;\n+ m_card_read += read_length;\n+\n+ if (m_card_read >= m_card_read_length)\n+ m_card_read_length = 0;\n+\n+ data_in += length;\n+ break;\n+ }\n+\n+ if (!validate_data_in_out(0, 5, "SerialB"))\n+ break;\n+\n+ data_out[data_offset++] = gcam_command;\n+ const u32 command_length_offset = data_offset;\n+ data_out[data_offset++] = 0x00; // len\n+\n+ data_out[data_offset++] = 0x02;\n+ const u32 checksum_start = data_offset;\n+\n+ data_out[data_offset++] = 0x00; // 0x00 len\n+\n+ data_out[data_offset++] = m_card_command; // 0x01 command\n+\n+ switch (CARDCommand(m_card_command))\n+ {\n+ case CARDCommand::Init:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x00; // 0x02\n+ data_out[data_offset++] = 0x30; // 0x03\n+ break;\n+ case CARDCommand::GetState:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x20 | m_card_bit; // 0x02\n+\n+ // bit 0: Please take your card\n+ // bit 1: Endless waiting causes UNK_E to be called\n+ data_out[data_offset++] = 0x00; // 0x03\n+ break;\n+ case CARDCommand::Read:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x02; // 0x02\n+ data_out[data_offset++] = 0x53; // 0x03\n+ break;\n+ case CARDCommand::IsPresent:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x22; // 0x02\n+ data_out[data_offset++] = 0x30; // 0x03\n+ break;\n+ case CARDCommand::Write:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x02; // 0x02\n+ data_out[data_offset++] = 0x00; // 0x03\n+ break;\n+ case CARDCommand::SetPrintParam:\n+ case CARDCommand::RegisterFont:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x00; // 0x02\n+ data_out[data_offset++] = 0x00; // 0x03\n+ break;\n+ case CARDCommand::WriteInfo:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x02; // 0x02\n+ data_out[data_offset++] = 0x00; // 0x03\n+ break;\n+ case CARDCommand::Erase:\n+ // TODO: CARDCommand::Erase is not handled.\n+ break;\n+ case CARDCommand::Eject:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ if (AMMediaboard::GetGameType() == FZeroAX)\n+ {\n+ data_out[data_offset++] = 0x01; // 0x02\n+ }\n+ else\n+ {\n+ data_out[data_offset++] = 0x31; // 0x02\n+ }\n+ data_out[data_offset++] = 0x30; // 0x03\n+ break;\n+ case CARDCommand::Clean:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x02; // 0x02\n+ data_out[data_offset++] = 0x00; // 0x03\n+ break;\n+ case CARDCommand::Load:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x02; // 0x02\n+ data_out[data_offset++] = 0x30; // 0x03\n+ break;\n+ case CARDCommand::SetShutter:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x00; // 0x02\n+ data_out[data_offset++] = 0x00; // 0x03\n+ break;\n+ }\n+\n+ if (!validate_data_in_out(0, 3, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x30; // 0x04\n+ data_out[data_offset++] = 0x00; // 0x05\n+\n+ data_out[data_offset++] = 0x03; // 0x06\n+\n+ data_out[checksum_start] = data_offset - checksum_start; // 0x00 len\n+\n+ if (!validate_data_in_out(0, 1, "SerialB"))\n+ break;\n+ data_out[data_offset] = 0; // 0x07\n+ for (u32 i = 0; i < data_out[checksum_start]; ++i)\n+ data_out[data_offset] ^= data_out[checksum_start + i];\n+\n+ if (!validate_data_in_out(0, 1, "SerialB"))\n+ break;\n+ data_offset++;\n+\n+ data_out[command_length_offset] = data_out[checksum_start] + 2;\n+ }\n+ else\n+ {\n+ if (!validate_data_in_out(length, 0, "SerialB"))\n+ break;\n+ if (u64{m_card_offset} + length > std::size(m_card_buffer))\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command SerialB, m_card_buffer overflow:\\n"\n+ " - m_card_buffer = {}\\n"\n+ " - m_card_offset = {}\\n"\n+ " - length = {}",\n+ fmt::ptr(m_card_buffer), m_card_offset, length);\n+ data_in = data_in_end;\n+ break;\n+ }\n+ for (u32 i = 0; i < length; ++i)\n+ m_card_buffer[m_card_offset + i] = data_in[i];\n+\n+ m_card_offset += length;\n+\n+ // Check if we got a complete command\n+ if (m_card_buffer[0] == 0x02)\n+ {\n+ if (m_card_buffer[1] == m_card_offset - 2)\n+ {\n+ if (m_card_buffer[m_card_offset - 2] == 0x03)\n+ {\n+ m_card_command = m_card_buffer[2];\n+\n+ switch (CARDCommand(m_card_command))\n+ {\n+ case CARDCommand::Init:\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD Init");\n+\n+ m_card_write_length = 0;\n+ m_card_bit = 0;\n+ m_card_memory_size = 0;\n+ m_card_state_call_count = 0;\n+ break;\n+ case CARDCommand::GetState:\n+ {\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD GetState({:02X})",\n+ m_card_bit);\n+\n+ if (m_card_memory_size == 0)\n+ {\n+ const std::string card_filename(\n+ fmt::format("{}tricard_{}.bin", File::GetUserPath(D_TRIUSER_IDX),\n+ SConfig::GetInstance().GetGameID()));\n+\n+ if (File::Exists(card_filename))\n+ {\n+ File::IOFile card(card_filename, "rb+");\n+ m_card_memory_size = static_cast<u32>(card.GetSize());\n+ if (m_card_memory_size > sizeof(m_card_memory))\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command CARD GetState overflow:\\n"\n+ " - file name = {}\\n"\n+ " - file size = {}\\n"\n+ " - card size = {}",\n+ card_filename, m_card_memory_size, sizeof(m_card_memory));\n+ data_in = data_in_end;\n+ break;\n+ }\n+ card.ReadBytes(m_card_memory, m_card_memory_size);\n+ card.Close();\n+\n+ m_card_is_inserted = true;\n+ }\n+ }\n+\n+ if (AMMediaboard::GetGameType() == FZeroAX && m_card_memory_size)\n+ {\n+ m_card_state_call_count++;\n+ if (m_card_state_call_count > 10)\n+ {\n+ if (m_card_bit & 2)\n+ m_card_bit &= ~2u;\n+ else\n+ m_card_bit |= 2;\n+\n+ m_card_state_call_count = 0;\n+ }\n+ }\n+\n+ if (m_card_clean == 1)\n+ {\n+ m_card_clean = 2;\n+ }\n+ else if (m_card_clean == 2)\n+ {\n+ const std::string card_filename(\n+ fmt::format("{}tricard_{}.bin", File::GetUserPath(D_TRIUSER_IDX),\n+ SConfig::GetInstance().GetGameID()));\n+\n+ if (File::Exists(card_filename))\n+ {\n+ m_card_memory_size = static_cast<u32>(File::GetSize(card_filename));\n+ if (m_card_memory_size)\n+ {\n+ if (AMMediaboard::GetGameType() == FZeroAX)\n+ {\n+ m_card_bit = 2;\n+ }\n+ else\n+ {\n+ m_card_bit = 1;\n+ }\n+ }\n+ }\n+ m_card_clean = 0;\n+ }\n+ break;\n+ }\n+ case CARDCommand::IsPresent:\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD IsPresent");\n+ break;\n+ case CARDCommand::RegisterFont:\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD RegisterFont");\n+ break;\n+ case CARDCommand::Load:\n+ {\n+ const u8 mode = m_card_buffer[6];\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD Load({:02X})",\n+ mode);\n+ break;\n+ }\n+ case CARDCommand::Clean:\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD Clean");\n+ m_card_clean = 1;\n+ break;\n+ case CARDCommand::Read:\n+ {\n+ const u8 mode = m_card_buffer[6];\n+ const u8 bitmode = m_card_buffer[7];\n+ const u8 track = m_card_buffer[8];\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command CARD Read({:02X},{:02X},{:02X})", mode,\n+ bitmode, track);\n+\n+ // Prepare read packet\n+ memset(m_card_read_packet, 0, 0xDB);\n+\n+ const std::string card_filename(\n+ fmt::format("{}tricard_{}.bin", File::GetUserPath(D_TRIUSER_IDX),\n+ SConfig::GetInstance().GetGameID()));\n+\n+ if (File::Exists(card_filename))\n+ {\n+ File::IOFile card(card_filename, "rb+");\n+ if (m_card_memory_size == 0)\n+ {\n+ m_card_memory_size = static_cast<u32>(card.GetSize());\n+ }\n+\n+ if (m_card_memory_size > sizeof(m_card_memory))\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command CARD Read overflow:\\n"\n+ " - file name = {}\\n"\n+ " - file size = {}\\n"\n+ " - card size = {}",\n+ card_filename, m_card_memory_size, sizeof(m_card_memory));\n+ data_in = data_in_end;\n+ break;\n+ }\n+ card.ReadBytes(m_card_memory, m_card_memory_size);\n+ card.Close();\n+\n+ m_card_is_inserted = true;\n+ }\n+ else if (m_card_memory_size > sizeof(m_card_memory))\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command CARD Read overflow:\\n"\n+ " - requested size = {}\\n"\n+ " - card size = {}",\n+ m_card_memory_size, sizeof(m_card_memory));\n+ data_in = data_in_end;\n+ break;\n+ }\n+\n+ m_card_read_packet[0] = 0x02; // SUB CMD\n+ m_card_read_packet[1] = 0x00; // SUB CMDLen\n+\n+ m_card_read_packet[2] = 0x33; // CARD CMD\n+\n+ if (m_card_is_inserted) // CARD Status\n+ {\n+ m_card_read_packet[3] = 0x31;\n+ }\n+ else\n+ {\n+ m_card_read_packet[3] = 0x30;\n+ }\n+\n+ m_card_read_packet[4] = 0x30;\n+ m_card_read_packet[5] = 0x30;\n+\n+ u32 packet_offset = 6;\n+ // Data reply\n+ static_assert(sizeof(m_card_read_packet) >= sizeof(m_card_memory) + 6);\n+ memcpy(m_card_read_packet + packet_offset, m_card_memory, m_card_memory_size);\n+ packet_offset += m_card_memory_size;\n+\n+ static_assert(sizeof(m_card_read_packet) >= sizeof(m_card_memory) + 7);\n+ m_card_read_packet[packet_offset++] = 0x03;\n+\n+ m_card_read_packet[1] = packet_offset - 1; // SUB CMDLen\n+\n+ static_assert(sizeof(m_card_read_packet) >= sizeof(m_card_memory) + 8);\n+ for (u32 i = 0; i < packet_offset - 1; ++i)\n+ m_card_read_packet[packet_offset] ^= m_card_read_packet[1 + i];\n+\n+ static_assert(sizeof(m_card_read_packet) >= sizeof(m_card_memory) + 9);\n+ packet_offset++;\n+\n+ m_card_read_length = packet_offset;\n+ m_card_read = 0;\n+ break;\n+ }\n+ case CARDCommand::Write:\n+ {\n+ const u8 mode = m_card_buffer[6];\n+ const u8 bitmode = m_card_buffer[7];\n+ const u8 track = m_card_buffer[8];\n+\n+ m_card_memory_size = m_card_buffer[1] - 9;\n+ if (m_card_memory_size > sizeof(m_card_memory))\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command CARD Write overflow:\\n"\n+ " - write size = {}\\n"\n+ " - card size = {}",\n+ m_card_memory_size, sizeof(m_card_memory));\n+ data_in = data_in_end;\n+ break;\n+ }\n+\n+ static_assert(sizeof(m_card_buffer) >= sizeof(m_card_memory) + 9);\n+ memcpy(m_card_memory, m_card_buffer + 9, m_card_memory_size);\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command CARD Write: {:02X} {:02X} {:02X} {}", mode,\n+ bitmode, track, m_card_memory_size);\n+\n+ const std::string card_filename(File::GetUserPath(D_TRIUSER_IDX) +\n+ "tricard_" +\n+ SConfig::GetInstance().GetGameID() + ".bin");\n+\n+ File::IOFile card(card_filename, "wb+");\n+ card.WriteBytes(m_card_memory, m_card_memory_size);\n+ card.Close();\n+\n+ m_card_bit = 2;\n+\n+ m_card_state_call_count = 0;\n+ break;\n+ }\n+ case CARDCommand::SetPrintParam:\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD SetPrintParam");\n+ break;\n+ case CARDCommand::WriteInfo:\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD WriteInfo");\n+ break;\n+ case CARDCommand::Erase:\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD Erase");\n+ break;\n+ case CARDCommand::Eject:\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD Eject");\n+ if (AMMediaboard::GetGameType() != FZeroAX)\n+ {\n+ m_card_bit = 0;\n+ }\n+ break;\n+ case CARDCommand::SetShutter:\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD SetShutter");\n+ if (AMMediaboard::GetGameType() != FZeroAX)\n+ {\n+ m_card_bit = 0;\n+ }\n+ // Close\n+ if (m_card_buffer[6] == 0x30)\n+ {\n+ m_card_shutter = false;\n+ }\n+ // Open\n+ else if (m_card_buffer[6] == 0x31)\n+ {\n+ m_card_shutter = true;\n+ }\n+ break;\n+ default:\n+ ERROR_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: CARD:Unhandled command!");\n+ ERROR_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: CARD:[{:08X}]", m_card_command);\n+ // hexdump( m_card_buffer, m_card_offset );\n+ break;\n+ }\n+ m_card_offset = 0;\n+ }\n+ }\n+ }\n+\n+ if (!validate_data_in_out(0, 3, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x32;\n+ data_out[data_offset++] = 0x01; // len\n+ data_out[data_offset++] = 0x06; // OK\n+ }\n+ }\n+ else\n+ {\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x00; // len\n+ }\n+ data_in += length;\n+ break;\n+ }\n+ case GCAMCommand::JVSIOA:\n+ case GCAMCommand::JVSIOB:\n+ {\n+ if (!validate_data_in_out(4, 0, "JVSIO"))\n+ break;\n+\n+ JVSIOMessage message;\n+\n+ static int delay = 0;\n+\n+ const u8* const frame = &data_in[0];\n+ const u8 nr_bytes = frame[3]; // Byte after E0 xx\n+ u32 frame_len = nr_bytes + 3; // Header(2) + length byte + payload + checksum\n+\n+ u8 jvs_buf[0x80];\n+\n+ frame_len = std::min<u32>(frame_len, sizeof(jvs_buf));\n+\n+ if (!validate_data_in_out(frame_len, 0, "JVSIO"))\n+ break;\n+ DEBUG_LOG_FMT(SERIALINTERFACE_JVSIO, "GC-AM: Command {:02x} (JVS IO), hexdump:\\n{}",\n+ gcam_command, HexDump(data_in, frame_len));\n+\n+ memcpy(jvs_buf, frame, frame_len);\n+\n+ // Extract node and payload pointers\n+ u8 node = jvs_buf[2];\n+ u8* jvs_io = jvs_buf + 4; // First payload byte\n+ u8* const jvs_end = jvs_buf + frame_len; // One byte before checksum\n+ u8* const jvs_begin = jvs_io;\n+\n+ message.Start(0);\n+ message.AddData(1);\n+\n+ // Helper to check that iterating over jvs_io n times is safe,\n+ // i.e. *jvs_io++ at most lead to jvs_end\n+ auto validate_jvs_io = [&](u32 n, std::string_view command) -> bool {\n+ if (jvs_io + n > jvs_end)\n+ ERROR_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: overflow in {}", command);\n+ else\n+ return true;\n+ ERROR_LOG_FMT(SERIALINTERFACE_JVSIO,\n+ "Overflow details:\\n"\n+ " - jvs_io(begin={}, current={}, end={}, n={})\\n"\n+ " - delay={}, node={}\\n"\n+ " - frame(begin={}, len={})",\n+ fmt::ptr(jvs_begin), fmt::ptr(jvs_io), fmt::ptr(jvs_end), n, delay, node,\n+ fmt::ptr(frame), frame_len);\n+ jvs_io = jvs_end;\n+ return false;\n+ };\n+\n+ // Now iterate over the payload\n+ while (jvs_io < jvs_end)\n+ {\n+ const u8 jvsio_command = *jvs_io++;\n+ DEBUG_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO:node={}, command={:02x}", node,\n+ jvsio_command);\n+\n+ switch (JVSIOCommand(jvsio_command))\n+ {\n+ case JVSIOCommand::IOID:\n+ message.AddData(StatusOkay);\n+ switch (AMMediaboard::GetGameType())\n+ {\n+ case FZeroAX:\n+ // Specific version that enables DX mode on AX machines, all this does is enable the\n+ // motion of a chair\n+ message.AddData("SEGA ENTERPRISES,LTD.;837-13844-01 I/O CNTL BD2 ;");\n+ break;\n+ case FZeroAXMonster:\n+ case MarioKartGP:\n+ case MarioKartGP2:\n+ default:\n+ message.AddData("namco ltd.;FCA-1;Ver1.01;JPN,Multipurpose + Rotary Encoder");\n+ break;\n+ case VirtuaStriker3:\n+ case VirtuaStriker4:\n+ case VirtuaStriker4_2006:\n+ message.AddData("SEGA ENTERPRISES,LTD.;I/O BD JVS;837-13551;Ver1.00");\n+ break;\n+ }\n+ NOTICE_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: Command 0x10, BoardID");\n+ message.AddData((u32)0);\n+ break;\n+ case JVSIOCommand::CommandRevision:\n+ message.AddData(StatusOkay);\n+ message.AddData(0x11);\n+ NOTICE_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: Command 0x11, CommandRevision");\n+ break;\n+ case JVSIOCommand::JVRevision:\n+ message.AddData(StatusOkay);\n+ message.AddData(0x20);\n+ NOTICE_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: Command 0x12, JVRevision");\n+ break;\n+ case JVSIOCommand::CommunicationVersion:\n+ message.AddData(StatusOkay);\n+ message.AddData(0x10);\n+ NOTICE_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: Command 0x13, CommunicationVersion");\n+ break;\n+\n+ // Slave features:\n+ //\n+ // Inputs:\n+ // 0x01: Switch input: players, buttons\n+ // 0x02: Coin input: slots\n+ // 0x03: Analog input: channels, bits\n+ // 0x04: Rotary input: channels\n+ // 0x05: Keycode input: 0,0,0 ?\n+ // 0x06: Screen position input: X bits, Y bits, channels\n+ //\n+ // Outputs:\n+ // 0x10: Card system: slots\n+ // 0x11: Medal hopper: channels\n+ // 0x12: GPO-out: slots\n+ // 0x13: Analog output: channels\n+ // 0x14: Character output: width, height, type\n+ // 0x15: Backup\n+ case JVSIOCommand::CheckFunctionality:\n+ message.AddData(StatusOkay);\n+ switch (AMMediaboard::GetGameType())\n+ {\n+ case FZeroAX:\n+ case FZeroAXMonster:\n+ // 2 Player (12bit) (p2=paddles), 1 Coin slot, 6 Analog-in\n+ // message.AddData((void *)"\\x01\\x02\\x0C\\x00", 4);\n+ // message.AddData((void *)"\\x02\\x01\\x00\\x00", 4);\n+ // message.AddData((void *)"\\x03\\x06\\x00\\x00", 4);\n+ // message.AddData((void *)"\\x00\\x00\\x00\\x00", 4);\n+ //\n+ // DX Version: 2 Player (22bit) (p2=paddles), 2 Coin slot, 8 Analog-in,\n+ // 22 Driver-out\n+ message.AddData("\\x01\\x02\\x12\\x00", 4);\n+ message.AddData("\\x02\\x02\\x00\\x00", 4);\n+ message.AddData("\\x03\\x08\\x0A\\x00", 4);\n+ message.AddData("\\x12\\x16\\x00\\x00", 4);\n+ message.AddData("\\x00\\x00\\x00\\x00", 4);\n+ break;\n+ case VirtuaStriker3:\n+ // 2 Player (13bit), 2 Coin slot, 4 Analog-in, 1 CARD, 8 Driver-out\n+ message.AddData("\\x01\\x02\\x0D\\x00", 4);\n+ message.AddData("\\x02\\x02\\x00\\x00", 4);\n+ message.AddData("\\x10\\x01\\x00\\x00", 4);\n+ message.AddData("\\x12\\x08\\x00\\x00", 4);\n+ message.AddData("\\x00\\x00\\x00\\x00", 4);\n+ break;\n+ case GekitouProYakyuu:\n+ // 2 Player (13bit), 2 Coin slot, 4 Analog-in, 1 CARD, 8 Driver-out\n+ message.AddData("\\x01\\x02\\x0D\\x00", 4);\n+ message.AddData("\\x02\\x02\\x00\\x00", 4);\n+ message.AddData("\\x03\\x04\\x00\\x00", 4);\n+ message.AddData("\\x10\\x01\\x00\\x00", 4);\n+ message.AddData("\\x12\\x08\\x00\\x00", 4);\n+ message.AddData("\\x00\\x00\\x00\\x00", 4);\n+ break;\n+ case VirtuaStriker4:\n+ case VirtuaStriker4_2006:\n+ // 2 Player (13bit), 1 Coin slot, 4 Analog-in, 1 CARD\n+ message.AddData("\\x01\\x02\\x0D\\x00", 4);\n+ message.AddData("\\x02\\x01\\x00\\x00", 4);\n+ message.AddData("\\x03\\x04\\x00\\x00", 4);\n+ message.AddData("\\x10\\x01\\x00\\x00", 4);\n+ message.AddData("\\x00\\x00\\x00\\x00", 4);\n+ break;\n+ case KeyOfAvalon:\n+ // 1 Player (15bit), 1 Coin slot, 3 Analog-in, Touch, 1 CARD, 1 Driver-out\n+ // (Unconfirmed)\n+ message.AddData("\\x01\\x01\\x0F\\x00", 4);\n+ message.AddData("\\x02\\x01\\x00\\x00", 4);\n+ message.AddData("\\x03\\x03\\x00\\x00", 4);\n+ message.AddData("\\x06\\x10\\x10\\x01", 4);\n+ message.AddData("\\x10\\x01\\x00\\x00", 4);\n+ message.AddData("\\x12\\x01\\x00\\x00", 4);\n+ message.AddData("\\x00\\x00\\x00\\x00", 4);\n+ break;\n+ case MarioKartGP:\n+ case MarioKartGP2:\n+ default:\n+ // 1 Player (15bit), 1 Coin slot, 3 Analog-in, 1 CARD, 1 Driver-out\n+ message.AddData("\\x01\\x01\\x0F\\x00", 4);\n+ message.AddData("\\x02\\x01\\x00\\x00", 4);\n+ message.AddData("\\x03\\x03\\x00\\x00", 4);\n+ message.AddData("\\x10\\x01\\x00\\x00", 4);\n+ message.AddData("\\x12\\x01\\x00\\x00", 4);\n+ message.AddData("\\x00\\x00\\x00\\x00", 4);\n+ break;\n+ }\n+ NOTICE_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: Command 0x14, CheckFunctionality");\n+ break;\n+ case JVSIOCommand::MainID:\n+ {\n+ const u8* const main_id = jvs_io;\n+ while (jvs_io < jvs_end && *jvs_io++)\n+ {\n+ }\n+ if (main_id < jvs_io)\n+ {\n+ DEBUG_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: Command MainId:\\n{}",\n+ HexDump(main_id, jvs_io - main_id));\n+ }\n+ message.AddData(StatusOkay);\n+ break;\n+ }\n+ case JVSIOCommand::SwitchesInput:\n+ {\n+ if (!validate_jvs_io(2, "SwitchesInput"))\n+ break;\n+ const u32 player_count = *jvs_io++;\n+ const u32 player_byte_count = *jvs_io++;\n+\n+ DEBUG_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: Command 0x20, SwitchInputs: {} {}",\n+ player_count, player_byte_count);\n+\n+ message.AddData(StatusOkay);\n+\n+ GCPadStatus pad_status = Pad::GetStatus(0);\n+\n+ // Test button\n+ if (pad_status.switches & SWITCH_TEST)\n+ {\n+ // Trying to access the test menu without SegaBoot present will cause a crash\n+ if (AMMediaboard::GetTestMenu())\n+ {\n+ message.AddData(0x80);\n+ }\n+ else\n+ {\n+ PanicAlertFmt("Test menu is disabled due missing SegaBoot");\n+ }\n+ }\n+ else\n+ {\n+ message.AddData((u32)0x00);\n+ }\n+\n+ for (u32 i = 0; i < player_count; ++i)\n+ {\n+ u8 player_data[3]{};\n+\n+ // Service button\n+ if (pad_status.switches & SWITCH_SERVICE)\n+ player_data[0] |= 0x40;\n+\n+ switch (AMMediaboard::GetGameType())\n+ {\n+ // Controller configuration for F-Zero AX (DX)\n+ case FZeroAX:\n+ if (i == 0)\n+ {\n+ if (m_fzdx_seatbelt)\n+ {\n+ player_data[0] |= 0x01;\n+ }\n+\n+ // Start\n+ if (pad_status.button & PAD_BUTTON_START)\n+ player_data[0] |= 0x80;\n+ // Boost\n+ if (pad_status.button & PAD_BUTTON_A)\n+ player_data[0] |= 0x02;\n+ // View Change 1\n+ if (pad_status.button & PAD_BUTTON_RIGHT)\n+ player_data[0] |= 0x20;\n+ // View Change 2\n+ if (pad_status.button & PAD_BUTTON_LEFT)\n+ player_data[0] |= 0x10;\n+ // View Change 3\n+ if (pad_status.button & PAD_BUTTON_UP)\n+ player_data[0] |= 0x08;\n+ // View Change 4\n+ if (pad_status.button & PAD_BUTTON_DOWN)\n+ player_data[0] |= 0x04;\n+ player_data[1] = m_rx_reply & 0xF0;\n+ }\n+ else if (i == 1)\n+ {\n+ // Paddle left\n+ if (pad_status.button & PAD_BUTTON_X)\n+ player_data[0] |= 0x20;\n+ // Paddle right\n+ if (pad_status.button & PAD_BUTTON_Y)\n+ player_data[0] |= 0x10;\n+\n+ if (m_fzdx_motion_stop)\n+ {\n+ player_data[0] |= 2;\n+ }\n+ if (m_fzdx_sensor_right)\n+ {\n+ player_data[0] |= 4;\n+ }\n+ if (m_fzdx_sensor_left)\n+ {\n+ player_data[0] |= 8;\n+ }\n+\n+ player_data[1] = m_rx_reply << 4;\n+ }\n+ break;\n+ // Controller configuration for F-Zero AX MonsterRide\n+ case FZeroAXMonster:\n+ if (i == 0)\n+ {\n+ if (m_fzcc_sensor)\n+ {\n+ player_data[0] |= 0x01;\n+ }\n+\n+ // Start\n+ if (pad_status.button & PAD_BUTTON_START)\n+ player_data[0] |= 0x80;\n+ // Boost\n+ if (pad_status.button & PAD_BUTTON_A)\n+ player_data[0] |= 0x02;\n+ // View Change 1\n+ if (pad_status.button & PAD_BUTTON_RIGHT)\n+ player_data[0] |= 0x20;\n+ // View Change 2\n+ if (pad_status.button & PAD_BUTTON_LEFT)\n+ player_data[0] |= 0x10;\n+ // View Change 3\n+ if (pad_status.button & PAD_BUTTON_UP)\n+ player_data[0] |= 0x08;\n+ // View Change 4\n+ if (pad_status.button & PAD_BUTTON_DOWN)\n+ player_data[0] |= 0x04;\n+\n+ player_data[1] = m_rx_reply & 0xF0;\n+ }\n+ else if (i == 1)\n+ {\n+ // Paddle left\n+ if (pad_status.button & PAD_BUTTON_X)\n+ player_data[0] |= 0x20;\n+ // Paddle right\n+ if (pad_status.button & PAD_BUTTON_Y)\n+ player_data[0] |= 0x10;\n+\n+ if (m_fzcc_seatbelt)\n+ {\n+ player_data[0] |= 2;\n+ }\n+ if (m_fzcc_service)\n+ {\n+ player_data[0] |= 4;\n+ }\n+ if (m_fzcc_emergency)\n+ {\n+ player_data[0] |= 8;\n+ }\n+ }\n+ break;\n+ // Controller configuration for Virtua Striker 3 games\n+ case VirtuaStriker3:\n+ pad_status = Pad::GetStatus(i);\n+ // Start\n+ if (pad_status.button & PAD_BUTTON_START)\n+ player_data[0] |= 0x80;\n+ // Shoot\n+ if (pad_status.button & PAD_BUTTON_B)\n+ player_data[0] |= 0x01;\n+ // Short Pass\n+ if (pad_status.button & PAD_BUTTON_A)\n+ player_data[1] |= 0x80;\n+ // Long Pass\n+ if (pad_status.button & PAD_BUTTON_X)\n+ player_data[0] |= 0x02;\n+ // Left\n+ if (pad_status.button & PAD_BUTTON_LEFT)\n+ player_data[0] |= 0x08;\n+ // Up\n+ if (pad_status.button & PAD_BUTTON_UP)\n+ player_data[0] |= 0x20;\n+ // Right\n+ if (pad_status.button & PAD_BUTTON_RIGHT)\n+ player_data[0] |= 0x04;\n+ // Down\n+ if (pad_status.button & PAD_BUTTON_DOWN)\n+ player_data[0] |= 0x10;\n+ break;\n+ // Controller configuration for Virtua Striker 4 games\n+ case VirtuaStriker4:\n+ case VirtuaStriker4_2006:\n+ {\n+ pad_status = Pad::GetStatus(i);\n+ // Start\n+ if (pad_status.button & PAD_BUTTON_START)\n+ player_data[0] |= 0x80;\n+ // Long Pass\n+ if (pad_status.button & PAD_BUTTON_X)\n+ player_data[0] |= 0x01;\n+ // Short Pass\n+ if (pad_status.button & PAD_BUTTON_A)\n+ player_data[0] |= 0x02;\n+ // Shoot\n+ if (pad_status.button & PAD_BUTTON_B)\n+ player_data[1] |= 0x80;\n+ // Dash\n+ if (pad_status.button & PAD_BUTTON_Y)\n+ player_data[1] |= 0x40;\n+ // Tactics (U)\n+ if (pad_status.button & PAD_BUTTON_LEFT)\n+ player_data[0] |= 0x20;\n+ // Tactics (M)\n+ if (pad_status.button & PAD_BUTTON_UP)\n+ player_data[0] |= 0x08;\n+ // Tactics (D)\n+ if (pad_status.button & PAD_BUTTON_RIGHT)\n+ player_data[0] |= 0x04;\n+\n+ if (i == 0)\n+ {\n+ player_data[0] |= 0x10; // IC-Card Switch ON\n+\n+ // IC-Card Lock\n+ if (pad_status.button & PAD_BUTTON_DOWN)\n+ player_data[1] |= 0x20;\n+ }\n+ }\n+ break;\n+ // Controller configuration for Gekitou Pro Yakyuu\n+ case GekitouProYakyuu:\n+ pad_status = Pad::GetStatus(i);\n+ // Start\n+ if (pad_status.button & PAD_BUTTON_START)\n+ player_data[0] |= 0x80;\n+ // A\n+ if (pad_status.button & PAD_BUTTON_B)\n+ player_data[0] |= 0x01;\n+ // B\n+ if (pad_status.button & PAD_BUTTON_A)\n+ player_data[0] |= 0x02;\n+ // Gekitou\n+ if (pad_status.button & PAD_TRIGGER_L)\n+ player_data[1] |= 0x80;\n+ // Left\n+ if (pad_status.button & PAD_BUTTON_LEFT)\n+ player_data[0] |= 0x08;\n+ // Up\n+ if (pad_status.button & PAD_BUTTON_UP)\n+ player_data[0] |= 0x20;\n+ // Right\n+ if (pad_status.button & PAD_BUTTON_RIGHT)\n+ player_data[0] |= 0x04;\n+ // Down\n+ if (pad_status.button & PAD_BUTTON_DOWN)\n+ player_data[0] |= 0x10;\n+ break;\n+ // Controller configuration for Mario Kart and other games\n+ default:\n+ case MarioKartGP:\n+ case MarioKartGP2:\n+ {\n+ // Start\n+ if (pad_status.button & PAD_BUTTON_START)\n+ player_data[0] |= 0x80;\n+ // Item button\n+ if (pad_status.button & PAD_BUTTON_A)\n+ player_data[1] |= 0x20;\n+ // VS-Cancel button\n+ if (pad_status.button & PAD_BUTTON_B)\n+ player_data[1] |= 0x02;\n+ }\n+ break;\n+ case KeyOfAvalon:\n+ {\n+ // Debug On\n+ if (pad_status.button & PAD_BUTTON_START)\n+ player_data[0] |= 0x80;\n+ // Switch 1\n+ if (pad_status.button & PAD_BUTTON_A)\n+ player_data[0] |= 0x04;\n+ // Switch 2\n+ if (pad_status.button & PAD_BUTTON_B)\n+ player_data[0] |= 0x08;\n+ // Toggle inserted card\n+ if (pad_status.button & PAD_TRIGGER_L)\n+ {\n+ m_ic_card_status ^= ICCARDStatus::NoCard;\n+ }\n+ }\n+ break;\n+ }\n+\n+ for (u32 j = 0; j < player_byte_count; ++j)\n+ message.AddData(player_data[j]);\n+ }\n+ break;\n+ }\n+ case JVSIOCommand::CoinInput:\n+ {\n+ if (!validate_jvs_io(1, "CoinInput"))\n+ break;\n+ const u32 slots = *jvs_io++;\n+ message.AddData(StatusOkay);\n+ for (u32 i = 0; i < slots; i++)\n+ {\n+ GCPadStatus pad_status = Pad::GetStatus(i);\n+ if ((pad_status.switches & SWITCH_COIN) && !m_coin_pressed[i])\n+ {\n+ m_coin[i]++;\n+ }\n+ m_coin_pressed[i] = pad_status.switches & SWITCH_COIN;\n+ message.AddData((m_coin[i] >> 8) & 0x3f);\n+ message.AddData(m_coin[i] & 0xff);\n+ }\n+ DEBUG_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: Command 0x21, CoinInput: {}", slots);\n+ break;\n+ }\n+ case JVSIOCommand::AnalogInput:\n+ {\n+ if (!validate_jvs_io(1, "AnalogInput"))\n+ break;\n+ message.AddData(StatusOkay);\n+\n+ const u32 analogs = *jvs_io++;\n+ GCPadStatus pad_status = Pad::GetStatus(0);\n+\n+ DEBUG_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: Command 0x22, AnalogInput: {}",\n+ analogs);\n+\n+ switch (AMMediaboard::GetGameType())\n+ {\n+ case FZeroAX:\n+ case FZeroAXMonster:\n+ // Steering\n+ if (m_motor_init == 1)\n+ {\n+ if (m_motor_force_y > 0)\n+ {\n+ message.AddData(0x80 - (m_motor_force_y >> 8));\n+ }\n+ else\n+ {\n+ message.AddData((m_motor_force_y >> 8));\n+ }\n+ message.AddData((u8)0);\n+\n+ message.AddData(pad_status.stickY);\n+ message.AddData((u8)0);\n+ }\n+ else\n+ {\n+ // The center for the Y axis is expected to be 78h this adjusts that\n+ message.AddData(pad_status.stickX - 12);\n+ message.AddData((u8)0);\n+\n+ message.AddData(pad_status.stickY);\n+ message.AddData((u8)0);\n+ }\n+\n+ // Unused\n+ message.AddData((u8)0);\n+ message.AddData((u8)0);\n+ message.AddData((u8)0);\n+ message.AddData((u8)0);\n+\n+ // Gas\n+ message.AddData(pad_status.triggerRight);\n+ message.AddData((u8)0);\n+\n+ // Brake\n+ message.AddData(pad_status.triggerLeft);\n+ message.AddData((u8)0);\n+\n+ message.AddData((u8)0x80); // Motion Stop\n+ message.AddData((u8)0);\n+\n+ message.AddData((u8)0);\n+ message.AddData((u8)0);\n+\n+ break;\n+ case VirtuaStriker4:\n+ case VirtuaStriker4_2006:\n+ {\n+ message.AddData(-pad_status.stickY);\n+ message.AddData((u8)0);\n+ message.AddData(pad_status.stickX);\n+ message.AddData((u8)0);\n+\n+ pad_status = Pad::GetStatus(1);\n+\n+ message.AddData(-pad_status.stickY);\n+ message.AddData((u8)0);\n+ message.AddData(pad_status.stickX);\n+ message.AddData((u8)0);\n+ }\n+ break;\n+ default:\n+ case MarioKartGP:\n+ case MarioKartGP2:\n+ // Steering\n+ message.AddData(pad_status.stickX);\n+ message.AddData((u8)0);\n+\n+ // Gas\n+ message.AddData(pad_status.triggerRight);\n+ message.AddData((u8)0);\n+\n+ // Brake\n+ message.AddData(pad_status.triggerLeft);\n+ message.AddData((u8)0);\n+ break;\n+ }\n+ break;\n+ }\n+ case JVSIOCommand::PositionInput:\n+ {\n+ if (!validate_jvs_io(1, "PositionInput"))\n+ break;\n+ const u32 channel = *jvs_io++;\n+\n+ const GCPadStatus pad_status = Pad::GetStatus(0);\n+\n+ if (pad_status.button & PAD_TRIGGER_R)\n+ {\n+ // Tap at center of screen (~320,240)\n+ message.AddData("\\x01\\x00\\x8C\\x01\\x95",\n+ 5); // X=320 (0x0140), Y=240 (0x00F0)\n+ }\n+ else\n+ {\n+ message.AddData("\\x01\\xFF\\xFF\\xFF\\xFF", 5);\n+ }\n+\n+ DEBUG_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: Command 0x25, PositionInput:{}",\n+ channel);\n+ break;\n+ }\n+ case JVSIOCommand::CoinSubOutput:\n+ {\n+ if (!validate_jvs_io(3, "CoinSubOutput"))\n+ break;\n+ const u32 slot = *jvs_io++;\n+ const u8 coinh = *jvs_io++;\n+ const u8 coinl = *jvs_io++;\n+\n+ m_coin[slot] -= (coinh << 8) | coinl;\n+\n+ message.AddData(StatusOkay);\n+ DEBUG_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: Command 0x30, CoinSubOutput: {}", slot);\n+ break;\n+ }\n+ case JVSIOCommand::GeneralDriverOutput:\n+ {\n+ if (!validate_jvs_io(1, "GeneralDriverOutput"))\n+ break;\n+ const u32 bytes = *jvs_io++;\n+\n+ if (bytes)\n+ {\n+ message.AddData(StatusOkay);\n+\n+ // The lamps are controlled via this\n+ if (AMMediaboard::GetGameType() == MarioKartGP)\n+ {\n+ if (!validate_jvs_io(1, "GeneralDriverOutput (MarioKartGP)"))\n+ break;\n+ const u32 status = *jvs_io++;\n+ if (status & 4)\n+ {\n+ DEBUG_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: Command 32, Item Button ON");\n+ }\n+ else\n+ {\n+ DEBUG_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: Command 32, Item Button OFF");\n+ }\n+ if (status & 8)\n+ {\n+ DEBUG_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: Command 32, Cancel Button ON");\n+ }\n+ else\n+ {\n+ DEBUG_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: Command 32, Cancel Button OFF");\n+ }\n+ break;\n+ }\n+\n+ if (!validate_jvs_io(bytes, "GeneralDriverOutput"))\n+ break;\n+ Common::UniqueBuffer<u8> buf(bytes);\n+\n+ for (u32 i = 0; i < bytes; ++i)\n+ {\n+ buf[i] = *jvs_io++;', 'path': 'Source/Core/Core/HW/SI/SI_DeviceAMBaseboard.cpp', 'position': 2326, 'original_position': 2326, 'commit_id': '5bf8fe93286ebe4f08fb469a44f259a8397c89e0', 'user': {'login': 'JosJuice', 'id': 6716818, 'node_id': 'MDQ6VXNlcjY3MTY4MTg=', 'avatar_url': 'https://avatars.githubusercontent.com/u/6716818?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/JosJuice', 'html_url': 'https://github.com/JosJuice', 'followers_url': 'https://api.github.com/users/JosJuice/followers', 'following_url': 'https://api.github.com/users/JosJuice/following{/other_user}', 'gists_url': 'https://api.github.com/users/JosJuice/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/JosJuice/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/JosJuice/subscriptions', 'organizations_url': 'https://api.github.com/users/JosJuice/orgs', 'repos_url': 'https://api.github.com/users/JosJuice/repos', 'events_url': 'https://api.github.com/users/JosJuice/events{/privacy}', 'received_events_url': 'https://api.github.com/users/JosJuice/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'body': 'Not a security issue, but why are we copying this to a buffer? We should be able to just read the values straight out of `jvs_io`.', 'created_at': '2026-02-01T15:25:43Z', 'updated_at': '2026-02-01T15:40:26Z', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/13844#discussion_r2751395550', 'pull_request_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13844', 'author_association': 'MEMBER', '_links': {'self': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2751395550'}, 'html': {'href': 'https://github.com/dolphin-emu/dolphin/pull/13844#discussion_r2751395550'}, 'pull_request': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13844'}}, 'original_commit_id': '5bf8fe93286ebe4f08fb469a44f259a8397c89e0', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2751395550/reactions', 'total_count': 0, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 0, 'confused': 0, 'heart': 0, 'rocket': 0, 'eyes': 0}}, {'id': 2751418497, 'node_id': 'PRRC_kwDOALCn2M6j_1CB', 'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2751418497', 'pull_request_review_id': 3735806017, 'diff_hunk': '@@ -12,13 +12,46 @@\n #include "DiscIO/DiscUtils.h"\n #include "DiscIO/Enums.h"\n #include "DiscIO/Filesystem.h"\n+#include "DiscIO/VolumeGC.h"\n \n namespace DiscIO\n {\n std::string VolumeDisc::GetGameID(const Partition& partition) const\n {\n char id[6];\n \n+ if (GetVolumeType() == Platform::Triforce)\n+ {\n+ // Triforce games have their Game ID stored in the boot.id file\n+ const BootID* boot_id = static_cast<const VolumeGC*>(this)->GetTriforceBootID();\n+\n+ // Construct game ID from the BTID\n+ id[0] = \'G\';\n+\n+ memcpy(id + 1, boot_id->game_id.data() + 2, 2);\n+\n+ switch (GetCountry())\n+ {\n+ default:\n+ case Country::Japan:\n+ id[3] = \'J\';\n+ break;\n+ case Country::Taiwan:\n+ id[3] = \'W\';\n+ break;\n+ case Country::USA:\n+ id[3] = \'E\';\n+ break;\n+ case Country::Europe:\n+ id[3] = \'P\';\n+ break;\n+ }\n+\n+ memcpy(id + 4, GetMakerID().c_str(), 2);', 'path': 'Source/Core/DiscIO/VolumeDisc.cpp', 'position': 39, 'original_position': 39, 'commit_id': '5bf8fe93286ebe4f08fb469a44f259a8397c89e0', 'user': {'login': 'JosJuice', 'id': 6716818, 'node_id': 'MDQ6VXNlcjY3MTY4MTg=', 'avatar_url': 'https://avatars.githubusercontent.com/u/6716818?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/JosJuice', 'html_url': 'https://github.com/JosJuice', 'followers_url': 'https://api.github.com/users/JosJuice/followers', 'following_url': 'https://api.github.com/users/JosJuice/following{/other_user}', 'gists_url': 'https://api.github.com/users/JosJuice/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/JosJuice/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/JosJuice/subscriptions', 'organizations_url': 'https://api.github.com/users/JosJuice/orgs', 'repos_url': 'https://api.github.com/users/JosJuice/repos', 'events_url': 'https://api.github.com/users/JosJuice/events{/privacy}', 'received_events_url': 'https://api.github.com/users/JosJuice/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'body': '`GetMakerID` can return an empty string.', 'created_at': '2026-02-01T15:34:31Z', 'updated_at': '2026-02-01T15:40:26Z', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/13844#discussion_r2751418497', 'pull_request_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13844', 'author_association': 'MEMBER', '_links': {'self': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2751418497'}, 'html': {'href': 'https://github.com/dolphin-emu/dolphin/pull/13844#discussion_r2751418497'}, 'pull_request': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13844'}}, 'original_commit_id': '5bf8fe93286ebe4f08fb469a44f259a8397c89e0', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2751418497/reactions', 'total_count': 0, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 0, 'confused': 0, 'heart': 0, 'rocket': 0, 'eyes': 0}}], 'type': 'gh_pull_request_review'}
2026-02-01T11:45:30.418183 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'JosJuice', 'action': 'submitted', 'pr_id': 13768, 'pr_title': 'Core: Create fastmem mappings for page address translation', 'state': 'commented', 'url': 'https://github.com/dolphin-emu/dolphin/pull/13768#pullrequestreview-3735600927', 'comments': [{'id': 2751118036, 'node_id': 'PRRC_kwDOALCn2M6j-rrU', 'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2751118036', 'pull_request_review_id': 3735600927, 'diff_hunk': '@@ -305,6 +319,184 @@ void MemoryManager::UpdateLogicalMemory(const PowerPC::BatTable& dbat_table)\n }\n }\n \n+void MemoryManager::AddPageTableMapping(u32 logical_address, u32 translated_address, bool writeable)\n+{\n+ switch (m_host_page_type)\n+ {\n+ case HostPageType::SmallPages:\n+ return AddHostPageTableMapping(logical_address, translated_address, writeable,\n+ PowerPC::HW_PAGE_SIZE);\n+ case HostPageType::LargePages:\n+ return TryAddLargePageTableMapping(logical_address, translated_address, writeable);\n+ default:\n+ return;\n+ }\n+}\n+\n+void MemoryManager::TryAddLargePageTableMapping(u32 logical_address, u32 translated_address,\n+ bool writeable)\n+{\n+ const bool add_readable =\n+ TryAddLargePageTableMapping(logical_address, translated_address, m_large_readable_pages);\n+\n+ const bool add_writeable =\n+ writeable &&\n+ TryAddLargePageTableMapping(logical_address, translated_address, m_large_writeable_pages);\n+\n+ if (add_readable || add_writeable)\n+ {\n+ AddHostPageTableMapping(logical_address & ~(m_page_size - 1),\n+ translated_address & ~(m_page_size - 1), add_writeable, m_page_size);\n+ }\n+}\n+\n+bool MemoryManager::TryAddLargePageTableMapping(u32 logical_address, u32 translated_address,\n+ std::map<u32, std::vector<u32>>& map)\n+{\n+ std::vector<u32>& entries = map[logical_address & ~(m_page_size - 1)];\n+\n+ if (entries.empty())\n+ entries = std::vector<u32>(m_guest_pages_per_host_page, INVALID_MAPPING);\n+\n+ entries[(logical_address & (m_page_size - 1)) / PowerPC::HW_PAGE_SIZE] = translated_address;\n+\n+ return CanCreateHostMappingForGuestPages(entries);\n+}\n+\n+bool MemoryManager::CanCreateHostMappingForGuestPages(std::vector<u32>& entries) const\n+{\n+ const u32 translated_address = entries[0];\n+ if ((translated_address & (m_page_size - 1)) != 0)\n+ return false;\n+\n+ for (size_t i = 1; i < m_guest_pages_per_host_page; ++i)\n+ {\n+ if (entries[i] != translated_address + i * PowerPC::HW_PAGE_SIZE)\n+ return false;\n+ }\n+\n+ return true;\n+}\n+\n+void MemoryManager::AddHostPageTableMapping(u32 logical_address, u32 translated_address,\n+ bool writeable, u32 logical_size)\n+{\n+ for (const auto& physical_region : m_physical_regions)\n+ {\n+ if (!physical_region.active)\n+ continue;\n+\n+ u32 mapping_address = physical_region.physical_address;\n+ u32 mapping_end = mapping_address + physical_region.size;\n+ u32 intersection_start = std::max(mapping_address, translated_address);\n+ u32 intersection_end = std::min(mapping_end, translated_address + logical_size);\n+ if (intersection_start < intersection_end)\n+ {\n+ // Found an overlapping region; map it.\n+ if (m_is_fastmem_arena_initialized)', 'path': 'Source/Core/Core/HW/Memmap.cpp', 'position': 216, 'original_position': 216, 'commit_id': '3fdce1e8bd9901e5d852dea23a271ca48434ac37', 'user': {'login': 'JosJuice', 'id': 6716818, 'node_id': 'MDQ6VXNlcjY3MTY4MTg=', 'avatar_url': 'https://avatars.githubusercontent.com/u/6716818?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/JosJuice', 'html_url': 'https://github.com/JosJuice', 'followers_url': 'https://api.github.com/users/JosJuice/followers', 'following_url': 'https://api.github.com/users/JosJuice/following{/other_user}', 'gists_url': 'https://api.github.com/users/JosJuice/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/JosJuice/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/JosJuice/subscriptions', 'organizations_url': 'https://api.github.com/users/JosJuice/orgs', 'repos_url': 'https://api.github.com/users/JosJuice/repos', 'events_url': 'https://api.github.com/users/JosJuice/events{/privacy}', 'received_events_url': 'https://api.github.com/users/JosJuice/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'body': "It's based on the existing `UpdateDBATMappings` function, where we can't just return early. I'll change it.", 'created_at': '2026-02-01T11:45:28Z', 'updated_at': '2026-02-01T11:45:28Z', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/13768#discussion_r2751118036', 'pull_request_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13768', 'author_association': 'MEMBER', '_links': {'self': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2751118036'}, 'html': {'href': 'https://github.com/dolphin-emu/dolphin/pull/13768#discussion_r2751118036'}, 'pull_request': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13768'}}, 'original_commit_id': '3fdce1e8bd9901e5d852dea23a271ca48434ac37', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2751118036/reactions', 'total_count': 0, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 0, 'confused': 0, 'heart': 0, 'rocket': 0, 'eyes': 0}, 'in_reply_to_id': 2726396883}], 'type': 'gh_pull_request_review'}
2026-01-31T22:44:15.583035 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'Dentomologist', 'action': 'submitted', 'pr_id': 14312, 'pr_title': 'WGL: Correctly load wglDestroyPbufferARB extension', 'state': 'approved', 'url': 'https://github.com/dolphin-emu/dolphin/pull/14312#pullrequestreview-3733793408', 'comments': [], 'type': 'gh_pull_request_review'}
2026-01-31T19:02:47.353318 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'JosJuice', 'action': 'submitted', 'pr_id': 13844, 'pr_title': 'Core: Triforce support', 'state': 'commented', 'url': 'https://github.com/dolphin-emu/dolphin/pull/13844#pullrequestreview-3733278263', 'comments': [{'id': 2749868593, 'node_id': 'PRRC_kwDOALCn2M6j56ox', 'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2749868593', 'pull_request_review_id': 3733278263, 'diff_hunk': '@@ -0,0 +1,2501 @@\n+// Copyright 2025 Dolphin Emulator Project\n+// SPDX-License-Identifier: GPL-2.0-or-later\n+\n+#include "Core/HW/SI/SI_DeviceAMBaseboard.h"\n+\n+#include <algorithm>\n+#include <numeric>\n+#include <string>\n+\n+#include <fmt/format.h>\n+\n+#include "Common/Buffer.h"\n+#include "Common/CommonTypes.h"\n+#include "Common/FileUtil.h"\n+#include "Common/IOFile.h"\n+#include "Common/Logging/Log.h"\n+#include "Common/MsgHandler.h"\n+#include "Common/Swap.h"\n+\n+#include "Core/Boot/Boot.h"\n+#include "Core/BootManager.h"\n+#include "Core/Config/MainSettings.h"\n+#include "Core/ConfigManager.h"\n+#include "Core/Core.h"\n+#include "Core/CoreTiming.h"\n+#include "Core/HW/DVD/AMMediaboard.h"\n+#include "Core/HW/DVD/DVDInterface.h"\n+#include "Core/HW/EXI/EXI.h"\n+#include "Core/HW/GCPad.h"\n+#include "Core/HW/MMIO.h"\n+#include "Core/HW/Memmap.h"\n+#include "Core/HW/ProcessorInterface.h"\n+#include "Core/HW/SI/SI.h"\n+#include "Core/HW/SI/SI_Device.h"\n+#include "Core/HW/SI/SI_DeviceGCController.h"\n+#include "Core/HW/SystemTimers.h"\n+#include "Core/Movie.h"\n+#include "Core/NetPlayProto.h"\n+#include "Core/System.h"\n+\n+#include "InputCommon/GCPadStatus.h"\n+\n+namespace SerialInterface\n+{\n+void JVSIOMessage::Start(int node)\n+{\n+ m_last_start = m_pointer;\n+ const u8 header[3] = {0xE0, (u8)node, 0};\n+ m_checksum = 0;\n+ AddData(header, 3, 1);\n+}\n+\n+void JVSIOMessage::AddData(const u8* dst, size_t len, int sync = 0)\n+{\n+ if (m_pointer + len >= sizeof(m_message))\n+ {\n+ PanicAlertFmt("JVSIOMessage overrun!");\n+ return;\n+ }\n+\n+ while (len--)\n+ {\n+ const u8 c = *dst++;\n+ if (!sync && ((c == 0xE0) || (c == 0xD0)))\n+ {\n+ if (m_pointer + 2 > sizeof(m_message))\n+ {\n+ PanicAlertFmt("JVSIOMessage overrun!");\n+ break;\n+ }\n+ m_message[m_pointer++] = 0xD0;\n+ m_message[m_pointer++] = c - 1;\n+ }\n+ else\n+ {\n+ if (m_pointer >= sizeof(m_message))\n+ {\n+ PanicAlertFmt("JVSIOMessage overrun!");\n+ break;\n+ }\n+ m_message[m_pointer++] = c;\n+ }\n+\n+ if (!sync)\n+ m_checksum += c;\n+ sync = 0;\n+ }\n+}\n+\n+void JVSIOMessage::AddData(const void* data, size_t len)\n+{\n+ AddData(static_cast<const u8*>(data), len);\n+}\n+\n+void JVSIOMessage::AddData(const char* data)\n+{\n+ AddData(data, strlen(data));\n+}\n+\n+void JVSIOMessage::AddData(u32 n)\n+{\n+ const u8 cs = n;\n+ AddData(&cs, 1);\n+}\n+\n+void JVSIOMessage::End()\n+{\n+ const u32 len = m_pointer - m_last_start;\n+ if (m_last_start + 2 < sizeof(m_message) && len >= 3)\n+ {\n+ m_message[m_last_start + 2] = len - 2; // assuming len <0xD0\n+ AddData(m_checksum + len - 2);\n+ }\n+ else\n+ {\n+ PanicAlertFmt("JVSIOMessage: Not enough space for checksum!");\n+ }\n+}\n+\n+static constexpr u8 CheckSumXOR(const u8* data, u32 length)\n+{\n+ return std::accumulate(data, data + length, u8{}, std::bit_xor());\n+}\n+\n+static constexpr char s_cdr_program_version[] = {" Version 1.22,2003/09/19,171-8213B"};\n+static constexpr char s_cdr_boot_version[] = {" Version 1.04,2003/06/17,171-8213B"};\n+static constexpr u8 s_cdr_card_data[] = {\n+ 0x00, 0x6E, 0x00, 0x00, 0x01, 0x00, 0x00, 0x06, 0x00, 0x00, 0x07, 0x00, 0x00, 0x0B,\n+ 0x00, 0x00, 0x0E, 0x00, 0x00, 0x10, 0x00, 0x00, 0x17, 0x00, 0x00, 0x19, 0x00, 0x00,\n+ 0x1A, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x20, 0x00,\n+ 0x00, 0x22, 0x00, 0x00, 0x23, 0x00, 0x00, 0x24, 0x00, 0x00, 0x27, 0x00, 0x00, 0x28,\n+ 0x00, 0x00, 0x2C, 0x00, 0x00, 0x2F, 0x00, 0x00, 0x34, 0x00, 0x00, 0x35, 0x00, 0x00,\n+ 0x37, 0x00, 0x00, 0x38, 0x00, 0x00, 0x39, 0x00, 0x00, 0x3D, 0x00};\n+\n+const constexpr u8 s_region_flags[] = "\\x00\\x00\\x30\\x00"\n+ // "\\x01\\xfe\\x00\\x00" // JAPAN\n+ "\\x02\\xfd\\x00\\x00" // USA\n+ //"\\x03\\xfc\\x00\\x00" // export\n+ "\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff";\n+// AM-Baseboard device on SI\n+CSIDevice_AMBaseboard::CSIDevice_AMBaseboard(Core::System& system, SIDevices device,\n+ int device_number)\n+ : ISIDevice(system, device, device_number)\n+{\n+ // Card ID\n+ m_ic_card_data[0x20] = 0x95;\n+ m_ic_card_data[0x21] = 0x71;\n+\n+ if (AMMediaboard::GetGameType() == KeyOfAvalon)\n+ {\n+ m_ic_card_data[0x22] = 0x26;\n+ m_ic_card_data[0x23] = 0x40;\n+ }\n+ else if (AMMediaboard::GetGameType() == VirtuaStriker4)\n+ {\n+ m_ic_card_data[0x22] = 0x44;\n+ m_ic_card_data[0x23] = 0x00;\n+ }\n+\n+ // Use count\n+ m_ic_card_data[0x28] = 0xFF;\n+ m_ic_card_data[0x29] = 0xFF;\n+}\n+\n+constexpr u32 SI_XFER_LENGTH_MASK = 0x7f;\n+\n+// Translate [0,1,2,...,126,127] to [128,1,2,...,126,127]\n+constexpr s32 ConvertSILengthField(u32 field)\n+{\n+ return ((field - 1) & SI_XFER_LENGTH_MASK) + 1;\n+}\n+\n+void CSIDevice_AMBaseboard::ICCardSendReply(ICCommand* iccommand, u8* buffer, u32* length)\n+{\n+ iccommand->status = Common::swap16(iccommand->status);\n+\n+ const auto iccommand_data = reinterpret_cast<const u8*>(iccommand);\n+ const u8 crc = CheckSumXOR(iccommand_data + 2, iccommand->pktlen - 1);\n+\n+ for (u32 i = 0; i < iccommand->pktlen + 1; ++i)\n+ {\n+ buffer[(*length)++] = iccommand_data[i];\n+ }\n+\n+ buffer[(*length)++] = crc;\n+}\n+\n+void CSIDevice_AMBaseboard::SwapBuffers(u8* buffer, u32* buffer_length)\n+{\n+ memcpy(m_last[1], buffer, 0x80); // Save current buffer\n+ memcpy(buffer, m_last[0], 0x80); // Load previous buffer\n+ memcpy(m_last[0], m_last[1], 0x80); // Update history\n+\n+ m_lastptr[1] = *buffer_length; // Swap lengths\n+ *buffer_length = m_lastptr[0];\n+ m_lastptr[0] = m_lastptr[1];\n+}\n+\n+int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length)\n+{\n+ const auto& serial_interface = m_system.GetSerialInterface();\n+ u32 buffer_length = ConvertSILengthField(serial_interface.GetInLength());\n+\n+ // Debug logging\n+ ISIDevice::RunBuffer(buffer, buffer_length);\n+\n+ u32 buffer_position = 0;\n+ while (buffer_position < buffer_length)\n+ {\n+ BaseBoardCommand command = static_cast<BaseBoardCommand>(buffer[buffer_position]);\n+ buffer_position++;\n+\n+ switch (command)\n+ {\n+ case BaseBoardCommand::GCAM_Reset: // Returns ID and dip switches\n+ {\n+ const u32 id = Common::swap32(SI_AM_BASEBOARD | 0x100);\n+ std::memcpy(buffer, &id, sizeof(id));\n+ return sizeof(id);\n+ }\n+ break;\n+ case BaseBoardCommand::GCAM_Command:\n+ {\n+ u32 checksum = 0;\n+ for (u32 i = 0; i < buffer_length; ++i)\n+ checksum += buffer[i];\n+\n+ std::array<u8, 0x80> data_out{};\n+ u32 data_offset = 0;\n+\n+ static u32 dip_switch_1 = 0xFE;\n+ static u32 dip_switch_0 = 0xFF;\n+\n+ data_out[data_offset++] = 1;\n+ data_out[data_offset++] = 1;\n+\n+ u8* data_in = buffer + 2;\n+ u8* const data_in_end = buffer + buffer[buffer_position] + 2;\n+\n+ // Helper to check that iterating over data n times is safe,\n+ // i.e. *data++ at most lead to data.end()\n+ auto validate_data_in_out = [&](u32 n_in, u32 n_out, std::string_view command) -> bool {\n+ if (data_in + n_in > data_in_end)\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: data_in overflow in {}", command);\n+ else if (data_offset + n_out > data_out.size())\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: data_out overflow in {}", command);\n+ else\n+ return true;\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "Overflow details:\\n"\n+ " - data_in(begin={}, current={}, end={}, n_in={})\\n"\n+ " - data_out(offset={}, size={}, n_out={})\\n"\n+ " - buffer(position={}, length={})",\n+ fmt::ptr(buffer + 2), fmt::ptr(data_in), fmt::ptr(data_in_end), n_in,\n+ data_offset, data_out.size(), n_out, buffer_position, buffer_length);\n+ data_in = data_in_end;\n+ return false;\n+ };\n+\n+ while (data_in < data_in_end)\n+ {\n+ const u32 gcam_command = *data_in++;\n+ switch (GCAMCommand(gcam_command))\n+ {\n+ case GCAMCommand::StatusSwitches:\n+ {\n+ if (!validate_data_in_out(1, 4, "StatusSwitches"))\n+ break;\n+\n+ const u8 status = *data_in++;\n+ DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x10, {:02x} (READ STATUS&SWITCHES)",\n+ status);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x2;\n+\n+ // We read Test/Service from the JVS-I/O SwitchesInput instead\n+ //\n+ // const GCPadStatus pad_status = Pad::GetStatus(ISIDevice::m_device_number);\n+ // baseboard test/service switches\n+ // if (pad_status.button & PAD_BUTTON_Y)\t// Test\n+ // dip_switch_0 &= ~0x80;\n+ // if (pad_status.button & PAD_BUTTON_X)\t// Service\n+ // dip_switch_0 &= ~0x40;\n+\n+ // Horizontal Scanning Frequency switch\n+ // Required for F-Zero AX booting via Sega Boot\n+ if (AMMediaboard::GetGameType() == FZeroAX ||\n+ AMMediaboard::GetGameType() == FZeroAXMonster)\n+ {\n+ dip_switch_0 &= ~0x20;\n+ }\n+\n+ // Disable camera in MKGP1/2\n+ if (AMMediaboard::GetGameType() == MarioKartGP ||\n+ AMMediaboard::GetGameType() == MarioKartGP2)\n+ {\n+ dip_switch_0 &= ~0x10;\n+ }\n+\n+ data_out[data_offset++] = dip_switch_0;\n+ data_out[data_offset++] = dip_switch_1;\n+ break;\n+ }\n+ case GCAMCommand::SerialNumber:\n+ {\n+ if (!validate_data_in_out(1, 18, "SerialNumber"))\n+ break;\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x11, {:02x} (READ SERIAL NR)",\n+ *data_in);\n+ data_in++;\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 16;\n+ memcpy(data_out.data() + data_offset, "AADE-01B98394904", 16);\n+\n+ data_offset += 16;\n+ break;\n+ }\n+ case GCAMCommand::Unknown_12:\n+ if (!validate_data_in_out(2, 2, "Unknown_12"))\n+ break;\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x12, {:02x} {:02x}", data_in[0],\n+ data_in[1]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x00;\n+\n+ data_in += 2;\n+ break;\n+ case GCAMCommand::Unknown_14:\n+ if (!validate_data_in_out(2, 2, "Unknown_14"))\n+ break;\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x14, {:02x} {:02x}", data_in[0],\n+ data_in[1]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x00;\n+\n+ data_in += 2;\n+ break;\n+ case GCAMCommand::FirmVersion:\n+ if (!validate_data_in_out(1, 4, "FirmVersion"))\n+ break;\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x15, {:02x} (READ FIRM VERSION)",\n+ *data_in);\n+ data_in++;\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x02;\n+ // 00.26\n+ data_out[data_offset++] = 0x00;\n+ data_out[data_offset++] = 0x26;\n+ break;\n+ case GCAMCommand::FPGAVersion:\n+ if (!validate_data_in_out(1, 4, "FPGAVersion"))\n+ break;\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x16, {:02x} (READ FPGA VERSION)",\n+ *data_in);\n+ data_in++;\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x02;\n+ // 07.06\n+ data_out[data_offset++] = 0x07;\n+ data_out[data_offset++] = 0x06;\n+ break;\n+ case GCAMCommand::RegionSettings:\n+ {\n+ if (!validate_data_in_out(5, 0x16, "RegionSettings"))\n+ break;\n+\n+ // Used by SegaBoot for region checks (dev mode skips this check)\n+ // In some games this also controls the displayed language\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x1F, {:02x} {:02x} {:02x} {:02x} {:02x} (REGION)",\n+ data_in[0], data_in[1], data_in[2], data_in[3], data_in[4]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x14;\n+\n+ for (int i = 0; i < 0x14; ++i)\n+ data_out[data_offset++] = s_region_flags[i];\n+\n+ data_in += 5;\n+ }\n+ break;\n+ // No reply\n+ // Note: Always sends three bytes even though size is set to two\n+ case GCAMCommand::Unknown_21:\n+ {\n+ if (!validate_data_in_out(4, 0, "Unknown_21"))\n+ break;\n+\n+ DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x21, {:02x}, {:02x}, {:02x}, {:02x}",\n+ data_in[0], data_in[1], data_in[2], data_in[3]);\n+ data_in += 4;\n+ }\n+ break;\n+ // No reply\n+ // Note: Always sends six bytes\n+ case GCAMCommand::Unknown_22:\n+ {\n+ if (!validate_data_in_out(7, 0, "Unknown_22"))\n+ break;\n+\n+ DEBUG_LOG_FMT(\n+ SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x22, {:02x}, {:02x}, {:02x}, {:02x}, {:02x}, {:02x}, {:02x}",\n+ data_in[0], data_in[1], data_in[2], data_in[3], data_in[4], data_in[5], data_in[6]);\n+\n+ const u32 in_size = data_in[0] + 1;\n+ if (!validate_data_in_out(in_size, 0, "Unknown_22"))\n+ break;\n+ data_in += in_size;\n+ }\n+ break;\n+ case GCAMCommand::Unknown_23:\n+ if (!validate_data_in_out(2, 2, "Unknown_23"))\n+ break;\n+\n+ DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x23, {:02x} {:02x}", data_in[0],\n+ data_in[1]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x00;\n+\n+ data_in += 2;\n+ break;\n+ case GCAMCommand::Unknown_24:\n+ if (!validate_data_in_out(2, 2, "Unknown_24"))\n+ break;\n+\n+ DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x24, {:02x} {:02x}", data_in[0],\n+ data_in[1]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x00;\n+\n+ data_in += 2;\n+ break;\n+ case GCAMCommand::SerialA:\n+ {\n+ if (!validate_data_in_out(1, 0, "SerialA"))\n+ break;\n+\n+ u32 length = *data_in++;\n+ if (length)\n+ {\n+ if (!validate_data_in_out(length, 0, "SerialA"))\n+ break;\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x31, length=0x{:02x}, hexdump:\\n{}",\n+ length, HexDump(data_in, length));\n+\n+ // Serial - Wheel\n+ if (AMMediaboard::GetGameType() == MarioKartGP ||\n+ AMMediaboard::GetGameType() == MarioKartGP2)\n+ {\n+ if (!validate_data_in_out(10, 2, "SerialA (Wheel)"))\n+ break;\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x31, (WHEEL) {:02x}{:02x} {:02x}{:02x} {:02x} {:02x} "\n+ "{:02x} {:02x} {:02x} {:02x}",\n+ data_in[0], data_in[1], data_in[2], data_in[3], data_in[4], data_in[5],\n+ data_in[6], data_in[7], data_in[8], data_in[9]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x03;\n+\n+ switch (m_wheel_init)\n+ {\n+ case 0:\n+ if (!validate_data_in_out(0, 3, "SerialA (Wheel)"))\n+ break;\n+ data_out[data_offset++] = \'E\'; // Error\n+ data_out[data_offset++] = \'0\';\n+ data_out[data_offset++] = \'0\';\n+ m_wheel_init++;\n+ break;\n+ case 1:\n+ if (!validate_data_in_out(0, 3, "SerialA (Wheel)"))\n+ break;\n+ data_out[data_offset++] = \'C\'; // Power Off\n+ data_out[data_offset++] = \'0\';\n+ data_out[data_offset++] = \'6\';\n+ // Only turn on when a wheel is connected\n+ if (serial_interface.GetDeviceType(1) == SerialInterface::SIDEVICE_GC_STEERING)\n+ {\n+ m_wheel_init++;\n+ }\n+ break;\n+ case 2:\n+ if (!validate_data_in_out(0, 3, "SerialA (Wheel)"))\n+ break;\n+ data_out[data_offset++] = \'C\'; // Power On\n+ data_out[data_offset++] = \'0\';\n+ data_out[data_offset++] = \'1\';\n+ break;\n+ default:\n+ break;\n+ }\n+\n+ // u16 CenteringForce= ptr(6);\n+ // u16 FrictionForce = ptr(8);\n+ // u16 Roll = ptr(10);\n+ if (!validate_data_in_out(length, 0, "SerialA (Wheel)"))\n+ break;\n+ data_in += length;\n+ break;\n+ }\n+\n+ // Serial - Unknown\n+ if (AMMediaboard::GetGameType() == GekitouProYakyuu)\n+ {\n+ if (!validate_data_in_out(sizeof(u32), 0, "SerialA (Unknown)"))\n+ break;\n+ const u32 serial_command = Common::BitCastPtr<u32>(data_in);\n+\n+ if (serial_command == 0x00001000)\n+ {\n+ if (!validate_data_in_out(0, 5, "SerialA (Unknown)"))\n+ break;\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x03;\n+ data_out[data_offset++] = 1;\n+ data_out[data_offset++] = 2;\n+ data_out[data_offset++] = 3;\n+ }\n+\n+ if (!validate_data_in_out(length, 0, "SerialA (Unknown)"))\n+ break;\n+ data_in += length;\n+ break;\n+ }\n+\n+ // Serial IC-CARD / Serial Deck Reader\n+ if (AMMediaboard::GetGameType() == VirtuaStriker4 ||\n+ AMMediaboard::GetGameType() == VirtuaStriker4_2006 ||\n+ AMMediaboard::GetGameType() == KeyOfAvalon)\n+ {\n+ if (!validate_data_in_out(2, 0, "SerialA (IC-CARD)"))\n+ break;\n+ u32 serial_command = data_in[1];\n+\n+ ICCommand icco;\n+\n+ // Set default reply\n+ icco.pktcmd = gcam_command;\n+ icco.pktlen = 7;\n+ icco.fixed = 0x10;\n+ icco.command = serial_command;\n+ icco.flag = 0;\n+ icco.length = 2;\n+ icco.status = 0;\n+ icco.extlen = 0;\n+\n+ // Check for rest of data from the write pages command\n+ if (m_ic_write_size && m_ic_write_offset)\n+ {\n+ const u32 size = data_in[1];\n+\n+ if (!validate_data_in_out(size + 2, 0, "SerialA (IC-CARD)"))\n+ break;\n+ DEBUG_LOG_FMT(SERIALINTERFACE_CARD, "Command: {}", HexDump(data_in, size + 2));\n+\n+ INFO_LOG_FMT(\n+ SERIALINTERFACE_CARD,\n+ "GC-AM: Command 25 (IC-CARD) Write Pages: Off:{:x} Size:{:x} PSize:{:x}",\n+ m_ic_write_offset, m_ic_write_size, size);\n+\n+ memcpy(m_ic_write_buffer + m_ic_write_offset, data_in + 2, size);\n+\n+ m_ic_write_offset += size;\n+\n+ if (m_ic_write_offset > m_ic_write_size)\n+ {\n+ m_ic_write_offset = 0;\n+\n+ const u16 page = m_ic_write_buffer[5];\n+ const u16 count = m_ic_write_buffer[7];\n+\n+ memcpy(m_ic_card_data + page * 8, m_ic_write_buffer + 10, count * 8);\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 25 (IC-CARD) Write Pages:{} Count:{}({:x})", page,\n+ count, size);\n+\n+ icco.command = WritePages;\n+\n+ ICCardSendReply(&icco, data_out.data(), &data_offset);\n+ }\n+ if (!validate_data_in_out(length, 0, "SerialA (IC-CARD)"))\n+ break;\n+ data_in += length;\n+ break;\n+ }\n+\n+ switch (ICCARDCommand(serial_command))\n+ {\n+ case ICCARDCommand::GetStatus:\n+ icco.status = m_ic_card_state;\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (IC-CARD) Get Status:{:02x}", m_ic_card_state);\n+ break;\n+ case ICCARDCommand::SetBaudrate:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (IC-CARD) Set Baudrate");\n+ break;\n+ case ICCARDCommand::FieldOn:\n+ m_ic_card_state |= 0x10;\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (IC-CARD) Field On");\n+ break;\n+ case ICCARDCommand::InsertCheck:\n+ icco.status = m_ic_card_status;\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (IC-CARD) Insert Check:{:02x}", m_ic_card_status);\n+ break;\n+ case ICCARDCommand::AntiCollision:\n+ icco.extlen = 8;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ // Card ID\n+ icco.extdata[0] = 0x00;\n+ icco.extdata[1] = 0x00;\n+ icco.extdata[2] = 0x54;\n+ icco.extdata[3] = 0x4D;\n+ icco.extdata[4] = 0x50;\n+ icco.extdata[5] = 0x00;\n+ icco.extdata[6] = 0x00;\n+ icco.extdata[7] = 0x00;\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (IC-CARD) Anti Collision");\n+ break;\n+ case ICCARDCommand::SelectCard:\n+ icco.extlen = 8;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ // Session\n+ icco.extdata[0] = 0x00;\n+ icco.extdata[1] = m_ic_card_session;\n+ icco.extdata[2] = 0x00;\n+ icco.extdata[3] = 0x00;\n+ icco.extdata[4] = 0x00;\n+ icco.extdata[5] = 0x00;\n+ icco.extdata[6] = 0x00;\n+ icco.extdata[7] = 0x00;\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (IC-CARD) Select Card:{}",\n+ m_ic_card_session);\n+ break;\n+ case ICCARDCommand::ReadPage:\n+ case ICCARDCommand::ReadUseCount:\n+ {\n+ if (!validate_data_in_out(8, 0, "SerialA (IC-CARD)"))\n+ break;\n+ const u16 page = Common::swap16(data_in + 6) & 0xFF; // 255 is max page\n+\n+ icco.extlen = 8;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ memcpy(icco.extdata, m_ic_card_data + page * 8, 8);\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 31 (IC-CARD) Read Page:{}",\n+ page);\n+ break;\n+ }\n+ case ICCARDCommand::WritePage:\n+ {\n+ if (!validate_data_in_out(10, 0, "SerialA (IC-CARD)"))\n+ break;\n+ const u16 page = Common::swap16(data_in + 8) & 0xFF; // 255 is max page\n+\n+ // Write only one page\n+ if (page == 4)\n+ {\n+ icco.status = 0x80;\n+ }\n+ else\n+ {\n+ if (!validate_data_in_out(18, 0, "SerialA (IC-CARD)"))\n+ break;\n+ memcpy(m_ic_card_data + page * 8, data_in + 10, 8);\n+ }\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (IC-CARD) Write Page:{}",\n+ page);\n+ break;\n+ }\n+ case ICCARDCommand::DecreaseUseCount:\n+ {\n+ if (!validate_data_in_out(8, 0, "SerialA (IC-CARD)"))\n+ break;\n+ const u16 page = Common::swap16(data_in + 6) & 0xFF; // 255 is max page\n+\n+ icco.extlen = 2;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ auto ic_card_data = Common::BitCastPtr<u16>(m_ic_card_data + 0x28);\n+ ic_card_data = ic_card_data - 1;\n+\n+ // Counter\n+ icco.extdata[0] = m_ic_card_data[0x28];\n+ icco.extdata[1] = m_ic_card_data[0x29];\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 31 (IC-CARD) Decrease Use Count:{}", page);\n+ break;\n+ }\n+ case ICCARDCommand::ReadPages:\n+ {\n+ if (!validate_data_in_out(10, 0, "SerialA (IC-CARD)"))\n+ break;\n+ const u16 page = Common::swap16(data_in + 6) & 0xFF; // 255 is max page\n+ const u16 count = Common::swap16(data_in + 8);\n+\n+ const u32 offs = page * 8;\n+ u32 cnt = count * 8;\n+\n+ // Limit read size to not overwrite the reply buffer\n+ if (cnt > (u32)0x50 - data_offset)\n+ {\n+ cnt = 5 * 8;\n+ }\n+\n+ icco.extlen = cnt;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ memcpy(icco.extdata, m_ic_card_data + offs, cnt);\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 31 (IC-CARD) Read Pages:{} Count:{}", page, count);\n+ break;\n+ }\n+ case ICCARDCommand::WritePages:\n+ {\n+ if (!validate_data_in_out(10, 0, "SerialA (IC-CARD)"))\n+ break;\n+ const u16 pksize = length;\n+ const u16 size = Common::swap16(data_in + 2);\n+ const u16 page = Common::swap16(data_in + 6) & 0xFF; // 255 is max page\n+ const u16 count = Common::swap16(data_in + 8);\n+\n+ // We got a complete packet\n+ if (pksize - 5 == size)\n+ {\n+ if (page == 4) // Read Only Page, must return error\n+ {\n+ icco.status = 0x80;\n+ }\n+ else\n+ {\n+ if (page * 8 + count * 8 > sizeof(m_ic_card_data))\n+ {\n+ ERROR_LOG_FMT(\n+ SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (IC-CARD) Data overflow: Pages:{} Count:{}({:x})",\n+ page, count, size);\n+ }\n+ else\n+ {\n+ if (!validate_data_in_out(13 + count * 8, 0, "SerialA (IC-CARD)"))\n+ break;\n+ memcpy(m_ic_card_data + page * 8, data_in + 13, count * 8);\n+ }\n+ }\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (IC-CARD) Write Pages:{} Count:{}({:x})", page,\n+ count, size);\n+ }\n+ // VirtuaStriker 4 splits the writes over multiple packets\n+ else\n+ {\n+ if (!validate_data_in_out(2 + pksize, 0, "SerialA (IC-CARD)"))\n+ break;\n+ memcpy(m_ic_write_buffer, data_in + 2, pksize);\n+ m_ic_write_offset += pksize;\n+ m_ic_write_size = size;\n+ }\n+ break;\n+ }\n+ default:\n+ // Handle Deck Reader commands\n+ if (!validate_data_in_out(1, 0, "SerialA (DECK READER)"))\n+ break;\n+ serial_command = data_in[0];\n+ icco.command = serial_command;\n+ icco.flag = 0;\n+ switch (CDReaderCommand(serial_command))\n+ {\n+ case CDReaderCommand::ProgramVersion:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Program Version");\n+\n+ icco.extlen = (u32)strlen(s_cdr_program_version);\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ memcpy(icco.extdata, s_cdr_program_version, icco.extlen);\n+ break;\n+ case CDReaderCommand::BootVersion:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Boot Version");\n+\n+ icco.extlen = (u32)strlen(s_cdr_boot_version);\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ memcpy(icco.extdata, s_cdr_boot_version, icco.extlen);\n+ break;\n+ case CDReaderCommand::ShutterGet:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Shutter Get");\n+\n+ icco.extlen = 4;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ icco.extdata[0] = 0;\n+ icco.extdata[1] = 0;\n+ icco.extdata[2] = 0;\n+ icco.extdata[3] = 0;\n+ break;\n+ case CDReaderCommand::CameraCheck:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Camera Check");\n+\n+ icco.extlen = 6;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ icco.extdata[0] = 0x23;\n+ icco.extdata[1] = 0x28;\n+ icco.extdata[2] = 0x45;\n+ icco.extdata[3] = 0x29;\n+ icco.extdata[4] = 0x45;\n+ icco.extdata[5] = 0x29;\n+ break;\n+ case CDReaderCommand::ProgramChecksum:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Program Checksum");\n+\n+ icco.extlen = 4;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ icco.extdata[0] = 0x23;\n+ icco.extdata[1] = 0x28;\n+ icco.extdata[2] = 0x45;\n+ icco.extdata[3] = 0x29;\n+ break;\n+ case CDReaderCommand::BootChecksum:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Boot Checksum");\n+\n+ icco.extlen = 4;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ icco.extdata[0] = 0x23;\n+ icco.extdata[1] = 0x28;\n+ icco.extdata[2] = 0x45;\n+ icco.extdata[3] = 0x29;\n+ break;\n+ case CDReaderCommand::SelfTest:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (DECK READER) Self Test");\n+ icco.flag = 0x00;\n+ break;\n+ case CDReaderCommand::SensLock:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (DECK READER) Sens Lock");\n+ icco.flag = 0x01;\n+ break;\n+ case CDReaderCommand::SensCard:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (DECK READER) Sens Card");\n+ break;\n+ case CDReaderCommand::ShutterCard:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Shutter Card");\n+ break;\n+ case CDReaderCommand::ReadCard:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (DECK READER) Read Card");\n+\n+ icco.fixed = 0xAA;\n+ icco.flag = 0xAA;\n+ icco.extlen = sizeof(s_cdr_card_data);\n+ icco.length = 0x72;\n+ icco.status = Common::swap16(icco.extlen);\n+\n+ icco.pktlen += icco.extlen;\n+\n+ memcpy(icco.extdata, s_cdr_card_data, sizeof(s_cdr_card_data));\n+\n+ break;\n+ default:\n+ if (!validate_data_in_out(14, 0, "SerialA (DECK READER)"))\n+ break;\n+ WARN_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (IC-Card) {:02x} {:02x} {:02x} {:02x} {:02x} "\n+ "{:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x}",\n+ data_in[2], data_in[3], data_in[4], data_in[5], data_in[6],\n+ data_in[7], data_in[8], data_in[9], data_in[10], data_in[11],\n+ data_in[12], data_in[13]);\n+ break;\n+ }\n+ break;\n+ }\n+\n+ if (!validate_data_in_out(0, icco.pktlen + 2, "SerialA (IC-CARD)"))\n+ break;\n+ ICCardSendReply(&icco, data_out.data(), &data_offset);\n+\n+ if (!validate_data_in_out(2, 0, "SerialA (IC-CARD)"))\n+ break;\n+ data_in += length;\n+ break;\n+ }\n+ }\n+\n+ u32 command_offset = 0;\n+ while (command_offset < length)\n+ {\n+ // All commands are OR\'d with 0x80\n+ // Last byte is checksum which we don\'t care about\n+ if (!validate_data_in_out(command_offset + 4, 0, "SerialA"))\n+ break;\n+ const u32 serial_command = Common::swap32(data_in + command_offset) ^ 0x80000000;\n+\n+ if (AMMediaboard::GetGameType() == FZeroAX ||\n+ AMMediaboard::GetGameType() == FZeroAXMonster)\n+ {\n+ INFO_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x31 (MOTOR) Length:{:02x} Command:{:04x}({:02x})",\n+ length, (serial_command >> 8) & 0xFFFF, serial_command >> 24);\n+ }\n+ else\n+ {\n+ INFO_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x31 (SERIAL) Command:{:06x}",\n+ serial_command);\n+\n+ if (serial_command == 0x801000)\n+ {\n+ if (!validate_data_in_out(0, 4, "SerialA"))\n+ break;\n+ data_out[data_offset++] = 0x31;\n+ data_out[data_offset++] = 0x02;\n+ data_out[data_offset++] = 0xFF;\n+ data_out[data_offset++] = 0x01;\n+ }\n+ }\n+\n+ command_offset += sizeof(u32);\n+\n+ if (AMMediaboard::GetGameType() == FZeroAX ||\n+ AMMediaboard::GetGameType() == FZeroAXMonster)\n+ {\n+ // Status\n+ m_motor_reply[command_offset + 2] = 0;\n+ m_motor_reply[command_offset + 3] = 0;\n+\n+ // Error\n+ m_motor_reply[command_offset + 4] = 0;\n+\n+ switch (serial_command >> 24)\n+ {\n+ case 0:\n+ case 1: // Set Maximum?\n+ case 2:\n+ break;\n+\n+ // 0x00-0x40: left\n+ // 0x40-0x80: right\n+ case 4: // Move Steering Wheel\n+ // Left\n+ if (serial_command & 0x010000)\n+ {\n+ m_motor_force_y = -((s16)serial_command & 0xFF00);\n+ }\n+ else // Right\n+ {\n+ m_motor_force_y = (serial_command - 0x4000) & 0xFF00;\n+ }\n+\n+ m_motor_force_y *= 2;\n+\n+ // FFB\n+ if (m_motor_init == 2)\n+ {\n+ if (serial_interface.GetDeviceType(1) == SerialInterface::SIDEVICE_GC_STEERING)\n+ {\n+ const GCPadStatus pad_status = Pad::GetStatus(1);\n+ if (pad_status.isConnected)\n+ {\n+ const ControlState mapped_strength = (double)(m_motor_force_y >> 8) / 127.f;\n+\n+ Pad::Rumble(1, mapped_strength);\n+ INFO_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x31 (MOTOR) mapped_strength:{}",\n+ mapped_strength);\n+ }\n+ }\n+ }\n+ break;\n+ case 6: // nice\n+ case 9:\n+ default:\n+ break;\n+ // Switch back to normal controls\n+ case 7:\n+ m_motor_init = 2;\n+ break;\n+ // Reset\n+ case 0x7F:\n+ m_motor_init = 1;\n+ memset(m_motor_reply, 0, sizeof(m_motor_reply));\n+ break;\n+ }\n+\n+ // Checksum\n+ m_motor_reply[command_offset + 5] = m_motor_reply[command_offset + 2] ^\n+ m_motor_reply[command_offset + 3] ^\n+ m_motor_reply[command_offset + 4];\n+ }\n+ }\n+\n+ if (length == 0)\n+ {\n+ if (!validate_data_in_out(length, 2, "SerialA"))\n+ break;\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x00;\n+ }\n+ else\n+ {\n+ if (m_motor_init)\n+ {\n+ // Motor\n+ m_motor_reply[0] = gcam_command;\n+ m_motor_reply[1] = length; // Same out as in size\n+\n+ const u32 reply_size = m_motor_reply[1] + 2;\n+ if (!validate_data_in_out(length, reply_size, "SerialA"))\n+ break;\n+\n+ memcpy(data_out.data() + data_offset, m_motor_reply, reply_size);\n+ data_offset += reply_size;\n+ }\n+ }\n+\n+ if (!validate_data_in_out(length, 0, "SerialA"))\n+ {\n+ break;\n+ }\n+ data_in += length;\n+ break;\n+ }\n+ case GCAMCommand::SerialB:\n+ {\n+ DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 32 (CARD-Interface)");\n+ if (!validate_data_in_out(1, 0, "SerialB"))\n+ break;\n+ const u32 length = *data_in++;\n+ if (!validate_data_in_out(length, 0, "SerialB"))\n+ break;\n+ if (length)\n+ {\n+ // Send Card Reply\n+ if (length == 1 && data_in[0] == 0x05)\n+ {\n+ if (m_card_read_length)\n+ {\n+ if (!validate_data_in_out(0, 1, "SerialB"))\n+ break;\n+ data_out[data_offset++] = gcam_command;\n+ u32 read_length = m_card_read_length - m_card_read;\n+\n+ if (AMMediaboard::GetGameType() == FZeroAX)\n+ {\n+ read_length = std::min<u32>(read_length, 0x2F);\n+ }\n+\n+ if (!validate_data_in_out(0, 1, "SerialB"))\n+ break;\n+ data_out[data_offset++] = read_length; // 0x2F (max size per packet)\n+\n+ if (!validate_data_in_out(0, read_length, "SerialB"))\n+ break;\n+ memcpy(data_out.data() + data_offset, m_card_read_packet + m_card_read,\n+ read_length);\n+\n+ data_offset += read_length;\n+ m_card_read += read_length;\n+\n+ if (m_card_read >= m_card_read_length)\n+ m_card_read_length = 0;\n+\n+ data_in += length;\n+ break;\n+ }\n+\n+ if (!validate_data_in_out(0, 5, "SerialB"))\n+ break;\n+\n+ data_out[data_offset++] = gcam_command;\n+ const u32 command_length_offset = data_offset;\n+ data_out[data_offset++] = 0x00; // len\n+\n+ data_out[data_offset++] = 0x02;\n+ const u32 checksum_start = data_offset;\n+\n+ data_out[data_offset++] = 0x00; // 0x00 len\n+\n+ data_out[data_offset++] = m_card_command; // 0x01 command\n+\n+ switch (CARDCommand(m_card_command))\n+ {\n+ case CARDCommand::Init:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x00; // 0x02\n+ data_out[data_offset++] = 0x30; // 0x03\n+ break;\n+ case CARDCommand::GetState:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x20 | m_card_bit; // 0x02\n+\n+ // bit 0: Please take your card\n+ // bit 1: Endless waiting causes UNK_E to be called\n+ data_out[data_offset++] = 0x00; // 0x03\n+ break;\n+ case CARDCommand::Read:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x02; // 0x02\n+ data_out[data_offset++] = 0x53; // 0x03\n+ break;\n+ case CARDCommand::IsPresent:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x22; // 0x02\n+ data_out[data_offset++] = 0x30; // 0x03\n+ break;\n+ case CARDCommand::Write:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x02; // 0x02\n+ data_out[data_offset++] = 0x00; // 0x03\n+ break;\n+ case CARDCommand::SetPrintParam:\n+ case CARDCommand::RegisterFont:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x00; // 0x02\n+ data_out[data_offset++] = 0x00; // 0x03\n+ break;\n+ case CARDCommand::WriteInfo:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x02; // 0x02\n+ data_out[data_offset++] = 0x00; // 0x03\n+ break;\n+ case CARDCommand::Erase:\n+ // TODO: CARDCommand::Erase is not handled.\n+ break;\n+ case CARDCommand::Eject:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ if (AMMediaboard::GetGameType() == FZeroAX)\n+ {\n+ data_out[data_offset++] = 0x01; // 0x02\n+ }\n+ else\n+ {\n+ data_out[data_offset++] = 0x31; // 0x02\n+ }\n+ data_out[data_offset++] = 0x30; // 0x03\n+ break;\n+ case CARDCommand::Clean:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x02; // 0x02\n+ data_out[data_offset++] = 0x00; // 0x03\n+ break;\n+ case CARDCommand::Load:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x02; // 0x02\n+ data_out[data_offset++] = 0x30; // 0x03\n+ break;\n+ case CARDCommand::SetShutter:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x00; // 0x02\n+ data_out[data_offset++] = 0x00; // 0x03\n+ break;\n+ }\n+\n+ if (!validate_data_in_out(0, 3, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x30; // 0x04\n+ data_out[data_offset++] = 0x00; // 0x05\n+\n+ data_out[data_offset++] = 0x03; // 0x06\n+\n+ data_out[checksum_start] = data_offset - checksum_start; // 0x00 len\n+\n+ if (!validate_data_in_out(0, 1, "SerialB"))\n+ break;\n+ data_out[data_offset] = 0; // 0x07\n+ for (u32 i = 0; i < data_out[checksum_start]; ++i)\n+ data_out[data_offset] ^= data_out[checksum_start + i];\n+\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_offset++;\n+\n+ data_out[command_length_offset] = data_out[checksum_start] + 2;\n+ }\n+ else\n+ {\n+ for (u32 i = 0; i < length; ++i)\n+ m_card_buffer[m_card_offset + i] = data_in[i];\n+\n+ m_card_offset += length;\n+\n+ // Check if we got a complete command\n+ if (m_card_buffer[0] == 0x02)\n+ {\n+ if (m_card_buffer[1] == m_card_offset - 2)\n+ {\n+ if (m_card_buffer[m_card_offset - 2] == 0x03)\n+ {\n+ m_card_command = m_card_buffer[2];\n+\n+ switch (CARDCommand(m_card_command))\n+ {\n+ case CARDCommand::Init:\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD Init");\n+\n+ m_card_write_length = 0;\n+ m_card_bit = 0;\n+ m_card_memory_size = 0;\n+ m_card_state_call_count = 0;\n+ break;\n+ case CARDCommand::GetState:\n+ {\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD GetState({:02X})",\n+ m_card_bit);\n+\n+ if (m_card_memory_size == 0)\n+ {\n+ const std::string card_filename(\n+ fmt::format("{}tricard_{}.bin", File::GetUserPath(D_TRIUSER_IDX),\n+ SConfig::GetInstance().GetGameID()));\n+\n+ if (File::Exists(card_filename))\n+ {\n+ File::IOFile card(card_filename, "rb+");\n+ m_card_memory_size = (u32)card.GetSize();\n+\n+ card.ReadBytes(m_card_memory, m_card_memory_size);\n+ card.Close();\n+\n+ m_card_is_inserted = true;\n+ }\n+ }\n+\n+ if (AMMediaboard::GetGameType() == FZeroAX && m_card_memory_size)\n+ {\n+ m_card_state_call_count++;\n+ if (m_card_state_call_count > 10)\n+ {\n+ if (m_card_bit & 2)\n+ m_card_bit &= ~2u;\n+ else\n+ m_card_bit |= 2;\n+\n+ m_card_state_call_count = 0;\n+ }\n+ }\n+\n+ if (m_card_clean == 1)\n+ {\n+ m_card_clean = 2;\n+ }\n+ else if (m_card_clean == 2)\n+ {\n+ const std::string card_filename(\n+ fmt::format("{}tricard_{}.bin", File::GetUserPath(D_TRIUSER_IDX),\n+ SConfig::GetInstance().GetGameID()));\n+\n+ if (File::Exists(card_filename))\n+ {\n+ m_card_memory_size = (u32)File::GetSize(card_filename);\n+ if (m_card_memory_size)\n+ {\n+ if (AMMediaboard::GetGameType() == FZeroAX)\n+ {\n+ m_card_bit = 2;\n+ }\n+ else\n+ {\n+ m_card_bit = 1;\n+ }\n+ }\n+ }\n+ m_card_clean = 0;\n+ }\n+ break;\n+ }\n+ case CARDCommand::IsPresent:\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD IsPresent");\n+ break;\n+ case CARDCommand::RegisterFont:\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD RegisterFont");\n+ break;\n+ case CARDCommand::Load:\n+ {\n+ u8 mode = m_card_buffer[6];\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD Load({:02X})",\n+ mode);\n+ break;\n+ }\n+ case CARDCommand::Clean:\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD Clean");\n+ m_card_clean = 1;\n+ break;\n+ case CARDCommand::Read:\n+ {\n+ const u8 mode = m_card_buffer[6];\n+ const u8 bitmode = m_card_buffer[7];\n+ const u8 track = m_card_buffer[8];\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command CARD Read({:02X},{:02X},{:02X})", mode,\n+ bitmode, track);\n+\n+ // Prepare read packet\n+ memset(m_card_read_packet, 0, 0xDB);\n+ u32 packet_offset = 0;\n+\n+ const std::string card_filename(\n+ fmt::format("{}tricard_{}.bin", File::GetUserPath(D_TRIUSER_IDX),\n+ SConfig::GetInstance().GetGameID()));\n+\n+ if (File::Exists(card_filename))\n+ {\n+ File::IOFile card(card_filename, "rb+");\n+ if (m_card_memory_size == 0)\n+ {\n+ m_card_memory_size = (u32)card.GetSize();\n+ }\n+\n+ card.ReadBytes(m_card_memory, m_card_memory_size);\n+ card.Close();\n+\n+ m_card_is_inserted = true;\n+ }\n+\n+ m_card_read_packet[packet_offset++] = 0x02; // SUB CMD\n+ m_card_read_packet[packet_offset++] = 0x00; // SUB CMDLen\n+\n+ m_card_read_packet[packet_offset++] = 0x33; // CARD CMD\n+\n+ if (m_card_is_inserted) // CARD Status\n+ {\n+ m_card_read_packet[packet_offset++] = 0x31;\n+ }\n+ else\n+ {\n+ m_card_read_packet[packet_offset++] = 0x30;\n+ }\n+\n+ m_card_read_packet[packet_offset++] = 0x30;\n+ m_card_read_packet[packet_offset++] = 0x30;\n+\n+ // Data reply\n+ memcpy(m_card_read_packet + packet_offset, m_card_memory, m_card_memory_size);', 'path': 'Source/Core/Core/HW/SI/SI_DeviceAMBaseboard.cpp', 'position': 1498, 'original_position': 1390, 'commit_id': '510fec88be973bc870021f2d0347593cdc285864', 'user': {'login': 'JosJuice', 'id': 6716818, 'node_id': 'MDQ6VXNlcjY3MTY4MTg=', 'avatar_url': 'https://avatars.githubusercontent.com/u/6716818?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/JosJuice', 'html_url': 'https://github.com/JosJuice', 'followers_url': 'https://api.github.com/users/JosJuice/followers', 'following_url': 'https://api.github.com/users/JosJuice/following{/other_user}', 'gists_url': 'https://api.github.com/users/JosJuice/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/JosJuice/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/JosJuice/subscriptions', 'organizations_url': 'https://api.github.com/users/JosJuice/orgs', 'repos_url': 'https://api.github.com/users/JosJuice/repos', 'events_url': 'https://api.github.com/users/JosJuice/events{/privacy}', 'received_events_url': 'https://api.github.com/users/JosJuice/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'body': "I admittedly missed that check, but that check only runs if the file exists. You should also check `m_card_memory_size` if the file doesn't exist.", 'created_at': '2026-01-31T19:02:45Z', 'updated_at': '2026-01-31T19:02:45Z', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/13844#discussion_r2749868593', 'pull_request_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13844', 'author_association': 'MEMBER', '_links': {'self': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2749868593'}, 'html': {'href': 'https://github.com/dolphin-emu/dolphin/pull/13844#discussion_r2749868593'}, 'pull_request': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13844'}}, 'original_commit_id': '2415f0c1c471b2446a2144a1a2b643d8a77d8cc7', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2749868593/reactions', 'total_count': 0, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 0, 'confused': 0, 'heart': 0, 'rocket': 0, 'eyes': 0}, 'in_reply_to_id': 2725629971}], 'type': 'gh_pull_request_review'}
2026-01-31T19:01:53.435137 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'JosJuice', 'action': 'submitted', 'pr_id': 13844, 'pr_title': 'Core: Triforce support', 'state': 'commented', 'url': 'https://github.com/dolphin-emu/dolphin/pull/13844#pullrequestreview-3733276394', 'comments': [{'id': 2749867718, 'node_id': 'PRRC_kwDOALCn2M6j56bG', 'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2749867718', 'pull_request_review_id': 3733276394, 'diff_hunk': '@@ -0,0 +1,2501 @@\n+// Copyright 2025 Dolphin Emulator Project\n+// SPDX-License-Identifier: GPL-2.0-or-later\n+\n+#include "Core/HW/SI/SI_DeviceAMBaseboard.h"\n+\n+#include <algorithm>\n+#include <numeric>\n+#include <string>\n+\n+#include <fmt/format.h>\n+\n+#include "Common/Buffer.h"\n+#include "Common/CommonTypes.h"\n+#include "Common/FileUtil.h"\n+#include "Common/IOFile.h"\n+#include "Common/Logging/Log.h"\n+#include "Common/MsgHandler.h"\n+#include "Common/Swap.h"\n+\n+#include "Core/Boot/Boot.h"\n+#include "Core/BootManager.h"\n+#include "Core/Config/MainSettings.h"\n+#include "Core/ConfigManager.h"\n+#include "Core/Core.h"\n+#include "Core/CoreTiming.h"\n+#include "Core/HW/DVD/AMMediaboard.h"\n+#include "Core/HW/DVD/DVDInterface.h"\n+#include "Core/HW/EXI/EXI.h"\n+#include "Core/HW/GCPad.h"\n+#include "Core/HW/MMIO.h"\n+#include "Core/HW/Memmap.h"\n+#include "Core/HW/ProcessorInterface.h"\n+#include "Core/HW/SI/SI.h"\n+#include "Core/HW/SI/SI_Device.h"\n+#include "Core/HW/SI/SI_DeviceGCController.h"\n+#include "Core/HW/SystemTimers.h"\n+#include "Core/Movie.h"\n+#include "Core/NetPlayProto.h"\n+#include "Core/System.h"\n+\n+#include "InputCommon/GCPadStatus.h"\n+\n+namespace SerialInterface\n+{\n+void JVSIOMessage::Start(int node)\n+{\n+ m_last_start = m_pointer;\n+ const u8 header[3] = {0xE0, (u8)node, 0};\n+ m_checksum = 0;\n+ AddData(header, 3, 1);\n+}\n+\n+void JVSIOMessage::AddData(const u8* dst, size_t len, int sync = 0)\n+{\n+ if (m_pointer + len >= sizeof(m_message))\n+ {\n+ PanicAlertFmt("JVSIOMessage overrun!");\n+ return;\n+ }\n+\n+ while (len--)\n+ {\n+ const u8 c = *dst++;\n+ if (!sync && ((c == 0xE0) || (c == 0xD0)))\n+ {\n+ if (m_pointer + 2 > sizeof(m_message))\n+ {\n+ PanicAlertFmt("JVSIOMessage overrun!");\n+ break;\n+ }\n+ m_message[m_pointer++] = 0xD0;\n+ m_message[m_pointer++] = c - 1;\n+ }\n+ else\n+ {\n+ if (m_pointer >= sizeof(m_message))\n+ {\n+ PanicAlertFmt("JVSIOMessage overrun!");\n+ break;\n+ }\n+ m_message[m_pointer++] = c;\n+ }\n+\n+ if (!sync)\n+ m_checksum += c;\n+ sync = 0;\n+ }\n+}\n+\n+void JVSIOMessage::AddData(const void* data, size_t len)\n+{\n+ AddData(static_cast<const u8*>(data), len);\n+}\n+\n+void JVSIOMessage::AddData(const char* data)\n+{\n+ AddData(data, strlen(data));\n+}\n+\n+void JVSIOMessage::AddData(u32 n)\n+{\n+ const u8 cs = n;\n+ AddData(&cs, 1);\n+}\n+\n+void JVSIOMessage::End()\n+{\n+ const u32 len = m_pointer - m_last_start;\n+ if (m_last_start + 2 < sizeof(m_message) && len >= 3)\n+ {\n+ m_message[m_last_start + 2] = len - 2; // assuming len <0xD0\n+ AddData(m_checksum + len - 2);\n+ }\n+ else\n+ {\n+ PanicAlertFmt("JVSIOMessage: Not enough space for checksum!");\n+ }\n+}\n+\n+static constexpr u8 CheckSumXOR(const u8* data, u32 length)\n+{\n+ return std::accumulate(data, data + length, u8{}, std::bit_xor());\n+}\n+\n+static constexpr char s_cdr_program_version[] = {" Version 1.22,2003/09/19,171-8213B"};\n+static constexpr char s_cdr_boot_version[] = {" Version 1.04,2003/06/17,171-8213B"};\n+static constexpr u8 s_cdr_card_data[] = {\n+ 0x00, 0x6E, 0x00, 0x00, 0x01, 0x00, 0x00, 0x06, 0x00, 0x00, 0x07, 0x00, 0x00, 0x0B,\n+ 0x00, 0x00, 0x0E, 0x00, 0x00, 0x10, 0x00, 0x00, 0x17, 0x00, 0x00, 0x19, 0x00, 0x00,\n+ 0x1A, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x20, 0x00,\n+ 0x00, 0x22, 0x00, 0x00, 0x23, 0x00, 0x00, 0x24, 0x00, 0x00, 0x27, 0x00, 0x00, 0x28,\n+ 0x00, 0x00, 0x2C, 0x00, 0x00, 0x2F, 0x00, 0x00, 0x34, 0x00, 0x00, 0x35, 0x00, 0x00,\n+ 0x37, 0x00, 0x00, 0x38, 0x00, 0x00, 0x39, 0x00, 0x00, 0x3D, 0x00};\n+\n+const constexpr u8 s_region_flags[] = "\\x00\\x00\\x30\\x00"\n+ // "\\x01\\xfe\\x00\\x00" // JAPAN\n+ "\\x02\\xfd\\x00\\x00" // USA\n+ //"\\x03\\xfc\\x00\\x00" // export\n+ "\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff";\n+// AM-Baseboard device on SI\n+CSIDevice_AMBaseboard::CSIDevice_AMBaseboard(Core::System& system, SIDevices device,\n+ int device_number)\n+ : ISIDevice(system, device, device_number)\n+{\n+ // Card ID\n+ m_ic_card_data[0x20] = 0x95;\n+ m_ic_card_data[0x21] = 0x71;\n+\n+ if (AMMediaboard::GetGameType() == KeyOfAvalon)\n+ {\n+ m_ic_card_data[0x22] = 0x26;\n+ m_ic_card_data[0x23] = 0x40;\n+ }\n+ else if (AMMediaboard::GetGameType() == VirtuaStriker4)\n+ {\n+ m_ic_card_data[0x22] = 0x44;\n+ m_ic_card_data[0x23] = 0x00;\n+ }\n+\n+ // Use count\n+ m_ic_card_data[0x28] = 0xFF;\n+ m_ic_card_data[0x29] = 0xFF;\n+}\n+\n+constexpr u32 SI_XFER_LENGTH_MASK = 0x7f;\n+\n+// Translate [0,1,2,...,126,127] to [128,1,2,...,126,127]\n+constexpr s32 ConvertSILengthField(u32 field)\n+{\n+ return ((field - 1) & SI_XFER_LENGTH_MASK) + 1;\n+}\n+\n+void CSIDevice_AMBaseboard::ICCardSendReply(ICCommand* iccommand, u8* buffer, u32* length)\n+{\n+ iccommand->status = Common::swap16(iccommand->status);\n+\n+ const auto iccommand_data = reinterpret_cast<const u8*>(iccommand);\n+ const u8 crc = CheckSumXOR(iccommand_data + 2, iccommand->pktlen - 1);\n+\n+ for (u32 i = 0; i < iccommand->pktlen + 1; ++i)\n+ {\n+ buffer[(*length)++] = iccommand_data[i];\n+ }\n+\n+ buffer[(*length)++] = crc;\n+}\n+\n+void CSIDevice_AMBaseboard::SwapBuffers(u8* buffer, u32* buffer_length)\n+{\n+ memcpy(m_last[1], buffer, 0x80); // Save current buffer\n+ memcpy(buffer, m_last[0], 0x80); // Load previous buffer\n+ memcpy(m_last[0], m_last[1], 0x80); // Update history\n+\n+ m_lastptr[1] = *buffer_length; // Swap lengths\n+ *buffer_length = m_lastptr[0];\n+ m_lastptr[0] = m_lastptr[1];\n+}\n+\n+int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length)\n+{\n+ const auto& serial_interface = m_system.GetSerialInterface();\n+ u32 buffer_length = ConvertSILengthField(serial_interface.GetInLength());\n+\n+ // Debug logging\n+ ISIDevice::RunBuffer(buffer, buffer_length);\n+\n+ u32 buffer_position = 0;\n+ while (buffer_position < buffer_length)\n+ {\n+ BaseBoardCommand command = static_cast<BaseBoardCommand>(buffer[buffer_position]);\n+ buffer_position++;\n+\n+ switch (command)\n+ {\n+ case BaseBoardCommand::GCAM_Reset: // Returns ID and dip switches\n+ {\n+ const u32 id = Common::swap32(SI_AM_BASEBOARD | 0x100);\n+ std::memcpy(buffer, &id, sizeof(id));\n+ return sizeof(id);\n+ }\n+ break;\n+ case BaseBoardCommand::GCAM_Command:\n+ {\n+ u32 checksum = 0;\n+ for (u32 i = 0; i < buffer_length; ++i)\n+ checksum += buffer[i];\n+\n+ std::array<u8, 0x80> data_out{};\n+ u32 data_offset = 0;\n+\n+ static u32 dip_switch_1 = 0xFE;\n+ static u32 dip_switch_0 = 0xFF;\n+\n+ data_out[data_offset++] = 1;\n+ data_out[data_offset++] = 1;\n+\n+ u8* data_in = buffer + 2;\n+ u8* const data_in_end = buffer + buffer[buffer_position] + 2;\n+\n+ // Helper to check that iterating over data n times is safe,\n+ // i.e. *data++ at most lead to data.end()\n+ auto validate_data_in_out = [&](u32 n_in, u32 n_out, std::string_view command) -> bool {\n+ if (data_in + n_in > data_in_end)\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: data_in overflow in {}", command);\n+ else if (data_offset + n_out > data_out.size())\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: data_out overflow in {}", command);\n+ else\n+ return true;\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "Overflow details:\\n"\n+ " - data_in(begin={}, current={}, end={}, n_in={})\\n"\n+ " - data_out(offset={}, size={}, n_out={})\\n"\n+ " - buffer(position={}, length={})",\n+ fmt::ptr(buffer + 2), fmt::ptr(data_in), fmt::ptr(data_in_end), n_in,\n+ data_offset, data_out.size(), n_out, buffer_position, buffer_length);\n+ data_in = data_in_end;\n+ return false;\n+ };\n+\n+ while (data_in < data_in_end)\n+ {\n+ const u32 gcam_command = *data_in++;\n+ switch (GCAMCommand(gcam_command))\n+ {\n+ case GCAMCommand::StatusSwitches:\n+ {\n+ if (!validate_data_in_out(1, 4, "StatusSwitches"))\n+ break;\n+\n+ const u8 status = *data_in++;\n+ DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x10, {:02x} (READ STATUS&SWITCHES)",\n+ status);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x2;\n+\n+ // We read Test/Service from the JVS-I/O SwitchesInput instead\n+ //\n+ // const GCPadStatus pad_status = Pad::GetStatus(ISIDevice::m_device_number);\n+ // baseboard test/service switches\n+ // if (pad_status.button & PAD_BUTTON_Y)\t// Test\n+ // dip_switch_0 &= ~0x80;\n+ // if (pad_status.button & PAD_BUTTON_X)\t// Service\n+ // dip_switch_0 &= ~0x40;\n+\n+ // Horizontal Scanning Frequency switch\n+ // Required for F-Zero AX booting via Sega Boot\n+ if (AMMediaboard::GetGameType() == FZeroAX ||\n+ AMMediaboard::GetGameType() == FZeroAXMonster)\n+ {\n+ dip_switch_0 &= ~0x20;\n+ }\n+\n+ // Disable camera in MKGP1/2\n+ if (AMMediaboard::GetGameType() == MarioKartGP ||\n+ AMMediaboard::GetGameType() == MarioKartGP2)\n+ {\n+ dip_switch_0 &= ~0x10;\n+ }\n+\n+ data_out[data_offset++] = dip_switch_0;\n+ data_out[data_offset++] = dip_switch_1;\n+ break;\n+ }\n+ case GCAMCommand::SerialNumber:\n+ {\n+ if (!validate_data_in_out(1, 18, "SerialNumber"))\n+ break;\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x11, {:02x} (READ SERIAL NR)",\n+ *data_in);\n+ data_in++;\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 16;\n+ memcpy(data_out.data() + data_offset, "AADE-01B98394904", 16);\n+\n+ data_offset += 16;\n+ break;\n+ }\n+ case GCAMCommand::Unknown_12:\n+ if (!validate_data_in_out(2, 2, "Unknown_12"))\n+ break;\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x12, {:02x} {:02x}", data_in[0],\n+ data_in[1]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x00;\n+\n+ data_in += 2;\n+ break;\n+ case GCAMCommand::Unknown_14:\n+ if (!validate_data_in_out(2, 2, "Unknown_14"))\n+ break;\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x14, {:02x} {:02x}", data_in[0],\n+ data_in[1]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x00;\n+\n+ data_in += 2;\n+ break;\n+ case GCAMCommand::FirmVersion:\n+ if (!validate_data_in_out(1, 4, "FirmVersion"))\n+ break;\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x15, {:02x} (READ FIRM VERSION)",\n+ *data_in);\n+ data_in++;\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x02;\n+ // 00.26\n+ data_out[data_offset++] = 0x00;\n+ data_out[data_offset++] = 0x26;\n+ break;\n+ case GCAMCommand::FPGAVersion:\n+ if (!validate_data_in_out(1, 4, "FPGAVersion"))\n+ break;\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x16, {:02x} (READ FPGA VERSION)",\n+ *data_in);\n+ data_in++;\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x02;\n+ // 07.06\n+ data_out[data_offset++] = 0x07;\n+ data_out[data_offset++] = 0x06;\n+ break;\n+ case GCAMCommand::RegionSettings:\n+ {\n+ if (!validate_data_in_out(5, 0x16, "RegionSettings"))\n+ break;\n+\n+ // Used by SegaBoot for region checks (dev mode skips this check)\n+ // In some games this also controls the displayed language\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x1F, {:02x} {:02x} {:02x} {:02x} {:02x} (REGION)",\n+ data_in[0], data_in[1], data_in[2], data_in[3], data_in[4]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x14;\n+\n+ for (int i = 0; i < 0x14; ++i)\n+ data_out[data_offset++] = s_region_flags[i];\n+\n+ data_in += 5;\n+ }\n+ break;\n+ // No reply\n+ // Note: Always sends three bytes even though size is set to two\n+ case GCAMCommand::Unknown_21:\n+ {\n+ if (!validate_data_in_out(4, 0, "Unknown_21"))\n+ break;\n+\n+ DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x21, {:02x}, {:02x}, {:02x}, {:02x}",\n+ data_in[0], data_in[1], data_in[2], data_in[3]);\n+ data_in += 4;\n+ }\n+ break;\n+ // No reply\n+ // Note: Always sends six bytes\n+ case GCAMCommand::Unknown_22:\n+ {\n+ if (!validate_data_in_out(7, 0, "Unknown_22"))\n+ break;\n+\n+ DEBUG_LOG_FMT(\n+ SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x22, {:02x}, {:02x}, {:02x}, {:02x}, {:02x}, {:02x}, {:02x}",\n+ data_in[0], data_in[1], data_in[2], data_in[3], data_in[4], data_in[5], data_in[6]);\n+\n+ const u32 in_size = data_in[0] + 1;\n+ if (!validate_data_in_out(in_size, 0, "Unknown_22"))\n+ break;\n+ data_in += in_size;\n+ }\n+ break;\n+ case GCAMCommand::Unknown_23:\n+ if (!validate_data_in_out(2, 2, "Unknown_23"))\n+ break;\n+\n+ DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x23, {:02x} {:02x}", data_in[0],\n+ data_in[1]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x00;\n+\n+ data_in += 2;\n+ break;\n+ case GCAMCommand::Unknown_24:\n+ if (!validate_data_in_out(2, 2, "Unknown_24"))\n+ break;\n+\n+ DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x24, {:02x} {:02x}", data_in[0],\n+ data_in[1]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x00;\n+\n+ data_in += 2;\n+ break;\n+ case GCAMCommand::SerialA:\n+ {\n+ if (!validate_data_in_out(1, 0, "SerialA"))\n+ break;\n+\n+ u32 length = *data_in++;\n+ if (length)\n+ {\n+ if (!validate_data_in_out(length, 0, "SerialA"))\n+ break;\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x31, length=0x{:02x}, hexdump:\\n{}",\n+ length, HexDump(data_in, length));\n+\n+ // Serial - Wheel\n+ if (AMMediaboard::GetGameType() == MarioKartGP ||\n+ AMMediaboard::GetGameType() == MarioKartGP2)\n+ {\n+ if (!validate_data_in_out(10, 2, "SerialA (Wheel)"))\n+ break;\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x31, (WHEEL) {:02x}{:02x} {:02x}{:02x} {:02x} {:02x} "\n+ "{:02x} {:02x} {:02x} {:02x}",\n+ data_in[0], data_in[1], data_in[2], data_in[3], data_in[4], data_in[5],\n+ data_in[6], data_in[7], data_in[8], data_in[9]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x03;\n+\n+ switch (m_wheel_init)\n+ {\n+ case 0:\n+ if (!validate_data_in_out(0, 3, "SerialA (Wheel)"))\n+ break;\n+ data_out[data_offset++] = \'E\'; // Error\n+ data_out[data_offset++] = \'0\';\n+ data_out[data_offset++] = \'0\';\n+ m_wheel_init++;\n+ break;\n+ case 1:\n+ if (!validate_data_in_out(0, 3, "SerialA (Wheel)"))\n+ break;\n+ data_out[data_offset++] = \'C\'; // Power Off\n+ data_out[data_offset++] = \'0\';\n+ data_out[data_offset++] = \'6\';\n+ // Only turn on when a wheel is connected\n+ if (serial_interface.GetDeviceType(1) == SerialInterface::SIDEVICE_GC_STEERING)\n+ {\n+ m_wheel_init++;\n+ }\n+ break;\n+ case 2:\n+ if (!validate_data_in_out(0, 3, "SerialA (Wheel)"))\n+ break;\n+ data_out[data_offset++] = \'C\'; // Power On\n+ data_out[data_offset++] = \'0\';\n+ data_out[data_offset++] = \'1\';\n+ break;\n+ default:\n+ break;\n+ }\n+\n+ // u16 CenteringForce= ptr(6);\n+ // u16 FrictionForce = ptr(8);\n+ // u16 Roll = ptr(10);\n+ if (!validate_data_in_out(length, 0, "SerialA (Wheel)"))\n+ break;\n+ data_in += length;\n+ break;\n+ }\n+\n+ // Serial - Unknown\n+ if (AMMediaboard::GetGameType() == GekitouProYakyuu)\n+ {\n+ if (!validate_data_in_out(sizeof(u32), 0, "SerialA (Unknown)"))\n+ break;\n+ const u32 serial_command = Common::BitCastPtr<u32>(data_in);\n+\n+ if (serial_command == 0x00001000)\n+ {\n+ if (!validate_data_in_out(0, 5, "SerialA (Unknown)"))\n+ break;\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x03;\n+ data_out[data_offset++] = 1;\n+ data_out[data_offset++] = 2;\n+ data_out[data_offset++] = 3;\n+ }\n+\n+ if (!validate_data_in_out(length, 0, "SerialA (Unknown)"))\n+ break;\n+ data_in += length;\n+ break;\n+ }\n+\n+ // Serial IC-CARD / Serial Deck Reader\n+ if (AMMediaboard::GetGameType() == VirtuaStriker4 ||\n+ AMMediaboard::GetGameType() == VirtuaStriker4_2006 ||\n+ AMMediaboard::GetGameType() == KeyOfAvalon)\n+ {\n+ if (!validate_data_in_out(2, 0, "SerialA (IC-CARD)"))\n+ break;\n+ u32 serial_command = data_in[1];\n+\n+ ICCommand icco;\n+\n+ // Set default reply\n+ icco.pktcmd = gcam_command;\n+ icco.pktlen = 7;\n+ icco.fixed = 0x10;\n+ icco.command = serial_command;\n+ icco.flag = 0;\n+ icco.length = 2;\n+ icco.status = 0;\n+ icco.extlen = 0;\n+\n+ // Check for rest of data from the write pages command\n+ if (m_ic_write_size && m_ic_write_offset)\n+ {\n+ const u32 size = data_in[1];\n+\n+ if (!validate_data_in_out(size + 2, 0, "SerialA (IC-CARD)"))\n+ break;\n+ DEBUG_LOG_FMT(SERIALINTERFACE_CARD, "Command: {}", HexDump(data_in, size + 2));\n+\n+ INFO_LOG_FMT(\n+ SERIALINTERFACE_CARD,\n+ "GC-AM: Command 25 (IC-CARD) Write Pages: Off:{:x} Size:{:x} PSize:{:x}",\n+ m_ic_write_offset, m_ic_write_size, size);\n+\n+ memcpy(m_ic_write_buffer + m_ic_write_offset, data_in + 2, size);\n+\n+ m_ic_write_offset += size;\n+\n+ if (m_ic_write_offset > m_ic_write_size)\n+ {\n+ m_ic_write_offset = 0;\n+\n+ const u16 page = m_ic_write_buffer[5];\n+ const u16 count = m_ic_write_buffer[7];\n+\n+ memcpy(m_ic_card_data + page * 8, m_ic_write_buffer + 10, count * 8);\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 25 (IC-CARD) Write Pages:{} Count:{}({:x})", page,\n+ count, size);\n+\n+ icco.command = WritePages;\n+\n+ ICCardSendReply(&icco, data_out.data(), &data_offset);\n+ }\n+ if (!validate_data_in_out(length, 0, "SerialA (IC-CARD)"))\n+ break;\n+ data_in += length;\n+ break;\n+ }\n+\n+ switch (ICCARDCommand(serial_command))\n+ {\n+ case ICCARDCommand::GetStatus:\n+ icco.status = m_ic_card_state;\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (IC-CARD) Get Status:{:02x}", m_ic_card_state);\n+ break;\n+ case ICCARDCommand::SetBaudrate:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (IC-CARD) Set Baudrate");\n+ break;\n+ case ICCARDCommand::FieldOn:\n+ m_ic_card_state |= 0x10;\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (IC-CARD) Field On");\n+ break;\n+ case ICCARDCommand::InsertCheck:\n+ icco.status = m_ic_card_status;\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (IC-CARD) Insert Check:{:02x}", m_ic_card_status);\n+ break;\n+ case ICCARDCommand::AntiCollision:\n+ icco.extlen = 8;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ // Card ID\n+ icco.extdata[0] = 0x00;\n+ icco.extdata[1] = 0x00;\n+ icco.extdata[2] = 0x54;\n+ icco.extdata[3] = 0x4D;\n+ icco.extdata[4] = 0x50;\n+ icco.extdata[5] = 0x00;\n+ icco.extdata[6] = 0x00;\n+ icco.extdata[7] = 0x00;\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (IC-CARD) Anti Collision");\n+ break;\n+ case ICCARDCommand::SelectCard:\n+ icco.extlen = 8;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ // Session\n+ icco.extdata[0] = 0x00;\n+ icco.extdata[1] = m_ic_card_session;\n+ icco.extdata[2] = 0x00;\n+ icco.extdata[3] = 0x00;\n+ icco.extdata[4] = 0x00;\n+ icco.extdata[5] = 0x00;\n+ icco.extdata[6] = 0x00;\n+ icco.extdata[7] = 0x00;\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (IC-CARD) Select Card:{}",\n+ m_ic_card_session);\n+ break;\n+ case ICCARDCommand::ReadPage:\n+ case ICCARDCommand::ReadUseCount:\n+ {\n+ if (!validate_data_in_out(8, 0, "SerialA (IC-CARD)"))\n+ break;\n+ const u16 page = Common::swap16(data_in + 6) & 0xFF; // 255 is max page\n+\n+ icco.extlen = 8;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ memcpy(icco.extdata, m_ic_card_data + page * 8, 8);\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 31 (IC-CARD) Read Page:{}",\n+ page);\n+ break;\n+ }\n+ case ICCARDCommand::WritePage:\n+ {\n+ if (!validate_data_in_out(10, 0, "SerialA (IC-CARD)"))\n+ break;\n+ const u16 page = Common::swap16(data_in + 8) & 0xFF; // 255 is max page\n+\n+ // Write only one page\n+ if (page == 4)\n+ {\n+ icco.status = 0x80;\n+ }\n+ else\n+ {\n+ if (!validate_data_in_out(18, 0, "SerialA (IC-CARD)"))\n+ break;\n+ memcpy(m_ic_card_data + page * 8, data_in + 10, 8);\n+ }\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (IC-CARD) Write Page:{}",\n+ page);\n+ break;\n+ }\n+ case ICCARDCommand::DecreaseUseCount:\n+ {\n+ if (!validate_data_in_out(8, 0, "SerialA (IC-CARD)"))\n+ break;\n+ const u16 page = Common::swap16(data_in + 6) & 0xFF; // 255 is max page\n+\n+ icco.extlen = 2;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ auto ic_card_data = Common::BitCastPtr<u16>(m_ic_card_data + 0x28);\n+ ic_card_data = ic_card_data - 1;\n+\n+ // Counter\n+ icco.extdata[0] = m_ic_card_data[0x28];\n+ icco.extdata[1] = m_ic_card_data[0x29];\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 31 (IC-CARD) Decrease Use Count:{}", page);\n+ break;\n+ }\n+ case ICCARDCommand::ReadPages:\n+ {\n+ if (!validate_data_in_out(10, 0, "SerialA (IC-CARD)"))\n+ break;\n+ const u16 page = Common::swap16(data_in + 6) & 0xFF; // 255 is max page\n+ const u16 count = Common::swap16(data_in + 8);\n+\n+ const u32 offs = page * 8;\n+ u32 cnt = count * 8;\n+\n+ // Limit read size to not overwrite the reply buffer\n+ if (cnt > (u32)0x50 - data_offset)\n+ {\n+ cnt = 5 * 8;\n+ }\n+\n+ icco.extlen = cnt;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ memcpy(icco.extdata, m_ic_card_data + offs, cnt);\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 31 (IC-CARD) Read Pages:{} Count:{}", page, count);\n+ break;\n+ }\n+ case ICCARDCommand::WritePages:\n+ {\n+ if (!validate_data_in_out(10, 0, "SerialA (IC-CARD)"))\n+ break;\n+ const u16 pksize = length;\n+ const u16 size = Common::swap16(data_in + 2);\n+ const u16 page = Common::swap16(data_in + 6) & 0xFF; // 255 is max page\n+ const u16 count = Common::swap16(data_in + 8);\n+\n+ // We got a complete packet\n+ if (pksize - 5 == size)\n+ {\n+ if (page == 4) // Read Only Page, must return error\n+ {\n+ icco.status = 0x80;\n+ }\n+ else\n+ {\n+ if (page * 8 + count * 8 > sizeof(m_ic_card_data))\n+ {\n+ ERROR_LOG_FMT(\n+ SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (IC-CARD) Data overflow: Pages:{} Count:{}({:x})",\n+ page, count, size);\n+ }\n+ else\n+ {\n+ if (!validate_data_in_out(13 + count * 8, 0, "SerialA (IC-CARD)"))\n+ break;\n+ memcpy(m_ic_card_data + page * 8, data_in + 13, count * 8);\n+ }\n+ }\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (IC-CARD) Write Pages:{} Count:{}({:x})", page,\n+ count, size);\n+ }\n+ // VirtuaStriker 4 splits the writes over multiple packets\n+ else\n+ {\n+ if (!validate_data_in_out(2 + pksize, 0, "SerialA (IC-CARD)"))\n+ break;\n+ memcpy(m_ic_write_buffer, data_in + 2, pksize);\n+ m_ic_write_offset += pksize;\n+ m_ic_write_size = size;\n+ }\n+ break;\n+ }\n+ default:\n+ // Handle Deck Reader commands\n+ if (!validate_data_in_out(1, 0, "SerialA (DECK READER)"))\n+ break;\n+ serial_command = data_in[0];\n+ icco.command = serial_command;\n+ icco.flag = 0;\n+ switch (CDReaderCommand(serial_command))\n+ {\n+ case CDReaderCommand::ProgramVersion:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Program Version");\n+\n+ icco.extlen = (u32)strlen(s_cdr_program_version);\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ memcpy(icco.extdata, s_cdr_program_version, icco.extlen);\n+ break;\n+ case CDReaderCommand::BootVersion:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Boot Version");\n+\n+ icco.extlen = (u32)strlen(s_cdr_boot_version);\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ memcpy(icco.extdata, s_cdr_boot_version, icco.extlen);\n+ break;\n+ case CDReaderCommand::ShutterGet:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Shutter Get");\n+\n+ icco.extlen = 4;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ icco.extdata[0] = 0;\n+ icco.extdata[1] = 0;\n+ icco.extdata[2] = 0;\n+ icco.extdata[3] = 0;\n+ break;\n+ case CDReaderCommand::CameraCheck:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Camera Check");\n+\n+ icco.extlen = 6;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ icco.extdata[0] = 0x23;\n+ icco.extdata[1] = 0x28;\n+ icco.extdata[2] = 0x45;\n+ icco.extdata[3] = 0x29;\n+ icco.extdata[4] = 0x45;\n+ icco.extdata[5] = 0x29;\n+ break;\n+ case CDReaderCommand::ProgramChecksum:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Program Checksum");\n+\n+ icco.extlen = 4;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ icco.extdata[0] = 0x23;\n+ icco.extdata[1] = 0x28;\n+ icco.extdata[2] = 0x45;\n+ icco.extdata[3] = 0x29;\n+ break;\n+ case CDReaderCommand::BootChecksum:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Boot Checksum");\n+\n+ icco.extlen = 4;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ icco.extdata[0] = 0x23;\n+ icco.extdata[1] = 0x28;\n+ icco.extdata[2] = 0x45;\n+ icco.extdata[3] = 0x29;\n+ break;\n+ case CDReaderCommand::SelfTest:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (DECK READER) Self Test");\n+ icco.flag = 0x00;\n+ break;\n+ case CDReaderCommand::SensLock:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (DECK READER) Sens Lock");\n+ icco.flag = 0x01;\n+ break;\n+ case CDReaderCommand::SensCard:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (DECK READER) Sens Card");\n+ break;\n+ case CDReaderCommand::ShutterCard:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Shutter Card");\n+ break;\n+ case CDReaderCommand::ReadCard:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (DECK READER) Read Card");\n+\n+ icco.fixed = 0xAA;\n+ icco.flag = 0xAA;\n+ icco.extlen = sizeof(s_cdr_card_data);\n+ icco.length = 0x72;\n+ icco.status = Common::swap16(icco.extlen);\n+\n+ icco.pktlen += icco.extlen;\n+\n+ memcpy(icco.extdata, s_cdr_card_data, sizeof(s_cdr_card_data));\n+\n+ break;\n+ default:\n+ if (!validate_data_in_out(14, 0, "SerialA (DECK READER)"))\n+ break;\n+ WARN_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (IC-Card) {:02x} {:02x} {:02x} {:02x} {:02x} "\n+ "{:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x}",\n+ data_in[2], data_in[3], data_in[4], data_in[5], data_in[6],\n+ data_in[7], data_in[8], data_in[9], data_in[10], data_in[11],\n+ data_in[12], data_in[13]);\n+ break;\n+ }\n+ break;\n+ }\n+\n+ if (!validate_data_in_out(0, icco.pktlen + 2, "SerialA (IC-CARD)"))\n+ break;\n+ ICCardSendReply(&icco, data_out.data(), &data_offset);\n+\n+ if (!validate_data_in_out(2, 0, "SerialA (IC-CARD)"))\n+ break;\n+ data_in += length;\n+ break;\n+ }\n+ }\n+\n+ u32 command_offset = 0;\n+ while (command_offset < length)\n+ {\n+ // All commands are OR\'d with 0x80\n+ // Last byte is checksum which we don\'t care about\n+ if (!validate_data_in_out(command_offset + 4, 0, "SerialA"))\n+ break;\n+ const u32 serial_command = Common::swap32(data_in + command_offset) ^ 0x80000000;\n+\n+ if (AMMediaboard::GetGameType() == FZeroAX ||\n+ AMMediaboard::GetGameType() == FZeroAXMonster)\n+ {\n+ INFO_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x31 (MOTOR) Length:{:02x} Command:{:04x}({:02x})",\n+ length, (serial_command >> 8) & 0xFFFF, serial_command >> 24);\n+ }\n+ else\n+ {\n+ INFO_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x31 (SERIAL) Command:{:06x}",\n+ serial_command);\n+\n+ if (serial_command == 0x801000)\n+ {\n+ if (!validate_data_in_out(0, 4, "SerialA"))\n+ break;\n+ data_out[data_offset++] = 0x31;\n+ data_out[data_offset++] = 0x02;\n+ data_out[data_offset++] = 0xFF;\n+ data_out[data_offset++] = 0x01;\n+ }\n+ }\n+\n+ command_offset += sizeof(u32);\n+\n+ if (AMMediaboard::GetGameType() == FZeroAX ||\n+ AMMediaboard::GetGameType() == FZeroAXMonster)\n+ {\n+ // Status\n+ m_motor_reply[command_offset + 2] = 0;\n+ m_motor_reply[command_offset + 3] = 0;\n+\n+ // Error\n+ m_motor_reply[command_offset + 4] = 0;\n+\n+ switch (serial_command >> 24)\n+ {\n+ case 0:\n+ case 1: // Set Maximum?\n+ case 2:\n+ break;\n+\n+ // 0x00-0x40: left\n+ // 0x40-0x80: right\n+ case 4: // Move Steering Wheel\n+ // Left\n+ if (serial_command & 0x010000)\n+ {\n+ m_motor_force_y = -((s16)serial_command & 0xFF00);\n+ }\n+ else // Right\n+ {\n+ m_motor_force_y = (serial_command - 0x4000) & 0xFF00;\n+ }\n+\n+ m_motor_force_y *= 2;\n+\n+ // FFB\n+ if (m_motor_init == 2)\n+ {\n+ if (serial_interface.GetDeviceType(1) == SerialInterface::SIDEVICE_GC_STEERING)\n+ {\n+ const GCPadStatus pad_status = Pad::GetStatus(1);\n+ if (pad_status.isConnected)\n+ {\n+ const ControlState mapped_strength = (double)(m_motor_force_y >> 8) / 127.f;\n+\n+ Pad::Rumble(1, mapped_strength);\n+ INFO_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x31 (MOTOR) mapped_strength:{}",\n+ mapped_strength);\n+ }\n+ }\n+ }\n+ break;\n+ case 6: // nice\n+ case 9:\n+ default:\n+ break;\n+ // Switch back to normal controls\n+ case 7:\n+ m_motor_init = 2;\n+ break;\n+ // Reset\n+ case 0x7F:\n+ m_motor_init = 1;\n+ memset(m_motor_reply, 0, sizeof(m_motor_reply));\n+ break;\n+ }\n+\n+ // Checksum\n+ m_motor_reply[command_offset + 5] = m_motor_reply[command_offset + 2] ^\n+ m_motor_reply[command_offset + 3] ^\n+ m_motor_reply[command_offset + 4];\n+ }\n+ }\n+\n+ if (length == 0)\n+ {\n+ if (!validate_data_in_out(length, 2, "SerialA"))\n+ break;\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x00;\n+ }\n+ else\n+ {\n+ if (m_motor_init)\n+ {\n+ // Motor\n+ m_motor_reply[0] = gcam_command;\n+ m_motor_reply[1] = length; // Same out as in size\n+\n+ const u32 reply_size = m_motor_reply[1] + 2;\n+ if (!validate_data_in_out(length, reply_size, "SerialA"))\n+ break;\n+\n+ memcpy(data_out.data() + data_offset, m_motor_reply, reply_size);\n+ data_offset += reply_size;\n+ }\n+ }\n+\n+ if (!validate_data_in_out(length, 0, "SerialA"))\n+ {\n+ break;\n+ }\n+ data_in += length;\n+ break;\n+ }\n+ case GCAMCommand::SerialB:\n+ {\n+ DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 32 (CARD-Interface)");\n+ if (!validate_data_in_out(1, 0, "SerialB"))\n+ break;\n+ const u32 length = *data_in++;\n+ if (!validate_data_in_out(length, 0, "SerialB"))\n+ break;\n+ if (length)\n+ {\n+ // Send Card Reply\n+ if (length == 1 && data_in[0] == 0x05)\n+ {\n+ if (m_card_read_length)\n+ {\n+ if (!validate_data_in_out(0, 1, "SerialB"))\n+ break;\n+ data_out[data_offset++] = gcam_command;\n+ u32 read_length = m_card_read_length - m_card_read;\n+\n+ if (AMMediaboard::GetGameType() == FZeroAX)\n+ {\n+ read_length = std::min<u32>(read_length, 0x2F);\n+ }\n+\n+ if (!validate_data_in_out(0, 1, "SerialB"))\n+ break;\n+ data_out[data_offset++] = read_length; // 0x2F (max size per packet)\n+\n+ if (!validate_data_in_out(0, read_length, "SerialB"))\n+ break;\n+ memcpy(data_out.data() + data_offset, m_card_read_packet + m_card_read,\n+ read_length);\n+\n+ data_offset += read_length;\n+ m_card_read += read_length;\n+\n+ if (m_card_read >= m_card_read_length)\n+ m_card_read_length = 0;\n+\n+ data_in += length;\n+ break;\n+ }\n+\n+ if (!validate_data_in_out(0, 5, "SerialB"))\n+ break;\n+\n+ data_out[data_offset++] = gcam_command;\n+ const u32 command_length_offset = data_offset;\n+ data_out[data_offset++] = 0x00; // len\n+\n+ data_out[data_offset++] = 0x02;\n+ const u32 checksum_start = data_offset;\n+\n+ data_out[data_offset++] = 0x00; // 0x00 len\n+\n+ data_out[data_offset++] = m_card_command; // 0x01 command\n+\n+ switch (CARDCommand(m_card_command))\n+ {\n+ case CARDCommand::Init:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x00; // 0x02\n+ data_out[data_offset++] = 0x30; // 0x03\n+ break;\n+ case CARDCommand::GetState:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x20 | m_card_bit; // 0x02\n+\n+ // bit 0: Please take your card\n+ // bit 1: Endless waiting causes UNK_E to be called\n+ data_out[data_offset++] = 0x00; // 0x03\n+ break;\n+ case CARDCommand::Read:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x02; // 0x02\n+ data_out[data_offset++] = 0x53; // 0x03\n+ break;\n+ case CARDCommand::IsPresent:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x22; // 0x02\n+ data_out[data_offset++] = 0x30; // 0x03\n+ break;\n+ case CARDCommand::Write:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x02; // 0x02\n+ data_out[data_offset++] = 0x00; // 0x03\n+ break;\n+ case CARDCommand::SetPrintParam:\n+ case CARDCommand::RegisterFont:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x00; // 0x02\n+ data_out[data_offset++] = 0x00; // 0x03\n+ break;\n+ case CARDCommand::WriteInfo:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x02; // 0x02\n+ data_out[data_offset++] = 0x00; // 0x03\n+ break;\n+ case CARDCommand::Erase:\n+ // TODO: CARDCommand::Erase is not handled.\n+ break;\n+ case CARDCommand::Eject:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ if (AMMediaboard::GetGameType() == FZeroAX)\n+ {\n+ data_out[data_offset++] = 0x01; // 0x02\n+ }\n+ else\n+ {\n+ data_out[data_offset++] = 0x31; // 0x02\n+ }\n+ data_out[data_offset++] = 0x30; // 0x03\n+ break;\n+ case CARDCommand::Clean:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x02; // 0x02\n+ data_out[data_offset++] = 0x00; // 0x03\n+ break;\n+ case CARDCommand::Load:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x02; // 0x02\n+ data_out[data_offset++] = 0x30; // 0x03\n+ break;\n+ case CARDCommand::SetShutter:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x00; // 0x02\n+ data_out[data_offset++] = 0x00; // 0x03\n+ break;\n+ }\n+\n+ if (!validate_data_in_out(0, 3, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x30; // 0x04\n+ data_out[data_offset++] = 0x00; // 0x05\n+\n+ data_out[data_offset++] = 0x03; // 0x06\n+\n+ data_out[checksum_start] = data_offset - checksum_start; // 0x00 len\n+\n+ if (!validate_data_in_out(0, 1, "SerialB"))\n+ break;\n+ data_out[data_offset] = 0; // 0x07\n+ for (u32 i = 0; i < data_out[checksum_start]; ++i)\n+ data_out[data_offset] ^= data_out[checksum_start + i];\n+\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_offset++;\n+\n+ data_out[command_length_offset] = data_out[checksum_start] + 2;\n+ }\n+ else\n+ {\n+ for (u32 i = 0; i < length; ++i)\n+ m_card_buffer[m_card_offset + i] = data_in[i];\n+\n+ m_card_offset += length;\n+\n+ // Check if we got a complete command\n+ if (m_card_buffer[0] == 0x02)\n+ {\n+ if (m_card_buffer[1] == m_card_offset - 2)\n+ {\n+ if (m_card_buffer[m_card_offset - 2] == 0x03)\n+ {\n+ m_card_command = m_card_buffer[2];\n+\n+ switch (CARDCommand(m_card_command))\n+ {\n+ case CARDCommand::Init:\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD Init");\n+\n+ m_card_write_length = 0;\n+ m_card_bit = 0;\n+ m_card_memory_size = 0;\n+ m_card_state_call_count = 0;\n+ break;\n+ case CARDCommand::GetState:\n+ {\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD GetState({:02X})",\n+ m_card_bit);\n+\n+ if (m_card_memory_size == 0)\n+ {\n+ const std::string card_filename(\n+ fmt::format("{}tricard_{}.bin", File::GetUserPath(D_TRIUSER_IDX),\n+ SConfig::GetInstance().GetGameID()));\n+\n+ if (File::Exists(card_filename))\n+ {\n+ File::IOFile card(card_filename, "rb+");\n+ m_card_memory_size = (u32)card.GetSize();\n+\n+ card.ReadBytes(m_card_memory, m_card_memory_size);\n+ card.Close();\n+\n+ m_card_is_inserted = true;\n+ }\n+ }\n+\n+ if (AMMediaboard::GetGameType() == FZeroAX && m_card_memory_size)\n+ {\n+ m_card_state_call_count++;\n+ if (m_card_state_call_count > 10)\n+ {\n+ if (m_card_bit & 2)\n+ m_card_bit &= ~2u;\n+ else\n+ m_card_bit |= 2;\n+\n+ m_card_state_call_count = 0;\n+ }\n+ }\n+\n+ if (m_card_clean == 1)\n+ {\n+ m_card_clean = 2;\n+ }\n+ else if (m_card_clean == 2)\n+ {\n+ const std::string card_filename(\n+ fmt::format("{}tricard_{}.bin", File::GetUserPath(D_TRIUSER_IDX),\n+ SConfig::GetInstance().GetGameID()));\n+\n+ if (File::Exists(card_filename))\n+ {\n+ m_card_memory_size = (u32)File::GetSize(card_filename);\n+ if (m_card_memory_size)\n+ {\n+ if (AMMediaboard::GetGameType() == FZeroAX)\n+ {\n+ m_card_bit = 2;\n+ }\n+ else\n+ {\n+ m_card_bit = 1;\n+ }\n+ }\n+ }\n+ m_card_clean = 0;\n+ }\n+ break;\n+ }\n+ case CARDCommand::IsPresent:\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD IsPresent");\n+ break;\n+ case CARDCommand::RegisterFont:\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD RegisterFont");\n+ break;\n+ case CARDCommand::Load:\n+ {\n+ u8 mode = m_card_buffer[6];\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD Load({:02X})",\n+ mode);\n+ break;\n+ }\n+ case CARDCommand::Clean:\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD Clean");\n+ m_card_clean = 1;\n+ break;\n+ case CARDCommand::Read:\n+ {\n+ const u8 mode = m_card_buffer[6];\n+ const u8 bitmode = m_card_buffer[7];\n+ const u8 track = m_card_buffer[8];\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command CARD Read({:02X},{:02X},{:02X})", mode,\n+ bitmode, track);\n+\n+ // Prepare read packet\n+ memset(m_card_read_packet, 0, 0xDB);\n+ u32 packet_offset = 0;\n+\n+ const std::string card_filename(\n+ fmt::format("{}tricard_{}.bin", File::GetUserPath(D_TRIUSER_IDX),\n+ SConfig::GetInstance().GetGameID()));\n+\n+ if (File::Exists(card_filename))\n+ {\n+ File::IOFile card(card_filename, "rb+");\n+ if (m_card_memory_size == 0)\n+ {\n+ m_card_memory_size = (u32)card.GetSize();\n+ }\n+\n+ card.ReadBytes(m_card_memory, m_card_memory_size);\n+ card.Close();\n+\n+ m_card_is_inserted = true;\n+ }\n+\n+ m_card_read_packet[packet_offset++] = 0x02; // SUB CMD\n+ m_card_read_packet[packet_offset++] = 0x00; // SUB CMDLen\n+\n+ m_card_read_packet[packet_offset++] = 0x33; // CARD CMD\n+\n+ if (m_card_is_inserted) // CARD Status\n+ {\n+ m_card_read_packet[packet_offset++] = 0x31;\n+ }\n+ else\n+ {\n+ m_card_read_packet[packet_offset++] = 0x30;\n+ }\n+\n+ m_card_read_packet[packet_offset++] = 0x30;\n+ m_card_read_packet[packet_offset++] = 0x30;\n+\n+ // Data reply\n+ memcpy(m_card_read_packet + packet_offset, m_card_memory, m_card_memory_size);\n+ packet_offset += m_card_memory_size;\n+\n+ m_card_read_packet[packet_offset++] = 0x03;\n+\n+ m_card_read_packet[1] = packet_offset - 1; // SUB CMDLen\n+\n+ for (u32 i = 0; i < packet_offset - 1; ++i)\n+ m_card_read_packet[packet_offset] ^= m_card_read_packet[1 + i];\n+\n+ packet_offset++;\n+\n+ m_card_read_length = packet_offset;\n+ m_card_read = 0;\n+ break;\n+ }\n+ case CARDCommand::Write:\n+ {\n+ const u8 mode = m_card_buffer[6];\n+ const u8 bitmode = m_card_buffer[7];\n+ const u8 track = m_card_buffer[8];\n+\n+ m_card_memory_size = m_card_buffer[1] - 9;\n+\n+ memcpy(m_card_memory, m_card_buffer + 9, m_card_memory_size);', 'path': 'Source/Core/Core/HW/SI/SI_DeviceAMBaseboard.cpp', 'position': 1536, 'original_position': 1414, 'commit_id': '510fec88be973bc870021f2d0347593cdc285864', 'user': {'login': 'JosJuice', 'id': 6716818, 'node_id': 'MDQ6VXNlcjY3MTY4MTg=', 'avatar_url': 'https://avatars.githubusercontent.com/u/6716818?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/JosJuice', 'html_url': 'https://github.com/JosJuice', 'followers_url': 'https://api.github.com/users/JosJuice/followers', 'following_url': 'https://api.github.com/users/JosJuice/following{/other_user}', 'gists_url': 'https://api.github.com/users/JosJuice/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/JosJuice/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/JosJuice/subscriptions', 'organizations_url': 'https://api.github.com/users/JosJuice/orgs', 'repos_url': 'https://api.github.com/users/JosJuice/repos', 'events_url': 'https://api.github.com/users/JosJuice/events{/privacy}', 'received_events_url': 'https://api.github.com/users/JosJuice/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'body': "My bad. I think I got distracted by the static_assert and didn't read far up enough to see the `m_card_memory_size` check.", 'created_at': '2026-01-31T19:01:49Z', 'updated_at': '2026-01-31T19:01:49Z', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/13844#discussion_r2749867718', 'pull_request_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13844', 'author_association': 'MEMBER', '_links': {'self': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2749867718'}, 'html': {'href': 'https://github.com/dolphin-emu/dolphin/pull/13844#discussion_r2749867718'}, 'pull_request': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13844'}}, 'original_commit_id': '2415f0c1c471b2446a2144a1a2b643d8a77d8cc7', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2749867718/reactions', 'total_count': 0, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 0, 'confused': 0, 'heart': 0, 'rocket': 0, 'eyes': 0}, 'in_reply_to_id': 2725631348}], 'type': 'gh_pull_request_review'}
2026-01-31T18:51:16.078448 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'sepalani', 'action': 'submitted', 'pr_id': 13844, 'pr_title': 'Core: Triforce support', 'state': 'commented', 'url': 'https://github.com/dolphin-emu/dolphin/pull/13844#pullrequestreview-3733256073', 'comments': [{'id': 2749856368, 'node_id': 'PRRC_kwDOALCn2M6j53pw', 'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2749856368', 'pull_request_review_id': 3733256073, 'diff_hunk': '@@ -0,0 +1,2501 @@\n+// Copyright 2025 Dolphin Emulator Project\n+// SPDX-License-Identifier: GPL-2.0-or-later\n+\n+#include "Core/HW/SI/SI_DeviceAMBaseboard.h"\n+\n+#include <algorithm>\n+#include <numeric>\n+#include <string>\n+\n+#include <fmt/format.h>\n+\n+#include "Common/Buffer.h"\n+#include "Common/CommonTypes.h"\n+#include "Common/FileUtil.h"\n+#include "Common/IOFile.h"\n+#include "Common/Logging/Log.h"\n+#include "Common/MsgHandler.h"\n+#include "Common/Swap.h"\n+\n+#include "Core/Boot/Boot.h"\n+#include "Core/BootManager.h"\n+#include "Core/Config/MainSettings.h"\n+#include "Core/ConfigManager.h"\n+#include "Core/Core.h"\n+#include "Core/CoreTiming.h"\n+#include "Core/HW/DVD/AMMediaboard.h"\n+#include "Core/HW/DVD/DVDInterface.h"\n+#include "Core/HW/EXI/EXI.h"\n+#include "Core/HW/GCPad.h"\n+#include "Core/HW/MMIO.h"\n+#include "Core/HW/Memmap.h"\n+#include "Core/HW/ProcessorInterface.h"\n+#include "Core/HW/SI/SI.h"\n+#include "Core/HW/SI/SI_Device.h"\n+#include "Core/HW/SI/SI_DeviceGCController.h"\n+#include "Core/HW/SystemTimers.h"\n+#include "Core/Movie.h"\n+#include "Core/NetPlayProto.h"\n+#include "Core/System.h"\n+\n+#include "InputCommon/GCPadStatus.h"\n+\n+namespace SerialInterface\n+{\n+void JVSIOMessage::Start(int node)\n+{\n+ m_last_start = m_pointer;\n+ const u8 header[3] = {0xE0, (u8)node, 0};\n+ m_checksum = 0;\n+ AddData(header, 3, 1);\n+}\n+\n+void JVSIOMessage::AddData(const u8* dst, size_t len, int sync = 0)\n+{\n+ if (m_pointer + len >= sizeof(m_message))\n+ {\n+ PanicAlertFmt("JVSIOMessage overrun!");\n+ return;\n+ }\n+\n+ while (len--)\n+ {\n+ const u8 c = *dst++;\n+ if (!sync && ((c == 0xE0) || (c == 0xD0)))\n+ {\n+ if (m_pointer + 2 > sizeof(m_message))\n+ {\n+ PanicAlertFmt("JVSIOMessage overrun!");\n+ break;\n+ }\n+ m_message[m_pointer++] = 0xD0;\n+ m_message[m_pointer++] = c - 1;\n+ }\n+ else\n+ {\n+ if (m_pointer >= sizeof(m_message))\n+ {\n+ PanicAlertFmt("JVSIOMessage overrun!");\n+ break;\n+ }\n+ m_message[m_pointer++] = c;\n+ }\n+\n+ if (!sync)\n+ m_checksum += c;\n+ sync = 0;\n+ }\n+}\n+\n+void JVSIOMessage::AddData(const void* data, size_t len)\n+{\n+ AddData(static_cast<const u8*>(data), len);\n+}\n+\n+void JVSIOMessage::AddData(const char* data)\n+{\n+ AddData(data, strlen(data));\n+}\n+\n+void JVSIOMessage::AddData(u32 n)\n+{\n+ const u8 cs = n;\n+ AddData(&cs, 1);\n+}\n+\n+void JVSIOMessage::End()\n+{\n+ const u32 len = m_pointer - m_last_start;\n+ if (m_last_start + 2 < sizeof(m_message) && len >= 3)\n+ {\n+ m_message[m_last_start + 2] = len - 2; // assuming len <0xD0\n+ AddData(m_checksum + len - 2);\n+ }\n+ else\n+ {\n+ PanicAlertFmt("JVSIOMessage: Not enough space for checksum!");\n+ }\n+}\n+\n+static constexpr u8 CheckSumXOR(const u8* data, u32 length)\n+{\n+ return std::accumulate(data, data + length, u8{}, std::bit_xor());\n+}\n+\n+static constexpr char s_cdr_program_version[] = {" Version 1.22,2003/09/19,171-8213B"};\n+static constexpr char s_cdr_boot_version[] = {" Version 1.04,2003/06/17,171-8213B"};\n+static constexpr u8 s_cdr_card_data[] = {\n+ 0x00, 0x6E, 0x00, 0x00, 0x01, 0x00, 0x00, 0x06, 0x00, 0x00, 0x07, 0x00, 0x00, 0x0B,\n+ 0x00, 0x00, 0x0E, 0x00, 0x00, 0x10, 0x00, 0x00, 0x17, 0x00, 0x00, 0x19, 0x00, 0x00,\n+ 0x1A, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x20, 0x00,\n+ 0x00, 0x22, 0x00, 0x00, 0x23, 0x00, 0x00, 0x24, 0x00, 0x00, 0x27, 0x00, 0x00, 0x28,\n+ 0x00, 0x00, 0x2C, 0x00, 0x00, 0x2F, 0x00, 0x00, 0x34, 0x00, 0x00, 0x35, 0x00, 0x00,\n+ 0x37, 0x00, 0x00, 0x38, 0x00, 0x00, 0x39, 0x00, 0x00, 0x3D, 0x00};\n+\n+const constexpr u8 s_region_flags[] = "\\x00\\x00\\x30\\x00"\n+ // "\\x01\\xfe\\x00\\x00" // JAPAN\n+ "\\x02\\xfd\\x00\\x00" // USA\n+ //"\\x03\\xfc\\x00\\x00" // export\n+ "\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff";\n+// AM-Baseboard device on SI\n+CSIDevice_AMBaseboard::CSIDevice_AMBaseboard(Core::System& system, SIDevices device,\n+ int device_number)\n+ : ISIDevice(system, device, device_number)\n+{\n+ // Card ID\n+ m_ic_card_data[0x20] = 0x95;\n+ m_ic_card_data[0x21] = 0x71;\n+\n+ if (AMMediaboard::GetGameType() == KeyOfAvalon)\n+ {\n+ m_ic_card_data[0x22] = 0x26;\n+ m_ic_card_data[0x23] = 0x40;\n+ }\n+ else if (AMMediaboard::GetGameType() == VirtuaStriker4)\n+ {\n+ m_ic_card_data[0x22] = 0x44;\n+ m_ic_card_data[0x23] = 0x00;\n+ }\n+\n+ // Use count\n+ m_ic_card_data[0x28] = 0xFF;\n+ m_ic_card_data[0x29] = 0xFF;\n+}\n+\n+constexpr u32 SI_XFER_LENGTH_MASK = 0x7f;\n+\n+// Translate [0,1,2,...,126,127] to [128,1,2,...,126,127]\n+constexpr s32 ConvertSILengthField(u32 field)\n+{\n+ return ((field - 1) & SI_XFER_LENGTH_MASK) + 1;\n+}\n+\n+void CSIDevice_AMBaseboard::ICCardSendReply(ICCommand* iccommand, u8* buffer, u32* length)\n+{\n+ iccommand->status = Common::swap16(iccommand->status);\n+\n+ const auto iccommand_data = reinterpret_cast<const u8*>(iccommand);\n+ const u8 crc = CheckSumXOR(iccommand_data + 2, iccommand->pktlen - 1);\n+\n+ for (u32 i = 0; i < iccommand->pktlen + 1; ++i)\n+ {\n+ buffer[(*length)++] = iccommand_data[i];\n+ }\n+\n+ buffer[(*length)++] = crc;\n+}\n+\n+void CSIDevice_AMBaseboard::SwapBuffers(u8* buffer, u32* buffer_length)\n+{\n+ memcpy(m_last[1], buffer, 0x80); // Save current buffer\n+ memcpy(buffer, m_last[0], 0x80); // Load previous buffer\n+ memcpy(m_last[0], m_last[1], 0x80); // Update history\n+\n+ m_lastptr[1] = *buffer_length; // Swap lengths\n+ *buffer_length = m_lastptr[0];\n+ m_lastptr[0] = m_lastptr[1];\n+}\n+\n+int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length)\n+{\n+ const auto& serial_interface = m_system.GetSerialInterface();\n+ u32 buffer_length = ConvertSILengthField(serial_interface.GetInLength());\n+\n+ // Debug logging\n+ ISIDevice::RunBuffer(buffer, buffer_length);\n+\n+ u32 buffer_position = 0;\n+ while (buffer_position < buffer_length)\n+ {\n+ BaseBoardCommand command = static_cast<BaseBoardCommand>(buffer[buffer_position]);\n+ buffer_position++;\n+\n+ switch (command)\n+ {\n+ case BaseBoardCommand::GCAM_Reset: // Returns ID and dip switches\n+ {\n+ const u32 id = Common::swap32(SI_AM_BASEBOARD | 0x100);\n+ std::memcpy(buffer, &id, sizeof(id));\n+ return sizeof(id);\n+ }\n+ break;\n+ case BaseBoardCommand::GCAM_Command:\n+ {\n+ u32 checksum = 0;\n+ for (u32 i = 0; i < buffer_length; ++i)\n+ checksum += buffer[i];\n+\n+ std::array<u8, 0x80> data_out{};\n+ u32 data_offset = 0;\n+\n+ static u32 dip_switch_1 = 0xFE;\n+ static u32 dip_switch_0 = 0xFF;\n+\n+ data_out[data_offset++] = 1;\n+ data_out[data_offset++] = 1;\n+\n+ u8* data_in = buffer + 2;\n+ u8* const data_in_end = buffer + buffer[buffer_position] + 2;\n+\n+ // Helper to check that iterating over data n times is safe,\n+ // i.e. *data++ at most lead to data.end()\n+ auto validate_data_in_out = [&](u32 n_in, u32 n_out, std::string_view command) -> bool {\n+ if (data_in + n_in > data_in_end)\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: data_in overflow in {}", command);\n+ else if (data_offset + n_out > data_out.size())\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: data_out overflow in {}", command);\n+ else\n+ return true;\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "Overflow details:\\n"\n+ " - data_in(begin={}, current={}, end={}, n_in={})\\n"\n+ " - data_out(offset={}, size={}, n_out={})\\n"\n+ " - buffer(position={}, length={})",\n+ fmt::ptr(buffer + 2), fmt::ptr(data_in), fmt::ptr(data_in_end), n_in,\n+ data_offset, data_out.size(), n_out, buffer_position, buffer_length);\n+ data_in = data_in_end;\n+ return false;\n+ };\n+\n+ while (data_in < data_in_end)\n+ {\n+ const u32 gcam_command = *data_in++;\n+ switch (GCAMCommand(gcam_command))\n+ {\n+ case GCAMCommand::StatusSwitches:\n+ {\n+ if (!validate_data_in_out(1, 4, "StatusSwitches"))\n+ break;\n+\n+ const u8 status = *data_in++;\n+ DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x10, {:02x} (READ STATUS&SWITCHES)",\n+ status);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x2;\n+\n+ // We read Test/Service from the JVS-I/O SwitchesInput instead\n+ //\n+ // const GCPadStatus pad_status = Pad::GetStatus(ISIDevice::m_device_number);\n+ // baseboard test/service switches\n+ // if (pad_status.button & PAD_BUTTON_Y)\t// Test\n+ // dip_switch_0 &= ~0x80;\n+ // if (pad_status.button & PAD_BUTTON_X)\t// Service\n+ // dip_switch_0 &= ~0x40;\n+\n+ // Horizontal Scanning Frequency switch\n+ // Required for F-Zero AX booting via Sega Boot\n+ if (AMMediaboard::GetGameType() == FZeroAX ||\n+ AMMediaboard::GetGameType() == FZeroAXMonster)\n+ {\n+ dip_switch_0 &= ~0x20;\n+ }\n+\n+ // Disable camera in MKGP1/2\n+ if (AMMediaboard::GetGameType() == MarioKartGP ||\n+ AMMediaboard::GetGameType() == MarioKartGP2)\n+ {\n+ dip_switch_0 &= ~0x10;\n+ }\n+\n+ data_out[data_offset++] = dip_switch_0;\n+ data_out[data_offset++] = dip_switch_1;\n+ break;\n+ }\n+ case GCAMCommand::SerialNumber:\n+ {\n+ if (!validate_data_in_out(1, 18, "SerialNumber"))\n+ break;\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x11, {:02x} (READ SERIAL NR)",\n+ *data_in);\n+ data_in++;\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 16;\n+ memcpy(data_out.data() + data_offset, "AADE-01B98394904", 16);\n+\n+ data_offset += 16;\n+ break;\n+ }\n+ case GCAMCommand::Unknown_12:\n+ if (!validate_data_in_out(2, 2, "Unknown_12"))\n+ break;\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x12, {:02x} {:02x}", data_in[0],\n+ data_in[1]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x00;\n+\n+ data_in += 2;\n+ break;\n+ case GCAMCommand::Unknown_14:\n+ if (!validate_data_in_out(2, 2, "Unknown_14"))\n+ break;\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x14, {:02x} {:02x}", data_in[0],\n+ data_in[1]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x00;\n+\n+ data_in += 2;\n+ break;\n+ case GCAMCommand::FirmVersion:\n+ if (!validate_data_in_out(1, 4, "FirmVersion"))\n+ break;\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x15, {:02x} (READ FIRM VERSION)",\n+ *data_in);\n+ data_in++;\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x02;\n+ // 00.26\n+ data_out[data_offset++] = 0x00;\n+ data_out[data_offset++] = 0x26;\n+ break;\n+ case GCAMCommand::FPGAVersion:\n+ if (!validate_data_in_out(1, 4, "FPGAVersion"))\n+ break;\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x16, {:02x} (READ FPGA VERSION)",\n+ *data_in);\n+ data_in++;\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x02;\n+ // 07.06\n+ data_out[data_offset++] = 0x07;\n+ data_out[data_offset++] = 0x06;\n+ break;\n+ case GCAMCommand::RegionSettings:\n+ {\n+ if (!validate_data_in_out(5, 0x16, "RegionSettings"))\n+ break;\n+\n+ // Used by SegaBoot for region checks (dev mode skips this check)\n+ // In some games this also controls the displayed language\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x1F, {:02x} {:02x} {:02x} {:02x} {:02x} (REGION)",\n+ data_in[0], data_in[1], data_in[2], data_in[3], data_in[4]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x14;\n+\n+ for (int i = 0; i < 0x14; ++i)\n+ data_out[data_offset++] = s_region_flags[i];\n+\n+ data_in += 5;\n+ }\n+ break;\n+ // No reply\n+ // Note: Always sends three bytes even though size is set to two\n+ case GCAMCommand::Unknown_21:\n+ {\n+ if (!validate_data_in_out(4, 0, "Unknown_21"))\n+ break;\n+\n+ DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x21, {:02x}, {:02x}, {:02x}, {:02x}",\n+ data_in[0], data_in[1], data_in[2], data_in[3]);\n+ data_in += 4;\n+ }\n+ break;\n+ // No reply\n+ // Note: Always sends six bytes\n+ case GCAMCommand::Unknown_22:\n+ {\n+ if (!validate_data_in_out(7, 0, "Unknown_22"))\n+ break;\n+\n+ DEBUG_LOG_FMT(\n+ SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x22, {:02x}, {:02x}, {:02x}, {:02x}, {:02x}, {:02x}, {:02x}",\n+ data_in[0], data_in[1], data_in[2], data_in[3], data_in[4], data_in[5], data_in[6]);\n+\n+ const u32 in_size = data_in[0] + 1;\n+ if (!validate_data_in_out(in_size, 0, "Unknown_22"))\n+ break;\n+ data_in += in_size;\n+ }\n+ break;\n+ case GCAMCommand::Unknown_23:\n+ if (!validate_data_in_out(2, 2, "Unknown_23"))\n+ break;\n+\n+ DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x23, {:02x} {:02x}", data_in[0],\n+ data_in[1]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x00;\n+\n+ data_in += 2;\n+ break;\n+ case GCAMCommand::Unknown_24:\n+ if (!validate_data_in_out(2, 2, "Unknown_24"))\n+ break;\n+\n+ DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x24, {:02x} {:02x}", data_in[0],\n+ data_in[1]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x00;\n+\n+ data_in += 2;\n+ break;\n+ case GCAMCommand::SerialA:\n+ {\n+ if (!validate_data_in_out(1, 0, "SerialA"))\n+ break;\n+\n+ u32 length = *data_in++;\n+ if (length)\n+ {\n+ if (!validate_data_in_out(length, 0, "SerialA"))\n+ break;\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x31, length=0x{:02x}, hexdump:\\n{}",\n+ length, HexDump(data_in, length));\n+\n+ // Serial - Wheel\n+ if (AMMediaboard::GetGameType() == MarioKartGP ||\n+ AMMediaboard::GetGameType() == MarioKartGP2)\n+ {\n+ if (!validate_data_in_out(10, 2, "SerialA (Wheel)"))\n+ break;\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x31, (WHEEL) {:02x}{:02x} {:02x}{:02x} {:02x} {:02x} "\n+ "{:02x} {:02x} {:02x} {:02x}",\n+ data_in[0], data_in[1], data_in[2], data_in[3], data_in[4], data_in[5],\n+ data_in[6], data_in[7], data_in[8], data_in[9]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x03;\n+\n+ switch (m_wheel_init)\n+ {\n+ case 0:\n+ if (!validate_data_in_out(0, 3, "SerialA (Wheel)"))\n+ break;\n+ data_out[data_offset++] = \'E\'; // Error\n+ data_out[data_offset++] = \'0\';\n+ data_out[data_offset++] = \'0\';\n+ m_wheel_init++;\n+ break;\n+ case 1:\n+ if (!validate_data_in_out(0, 3, "SerialA (Wheel)"))\n+ break;\n+ data_out[data_offset++] = \'C\'; // Power Off\n+ data_out[data_offset++] = \'0\';\n+ data_out[data_offset++] = \'6\';\n+ // Only turn on when a wheel is connected\n+ if (serial_interface.GetDeviceType(1) == SerialInterface::SIDEVICE_GC_STEERING)\n+ {\n+ m_wheel_init++;\n+ }\n+ break;\n+ case 2:\n+ if (!validate_data_in_out(0, 3, "SerialA (Wheel)"))\n+ break;\n+ data_out[data_offset++] = \'C\'; // Power On\n+ data_out[data_offset++] = \'0\';\n+ data_out[data_offset++] = \'1\';\n+ break;\n+ default:\n+ break;\n+ }\n+\n+ // u16 CenteringForce= ptr(6);\n+ // u16 FrictionForce = ptr(8);\n+ // u16 Roll = ptr(10);\n+ if (!validate_data_in_out(length, 0, "SerialA (Wheel)"))\n+ break;\n+ data_in += length;\n+ break;\n+ }\n+\n+ // Serial - Unknown\n+ if (AMMediaboard::GetGameType() == GekitouProYakyuu)\n+ {\n+ if (!validate_data_in_out(sizeof(u32), 0, "SerialA (Unknown)"))\n+ break;\n+ const u32 serial_command = Common::BitCastPtr<u32>(data_in);\n+\n+ if (serial_command == 0x00001000)\n+ {\n+ if (!validate_data_in_out(0, 5, "SerialA (Unknown)"))\n+ break;\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x03;\n+ data_out[data_offset++] = 1;\n+ data_out[data_offset++] = 2;\n+ data_out[data_offset++] = 3;\n+ }\n+\n+ if (!validate_data_in_out(length, 0, "SerialA (Unknown)"))\n+ break;\n+ data_in += length;\n+ break;\n+ }\n+\n+ // Serial IC-CARD / Serial Deck Reader\n+ if (AMMediaboard::GetGameType() == VirtuaStriker4 ||\n+ AMMediaboard::GetGameType() == VirtuaStriker4_2006 ||\n+ AMMediaboard::GetGameType() == KeyOfAvalon)\n+ {\n+ if (!validate_data_in_out(2, 0, "SerialA (IC-CARD)"))\n+ break;\n+ u32 serial_command = data_in[1];\n+\n+ ICCommand icco;\n+\n+ // Set default reply\n+ icco.pktcmd = gcam_command;\n+ icco.pktlen = 7;\n+ icco.fixed = 0x10;\n+ icco.command = serial_command;\n+ icco.flag = 0;\n+ icco.length = 2;\n+ icco.status = 0;\n+ icco.extlen = 0;\n+\n+ // Check for rest of data from the write pages command\n+ if (m_ic_write_size && m_ic_write_offset)\n+ {\n+ const u32 size = data_in[1];\n+\n+ if (!validate_data_in_out(size + 2, 0, "SerialA (IC-CARD)"))\n+ break;\n+ DEBUG_LOG_FMT(SERIALINTERFACE_CARD, "Command: {}", HexDump(data_in, size + 2));\n+\n+ INFO_LOG_FMT(\n+ SERIALINTERFACE_CARD,\n+ "GC-AM: Command 25 (IC-CARD) Write Pages: Off:{:x} Size:{:x} PSize:{:x}",\n+ m_ic_write_offset, m_ic_write_size, size);\n+\n+ memcpy(m_ic_write_buffer + m_ic_write_offset, data_in + 2, size);\n+\n+ m_ic_write_offset += size;\n+\n+ if (m_ic_write_offset > m_ic_write_size)\n+ {\n+ m_ic_write_offset = 0;\n+\n+ const u16 page = m_ic_write_buffer[5];\n+ const u16 count = m_ic_write_buffer[7];\n+\n+ memcpy(m_ic_card_data + page * 8, m_ic_write_buffer + 10, count * 8);\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 25 (IC-CARD) Write Pages:{} Count:{}({:x})", page,\n+ count, size);\n+\n+ icco.command = WritePages;\n+\n+ ICCardSendReply(&icco, data_out.data(), &data_offset);\n+ }\n+ if (!validate_data_in_out(length, 0, "SerialA (IC-CARD)"))\n+ break;\n+ data_in += length;\n+ break;\n+ }\n+\n+ switch (ICCARDCommand(serial_command))\n+ {\n+ case ICCARDCommand::GetStatus:\n+ icco.status = m_ic_card_state;\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (IC-CARD) Get Status:{:02x}", m_ic_card_state);\n+ break;\n+ case ICCARDCommand::SetBaudrate:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (IC-CARD) Set Baudrate");\n+ break;\n+ case ICCARDCommand::FieldOn:\n+ m_ic_card_state |= 0x10;\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (IC-CARD) Field On");\n+ break;\n+ case ICCARDCommand::InsertCheck:\n+ icco.status = m_ic_card_status;\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (IC-CARD) Insert Check:{:02x}", m_ic_card_status);\n+ break;\n+ case ICCARDCommand::AntiCollision:\n+ icco.extlen = 8;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ // Card ID\n+ icco.extdata[0] = 0x00;\n+ icco.extdata[1] = 0x00;\n+ icco.extdata[2] = 0x54;\n+ icco.extdata[3] = 0x4D;\n+ icco.extdata[4] = 0x50;\n+ icco.extdata[5] = 0x00;\n+ icco.extdata[6] = 0x00;\n+ icco.extdata[7] = 0x00;\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (IC-CARD) Anti Collision");\n+ break;\n+ case ICCARDCommand::SelectCard:\n+ icco.extlen = 8;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ // Session\n+ icco.extdata[0] = 0x00;\n+ icco.extdata[1] = m_ic_card_session;\n+ icco.extdata[2] = 0x00;\n+ icco.extdata[3] = 0x00;\n+ icco.extdata[4] = 0x00;\n+ icco.extdata[5] = 0x00;\n+ icco.extdata[6] = 0x00;\n+ icco.extdata[7] = 0x00;\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (IC-CARD) Select Card:{}",\n+ m_ic_card_session);\n+ break;\n+ case ICCARDCommand::ReadPage:\n+ case ICCARDCommand::ReadUseCount:\n+ {\n+ if (!validate_data_in_out(8, 0, "SerialA (IC-CARD)"))\n+ break;\n+ const u16 page = Common::swap16(data_in + 6) & 0xFF; // 255 is max page\n+\n+ icco.extlen = 8;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ memcpy(icco.extdata, m_ic_card_data + page * 8, 8);\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 31 (IC-CARD) Read Page:{}",\n+ page);\n+ break;\n+ }\n+ case ICCARDCommand::WritePage:\n+ {\n+ if (!validate_data_in_out(10, 0, "SerialA (IC-CARD)"))\n+ break;\n+ const u16 page = Common::swap16(data_in + 8) & 0xFF; // 255 is max page\n+\n+ // Write only one page\n+ if (page == 4)\n+ {\n+ icco.status = 0x80;\n+ }\n+ else\n+ {\n+ if (!validate_data_in_out(18, 0, "SerialA (IC-CARD)"))\n+ break;\n+ memcpy(m_ic_card_data + page * 8, data_in + 10, 8);\n+ }\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (IC-CARD) Write Page:{}",\n+ page);\n+ break;\n+ }\n+ case ICCARDCommand::DecreaseUseCount:\n+ {\n+ if (!validate_data_in_out(8, 0, "SerialA (IC-CARD)"))\n+ break;\n+ const u16 page = Common::swap16(data_in + 6) & 0xFF; // 255 is max page\n+\n+ icco.extlen = 2;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ auto ic_card_data = Common::BitCastPtr<u16>(m_ic_card_data + 0x28);\n+ ic_card_data = ic_card_data - 1;\n+\n+ // Counter\n+ icco.extdata[0] = m_ic_card_data[0x28];\n+ icco.extdata[1] = m_ic_card_data[0x29];\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 31 (IC-CARD) Decrease Use Count:{}", page);\n+ break;\n+ }\n+ case ICCARDCommand::ReadPages:\n+ {\n+ if (!validate_data_in_out(10, 0, "SerialA (IC-CARD)"))\n+ break;\n+ const u16 page = Common::swap16(data_in + 6) & 0xFF; // 255 is max page\n+ const u16 count = Common::swap16(data_in + 8);\n+\n+ const u32 offs = page * 8;\n+ u32 cnt = count * 8;\n+\n+ // Limit read size to not overwrite the reply buffer\n+ if (cnt > (u32)0x50 - data_offset)\n+ {\n+ cnt = 5 * 8;\n+ }\n+\n+ icco.extlen = cnt;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ memcpy(icco.extdata, m_ic_card_data + offs, cnt);\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 31 (IC-CARD) Read Pages:{} Count:{}", page, count);\n+ break;\n+ }\n+ case ICCARDCommand::WritePages:\n+ {\n+ if (!validate_data_in_out(10, 0, "SerialA (IC-CARD)"))\n+ break;\n+ const u16 pksize = length;\n+ const u16 size = Common::swap16(data_in + 2);\n+ const u16 page = Common::swap16(data_in + 6) & 0xFF; // 255 is max page\n+ const u16 count = Common::swap16(data_in + 8);\n+\n+ // We got a complete packet\n+ if (pksize - 5 == size)\n+ {\n+ if (page == 4) // Read Only Page, must return error\n+ {\n+ icco.status = 0x80;\n+ }\n+ else\n+ {\n+ if (page * 8 + count * 8 > sizeof(m_ic_card_data))\n+ {\n+ ERROR_LOG_FMT(\n+ SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (IC-CARD) Data overflow: Pages:{} Count:{}({:x})",\n+ page, count, size);\n+ }\n+ else\n+ {\n+ if (!validate_data_in_out(13 + count * 8, 0, "SerialA (IC-CARD)"))\n+ break;\n+ memcpy(m_ic_card_data + page * 8, data_in + 13, count * 8);\n+ }\n+ }\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (IC-CARD) Write Pages:{} Count:{}({:x})", page,\n+ count, size);\n+ }\n+ // VirtuaStriker 4 splits the writes over multiple packets\n+ else\n+ {\n+ if (!validate_data_in_out(2 + pksize, 0, "SerialA (IC-CARD)"))\n+ break;\n+ memcpy(m_ic_write_buffer, data_in + 2, pksize);\n+ m_ic_write_offset += pksize;\n+ m_ic_write_size = size;\n+ }\n+ break;\n+ }\n+ default:\n+ // Handle Deck Reader commands\n+ if (!validate_data_in_out(1, 0, "SerialA (DECK READER)"))\n+ break;\n+ serial_command = data_in[0];\n+ icco.command = serial_command;\n+ icco.flag = 0;\n+ switch (CDReaderCommand(serial_command))\n+ {\n+ case CDReaderCommand::ProgramVersion:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Program Version");\n+\n+ icco.extlen = (u32)strlen(s_cdr_program_version);\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ memcpy(icco.extdata, s_cdr_program_version, icco.extlen);\n+ break;\n+ case CDReaderCommand::BootVersion:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Boot Version");\n+\n+ icco.extlen = (u32)strlen(s_cdr_boot_version);\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ memcpy(icco.extdata, s_cdr_boot_version, icco.extlen);\n+ break;\n+ case CDReaderCommand::ShutterGet:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Shutter Get");\n+\n+ icco.extlen = 4;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ icco.extdata[0] = 0;\n+ icco.extdata[1] = 0;\n+ icco.extdata[2] = 0;\n+ icco.extdata[3] = 0;\n+ break;\n+ case CDReaderCommand::CameraCheck:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Camera Check");\n+\n+ icco.extlen = 6;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ icco.extdata[0] = 0x23;\n+ icco.extdata[1] = 0x28;\n+ icco.extdata[2] = 0x45;\n+ icco.extdata[3] = 0x29;\n+ icco.extdata[4] = 0x45;\n+ icco.extdata[5] = 0x29;\n+ break;\n+ case CDReaderCommand::ProgramChecksum:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Program Checksum");\n+\n+ icco.extlen = 4;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ icco.extdata[0] = 0x23;\n+ icco.extdata[1] = 0x28;\n+ icco.extdata[2] = 0x45;\n+ icco.extdata[3] = 0x29;\n+ break;\n+ case CDReaderCommand::BootChecksum:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Boot Checksum");\n+\n+ icco.extlen = 4;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ icco.extdata[0] = 0x23;\n+ icco.extdata[1] = 0x28;\n+ icco.extdata[2] = 0x45;\n+ icco.extdata[3] = 0x29;\n+ break;\n+ case CDReaderCommand::SelfTest:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (DECK READER) Self Test");\n+ icco.flag = 0x00;\n+ break;\n+ case CDReaderCommand::SensLock:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (DECK READER) Sens Lock");\n+ icco.flag = 0x01;\n+ break;\n+ case CDReaderCommand::SensCard:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (DECK READER) Sens Card");\n+ break;\n+ case CDReaderCommand::ShutterCard:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Shutter Card");\n+ break;\n+ case CDReaderCommand::ReadCard:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (DECK READER) Read Card");\n+\n+ icco.fixed = 0xAA;\n+ icco.flag = 0xAA;\n+ icco.extlen = sizeof(s_cdr_card_data);\n+ icco.length = 0x72;\n+ icco.status = Common::swap16(icco.extlen);\n+\n+ icco.pktlen += icco.extlen;\n+\n+ memcpy(icco.extdata, s_cdr_card_data, sizeof(s_cdr_card_data));\n+\n+ break;\n+ default:\n+ if (!validate_data_in_out(14, 0, "SerialA (DECK READER)"))\n+ break;\n+ WARN_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (IC-Card) {:02x} {:02x} {:02x} {:02x} {:02x} "\n+ "{:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x}",\n+ data_in[2], data_in[3], data_in[4], data_in[5], data_in[6],\n+ data_in[7], data_in[8], data_in[9], data_in[10], data_in[11],\n+ data_in[12], data_in[13]);\n+ break;\n+ }\n+ break;\n+ }\n+\n+ if (!validate_data_in_out(0, icco.pktlen + 2, "SerialA (IC-CARD)"))\n+ break;\n+ ICCardSendReply(&icco, data_out.data(), &data_offset);\n+\n+ if (!validate_data_in_out(2, 0, "SerialA (IC-CARD)"))\n+ break;\n+ data_in += length;\n+ break;\n+ }\n+ }\n+\n+ u32 command_offset = 0;\n+ while (command_offset < length)\n+ {\n+ // All commands are OR\'d with 0x80\n+ // Last byte is checksum which we don\'t care about\n+ if (!validate_data_in_out(command_offset + 4, 0, "SerialA"))\n+ break;\n+ const u32 serial_command = Common::swap32(data_in + command_offset) ^ 0x80000000;\n+\n+ if (AMMediaboard::GetGameType() == FZeroAX ||\n+ AMMediaboard::GetGameType() == FZeroAXMonster)\n+ {\n+ INFO_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x31 (MOTOR) Length:{:02x} Command:{:04x}({:02x})",\n+ length, (serial_command >> 8) & 0xFFFF, serial_command >> 24);\n+ }\n+ else\n+ {\n+ INFO_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x31 (SERIAL) Command:{:06x}",\n+ serial_command);\n+\n+ if (serial_command == 0x801000)\n+ {\n+ if (!validate_data_in_out(0, 4, "SerialA"))\n+ break;\n+ data_out[data_offset++] = 0x31;\n+ data_out[data_offset++] = 0x02;\n+ data_out[data_offset++] = 0xFF;\n+ data_out[data_offset++] = 0x01;\n+ }\n+ }\n+\n+ command_offset += sizeof(u32);\n+\n+ if (AMMediaboard::GetGameType() == FZeroAX ||\n+ AMMediaboard::GetGameType() == FZeroAXMonster)\n+ {\n+ // Status\n+ m_motor_reply[command_offset + 2] = 0;\n+ m_motor_reply[command_offset + 3] = 0;\n+\n+ // Error\n+ m_motor_reply[command_offset + 4] = 0;\n+\n+ switch (serial_command >> 24)\n+ {\n+ case 0:\n+ case 1: // Set Maximum?\n+ case 2:\n+ break;\n+\n+ // 0x00-0x40: left\n+ // 0x40-0x80: right\n+ case 4: // Move Steering Wheel\n+ // Left\n+ if (serial_command & 0x010000)\n+ {\n+ m_motor_force_y = -((s16)serial_command & 0xFF00);\n+ }\n+ else // Right\n+ {\n+ m_motor_force_y = (serial_command - 0x4000) & 0xFF00;\n+ }\n+\n+ m_motor_force_y *= 2;\n+\n+ // FFB\n+ if (m_motor_init == 2)\n+ {\n+ if (serial_interface.GetDeviceType(1) == SerialInterface::SIDEVICE_GC_STEERING)\n+ {\n+ const GCPadStatus pad_status = Pad::GetStatus(1);\n+ if (pad_status.isConnected)\n+ {\n+ const ControlState mapped_strength = (double)(m_motor_force_y >> 8) / 127.f;\n+\n+ Pad::Rumble(1, mapped_strength);\n+ INFO_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x31 (MOTOR) mapped_strength:{}",\n+ mapped_strength);\n+ }\n+ }\n+ }\n+ break;\n+ case 6: // nice\n+ case 9:\n+ default:\n+ break;\n+ // Switch back to normal controls\n+ case 7:\n+ m_motor_init = 2;\n+ break;\n+ // Reset\n+ case 0x7F:\n+ m_motor_init = 1;\n+ memset(m_motor_reply, 0, sizeof(m_motor_reply));\n+ break;\n+ }\n+\n+ // Checksum\n+ m_motor_reply[command_offset + 5] = m_motor_reply[command_offset + 2] ^\n+ m_motor_reply[command_offset + 3] ^\n+ m_motor_reply[command_offset + 4];\n+ }\n+ }\n+\n+ if (length == 0)\n+ {\n+ if (!validate_data_in_out(length, 2, "SerialA"))\n+ break;\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x00;\n+ }\n+ else\n+ {\n+ if (m_motor_init)\n+ {\n+ // Motor\n+ m_motor_reply[0] = gcam_command;\n+ m_motor_reply[1] = length; // Same out as in size\n+\n+ const u32 reply_size = m_motor_reply[1] + 2;\n+ if (!validate_data_in_out(length, reply_size, "SerialA"))\n+ break;\n+\n+ memcpy(data_out.data() + data_offset, m_motor_reply, reply_size);\n+ data_offset += reply_size;\n+ }\n+ }\n+\n+ if (!validate_data_in_out(length, 0, "SerialA"))\n+ {\n+ break;\n+ }\n+ data_in += length;\n+ break;\n+ }\n+ case GCAMCommand::SerialB:\n+ {\n+ DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 32 (CARD-Interface)");\n+ if (!validate_data_in_out(1, 0, "SerialB"))\n+ break;\n+ const u32 length = *data_in++;\n+ if (!validate_data_in_out(length, 0, "SerialB"))\n+ break;\n+ if (length)\n+ {\n+ // Send Card Reply\n+ if (length == 1 && data_in[0] == 0x05)\n+ {\n+ if (m_card_read_length)\n+ {\n+ if (!validate_data_in_out(0, 1, "SerialB"))\n+ break;\n+ data_out[data_offset++] = gcam_command;\n+ u32 read_length = m_card_read_length - m_card_read;\n+\n+ if (AMMediaboard::GetGameType() == FZeroAX)\n+ {\n+ read_length = std::min<u32>(read_length, 0x2F);\n+ }\n+\n+ if (!validate_data_in_out(0, 1, "SerialB"))\n+ break;\n+ data_out[data_offset++] = read_length; // 0x2F (max size per packet)\n+\n+ if (!validate_data_in_out(0, read_length, "SerialB"))\n+ break;\n+ memcpy(data_out.data() + data_offset, m_card_read_packet + m_card_read,\n+ read_length);\n+\n+ data_offset += read_length;\n+ m_card_read += read_length;\n+\n+ if (m_card_read >= m_card_read_length)\n+ m_card_read_length = 0;\n+\n+ data_in += length;\n+ break;\n+ }\n+\n+ if (!validate_data_in_out(0, 5, "SerialB"))\n+ break;\n+\n+ data_out[data_offset++] = gcam_command;\n+ const u32 command_length_offset = data_offset;\n+ data_out[data_offset++] = 0x00; // len\n+\n+ data_out[data_offset++] = 0x02;\n+ const u32 checksum_start = data_offset;\n+\n+ data_out[data_offset++] = 0x00; // 0x00 len\n+\n+ data_out[data_offset++] = m_card_command; // 0x01 command\n+\n+ switch (CARDCommand(m_card_command))\n+ {\n+ case CARDCommand::Init:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x00; // 0x02\n+ data_out[data_offset++] = 0x30; // 0x03\n+ break;\n+ case CARDCommand::GetState:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x20 | m_card_bit; // 0x02\n+\n+ // bit 0: Please take your card\n+ // bit 1: Endless waiting causes UNK_E to be called\n+ data_out[data_offset++] = 0x00; // 0x03\n+ break;\n+ case CARDCommand::Read:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x02; // 0x02\n+ data_out[data_offset++] = 0x53; // 0x03\n+ break;\n+ case CARDCommand::IsPresent:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x22; // 0x02\n+ data_out[data_offset++] = 0x30; // 0x03\n+ break;\n+ case CARDCommand::Write:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x02; // 0x02\n+ data_out[data_offset++] = 0x00; // 0x03\n+ break;\n+ case CARDCommand::SetPrintParam:\n+ case CARDCommand::RegisterFont:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x00; // 0x02\n+ data_out[data_offset++] = 0x00; // 0x03\n+ break;\n+ case CARDCommand::WriteInfo:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x02; // 0x02\n+ data_out[data_offset++] = 0x00; // 0x03\n+ break;\n+ case CARDCommand::Erase:\n+ // TODO: CARDCommand::Erase is not handled.\n+ break;\n+ case CARDCommand::Eject:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ if (AMMediaboard::GetGameType() == FZeroAX)\n+ {\n+ data_out[data_offset++] = 0x01; // 0x02\n+ }\n+ else\n+ {\n+ data_out[data_offset++] = 0x31; // 0x02\n+ }\n+ data_out[data_offset++] = 0x30; // 0x03\n+ break;\n+ case CARDCommand::Clean:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x02; // 0x02\n+ data_out[data_offset++] = 0x00; // 0x03\n+ break;\n+ case CARDCommand::Load:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x02; // 0x02\n+ data_out[data_offset++] = 0x30; // 0x03\n+ break;\n+ case CARDCommand::SetShutter:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x00; // 0x02\n+ data_out[data_offset++] = 0x00; // 0x03\n+ break;\n+ }\n+\n+ if (!validate_data_in_out(0, 3, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x30; // 0x04\n+ data_out[data_offset++] = 0x00; // 0x05\n+\n+ data_out[data_offset++] = 0x03; // 0x06\n+\n+ data_out[checksum_start] = data_offset - checksum_start; // 0x00 len\n+\n+ if (!validate_data_in_out(0, 1, "SerialB"))\n+ break;\n+ data_out[data_offset] = 0; // 0x07\n+ for (u32 i = 0; i < data_out[checksum_start]; ++i)\n+ data_out[data_offset] ^= data_out[checksum_start + i];\n+\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_offset++;\n+\n+ data_out[command_length_offset] = data_out[checksum_start] + 2;\n+ }\n+ else\n+ {\n+ for (u32 i = 0; i < length; ++i)\n+ m_card_buffer[m_card_offset + i] = data_in[i];\n+\n+ m_card_offset += length;\n+\n+ // Check if we got a complete command\n+ if (m_card_buffer[0] == 0x02)\n+ {\n+ if (m_card_buffer[1] == m_card_offset - 2)\n+ {\n+ if (m_card_buffer[m_card_offset - 2] == 0x03)\n+ {\n+ m_card_command = m_card_buffer[2];\n+\n+ switch (CARDCommand(m_card_command))\n+ {\n+ case CARDCommand::Init:\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD Init");\n+\n+ m_card_write_length = 0;\n+ m_card_bit = 0;\n+ m_card_memory_size = 0;\n+ m_card_state_call_count = 0;\n+ break;\n+ case CARDCommand::GetState:\n+ {\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD GetState({:02X})",\n+ m_card_bit);\n+\n+ if (m_card_memory_size == 0)\n+ {\n+ const std::string card_filename(\n+ fmt::format("{}tricard_{}.bin", File::GetUserPath(D_TRIUSER_IDX),\n+ SConfig::GetInstance().GetGameID()));\n+\n+ if (File::Exists(card_filename))\n+ {\n+ File::IOFile card(card_filename, "rb+");\n+ m_card_memory_size = (u32)card.GetSize();\n+\n+ card.ReadBytes(m_card_memory, m_card_memory_size);\n+ card.Close();\n+\n+ m_card_is_inserted = true;\n+ }\n+ }\n+\n+ if (AMMediaboard::GetGameType() == FZeroAX && m_card_memory_size)\n+ {\n+ m_card_state_call_count++;\n+ if (m_card_state_call_count > 10)\n+ {\n+ if (m_card_bit & 2)\n+ m_card_bit &= ~2u;\n+ else\n+ m_card_bit |= 2;\n+\n+ m_card_state_call_count = 0;\n+ }\n+ }\n+\n+ if (m_card_clean == 1)\n+ {\n+ m_card_clean = 2;\n+ }\n+ else if (m_card_clean == 2)\n+ {\n+ const std::string card_filename(\n+ fmt::format("{}tricard_{}.bin", File::GetUserPath(D_TRIUSER_IDX),\n+ SConfig::GetInstance().GetGameID()));\n+\n+ if (File::Exists(card_filename))\n+ {\n+ m_card_memory_size = (u32)File::GetSize(card_filename);\n+ if (m_card_memory_size)\n+ {\n+ if (AMMediaboard::GetGameType() == FZeroAX)\n+ {\n+ m_card_bit = 2;\n+ }\n+ else\n+ {\n+ m_card_bit = 1;\n+ }\n+ }\n+ }\n+ m_card_clean = 0;\n+ }\n+ break;\n+ }\n+ case CARDCommand::IsPresent:\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD IsPresent");\n+ break;\n+ case CARDCommand::RegisterFont:\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD RegisterFont");\n+ break;\n+ case CARDCommand::Load:\n+ {\n+ u8 mode = m_card_buffer[6];\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD Load({:02X})",\n+ mode);\n+ break;\n+ }\n+ case CARDCommand::Clean:\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD Clean");\n+ m_card_clean = 1;\n+ break;\n+ case CARDCommand::Read:\n+ {\n+ const u8 mode = m_card_buffer[6];\n+ const u8 bitmode = m_card_buffer[7];\n+ const u8 track = m_card_buffer[8];\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command CARD Read({:02X},{:02X},{:02X})", mode,\n+ bitmode, track);\n+\n+ // Prepare read packet\n+ memset(m_card_read_packet, 0, 0xDB);\n+ u32 packet_offset = 0;\n+\n+ const std::string card_filename(\n+ fmt::format("{}tricard_{}.bin", File::GetUserPath(D_TRIUSER_IDX),\n+ SConfig::GetInstance().GetGameID()));\n+\n+ if (File::Exists(card_filename))\n+ {\n+ File::IOFile card(card_filename, "rb+");\n+ if (m_card_memory_size == 0)\n+ {\n+ m_card_memory_size = (u32)card.GetSize();\n+ }\n+\n+ card.ReadBytes(m_card_memory, m_card_memory_size);\n+ card.Close();\n+\n+ m_card_is_inserted = true;\n+ }\n+\n+ m_card_read_packet[packet_offset++] = 0x02; // SUB CMD\n+ m_card_read_packet[packet_offset++] = 0x00; // SUB CMDLen\n+\n+ m_card_read_packet[packet_offset++] = 0x33; // CARD CMD\n+\n+ if (m_card_is_inserted) // CARD Status\n+ {\n+ m_card_read_packet[packet_offset++] = 0x31;\n+ }\n+ else\n+ {\n+ m_card_read_packet[packet_offset++] = 0x30;\n+ }\n+\n+ m_card_read_packet[packet_offset++] = 0x30;\n+ m_card_read_packet[packet_offset++] = 0x30;\n+\n+ // Data reply\n+ memcpy(m_card_read_packet + packet_offset, m_card_memory, m_card_memory_size);', 'path': 'Source/Core/Core/HW/SI/SI_DeviceAMBaseboard.cpp', 'position': 1498, 'original_position': 1390, 'commit_id': '510fec88be973bc870021f2d0347593cdc285864', 'user': {'login': 'sepalani', 'id': 7890055, 'node_id': 'MDQ6VXNlcjc4OTAwNTU=', 'avatar_url': 'https://avatars.githubusercontent.com/u/7890055?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/sepalani', 'html_url': 'https://github.com/sepalani', 'followers_url': 'https://api.github.com/users/sepalani/followers', 'following_url': 'https://api.github.com/users/sepalani/following{/other_user}', 'gists_url': 'https://api.github.com/users/sepalani/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/sepalani/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/sepalani/subscriptions', 'organizations_url': 'https://api.github.com/users/sepalani/orgs', 'repos_url': 'https://api.github.com/users/sepalani/repos', 'events_url': 'https://api.github.com/users/sepalani/events{/privacy}', 'received_events_url': 'https://api.github.com/users/sepalani/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'body': 'Similar situation here, there is an early exit when `m_card_memory_size` is greater than `sizeof(m_card_memory)`, so the `static_assert` should be enough for the other parameter bound check since `packet_offset` is guaranteed to be `6`. I rewrote part of the code to declare `packet_offset` later to make that clearer.', 'created_at': '2026-01-31T18:51:14Z', 'updated_at': '2026-01-31T18:51:14Z', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/13844#discussion_r2749856368', 'pull_request_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13844', 'author_association': 'MEMBER', '_links': {'self': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2749856368'}, 'html': {'href': 'https://github.com/dolphin-emu/dolphin/pull/13844#discussion_r2749856368'}, 'pull_request': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13844'}}, 'original_commit_id': '2415f0c1c471b2446a2144a1a2b643d8a77d8cc7', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2749856368/reactions', 'total_count': 0, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 0, 'confused': 0, 'heart': 0, 'rocket': 0, 'eyes': 0}, 'in_reply_to_id': 2725629971}], 'type': 'gh_pull_request_review'}
2026-01-31T18:40:52.590166 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'sepalani', 'action': 'submitted', 'pr_id': 13844, 'pr_title': 'Core: Triforce support', 'state': 'commented', 'url': 'https://github.com/dolphin-emu/dolphin/pull/13844#pullrequestreview-3733235981', 'comments': [{'id': 2749846854, 'node_id': 'PRRC_kwDOALCn2M6j51VG', 'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2749846854', 'pull_request_review_id': 3733235981, 'diff_hunk': '@@ -0,0 +1,2501 @@\n+// Copyright 2025 Dolphin Emulator Project\n+// SPDX-License-Identifier: GPL-2.0-or-later\n+\n+#include "Core/HW/SI/SI_DeviceAMBaseboard.h"\n+\n+#include <algorithm>\n+#include <numeric>\n+#include <string>\n+\n+#include <fmt/format.h>\n+\n+#include "Common/Buffer.h"\n+#include "Common/CommonTypes.h"\n+#include "Common/FileUtil.h"\n+#include "Common/IOFile.h"\n+#include "Common/Logging/Log.h"\n+#include "Common/MsgHandler.h"\n+#include "Common/Swap.h"\n+\n+#include "Core/Boot/Boot.h"\n+#include "Core/BootManager.h"\n+#include "Core/Config/MainSettings.h"\n+#include "Core/ConfigManager.h"\n+#include "Core/Core.h"\n+#include "Core/CoreTiming.h"\n+#include "Core/HW/DVD/AMMediaboard.h"\n+#include "Core/HW/DVD/DVDInterface.h"\n+#include "Core/HW/EXI/EXI.h"\n+#include "Core/HW/GCPad.h"\n+#include "Core/HW/MMIO.h"\n+#include "Core/HW/Memmap.h"\n+#include "Core/HW/ProcessorInterface.h"\n+#include "Core/HW/SI/SI.h"\n+#include "Core/HW/SI/SI_Device.h"\n+#include "Core/HW/SI/SI_DeviceGCController.h"\n+#include "Core/HW/SystemTimers.h"\n+#include "Core/Movie.h"\n+#include "Core/NetPlayProto.h"\n+#include "Core/System.h"\n+\n+#include "InputCommon/GCPadStatus.h"\n+\n+namespace SerialInterface\n+{\n+void JVSIOMessage::Start(int node)\n+{\n+ m_last_start = m_pointer;\n+ const u8 header[3] = {0xE0, (u8)node, 0};\n+ m_checksum = 0;\n+ AddData(header, 3, 1);\n+}\n+\n+void JVSIOMessage::AddData(const u8* dst, size_t len, int sync = 0)\n+{\n+ if (m_pointer + len >= sizeof(m_message))\n+ {\n+ PanicAlertFmt("JVSIOMessage overrun!");\n+ return;\n+ }\n+\n+ while (len--)\n+ {\n+ const u8 c = *dst++;\n+ if (!sync && ((c == 0xE0) || (c == 0xD0)))\n+ {\n+ if (m_pointer + 2 > sizeof(m_message))\n+ {\n+ PanicAlertFmt("JVSIOMessage overrun!");\n+ break;\n+ }\n+ m_message[m_pointer++] = 0xD0;\n+ m_message[m_pointer++] = c - 1;\n+ }\n+ else\n+ {\n+ if (m_pointer >= sizeof(m_message))\n+ {\n+ PanicAlertFmt("JVSIOMessage overrun!");\n+ break;\n+ }\n+ m_message[m_pointer++] = c;\n+ }\n+\n+ if (!sync)\n+ m_checksum += c;\n+ sync = 0;\n+ }\n+}\n+\n+void JVSIOMessage::AddData(const void* data, size_t len)\n+{\n+ AddData(static_cast<const u8*>(data), len);\n+}\n+\n+void JVSIOMessage::AddData(const char* data)\n+{\n+ AddData(data, strlen(data));\n+}\n+\n+void JVSIOMessage::AddData(u32 n)\n+{\n+ const u8 cs = n;\n+ AddData(&cs, 1);\n+}\n+\n+void JVSIOMessage::End()\n+{\n+ const u32 len = m_pointer - m_last_start;\n+ if (m_last_start + 2 < sizeof(m_message) && len >= 3)\n+ {\n+ m_message[m_last_start + 2] = len - 2; // assuming len <0xD0\n+ AddData(m_checksum + len - 2);\n+ }\n+ else\n+ {\n+ PanicAlertFmt("JVSIOMessage: Not enough space for checksum!");\n+ }\n+}\n+\n+static constexpr u8 CheckSumXOR(const u8* data, u32 length)\n+{\n+ return std::accumulate(data, data + length, u8{}, std::bit_xor());\n+}\n+\n+static constexpr char s_cdr_program_version[] = {" Version 1.22,2003/09/19,171-8213B"};\n+static constexpr char s_cdr_boot_version[] = {" Version 1.04,2003/06/17,171-8213B"};\n+static constexpr u8 s_cdr_card_data[] = {\n+ 0x00, 0x6E, 0x00, 0x00, 0x01, 0x00, 0x00, 0x06, 0x00, 0x00, 0x07, 0x00, 0x00, 0x0B,\n+ 0x00, 0x00, 0x0E, 0x00, 0x00, 0x10, 0x00, 0x00, 0x17, 0x00, 0x00, 0x19, 0x00, 0x00,\n+ 0x1A, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x20, 0x00,\n+ 0x00, 0x22, 0x00, 0x00, 0x23, 0x00, 0x00, 0x24, 0x00, 0x00, 0x27, 0x00, 0x00, 0x28,\n+ 0x00, 0x00, 0x2C, 0x00, 0x00, 0x2F, 0x00, 0x00, 0x34, 0x00, 0x00, 0x35, 0x00, 0x00,\n+ 0x37, 0x00, 0x00, 0x38, 0x00, 0x00, 0x39, 0x00, 0x00, 0x3D, 0x00};\n+\n+const constexpr u8 s_region_flags[] = "\\x00\\x00\\x30\\x00"\n+ // "\\x01\\xfe\\x00\\x00" // JAPAN\n+ "\\x02\\xfd\\x00\\x00" // USA\n+ //"\\x03\\xfc\\x00\\x00" // export\n+ "\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff";\n+// AM-Baseboard device on SI\n+CSIDevice_AMBaseboard::CSIDevice_AMBaseboard(Core::System& system, SIDevices device,\n+ int device_number)\n+ : ISIDevice(system, device, device_number)\n+{\n+ // Card ID\n+ m_ic_card_data[0x20] = 0x95;\n+ m_ic_card_data[0x21] = 0x71;\n+\n+ if (AMMediaboard::GetGameType() == KeyOfAvalon)\n+ {\n+ m_ic_card_data[0x22] = 0x26;\n+ m_ic_card_data[0x23] = 0x40;\n+ }\n+ else if (AMMediaboard::GetGameType() == VirtuaStriker4)\n+ {\n+ m_ic_card_data[0x22] = 0x44;\n+ m_ic_card_data[0x23] = 0x00;\n+ }\n+\n+ // Use count\n+ m_ic_card_data[0x28] = 0xFF;\n+ m_ic_card_data[0x29] = 0xFF;\n+}\n+\n+constexpr u32 SI_XFER_LENGTH_MASK = 0x7f;\n+\n+// Translate [0,1,2,...,126,127] to [128,1,2,...,126,127]\n+constexpr s32 ConvertSILengthField(u32 field)\n+{\n+ return ((field - 1) & SI_XFER_LENGTH_MASK) + 1;\n+}\n+\n+void CSIDevice_AMBaseboard::ICCardSendReply(ICCommand* iccommand, u8* buffer, u32* length)\n+{\n+ iccommand->status = Common::swap16(iccommand->status);\n+\n+ const auto iccommand_data = reinterpret_cast<const u8*>(iccommand);\n+ const u8 crc = CheckSumXOR(iccommand_data + 2, iccommand->pktlen - 1);\n+\n+ for (u32 i = 0; i < iccommand->pktlen + 1; ++i)\n+ {\n+ buffer[(*length)++] = iccommand_data[i];\n+ }\n+\n+ buffer[(*length)++] = crc;\n+}\n+\n+void CSIDevice_AMBaseboard::SwapBuffers(u8* buffer, u32* buffer_length)\n+{\n+ memcpy(m_last[1], buffer, 0x80); // Save current buffer\n+ memcpy(buffer, m_last[0], 0x80); // Load previous buffer\n+ memcpy(m_last[0], m_last[1], 0x80); // Update history\n+\n+ m_lastptr[1] = *buffer_length; // Swap lengths\n+ *buffer_length = m_lastptr[0];\n+ m_lastptr[0] = m_lastptr[1];\n+}\n+\n+int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length)\n+{\n+ const auto& serial_interface = m_system.GetSerialInterface();\n+ u32 buffer_length = ConvertSILengthField(serial_interface.GetInLength());\n+\n+ // Debug logging\n+ ISIDevice::RunBuffer(buffer, buffer_length);\n+\n+ u32 buffer_position = 0;\n+ while (buffer_position < buffer_length)\n+ {\n+ BaseBoardCommand command = static_cast<BaseBoardCommand>(buffer[buffer_position]);\n+ buffer_position++;\n+\n+ switch (command)\n+ {\n+ case BaseBoardCommand::GCAM_Reset: // Returns ID and dip switches\n+ {\n+ const u32 id = Common::swap32(SI_AM_BASEBOARD | 0x100);\n+ std::memcpy(buffer, &id, sizeof(id));\n+ return sizeof(id);\n+ }\n+ break;\n+ case BaseBoardCommand::GCAM_Command:\n+ {\n+ u32 checksum = 0;\n+ for (u32 i = 0; i < buffer_length; ++i)\n+ checksum += buffer[i];\n+\n+ std::array<u8, 0x80> data_out{};\n+ u32 data_offset = 0;\n+\n+ static u32 dip_switch_1 = 0xFE;\n+ static u32 dip_switch_0 = 0xFF;\n+\n+ data_out[data_offset++] = 1;\n+ data_out[data_offset++] = 1;\n+\n+ u8* data_in = buffer + 2;\n+ u8* const data_in_end = buffer + buffer[buffer_position] + 2;\n+\n+ // Helper to check that iterating over data n times is safe,\n+ // i.e. *data++ at most lead to data.end()\n+ auto validate_data_in_out = [&](u32 n_in, u32 n_out, std::string_view command) -> bool {\n+ if (data_in + n_in > data_in_end)\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: data_in overflow in {}", command);\n+ else if (data_offset + n_out > data_out.size())\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: data_out overflow in {}", command);\n+ else\n+ return true;\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "Overflow details:\\n"\n+ " - data_in(begin={}, current={}, end={}, n_in={})\\n"\n+ " - data_out(offset={}, size={}, n_out={})\\n"\n+ " - buffer(position={}, length={})",\n+ fmt::ptr(buffer + 2), fmt::ptr(data_in), fmt::ptr(data_in_end), n_in,\n+ data_offset, data_out.size(), n_out, buffer_position, buffer_length);\n+ data_in = data_in_end;\n+ return false;\n+ };\n+\n+ while (data_in < data_in_end)\n+ {\n+ const u32 gcam_command = *data_in++;\n+ switch (GCAMCommand(gcam_command))\n+ {\n+ case GCAMCommand::StatusSwitches:\n+ {\n+ if (!validate_data_in_out(1, 4, "StatusSwitches"))\n+ break;\n+\n+ const u8 status = *data_in++;\n+ DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x10, {:02x} (READ STATUS&SWITCHES)",\n+ status);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x2;\n+\n+ // We read Test/Service from the JVS-I/O SwitchesInput instead\n+ //\n+ // const GCPadStatus pad_status = Pad::GetStatus(ISIDevice::m_device_number);\n+ // baseboard test/service switches\n+ // if (pad_status.button & PAD_BUTTON_Y)\t// Test\n+ // dip_switch_0 &= ~0x80;\n+ // if (pad_status.button & PAD_BUTTON_X)\t// Service\n+ // dip_switch_0 &= ~0x40;\n+\n+ // Horizontal Scanning Frequency switch\n+ // Required for F-Zero AX booting via Sega Boot\n+ if (AMMediaboard::GetGameType() == FZeroAX ||\n+ AMMediaboard::GetGameType() == FZeroAXMonster)\n+ {\n+ dip_switch_0 &= ~0x20;\n+ }\n+\n+ // Disable camera in MKGP1/2\n+ if (AMMediaboard::GetGameType() == MarioKartGP ||\n+ AMMediaboard::GetGameType() == MarioKartGP2)\n+ {\n+ dip_switch_0 &= ~0x10;\n+ }\n+\n+ data_out[data_offset++] = dip_switch_0;\n+ data_out[data_offset++] = dip_switch_1;\n+ break;\n+ }\n+ case GCAMCommand::SerialNumber:\n+ {\n+ if (!validate_data_in_out(1, 18, "SerialNumber"))\n+ break;\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x11, {:02x} (READ SERIAL NR)",\n+ *data_in);\n+ data_in++;\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 16;\n+ memcpy(data_out.data() + data_offset, "AADE-01B98394904", 16);\n+\n+ data_offset += 16;\n+ break;\n+ }\n+ case GCAMCommand::Unknown_12:\n+ if (!validate_data_in_out(2, 2, "Unknown_12"))\n+ break;\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x12, {:02x} {:02x}", data_in[0],\n+ data_in[1]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x00;\n+\n+ data_in += 2;\n+ break;\n+ case GCAMCommand::Unknown_14:\n+ if (!validate_data_in_out(2, 2, "Unknown_14"))\n+ break;\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x14, {:02x} {:02x}", data_in[0],\n+ data_in[1]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x00;\n+\n+ data_in += 2;\n+ break;\n+ case GCAMCommand::FirmVersion:\n+ if (!validate_data_in_out(1, 4, "FirmVersion"))\n+ break;\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x15, {:02x} (READ FIRM VERSION)",\n+ *data_in);\n+ data_in++;\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x02;\n+ // 00.26\n+ data_out[data_offset++] = 0x00;\n+ data_out[data_offset++] = 0x26;\n+ break;\n+ case GCAMCommand::FPGAVersion:\n+ if (!validate_data_in_out(1, 4, "FPGAVersion"))\n+ break;\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x16, {:02x} (READ FPGA VERSION)",\n+ *data_in);\n+ data_in++;\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x02;\n+ // 07.06\n+ data_out[data_offset++] = 0x07;\n+ data_out[data_offset++] = 0x06;\n+ break;\n+ case GCAMCommand::RegionSettings:\n+ {\n+ if (!validate_data_in_out(5, 0x16, "RegionSettings"))\n+ break;\n+\n+ // Used by SegaBoot for region checks (dev mode skips this check)\n+ // In some games this also controls the displayed language\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x1F, {:02x} {:02x} {:02x} {:02x} {:02x} (REGION)",\n+ data_in[0], data_in[1], data_in[2], data_in[3], data_in[4]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x14;\n+\n+ for (int i = 0; i < 0x14; ++i)\n+ data_out[data_offset++] = s_region_flags[i];\n+\n+ data_in += 5;\n+ }\n+ break;\n+ // No reply\n+ // Note: Always sends three bytes even though size is set to two\n+ case GCAMCommand::Unknown_21:\n+ {\n+ if (!validate_data_in_out(4, 0, "Unknown_21"))\n+ break;\n+\n+ DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x21, {:02x}, {:02x}, {:02x}, {:02x}",\n+ data_in[0], data_in[1], data_in[2], data_in[3]);\n+ data_in += 4;\n+ }\n+ break;\n+ // No reply\n+ // Note: Always sends six bytes\n+ case GCAMCommand::Unknown_22:\n+ {\n+ if (!validate_data_in_out(7, 0, "Unknown_22"))\n+ break;\n+\n+ DEBUG_LOG_FMT(\n+ SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x22, {:02x}, {:02x}, {:02x}, {:02x}, {:02x}, {:02x}, {:02x}",\n+ data_in[0], data_in[1], data_in[2], data_in[3], data_in[4], data_in[5], data_in[6]);\n+\n+ const u32 in_size = data_in[0] + 1;\n+ if (!validate_data_in_out(in_size, 0, "Unknown_22"))\n+ break;\n+ data_in += in_size;\n+ }\n+ break;\n+ case GCAMCommand::Unknown_23:\n+ if (!validate_data_in_out(2, 2, "Unknown_23"))\n+ break;\n+\n+ DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x23, {:02x} {:02x}", data_in[0],\n+ data_in[1]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x00;\n+\n+ data_in += 2;\n+ break;\n+ case GCAMCommand::Unknown_24:\n+ if (!validate_data_in_out(2, 2, "Unknown_24"))\n+ break;\n+\n+ DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x24, {:02x} {:02x}", data_in[0],\n+ data_in[1]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x00;\n+\n+ data_in += 2;\n+ break;\n+ case GCAMCommand::SerialA:\n+ {\n+ if (!validate_data_in_out(1, 0, "SerialA"))\n+ break;\n+\n+ u32 length = *data_in++;\n+ if (length)\n+ {\n+ if (!validate_data_in_out(length, 0, "SerialA"))\n+ break;\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x31, length=0x{:02x}, hexdump:\\n{}",\n+ length, HexDump(data_in, length));\n+\n+ // Serial - Wheel\n+ if (AMMediaboard::GetGameType() == MarioKartGP ||\n+ AMMediaboard::GetGameType() == MarioKartGP2)\n+ {\n+ if (!validate_data_in_out(10, 2, "SerialA (Wheel)"))\n+ break;\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x31, (WHEEL) {:02x}{:02x} {:02x}{:02x} {:02x} {:02x} "\n+ "{:02x} {:02x} {:02x} {:02x}",\n+ data_in[0], data_in[1], data_in[2], data_in[3], data_in[4], data_in[5],\n+ data_in[6], data_in[7], data_in[8], data_in[9]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x03;\n+\n+ switch (m_wheel_init)\n+ {\n+ case 0:\n+ if (!validate_data_in_out(0, 3, "SerialA (Wheel)"))\n+ break;\n+ data_out[data_offset++] = \'E\'; // Error\n+ data_out[data_offset++] = \'0\';\n+ data_out[data_offset++] = \'0\';\n+ m_wheel_init++;\n+ break;\n+ case 1:\n+ if (!validate_data_in_out(0, 3, "SerialA (Wheel)"))\n+ break;\n+ data_out[data_offset++] = \'C\'; // Power Off\n+ data_out[data_offset++] = \'0\';\n+ data_out[data_offset++] = \'6\';\n+ // Only turn on when a wheel is connected\n+ if (serial_interface.GetDeviceType(1) == SerialInterface::SIDEVICE_GC_STEERING)\n+ {\n+ m_wheel_init++;\n+ }\n+ break;\n+ case 2:\n+ if (!validate_data_in_out(0, 3, "SerialA (Wheel)"))\n+ break;\n+ data_out[data_offset++] = \'C\'; // Power On\n+ data_out[data_offset++] = \'0\';\n+ data_out[data_offset++] = \'1\';\n+ break;\n+ default:\n+ break;\n+ }\n+\n+ // u16 CenteringForce= ptr(6);\n+ // u16 FrictionForce = ptr(8);\n+ // u16 Roll = ptr(10);\n+ if (!validate_data_in_out(length, 0, "SerialA (Wheel)"))\n+ break;\n+ data_in += length;\n+ break;\n+ }\n+\n+ // Serial - Unknown\n+ if (AMMediaboard::GetGameType() == GekitouProYakyuu)\n+ {\n+ if (!validate_data_in_out(sizeof(u32), 0, "SerialA (Unknown)"))\n+ break;\n+ const u32 serial_command = Common::BitCastPtr<u32>(data_in);\n+\n+ if (serial_command == 0x00001000)\n+ {\n+ if (!validate_data_in_out(0, 5, "SerialA (Unknown)"))\n+ break;\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x03;\n+ data_out[data_offset++] = 1;\n+ data_out[data_offset++] = 2;\n+ data_out[data_offset++] = 3;\n+ }\n+\n+ if (!validate_data_in_out(length, 0, "SerialA (Unknown)"))\n+ break;\n+ data_in += length;\n+ break;\n+ }\n+\n+ // Serial IC-CARD / Serial Deck Reader\n+ if (AMMediaboard::GetGameType() == VirtuaStriker4 ||\n+ AMMediaboard::GetGameType() == VirtuaStriker4_2006 ||\n+ AMMediaboard::GetGameType() == KeyOfAvalon)\n+ {\n+ if (!validate_data_in_out(2, 0, "SerialA (IC-CARD)"))\n+ break;\n+ u32 serial_command = data_in[1];\n+\n+ ICCommand icco;\n+\n+ // Set default reply\n+ icco.pktcmd = gcam_command;\n+ icco.pktlen = 7;\n+ icco.fixed = 0x10;\n+ icco.command = serial_command;\n+ icco.flag = 0;\n+ icco.length = 2;\n+ icco.status = 0;\n+ icco.extlen = 0;\n+\n+ // Check for rest of data from the write pages command\n+ if (m_ic_write_size && m_ic_write_offset)\n+ {\n+ const u32 size = data_in[1];\n+\n+ if (!validate_data_in_out(size + 2, 0, "SerialA (IC-CARD)"))\n+ break;\n+ DEBUG_LOG_FMT(SERIALINTERFACE_CARD, "Command: {}", HexDump(data_in, size + 2));\n+\n+ INFO_LOG_FMT(\n+ SERIALINTERFACE_CARD,\n+ "GC-AM: Command 25 (IC-CARD) Write Pages: Off:{:x} Size:{:x} PSize:{:x}",\n+ m_ic_write_offset, m_ic_write_size, size);\n+\n+ memcpy(m_ic_write_buffer + m_ic_write_offset, data_in + 2, size);\n+\n+ m_ic_write_offset += size;\n+\n+ if (m_ic_write_offset > m_ic_write_size)\n+ {\n+ m_ic_write_offset = 0;\n+\n+ const u16 page = m_ic_write_buffer[5];\n+ const u16 count = m_ic_write_buffer[7];\n+\n+ memcpy(m_ic_card_data + page * 8, m_ic_write_buffer + 10, count * 8);\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 25 (IC-CARD) Write Pages:{} Count:{}({:x})", page,\n+ count, size);\n+\n+ icco.command = WritePages;\n+\n+ ICCardSendReply(&icco, data_out.data(), &data_offset);\n+ }\n+ if (!validate_data_in_out(length, 0, "SerialA (IC-CARD)"))\n+ break;\n+ data_in += length;\n+ break;\n+ }\n+\n+ switch (ICCARDCommand(serial_command))\n+ {\n+ case ICCARDCommand::GetStatus:\n+ icco.status = m_ic_card_state;\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (IC-CARD) Get Status:{:02x}", m_ic_card_state);\n+ break;\n+ case ICCARDCommand::SetBaudrate:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (IC-CARD) Set Baudrate");\n+ break;\n+ case ICCARDCommand::FieldOn:\n+ m_ic_card_state |= 0x10;\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (IC-CARD) Field On");\n+ break;\n+ case ICCARDCommand::InsertCheck:\n+ icco.status = m_ic_card_status;\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (IC-CARD) Insert Check:{:02x}", m_ic_card_status);\n+ break;\n+ case ICCARDCommand::AntiCollision:\n+ icco.extlen = 8;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ // Card ID\n+ icco.extdata[0] = 0x00;\n+ icco.extdata[1] = 0x00;\n+ icco.extdata[2] = 0x54;\n+ icco.extdata[3] = 0x4D;\n+ icco.extdata[4] = 0x50;\n+ icco.extdata[5] = 0x00;\n+ icco.extdata[6] = 0x00;\n+ icco.extdata[7] = 0x00;\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (IC-CARD) Anti Collision");\n+ break;\n+ case ICCARDCommand::SelectCard:\n+ icco.extlen = 8;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ // Session\n+ icco.extdata[0] = 0x00;\n+ icco.extdata[1] = m_ic_card_session;\n+ icco.extdata[2] = 0x00;\n+ icco.extdata[3] = 0x00;\n+ icco.extdata[4] = 0x00;\n+ icco.extdata[5] = 0x00;\n+ icco.extdata[6] = 0x00;\n+ icco.extdata[7] = 0x00;\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (IC-CARD) Select Card:{}",\n+ m_ic_card_session);\n+ break;\n+ case ICCARDCommand::ReadPage:\n+ case ICCARDCommand::ReadUseCount:\n+ {\n+ if (!validate_data_in_out(8, 0, "SerialA (IC-CARD)"))\n+ break;\n+ const u16 page = Common::swap16(data_in + 6) & 0xFF; // 255 is max page\n+\n+ icco.extlen = 8;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ memcpy(icco.extdata, m_ic_card_data + page * 8, 8);\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 31 (IC-CARD) Read Page:{}",\n+ page);\n+ break;\n+ }\n+ case ICCARDCommand::WritePage:\n+ {\n+ if (!validate_data_in_out(10, 0, "SerialA (IC-CARD)"))\n+ break;\n+ const u16 page = Common::swap16(data_in + 8) & 0xFF; // 255 is max page\n+\n+ // Write only one page\n+ if (page == 4)\n+ {\n+ icco.status = 0x80;\n+ }\n+ else\n+ {\n+ if (!validate_data_in_out(18, 0, "SerialA (IC-CARD)"))\n+ break;\n+ memcpy(m_ic_card_data + page * 8, data_in + 10, 8);\n+ }\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (IC-CARD) Write Page:{}",\n+ page);\n+ break;\n+ }\n+ case ICCARDCommand::DecreaseUseCount:\n+ {\n+ if (!validate_data_in_out(8, 0, "SerialA (IC-CARD)"))\n+ break;\n+ const u16 page = Common::swap16(data_in + 6) & 0xFF; // 255 is max page\n+\n+ icco.extlen = 2;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ auto ic_card_data = Common::BitCastPtr<u16>(m_ic_card_data + 0x28);\n+ ic_card_data = ic_card_data - 1;\n+\n+ // Counter\n+ icco.extdata[0] = m_ic_card_data[0x28];\n+ icco.extdata[1] = m_ic_card_data[0x29];\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 31 (IC-CARD) Decrease Use Count:{}", page);\n+ break;\n+ }\n+ case ICCARDCommand::ReadPages:\n+ {\n+ if (!validate_data_in_out(10, 0, "SerialA (IC-CARD)"))\n+ break;\n+ const u16 page = Common::swap16(data_in + 6) & 0xFF; // 255 is max page\n+ const u16 count = Common::swap16(data_in + 8);\n+\n+ const u32 offs = page * 8;\n+ u32 cnt = count * 8;\n+\n+ // Limit read size to not overwrite the reply buffer\n+ if (cnt > (u32)0x50 - data_offset)\n+ {\n+ cnt = 5 * 8;\n+ }\n+\n+ icco.extlen = cnt;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ memcpy(icco.extdata, m_ic_card_data + offs, cnt);\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 31 (IC-CARD) Read Pages:{} Count:{}", page, count);\n+ break;\n+ }\n+ case ICCARDCommand::WritePages:\n+ {\n+ if (!validate_data_in_out(10, 0, "SerialA (IC-CARD)"))\n+ break;\n+ const u16 pksize = length;\n+ const u16 size = Common::swap16(data_in + 2);\n+ const u16 page = Common::swap16(data_in + 6) & 0xFF; // 255 is max page\n+ const u16 count = Common::swap16(data_in + 8);\n+\n+ // We got a complete packet\n+ if (pksize - 5 == size)\n+ {\n+ if (page == 4) // Read Only Page, must return error\n+ {\n+ icco.status = 0x80;\n+ }\n+ else\n+ {\n+ if (page * 8 + count * 8 > sizeof(m_ic_card_data))\n+ {\n+ ERROR_LOG_FMT(\n+ SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (IC-CARD) Data overflow: Pages:{} Count:{}({:x})",\n+ page, count, size);\n+ }\n+ else\n+ {\n+ if (!validate_data_in_out(13 + count * 8, 0, "SerialA (IC-CARD)"))\n+ break;\n+ memcpy(m_ic_card_data + page * 8, data_in + 13, count * 8);\n+ }\n+ }\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (IC-CARD) Write Pages:{} Count:{}({:x})", page,\n+ count, size);\n+ }\n+ // VirtuaStriker 4 splits the writes over multiple packets\n+ else\n+ {\n+ if (!validate_data_in_out(2 + pksize, 0, "SerialA (IC-CARD)"))\n+ break;\n+ memcpy(m_ic_write_buffer, data_in + 2, pksize);\n+ m_ic_write_offset += pksize;\n+ m_ic_write_size = size;\n+ }\n+ break;\n+ }\n+ default:\n+ // Handle Deck Reader commands\n+ if (!validate_data_in_out(1, 0, "SerialA (DECK READER)"))\n+ break;\n+ serial_command = data_in[0];\n+ icco.command = serial_command;\n+ icco.flag = 0;\n+ switch (CDReaderCommand(serial_command))\n+ {\n+ case CDReaderCommand::ProgramVersion:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Program Version");\n+\n+ icco.extlen = (u32)strlen(s_cdr_program_version);\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ memcpy(icco.extdata, s_cdr_program_version, icco.extlen);\n+ break;\n+ case CDReaderCommand::BootVersion:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Boot Version");\n+\n+ icco.extlen = (u32)strlen(s_cdr_boot_version);\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ memcpy(icco.extdata, s_cdr_boot_version, icco.extlen);\n+ break;\n+ case CDReaderCommand::ShutterGet:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Shutter Get");\n+\n+ icco.extlen = 4;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ icco.extdata[0] = 0;\n+ icco.extdata[1] = 0;\n+ icco.extdata[2] = 0;\n+ icco.extdata[3] = 0;\n+ break;\n+ case CDReaderCommand::CameraCheck:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Camera Check");\n+\n+ icco.extlen = 6;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ icco.extdata[0] = 0x23;\n+ icco.extdata[1] = 0x28;\n+ icco.extdata[2] = 0x45;\n+ icco.extdata[3] = 0x29;\n+ icco.extdata[4] = 0x45;\n+ icco.extdata[5] = 0x29;\n+ break;\n+ case CDReaderCommand::ProgramChecksum:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Program Checksum");\n+\n+ icco.extlen = 4;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ icco.extdata[0] = 0x23;\n+ icco.extdata[1] = 0x28;\n+ icco.extdata[2] = 0x45;\n+ icco.extdata[3] = 0x29;\n+ break;\n+ case CDReaderCommand::BootChecksum:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Boot Checksum");\n+\n+ icco.extlen = 4;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ icco.extdata[0] = 0x23;\n+ icco.extdata[1] = 0x28;\n+ icco.extdata[2] = 0x45;\n+ icco.extdata[3] = 0x29;\n+ break;\n+ case CDReaderCommand::SelfTest:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (DECK READER) Self Test");\n+ icco.flag = 0x00;\n+ break;\n+ case CDReaderCommand::SensLock:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (DECK READER) Sens Lock");\n+ icco.flag = 0x01;\n+ break;\n+ case CDReaderCommand::SensCard:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (DECK READER) Sens Card");\n+ break;\n+ case CDReaderCommand::ShutterCard:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Shutter Card");\n+ break;\n+ case CDReaderCommand::ReadCard:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (DECK READER) Read Card");\n+\n+ icco.fixed = 0xAA;\n+ icco.flag = 0xAA;\n+ icco.extlen = sizeof(s_cdr_card_data);\n+ icco.length = 0x72;\n+ icco.status = Common::swap16(icco.extlen);\n+\n+ icco.pktlen += icco.extlen;\n+\n+ memcpy(icco.extdata, s_cdr_card_data, sizeof(s_cdr_card_data));\n+\n+ break;\n+ default:\n+ if (!validate_data_in_out(14, 0, "SerialA (DECK READER)"))\n+ break;\n+ WARN_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (IC-Card) {:02x} {:02x} {:02x} {:02x} {:02x} "\n+ "{:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x}",\n+ data_in[2], data_in[3], data_in[4], data_in[5], data_in[6],\n+ data_in[7], data_in[8], data_in[9], data_in[10], data_in[11],\n+ data_in[12], data_in[13]);\n+ break;\n+ }\n+ break;\n+ }\n+\n+ if (!validate_data_in_out(0, icco.pktlen + 2, "SerialA (IC-CARD)"))\n+ break;\n+ ICCardSendReply(&icco, data_out.data(), &data_offset);\n+\n+ if (!validate_data_in_out(2, 0, "SerialA (IC-CARD)"))\n+ break;\n+ data_in += length;\n+ break;\n+ }\n+ }\n+\n+ u32 command_offset = 0;\n+ while (command_offset < length)\n+ {\n+ // All commands are OR\'d with 0x80\n+ // Last byte is checksum which we don\'t care about\n+ if (!validate_data_in_out(command_offset + 4, 0, "SerialA"))\n+ break;\n+ const u32 serial_command = Common::swap32(data_in + command_offset) ^ 0x80000000;\n+\n+ if (AMMediaboard::GetGameType() == FZeroAX ||\n+ AMMediaboard::GetGameType() == FZeroAXMonster)\n+ {\n+ INFO_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x31 (MOTOR) Length:{:02x} Command:{:04x}({:02x})",\n+ length, (serial_command >> 8) & 0xFFFF, serial_command >> 24);\n+ }\n+ else\n+ {\n+ INFO_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x31 (SERIAL) Command:{:06x}",\n+ serial_command);\n+\n+ if (serial_command == 0x801000)\n+ {\n+ if (!validate_data_in_out(0, 4, "SerialA"))\n+ break;\n+ data_out[data_offset++] = 0x31;\n+ data_out[data_offset++] = 0x02;\n+ data_out[data_offset++] = 0xFF;\n+ data_out[data_offset++] = 0x01;\n+ }\n+ }\n+\n+ command_offset += sizeof(u32);\n+\n+ if (AMMediaboard::GetGameType() == FZeroAX ||\n+ AMMediaboard::GetGameType() == FZeroAXMonster)\n+ {\n+ // Status\n+ m_motor_reply[command_offset + 2] = 0;\n+ m_motor_reply[command_offset + 3] = 0;\n+\n+ // Error\n+ m_motor_reply[command_offset + 4] = 0;\n+\n+ switch (serial_command >> 24)\n+ {\n+ case 0:\n+ case 1: // Set Maximum?\n+ case 2:\n+ break;\n+\n+ // 0x00-0x40: left\n+ // 0x40-0x80: right\n+ case 4: // Move Steering Wheel\n+ // Left\n+ if (serial_command & 0x010000)\n+ {\n+ m_motor_force_y = -((s16)serial_command & 0xFF00);\n+ }\n+ else // Right\n+ {\n+ m_motor_force_y = (serial_command - 0x4000) & 0xFF00;\n+ }\n+\n+ m_motor_force_y *= 2;\n+\n+ // FFB\n+ if (m_motor_init == 2)\n+ {\n+ if (serial_interface.GetDeviceType(1) == SerialInterface::SIDEVICE_GC_STEERING)\n+ {\n+ const GCPadStatus pad_status = Pad::GetStatus(1);\n+ if (pad_status.isConnected)\n+ {\n+ const ControlState mapped_strength = (double)(m_motor_force_y >> 8) / 127.f;\n+\n+ Pad::Rumble(1, mapped_strength);\n+ INFO_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x31 (MOTOR) mapped_strength:{}",\n+ mapped_strength);\n+ }\n+ }\n+ }\n+ break;\n+ case 6: // nice\n+ case 9:\n+ default:\n+ break;\n+ // Switch back to normal controls\n+ case 7:\n+ m_motor_init = 2;\n+ break;\n+ // Reset\n+ case 0x7F:\n+ m_motor_init = 1;\n+ memset(m_motor_reply, 0, sizeof(m_motor_reply));\n+ break;\n+ }\n+\n+ // Checksum\n+ m_motor_reply[command_offset + 5] = m_motor_reply[command_offset + 2] ^\n+ m_motor_reply[command_offset + 3] ^\n+ m_motor_reply[command_offset + 4];\n+ }\n+ }\n+\n+ if (length == 0)\n+ {\n+ if (!validate_data_in_out(length, 2, "SerialA"))\n+ break;\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x00;\n+ }\n+ else\n+ {\n+ if (m_motor_init)\n+ {\n+ // Motor\n+ m_motor_reply[0] = gcam_command;\n+ m_motor_reply[1] = length; // Same out as in size\n+\n+ const u32 reply_size = m_motor_reply[1] + 2;\n+ if (!validate_data_in_out(length, reply_size, "SerialA"))\n+ break;\n+\n+ memcpy(data_out.data() + data_offset, m_motor_reply, reply_size);\n+ data_offset += reply_size;\n+ }\n+ }\n+\n+ if (!validate_data_in_out(length, 0, "SerialA"))\n+ {\n+ break;\n+ }\n+ data_in += length;\n+ break;\n+ }\n+ case GCAMCommand::SerialB:\n+ {\n+ DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 32 (CARD-Interface)");\n+ if (!validate_data_in_out(1, 0, "SerialB"))\n+ break;\n+ const u32 length = *data_in++;\n+ if (!validate_data_in_out(length, 0, "SerialB"))\n+ break;\n+ if (length)\n+ {\n+ // Send Card Reply\n+ if (length == 1 && data_in[0] == 0x05)\n+ {\n+ if (m_card_read_length)\n+ {\n+ if (!validate_data_in_out(0, 1, "SerialB"))\n+ break;\n+ data_out[data_offset++] = gcam_command;\n+ u32 read_length = m_card_read_length - m_card_read;\n+\n+ if (AMMediaboard::GetGameType() == FZeroAX)\n+ {\n+ read_length = std::min<u32>(read_length, 0x2F);\n+ }\n+\n+ if (!validate_data_in_out(0, 1, "SerialB"))\n+ break;\n+ data_out[data_offset++] = read_length; // 0x2F (max size per packet)\n+\n+ if (!validate_data_in_out(0, read_length, "SerialB"))\n+ break;\n+ memcpy(data_out.data() + data_offset, m_card_read_packet + m_card_read,\n+ read_length);\n+\n+ data_offset += read_length;\n+ m_card_read += read_length;\n+\n+ if (m_card_read >= m_card_read_length)\n+ m_card_read_length = 0;\n+\n+ data_in += length;\n+ break;\n+ }\n+\n+ if (!validate_data_in_out(0, 5, "SerialB"))\n+ break;\n+\n+ data_out[data_offset++] = gcam_command;\n+ const u32 command_length_offset = data_offset;\n+ data_out[data_offset++] = 0x00; // len\n+\n+ data_out[data_offset++] = 0x02;\n+ const u32 checksum_start = data_offset;\n+\n+ data_out[data_offset++] = 0x00; // 0x00 len\n+\n+ data_out[data_offset++] = m_card_command; // 0x01 command\n+\n+ switch (CARDCommand(m_card_command))\n+ {\n+ case CARDCommand::Init:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x00; // 0x02\n+ data_out[data_offset++] = 0x30; // 0x03\n+ break;\n+ case CARDCommand::GetState:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x20 | m_card_bit; // 0x02\n+\n+ // bit 0: Please take your card\n+ // bit 1: Endless waiting causes UNK_E to be called\n+ data_out[data_offset++] = 0x00; // 0x03\n+ break;\n+ case CARDCommand::Read:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x02; // 0x02\n+ data_out[data_offset++] = 0x53; // 0x03\n+ break;\n+ case CARDCommand::IsPresent:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x22; // 0x02\n+ data_out[data_offset++] = 0x30; // 0x03\n+ break;\n+ case CARDCommand::Write:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x02; // 0x02\n+ data_out[data_offset++] = 0x00; // 0x03\n+ break;\n+ case CARDCommand::SetPrintParam:\n+ case CARDCommand::RegisterFont:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x00; // 0x02\n+ data_out[data_offset++] = 0x00; // 0x03\n+ break;\n+ case CARDCommand::WriteInfo:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x02; // 0x02\n+ data_out[data_offset++] = 0x00; // 0x03\n+ break;\n+ case CARDCommand::Erase:\n+ // TODO: CARDCommand::Erase is not handled.\n+ break;\n+ case CARDCommand::Eject:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ if (AMMediaboard::GetGameType() == FZeroAX)\n+ {\n+ data_out[data_offset++] = 0x01; // 0x02\n+ }\n+ else\n+ {\n+ data_out[data_offset++] = 0x31; // 0x02\n+ }\n+ data_out[data_offset++] = 0x30; // 0x03\n+ break;\n+ case CARDCommand::Clean:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x02; // 0x02\n+ data_out[data_offset++] = 0x00; // 0x03\n+ break;\n+ case CARDCommand::Load:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x02; // 0x02\n+ data_out[data_offset++] = 0x30; // 0x03\n+ break;\n+ case CARDCommand::SetShutter:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x00; // 0x02\n+ data_out[data_offset++] = 0x00; // 0x03\n+ break;\n+ }\n+\n+ if (!validate_data_in_out(0, 3, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x30; // 0x04\n+ data_out[data_offset++] = 0x00; // 0x05\n+\n+ data_out[data_offset++] = 0x03; // 0x06\n+\n+ data_out[checksum_start] = data_offset - checksum_start; // 0x00 len\n+\n+ if (!validate_data_in_out(0, 1, "SerialB"))\n+ break;\n+ data_out[data_offset] = 0; // 0x07\n+ for (u32 i = 0; i < data_out[checksum_start]; ++i)\n+ data_out[data_offset] ^= data_out[checksum_start + i];\n+\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_offset++;\n+\n+ data_out[command_length_offset] = data_out[checksum_start] + 2;\n+ }\n+ else\n+ {\n+ for (u32 i = 0; i < length; ++i)\n+ m_card_buffer[m_card_offset + i] = data_in[i];\n+\n+ m_card_offset += length;\n+\n+ // Check if we got a complete command\n+ if (m_card_buffer[0] == 0x02)\n+ {\n+ if (m_card_buffer[1] == m_card_offset - 2)\n+ {\n+ if (m_card_buffer[m_card_offset - 2] == 0x03)\n+ {\n+ m_card_command = m_card_buffer[2];\n+\n+ switch (CARDCommand(m_card_command))\n+ {\n+ case CARDCommand::Init:\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD Init");\n+\n+ m_card_write_length = 0;\n+ m_card_bit = 0;\n+ m_card_memory_size = 0;\n+ m_card_state_call_count = 0;\n+ break;\n+ case CARDCommand::GetState:\n+ {\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD GetState({:02X})",\n+ m_card_bit);\n+\n+ if (m_card_memory_size == 0)\n+ {\n+ const std::string card_filename(\n+ fmt::format("{}tricard_{}.bin", File::GetUserPath(D_TRIUSER_IDX),\n+ SConfig::GetInstance().GetGameID()));\n+\n+ if (File::Exists(card_filename))\n+ {\n+ File::IOFile card(card_filename, "rb+");\n+ m_card_memory_size = (u32)card.GetSize();\n+\n+ card.ReadBytes(m_card_memory, m_card_memory_size);\n+ card.Close();\n+\n+ m_card_is_inserted = true;\n+ }\n+ }\n+\n+ if (AMMediaboard::GetGameType() == FZeroAX && m_card_memory_size)\n+ {\n+ m_card_state_call_count++;\n+ if (m_card_state_call_count > 10)\n+ {\n+ if (m_card_bit & 2)\n+ m_card_bit &= ~2u;\n+ else\n+ m_card_bit |= 2;\n+\n+ m_card_state_call_count = 0;\n+ }\n+ }\n+\n+ if (m_card_clean == 1)\n+ {\n+ m_card_clean = 2;\n+ }\n+ else if (m_card_clean == 2)\n+ {\n+ const std::string card_filename(\n+ fmt::format("{}tricard_{}.bin", File::GetUserPath(D_TRIUSER_IDX),\n+ SConfig::GetInstance().GetGameID()));\n+\n+ if (File::Exists(card_filename))\n+ {\n+ m_card_memory_size = (u32)File::GetSize(card_filename);\n+ if (m_card_memory_size)\n+ {\n+ if (AMMediaboard::GetGameType() == FZeroAX)\n+ {\n+ m_card_bit = 2;\n+ }\n+ else\n+ {\n+ m_card_bit = 1;\n+ }\n+ }\n+ }\n+ m_card_clean = 0;\n+ }\n+ break;\n+ }\n+ case CARDCommand::IsPresent:\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD IsPresent");\n+ break;\n+ case CARDCommand::RegisterFont:\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD RegisterFont");\n+ break;\n+ case CARDCommand::Load:\n+ {\n+ u8 mode = m_card_buffer[6];\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD Load({:02X})",\n+ mode);\n+ break;\n+ }\n+ case CARDCommand::Clean:\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD Clean");\n+ m_card_clean = 1;\n+ break;\n+ case CARDCommand::Read:\n+ {\n+ const u8 mode = m_card_buffer[6];\n+ const u8 bitmode = m_card_buffer[7];\n+ const u8 track = m_card_buffer[8];\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command CARD Read({:02X},{:02X},{:02X})", mode,\n+ bitmode, track);\n+\n+ // Prepare read packet\n+ memset(m_card_read_packet, 0, 0xDB);\n+ u32 packet_offset = 0;\n+\n+ const std::string card_filename(\n+ fmt::format("{}tricard_{}.bin", File::GetUserPath(D_TRIUSER_IDX),\n+ SConfig::GetInstance().GetGameID()));\n+\n+ if (File::Exists(card_filename))\n+ {\n+ File::IOFile card(card_filename, "rb+");\n+ if (m_card_memory_size == 0)\n+ {\n+ m_card_memory_size = (u32)card.GetSize();\n+ }\n+\n+ card.ReadBytes(m_card_memory, m_card_memory_size);\n+ card.Close();\n+\n+ m_card_is_inserted = true;\n+ }\n+\n+ m_card_read_packet[packet_offset++] = 0x02; // SUB CMD\n+ m_card_read_packet[packet_offset++] = 0x00; // SUB CMDLen\n+\n+ m_card_read_packet[packet_offset++] = 0x33; // CARD CMD\n+\n+ if (m_card_is_inserted) // CARD Status\n+ {\n+ m_card_read_packet[packet_offset++] = 0x31;\n+ }\n+ else\n+ {\n+ m_card_read_packet[packet_offset++] = 0x30;\n+ }\n+\n+ m_card_read_packet[packet_offset++] = 0x30;\n+ m_card_read_packet[packet_offset++] = 0x30;\n+\n+ // Data reply\n+ memcpy(m_card_read_packet + packet_offset, m_card_memory, m_card_memory_size);\n+ packet_offset += m_card_memory_size;\n+\n+ m_card_read_packet[packet_offset++] = 0x03;\n+\n+ m_card_read_packet[1] = packet_offset - 1; // SUB CMDLen\n+\n+ for (u32 i = 0; i < packet_offset - 1; ++i)\n+ m_card_read_packet[packet_offset] ^= m_card_read_packet[1 + i];\n+\n+ packet_offset++;\n+\n+ m_card_read_length = packet_offset;\n+ m_card_read = 0;\n+ break;\n+ }\n+ case CARDCommand::Write:\n+ {\n+ const u8 mode = m_card_buffer[6];\n+ const u8 bitmode = m_card_buffer[7];\n+ const u8 track = m_card_buffer[8];\n+\n+ m_card_memory_size = m_card_buffer[1] - 9;\n+\n+ memcpy(m_card_memory, m_card_buffer + 9, m_card_memory_size);', 'path': 'Source/Core/Core/HW/SI/SI_DeviceAMBaseboard.cpp', 'position': 1536, 'original_position': 1414, 'commit_id': '510fec88be973bc870021f2d0347593cdc285864', 'user': {'login': 'sepalani', 'id': 7890055, 'node_id': 'MDQ6VXNlcjc4OTAwNTU=', 'avatar_url': 'https://avatars.githubusercontent.com/u/7890055?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/sepalani', 'html_url': 'https://github.com/sepalani', 'followers_url': 'https://api.github.com/users/sepalani/followers', 'following_url': 'https://api.github.com/users/sepalani/following{/other_user}', 'gists_url': 'https://api.github.com/users/sepalani/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/sepalani/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/sepalani/subscriptions', 'organizations_url': 'https://api.github.com/users/sepalani/orgs', 'repos_url': 'https://api.github.com/users/sepalani/repos', 'events_url': 'https://api.github.com/users/sepalani/events{/privacy}', 'received_events_url': 'https://api.github.com/users/sepalani/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'body': "Isn't the `static_assert` + the above check enough?\r\n```C++\r\nif (m_card_memory_size > sizeof(m_card_memory)) // <-- early exit\r\n```\r\n\r\nWon't that imply the following regarding the `static_assert`:\r\n```C++\r\nstatic_assert(sizeof(m_card_buffer) >= sizeof(m_card_memory) + 9);\r\n// i.e. worst case since m_card_memory_size <= sizeof(m_card_memory):\r\n// sizeof(m_card_buffer) >= sizeof(m_card_memory) + 9 > sizeof(m_card_memory) >= m_card_memory_size\r\n```\r\n\r\nUnless I'm mistaken, if `m_card_buffer` (offset subtracted) is bigger than `m_card_memory`'s, it shouldn't overflow because `m_card_memory_size` is at most `sizeof(m_card_memory)`. In other words:\r\n - The early exit checks `memcpy`'s source parameter for overflow _(i.e. `m_card_memory_size <= sizeof(m_card_memory)` bytes are available. \r\n - The `static_assert` checks `memcpy`'s destination parameter for overflow (based on the fact that `m_card_memory_size` is at most `sizeof(m_card_memory)` bytes.\r\n\r\nI'm also open to suggestions regarding other and probably better ways to check this. \r\n", 'created_at': '2026-01-31T18:40:50Z', 'updated_at': '2026-01-31T18:40:50Z', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/13844#discussion_r2749846854', 'pull_request_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13844', 'author_association': 'MEMBER', '_links': {'self': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2749846854'}, 'html': {'href': 'https://github.com/dolphin-emu/dolphin/pull/13844#discussion_r2749846854'}, 'pull_request': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13844'}}, 'original_commit_id': '2415f0c1c471b2446a2144a1a2b643d8a77d8cc7', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2749846854/reactions', 'total_count': 0, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 0, 'confused': 0, 'heart': 0, 'rocket': 0, 'eyes': 0}, 'in_reply_to_id': 2725631348}], 'type': 'gh_pull_request_review'}
2026-01-31T17:12:51.138959 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'iwubcode', 'action': 'submitted', 'pr_id': 14312, 'pr_title': 'WGL: Correctly load wglDestroyPbufferARB extension', 'state': 'approved', 'url': 'https://github.com/dolphin-emu/dolphin/pull/14312#pullrequestreview-3733074436', 'comments': [], 'type': 'gh_pull_request_review'}
2026-01-31T13:33:14.945033 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'JosJuice', 'action': 'submitted', 'pr_id': 13844, 'pr_title': 'Core: Triforce support', 'state': 'commented', 'url': 'https://github.com/dolphin-emu/dolphin/pull/13844#pullrequestreview-3732508353', 'comments': [{'id': 2749545372, 'node_id': 'PRRC_kwDOALCn2M6j4ruc', 'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2749545372', 'pull_request_review_id': 3732508353, 'diff_hunk': '@@ -1,20 +1,112 @@\n-// Copyright 2013 Dolphin Emulator Project\n+// Copyright 2025 Dolphin Emulator Project\n // SPDX-License-Identifier: GPL-2.0-or-later\n \n #include "Core/HW/EXI/EXI_DeviceBaseboard.h"\n \n-#include "Common/ChunkFile.h"\n+#include <numeric>\n+#include <string>\n+\n+#include <fmt/format.h>\n+\n+#include "Common/Buffer.h"\n #include "Common/CommonTypes.h"\n+#include "Common/FileUtil.h"\n+#include "Common/IOFile.h"\n #include "Common/Logging/Log.h"\n \n+#include "Core/Boot/Boot.h"\n+#include "Core/BootManager.h"\n+#include "Core/ConfigManager.h"\n+#include "Core/Core.h"\n+#include "Core/HW/DVD/AMMediaboard.h"\n+#include "Core/HW/DVD/DVDInterface.h"\n+#include "Core/HW/EXI/EXI.h"\n+#include "Core/HW/EXI/EXI_Device.h"\n+#include "Core/HW/MMIO.h"\n+#include "Core/HW/Memmap.h"\n+#include "Core/Movie.h"\n+#include "Core/PowerPC/PowerPC.h"\n+#include "Core/System.h"\n+\n+static bool s_interrupt_set = false;\n+static u32 s_irq_timer = 0;\n+static u32 s_irq_status = 0;\n+\n+static u16 CheckSum(const u8* data, u32 length)\n+{\n+ return std::accumulate(data, data + length, 0);\n+}\n+\n namespace ExpansionInterface\n {\n+\n+void GenerateInterrupt(int flag)\n+{\n+ auto& system = Core::System::GetInstance();\n+\n+ s_interrupt_set = true;\n+ s_irq_timer = 0;\n+ s_irq_status = flag;\n+\n+ system.GetExpansionInterface().UpdateInterrupts();\n+}\n+\n CEXIBaseboard::CEXIBaseboard(Core::System& system) : IEXIDevice(system)\n+{\n+ std::string backup_filename(fmt::format("{}tribackup_{}.bin", File::GetUserPath(D_TRIUSER_IDX),\n+ SConfig::GetInstance().GetGameID()));\n+\n+ m_backup = File::IOFile(backup_filename, "rb+");\n+ if (!m_backup.IsOpen())\n+ {\n+ m_backup = File::IOFile(backup_filename, "wb+");\n+ }\n+\n+ // Some games share the same ID Client/Server\n+ if (!m_backup.IsGood())\n+ {\n+ PanicAlertFmt("Failed to open {}\\nFile might be in use.", backup_filename.c_str());\n+\n+ std::srand(static_cast<u32>(std::time(nullptr)));\n+\n+ backup_filename = fmt::format("{}tribackup_tmp_{}{}.bin", File::GetUserPath(D_TRIUSER_IDX),\n+ rand(), SConfig::GetInstance().GetGameID());\n+\n+ m_backup = File::IOFile(backup_filename, "wb+");\n+ }\n+\n+ // Virtua Striker 4 and Gekitou Pro Yakyuu need a higher FIRM version\n+ // Which is read from the backup data?!\n+ if (AMMediaboard::GetGameType() == VirtuaStriker4 ||\n+ AMMediaboard::GetGameType() == GekitouProYakyuu)\n+ {\n+ if (m_backup.GetSize() >= 0x20C + 0x1F4)\n+ {\n+ Common::UniqueBuffer<u8> data(m_backup.GetSize());', 'path': 'Source/Core/Core/HW/EXI/EXI_DeviceBaseboard.cpp', 'position': 87, 'original_position': 87, 'commit_id': '510fec88be973bc870021f2d0347593cdc285864', 'user': {'login': 'JosJuice', 'id': 6716818, 'node_id': 'MDQ6VXNlcjY3MTY4MTg=', 'avatar_url': 'https://avatars.githubusercontent.com/u/6716818?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/JosJuice', 'html_url': 'https://github.com/JosJuice', 'followers_url': 'https://api.github.com/users/JosJuice/followers', 'following_url': 'https://api.github.com/users/JosJuice/following{/other_user}', 'gists_url': 'https://api.github.com/users/JosJuice/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/JosJuice/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/JosJuice/subscriptions', 'organizations_url': 'https://api.github.com/users/JosJuice/orgs', 'repos_url': 'https://api.github.com/users/JosJuice/repos', 'events_url': 'https://api.github.com/users/JosJuice/events{/privacy}', 'received_events_url': 'https://api.github.com/users/JosJuice/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'body': "Time of check vs time of use error for `m_backup.GetSize()`. This isn't a security issue that's actually going to get triggered since you need write access to the local file system to do it, but calling `GetSize` only once is good for performance anyway.", 'created_at': '2026-01-31T13:02:11Z', 'updated_at': '2026-01-31T13:33:12Z', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/13844#discussion_r2749545372', 'pull_request_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13844', 'author_association': 'MEMBER', '_links': {'self': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2749545372'}, 'html': {'href': 'https://github.com/dolphin-emu/dolphin/pull/13844#discussion_r2749545372'}, 'pull_request': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13844'}}, 'original_commit_id': '510fec88be973bc870021f2d0347593cdc285864', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2749545372/reactions', 'total_count': 0, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 0, 'confused': 0, 'heart': 0, 'rocket': 0, 'eyes': 0}}, {'id': 2749551553, 'node_id': 'PRRC_kwDOALCn2M6j4tPB', 'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2749551553', 'pull_request_review_id': 3732508353, 'diff_hunk': '@@ -0,0 +1,2682 @@\n+// Copyright 2025 Dolphin Emulator Project\n+// SPDX-License-Identifier: GPL-2.0-or-later\n+\n+#include "Core/HW/SI/SI_DeviceAMBaseboard.h"\n+\n+#include <algorithm>\n+#include <numeric>\n+#include <string>\n+\n+#include <fmt/format.h>\n+\n+#include "Common/Buffer.h"\n+#include "Common/CommonTypes.h"\n+#include "Common/FileUtil.h"\n+#include "Common/IOFile.h"\n+#include "Common/Logging/Log.h"\n+#include "Common/MsgHandler.h"\n+#include "Common/Swap.h"\n+\n+#include "Core/Boot/Boot.h"\n+#include "Core/BootManager.h"\n+#include "Core/Config/MainSettings.h"\n+#include "Core/ConfigManager.h"\n+#include "Core/Core.h"\n+#include "Core/CoreTiming.h"\n+#include "Core/HW/DVD/AMMediaboard.h"\n+#include "Core/HW/DVD/DVDInterface.h"\n+#include "Core/HW/EXI/EXI.h"\n+#include "Core/HW/GCPad.h"\n+#include "Core/HW/MMIO.h"\n+#include "Core/HW/Memmap.h"\n+#include "Core/HW/ProcessorInterface.h"\n+#include "Core/HW/SI/SI.h"\n+#include "Core/HW/SI/SI_Device.h"\n+#include "Core/HW/SI/SI_DeviceGCController.h"\n+#include "Core/HW/SystemTimers.h"\n+#include "Core/Movie.h"\n+#include "Core/NetPlayProto.h"\n+#include "Core/System.h"\n+\n+#include "InputCommon/GCPadStatus.h"\n+\n+namespace SerialInterface\n+{\n+void JVSIOMessage::Start(int node)\n+{\n+ m_last_start = m_pointer;\n+ const u8 header[3] = {0xE0, (u8)node, 0};\n+ m_checksum = 0;\n+ AddData(header, 3, 1);\n+}\n+\n+void JVSIOMessage::AddData(const u8* dst, size_t len, int sync = 0)\n+{\n+ if (m_pointer + len >= sizeof(m_message))\n+ {\n+ PanicAlertFmt("JVSIOMessage overrun!");\n+ return;\n+ }\n+\n+ while (len--)\n+ {\n+ const u8 c = *dst++;\n+ if (!sync && ((c == 0xE0) || (c == 0xD0)))\n+ {\n+ if (m_pointer + 2 > sizeof(m_message))\n+ {\n+ PanicAlertFmt("JVSIOMessage overrun!");\n+ break;\n+ }\n+ m_message[m_pointer++] = 0xD0;\n+ m_message[m_pointer++] = c - 1;\n+ }\n+ else\n+ {\n+ if (m_pointer >= sizeof(m_message))\n+ {\n+ PanicAlertFmt("JVSIOMessage overrun!");\n+ break;\n+ }\n+ m_message[m_pointer++] = c;\n+ }\n+\n+ if (!sync)\n+ m_checksum += c;\n+ sync = 0;\n+ }\n+}\n+\n+void JVSIOMessage::AddData(const void* data, size_t len)\n+{\n+ AddData(static_cast<const u8*>(data), len);\n+}\n+\n+void JVSIOMessage::AddData(const char* data)\n+{\n+ AddData(data, strlen(data));\n+}\n+\n+void JVSIOMessage::AddData(u32 n)\n+{\n+ const u8 cs = n;\n+ AddData(&cs, 1);\n+}\n+\n+void JVSIOMessage::End()\n+{\n+ const u32 len = m_pointer - m_last_start;\n+ if (m_last_start + 2 < sizeof(m_message) && len >= 3)\n+ {\n+ m_message[m_last_start + 2] = len - 2; // assuming len <0xD0\n+ AddData(m_checksum + len - 2);\n+ }\n+ else\n+ {\n+ PanicAlertFmt("JVSIOMessage: Not enough space for checksum!");\n+ }\n+}\n+\n+static constexpr u8 CheckSumXOR(const u8* data, u32 length)\n+{\n+ return std::accumulate(data, data + length, u8{}, std::bit_xor());\n+}\n+\n+static constexpr char s_cdr_program_version[] = {" Version 1.22,2003/09/19,171-8213B"};\n+static constexpr char s_cdr_boot_version[] = {" Version 1.04,2003/06/17,171-8213B"};\n+static constexpr u8 s_cdr_card_data[] = {\n+ 0x00, 0x6E, 0x00, 0x00, 0x01, 0x00, 0x00, 0x06, 0x00, 0x00, 0x07, 0x00, 0x00, 0x0B,\n+ 0x00, 0x00, 0x0E, 0x00, 0x00, 0x10, 0x00, 0x00, 0x17, 0x00, 0x00, 0x19, 0x00, 0x00,\n+ 0x1A, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x20, 0x00,\n+ 0x00, 0x22, 0x00, 0x00, 0x23, 0x00, 0x00, 0x24, 0x00, 0x00, 0x27, 0x00, 0x00, 0x28,\n+ 0x00, 0x00, 0x2C, 0x00, 0x00, 0x2F, 0x00, 0x00, 0x34, 0x00, 0x00, 0x35, 0x00, 0x00,\n+ 0x37, 0x00, 0x00, 0x38, 0x00, 0x00, 0x39, 0x00, 0x00, 0x3D, 0x00};\n+\n+const constexpr u8 s_region_flags[] = "\\x00\\x00\\x30\\x00"\n+ // "\\x01\\xfe\\x00\\x00" // JAPAN\n+ "\\x02\\xfd\\x00\\x00" // USA\n+ //"\\x03\\xfc\\x00\\x00" // export\n+ "\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff";\n+// AM-Baseboard device on SI\n+CSIDevice_AMBaseboard::CSIDevice_AMBaseboard(Core::System& system, SIDevices device,\n+ int device_number)\n+ : ISIDevice(system, device, device_number)\n+{\n+ // Card ID\n+ m_ic_card_data[0x20] = 0x95;\n+ m_ic_card_data[0x21] = 0x71;\n+\n+ if (AMMediaboard::GetGameType() == KeyOfAvalon)\n+ {\n+ m_ic_card_data[0x22] = 0x26;\n+ m_ic_card_data[0x23] = 0x40;\n+ }\n+ else if (AMMediaboard::GetGameType() == VirtuaStriker4)\n+ {\n+ m_ic_card_data[0x22] = 0x44;\n+ m_ic_card_data[0x23] = 0x00;\n+ }\n+\n+ // Use count\n+ m_ic_card_data[0x28] = 0xFF;\n+ m_ic_card_data[0x29] = 0xFF;\n+}\n+\n+constexpr u32 SI_XFER_LENGTH_MASK = 0x7f;\n+\n+// Translate [0,1,2,...,126,127] to [128,1,2,...,126,127]\n+constexpr s32 ConvertSILengthField(u32 field)\n+{\n+ return ((field - 1) & SI_XFER_LENGTH_MASK) + 1;\n+}\n+\n+void CSIDevice_AMBaseboard::ICCardSendReply(ICCommand* iccommand, u8* buffer, u32* length)\n+{\n+ iccommand->status = Common::swap16(iccommand->status);\n+\n+ const auto iccommand_data = reinterpret_cast<const u8*>(iccommand);\n+ const u8 crc = CheckSumXOR(iccommand_data + 2, iccommand->pktlen - 1);\n+\n+ for (u32 i = 0; i < iccommand->pktlen + 1; ++i)\n+ {\n+ buffer[(*length)++] = iccommand_data[i];\n+ }\n+\n+ buffer[(*length)++] = crc;\n+}\n+\n+void CSIDevice_AMBaseboard::SwapBuffers(u8* buffer, u32* buffer_length)\n+{\n+ memcpy(m_last[1], buffer, 0x80); // Save current buffer\n+ memcpy(buffer, m_last[0], 0x80); // Load previous buffer\n+ memcpy(m_last[0], m_last[1], 0x80); // Update history\n+\n+ m_lastptr[1] = *buffer_length; // Swap lengths\n+ *buffer_length = m_lastptr[0];\n+ m_lastptr[0] = m_lastptr[1];\n+}\n+\n+int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length)\n+{\n+ const auto& serial_interface = m_system.GetSerialInterface();\n+ u32 buffer_length = ConvertSILengthField(serial_interface.GetInLength());\n+\n+ // Debug logging\n+ ISIDevice::RunBuffer(buffer, buffer_length);\n+\n+ u32 buffer_position = 0;\n+ while (buffer_position < buffer_length)\n+ {\n+ BaseBoardCommand command = static_cast<BaseBoardCommand>(buffer[buffer_position]);\n+ buffer_position++;\n+\n+ switch (command)\n+ {\n+ case BaseBoardCommand::GCAM_Reset: // Returns ID and dip switches\n+ {\n+ const u32 id = Common::swap32(SI_AM_BASEBOARD | 0x100);\n+ std::memcpy(buffer, &id, sizeof(id));\n+ return sizeof(id);\n+ }\n+ break;\n+ case BaseBoardCommand::GCAM_Command:\n+ {\n+ u32 checksum = 0;\n+ for (u32 i = 0; i < buffer_length; ++i)\n+ checksum += buffer[i];\n+\n+ std::array<u8, 0x80> data_out{};\n+ u32 data_offset = 0;\n+\n+ static u32 dip_switch_1 = 0xFE;\n+ static u32 dip_switch_0 = 0xFF;\n+\n+ data_out[data_offset++] = 1;\n+ data_out[data_offset++] = 1;\n+\n+ u8* data_in = buffer + 2;\n+ u8* const data_in_end = buffer + buffer[buffer_position] + 2;\n+\n+ // Helper to check that iterating over data n times is safe,\n+ // i.e. *data++ at most lead to data.end()\n+ auto validate_data_in_out = [&](u32 n_in, u32 n_out, std::string_view command) -> bool {\n+ if (data_in + n_in > data_in_end)\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: data_in overflow in {}", command);\n+ else if (std::size_t{data_offset} + n_out > data_out.size())\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: data_out overflow in {}", command);\n+ else\n+ return true;\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "Overflow details:\\n"\n+ " - data_in(begin={}, current={}, end={}, n_in={})\\n"\n+ " - data_out(offset={}, size={}, n_out={})\\n"\n+ " - buffer(position={}, length={})",\n+ fmt::ptr(buffer + 2), fmt::ptr(data_in), fmt::ptr(data_in_end), n_in,\n+ data_offset, data_out.size(), n_out, buffer_position, buffer_length);\n+ data_in = data_in_end;\n+ return false;\n+ };\n+\n+ while (data_in < data_in_end)\n+ {\n+ const u32 gcam_command = *data_in++;\n+ switch (GCAMCommand(gcam_command))\n+ {\n+ case GCAMCommand::StatusSwitches:\n+ {\n+ if (!validate_data_in_out(1, 4, "StatusSwitches"))\n+ break;\n+\n+ const u8 status = *data_in++;\n+ DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x10, {:02x} (READ STATUS&SWITCHES)",\n+ status);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x2;\n+\n+ // We read Test/Service from the JVS-I/O SwitchesInput instead\n+ //\n+ // const GCPadStatus pad_status = Pad::GetStatus(ISIDevice::m_device_number);\n+ // baseboard test/service switches\n+ // if (pad_status.button & PAD_BUTTON_Y)\t// Test\n+ // dip_switch_0 &= ~0x80;\n+ // if (pad_status.button & PAD_BUTTON_X)\t// Service\n+ // dip_switch_0 &= ~0x40;\n+\n+ // Horizontal Scanning Frequency switch\n+ // Required for F-Zero AX booting via Sega Boot\n+ if (AMMediaboard::GetGameType() == FZeroAX ||\n+ AMMediaboard::GetGameType() == FZeroAXMonster)\n+ {\n+ dip_switch_0 &= ~0x20;\n+ }\n+\n+ // Disable camera in MKGP1/2\n+ if (AMMediaboard::GetGameType() == MarioKartGP ||\n+ AMMediaboard::GetGameType() == MarioKartGP2)\n+ {\n+ dip_switch_0 &= ~0x10;\n+ }\n+\n+ data_out[data_offset++] = dip_switch_0;\n+ data_out[data_offset++] = dip_switch_1;\n+ break;\n+ }\n+ case GCAMCommand::SerialNumber:\n+ {\n+ if (!validate_data_in_out(1, 18, "SerialNumber"))\n+ break;\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x11, {:02x} (READ SERIAL NR)",\n+ *data_in);\n+ data_in++;\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 16;\n+ memcpy(data_out.data() + data_offset, "AADE-01B98394904", 16);\n+\n+ data_offset += 16;\n+ break;\n+ }\n+ case GCAMCommand::Unknown_12:\n+ if (!validate_data_in_out(2, 2, "Unknown_12"))\n+ break;\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x12, {:02x} {:02x}", data_in[0],\n+ data_in[1]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x00;\n+\n+ data_in += 2;\n+ break;\n+ case GCAMCommand::Unknown_14:\n+ if (!validate_data_in_out(2, 2, "Unknown_14"))\n+ break;\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x14, {:02x} {:02x}", data_in[0],\n+ data_in[1]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x00;\n+\n+ data_in += 2;\n+ break;\n+ case GCAMCommand::FirmVersion:\n+ if (!validate_data_in_out(1, 4, "FirmVersion"))\n+ break;\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x15, {:02x} (READ FIRM VERSION)",\n+ *data_in);\n+ data_in++;\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x02;\n+ // 00.26\n+ data_out[data_offset++] = 0x00;\n+ data_out[data_offset++] = 0x26;\n+ break;\n+ case GCAMCommand::FPGAVersion:\n+ if (!validate_data_in_out(1, 4, "FPGAVersion"))\n+ break;\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x16, {:02x} (READ FPGA VERSION)",\n+ *data_in);\n+ data_in++;\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x02;\n+ // 07.06\n+ data_out[data_offset++] = 0x07;\n+ data_out[data_offset++] = 0x06;\n+ break;\n+ case GCAMCommand::RegionSettings:\n+ {\n+ if (!validate_data_in_out(5, 0x16, "RegionSettings"))\n+ break;\n+\n+ // Used by SegaBoot for region checks (dev mode skips this check)\n+ // In some games this also controls the displayed language\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x1F, {:02x} {:02x} {:02x} {:02x} {:02x} (REGION)",\n+ data_in[0], data_in[1], data_in[2], data_in[3], data_in[4]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x14;\n+\n+ for (int i = 0; i < 0x14; ++i)\n+ data_out[data_offset++] = s_region_flags[i];\n+\n+ data_in += 5;\n+ }\n+ break;\n+ // No reply\n+ // Note: Always sends three bytes even though size is set to two\n+ case GCAMCommand::Unknown_21:\n+ {\n+ if (!validate_data_in_out(4, 0, "Unknown_21"))\n+ break;\n+\n+ DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x21, {:02x}, {:02x}, {:02x}, {:02x}",\n+ data_in[0], data_in[1], data_in[2], data_in[3]);\n+ data_in += 4;\n+ }\n+ break;\n+ // No reply\n+ // Note: Always sends six bytes\n+ case GCAMCommand::Unknown_22:\n+ {\n+ if (!validate_data_in_out(7, 0, "Unknown_22"))\n+ break;\n+\n+ DEBUG_LOG_FMT(\n+ SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x22, {:02x}, {:02x}, {:02x}, {:02x}, {:02x}, {:02x}, {:02x}",\n+ data_in[0], data_in[1], data_in[2], data_in[3], data_in[4], data_in[5], data_in[6]);\n+\n+ const u32 in_size = data_in[0] + 1;\n+ if (!validate_data_in_out(in_size, 0, "Unknown_22"))\n+ break;\n+ data_in += in_size;\n+ }\n+ break;\n+ case GCAMCommand::Unknown_23:\n+ if (!validate_data_in_out(2, 2, "Unknown_23"))\n+ break;\n+\n+ DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x23, {:02x} {:02x}", data_in[0],\n+ data_in[1]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x00;\n+\n+ data_in += 2;\n+ break;\n+ case GCAMCommand::Unknown_24:\n+ if (!validate_data_in_out(2, 2, "Unknown_24"))\n+ break;\n+\n+ DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x24, {:02x} {:02x}", data_in[0],\n+ data_in[1]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x00;\n+\n+ data_in += 2;\n+ break;\n+ case GCAMCommand::SerialA:\n+ {\n+ if (!validate_data_in_out(1, 0, "SerialA"))\n+ break;\n+\n+ const u32 length = *data_in++;\n+ if (length)\n+ {\n+ if (!validate_data_in_out(length, 0, "SerialA"))\n+ break;\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x31, length=0x{:02x}, hexdump:\\n{}",\n+ length, HexDump(data_in, length));\n+\n+ // Serial - Wheel\n+ if (AMMediaboard::GetGameType() == MarioKartGP ||\n+ AMMediaboard::GetGameType() == MarioKartGP2)\n+ {\n+ if (!validate_data_in_out(10, 2, "SerialA (Wheel)"))\n+ break;\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x31, (WHEEL) {:02x}{:02x} {:02x}{:02x} {:02x} {:02x} "\n+ "{:02x} {:02x} {:02x} {:02x}",\n+ data_in[0], data_in[1], data_in[2], data_in[3], data_in[4], data_in[5],\n+ data_in[6], data_in[7], data_in[8], data_in[9]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x03;\n+\n+ switch (m_wheel_init)\n+ {\n+ case 0:\n+ if (!validate_data_in_out(0, 3, "SerialA (Wheel)"))\n+ break;\n+ data_out[data_offset++] = \'E\'; // Error\n+ data_out[data_offset++] = \'0\';\n+ data_out[data_offset++] = \'0\';\n+ m_wheel_init++;\n+ break;\n+ case 1:\n+ if (!validate_data_in_out(0, 3, "SerialA (Wheel)"))\n+ break;\n+ data_out[data_offset++] = \'C\'; // Power Off\n+ data_out[data_offset++] = \'0\';\n+ data_out[data_offset++] = \'6\';\n+ // Only turn on when a wheel is connected\n+ if (serial_interface.GetDeviceType(1) == SerialInterface::SIDEVICE_GC_STEERING)\n+ {\n+ m_wheel_init++;\n+ }\n+ break;\n+ case 2:\n+ if (!validate_data_in_out(0, 3, "SerialA (Wheel)"))\n+ break;\n+ data_out[data_offset++] = \'C\'; // Power On\n+ data_out[data_offset++] = \'0\';\n+ data_out[data_offset++] = \'1\';\n+ break;\n+ default:\n+ break;\n+ }\n+\n+ // u16 CenteringForce= ptr(6);\n+ // u16 FrictionForce = ptr(8);\n+ // u16 Roll = ptr(10);\n+ if (!validate_data_in_out(length, 0, "SerialA (Wheel)"))\n+ break;\n+ data_in += length;\n+ break;\n+ }\n+\n+ // Serial - Unknown\n+ if (AMMediaboard::GetGameType() == GekitouProYakyuu)\n+ {\n+ if (!validate_data_in_out(sizeof(u32), 0, "SerialA (Unknown)"))\n+ break;\n+ const u32 serial_command = Common::BitCastPtr<u32>(data_in);\n+\n+ if (serial_command == 0x00001000)\n+ {\n+ if (!validate_data_in_out(0, 5, "SerialA (Unknown)"))\n+ break;\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x03;\n+ data_out[data_offset++] = 1;\n+ data_out[data_offset++] = 2;\n+ data_out[data_offset++] = 3;\n+ }\n+\n+ if (!validate_data_in_out(length, 0, "SerialA (Unknown)"))\n+ break;\n+ data_in += length;\n+ break;\n+ }\n+\n+ // Serial IC-CARD / Serial Deck Reader\n+ if (AMMediaboard::GetGameType() == VirtuaStriker4 ||\n+ AMMediaboard::GetGameType() == VirtuaStriker4_2006 ||\n+ AMMediaboard::GetGameType() == KeyOfAvalon)\n+ {\n+ if (!validate_data_in_out(2, 0, "SerialA (IC-CARD)"))\n+ break;\n+ u32 serial_command = data_in[1];\n+\n+ ICCommand icco;\n+\n+ // Set default reply\n+ icco.pktcmd = gcam_command;\n+ icco.pktlen = 7;\n+ icco.fixed = 0x10;\n+ icco.command = serial_command;\n+ icco.flag = 0;\n+ icco.length = 2;\n+ icco.status = 0;\n+ icco.extlen = 0;\n+\n+ // Check for rest of data from the write pages command\n+ if (m_ic_write_size && m_ic_write_offset)\n+ {\n+ const u32 size = data_in[1];\n+\n+ if (!validate_data_in_out(size + 2, 0, "SerialA (IC-CARD)"))\n+ break;\n+ DEBUG_LOG_FMT(SERIALINTERFACE_CARD, "Command: {}", HexDump(data_in, size + 2));\n+\n+ INFO_LOG_FMT(\n+ SERIALINTERFACE_CARD,\n+ "GC-AM: Command 25 (IC-CARD) Write Pages: Off:{:x} Size:{:x} PSize:{:x}",\n+ m_ic_write_offset, m_ic_write_size, size);\n+\n+ if (std::size_t{m_ic_write_offset} + size > sizeof(m_ic_write_buffer))', 'path': 'Source/Core/Core/HW/SI/SI_DeviceAMBaseboard.cpp', 'position': 578, 'original_position': 578, 'commit_id': '510fec88be973bc870021f2d0347593cdc285864', 'user': {'login': 'JosJuice', 'id': 6716818, 'node_id': 'MDQ6VXNlcjY3MTY4MTg=', 'avatar_url': 'https://avatars.githubusercontent.com/u/6716818?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/JosJuice', 'html_url': 'https://github.com/JosJuice', 'followers_url': 'https://api.github.com/users/JosJuice/followers', 'following_url': 'https://api.github.com/users/JosJuice/following{/other_user}', 'gists_url': 'https://api.github.com/users/JosJuice/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/JosJuice/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/JosJuice/subscriptions', 'organizations_url': 'https://api.github.com/users/JosJuice/orgs', 'repos_url': 'https://api.github.com/users/JosJuice/repos', 'events_url': 'https://api.github.com/users/JosJuice/events{/privacy}', 'received_events_url': 'https://api.github.com/users/JosJuice/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'body': '```suggestion\r\n if (u64{m_ic_write_offset} + size > sizeof(m_ic_write_buffer))\r\n```', 'created_at': '2026-01-31T13:11:17Z', 'updated_at': '2026-01-31T13:33:12Z', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/13844#discussion_r2749551553', 'pull_request_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13844', 'author_association': 'MEMBER', '_links': {'self': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2749551553'}, 'html': {'href': 'https://github.com/dolphin-emu/dolphin/pull/13844#discussion_r2749551553'}, 'pull_request': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13844'}}, 'original_commit_id': '510fec88be973bc870021f2d0347593cdc285864', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2749551553/reactions', 'total_count': 0, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 0, 'confused': 0, 'heart': 0, 'rocket': 0, 'eyes': 0}}, {'id': 2749557847, 'node_id': 'PRRC_kwDOALCn2M6j4uxX', 'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2749557847', 'pull_request_review_id': 3732508353, 'diff_hunk': '@@ -0,0 +1,2682 @@\n+// Copyright 2025 Dolphin Emulator Project\n+// SPDX-License-Identifier: GPL-2.0-or-later\n+\n+#include "Core/HW/SI/SI_DeviceAMBaseboard.h"\n+\n+#include <algorithm>\n+#include <numeric>\n+#include <string>\n+\n+#include <fmt/format.h>\n+\n+#include "Common/Buffer.h"\n+#include "Common/CommonTypes.h"\n+#include "Common/FileUtil.h"\n+#include "Common/IOFile.h"\n+#include "Common/Logging/Log.h"\n+#include "Common/MsgHandler.h"\n+#include "Common/Swap.h"\n+\n+#include "Core/Boot/Boot.h"\n+#include "Core/BootManager.h"\n+#include "Core/Config/MainSettings.h"\n+#include "Core/ConfigManager.h"\n+#include "Core/Core.h"\n+#include "Core/CoreTiming.h"\n+#include "Core/HW/DVD/AMMediaboard.h"\n+#include "Core/HW/DVD/DVDInterface.h"\n+#include "Core/HW/EXI/EXI.h"\n+#include "Core/HW/GCPad.h"\n+#include "Core/HW/MMIO.h"\n+#include "Core/HW/Memmap.h"\n+#include "Core/HW/ProcessorInterface.h"\n+#include "Core/HW/SI/SI.h"\n+#include "Core/HW/SI/SI_Device.h"\n+#include "Core/HW/SI/SI_DeviceGCController.h"\n+#include "Core/HW/SystemTimers.h"\n+#include "Core/Movie.h"\n+#include "Core/NetPlayProto.h"\n+#include "Core/System.h"\n+\n+#include "InputCommon/GCPadStatus.h"\n+\n+namespace SerialInterface\n+{\n+void JVSIOMessage::Start(int node)\n+{\n+ m_last_start = m_pointer;\n+ const u8 header[3] = {0xE0, (u8)node, 0};\n+ m_checksum = 0;\n+ AddData(header, 3, 1);\n+}\n+\n+void JVSIOMessage::AddData(const u8* dst, size_t len, int sync = 0)\n+{\n+ if (m_pointer + len >= sizeof(m_message))\n+ {\n+ PanicAlertFmt("JVSIOMessage overrun!");\n+ return;\n+ }\n+\n+ while (len--)\n+ {\n+ const u8 c = *dst++;\n+ if (!sync && ((c == 0xE0) || (c == 0xD0)))\n+ {\n+ if (m_pointer + 2 > sizeof(m_message))\n+ {\n+ PanicAlertFmt("JVSIOMessage overrun!");\n+ break;\n+ }\n+ m_message[m_pointer++] = 0xD0;\n+ m_message[m_pointer++] = c - 1;\n+ }\n+ else\n+ {\n+ if (m_pointer >= sizeof(m_message))\n+ {\n+ PanicAlertFmt("JVSIOMessage overrun!");\n+ break;\n+ }\n+ m_message[m_pointer++] = c;\n+ }\n+\n+ if (!sync)\n+ m_checksum += c;\n+ sync = 0;\n+ }\n+}\n+\n+void JVSIOMessage::AddData(const void* data, size_t len)\n+{\n+ AddData(static_cast<const u8*>(data), len);\n+}\n+\n+void JVSIOMessage::AddData(const char* data)\n+{\n+ AddData(data, strlen(data));\n+}\n+\n+void JVSIOMessage::AddData(u32 n)\n+{\n+ const u8 cs = n;\n+ AddData(&cs, 1);\n+}\n+\n+void JVSIOMessage::End()\n+{\n+ const u32 len = m_pointer - m_last_start;\n+ if (m_last_start + 2 < sizeof(m_message) && len >= 3)\n+ {\n+ m_message[m_last_start + 2] = len - 2; // assuming len <0xD0\n+ AddData(m_checksum + len - 2);\n+ }\n+ else\n+ {\n+ PanicAlertFmt("JVSIOMessage: Not enough space for checksum!");\n+ }\n+}\n+\n+static constexpr u8 CheckSumXOR(const u8* data, u32 length)\n+{\n+ return std::accumulate(data, data + length, u8{}, std::bit_xor());\n+}\n+\n+static constexpr char s_cdr_program_version[] = {" Version 1.22,2003/09/19,171-8213B"};\n+static constexpr char s_cdr_boot_version[] = {" Version 1.04,2003/06/17,171-8213B"};\n+static constexpr u8 s_cdr_card_data[] = {\n+ 0x00, 0x6E, 0x00, 0x00, 0x01, 0x00, 0x00, 0x06, 0x00, 0x00, 0x07, 0x00, 0x00, 0x0B,\n+ 0x00, 0x00, 0x0E, 0x00, 0x00, 0x10, 0x00, 0x00, 0x17, 0x00, 0x00, 0x19, 0x00, 0x00,\n+ 0x1A, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x20, 0x00,\n+ 0x00, 0x22, 0x00, 0x00, 0x23, 0x00, 0x00, 0x24, 0x00, 0x00, 0x27, 0x00, 0x00, 0x28,\n+ 0x00, 0x00, 0x2C, 0x00, 0x00, 0x2F, 0x00, 0x00, 0x34, 0x00, 0x00, 0x35, 0x00, 0x00,\n+ 0x37, 0x00, 0x00, 0x38, 0x00, 0x00, 0x39, 0x00, 0x00, 0x3D, 0x00};\n+\n+const constexpr u8 s_region_flags[] = "\\x00\\x00\\x30\\x00"\n+ // "\\x01\\xfe\\x00\\x00" // JAPAN\n+ "\\x02\\xfd\\x00\\x00" // USA\n+ //"\\x03\\xfc\\x00\\x00" // export\n+ "\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff";\n+// AM-Baseboard device on SI\n+CSIDevice_AMBaseboard::CSIDevice_AMBaseboard(Core::System& system, SIDevices device,\n+ int device_number)\n+ : ISIDevice(system, device, device_number)\n+{\n+ // Card ID\n+ m_ic_card_data[0x20] = 0x95;\n+ m_ic_card_data[0x21] = 0x71;\n+\n+ if (AMMediaboard::GetGameType() == KeyOfAvalon)\n+ {\n+ m_ic_card_data[0x22] = 0x26;\n+ m_ic_card_data[0x23] = 0x40;\n+ }\n+ else if (AMMediaboard::GetGameType() == VirtuaStriker4)\n+ {\n+ m_ic_card_data[0x22] = 0x44;\n+ m_ic_card_data[0x23] = 0x00;\n+ }\n+\n+ // Use count\n+ m_ic_card_data[0x28] = 0xFF;\n+ m_ic_card_data[0x29] = 0xFF;\n+}\n+\n+constexpr u32 SI_XFER_LENGTH_MASK = 0x7f;\n+\n+// Translate [0,1,2,...,126,127] to [128,1,2,...,126,127]\n+constexpr s32 ConvertSILengthField(u32 field)\n+{\n+ return ((field - 1) & SI_XFER_LENGTH_MASK) + 1;\n+}\n+\n+void CSIDevice_AMBaseboard::ICCardSendReply(ICCommand* iccommand, u8* buffer, u32* length)\n+{\n+ iccommand->status = Common::swap16(iccommand->status);\n+\n+ const auto iccommand_data = reinterpret_cast<const u8*>(iccommand);\n+ const u8 crc = CheckSumXOR(iccommand_data + 2, iccommand->pktlen - 1);\n+\n+ for (u32 i = 0; i < iccommand->pktlen + 1; ++i)\n+ {\n+ buffer[(*length)++] = iccommand_data[i];\n+ }\n+\n+ buffer[(*length)++] = crc;\n+}\n+\n+void CSIDevice_AMBaseboard::SwapBuffers(u8* buffer, u32* buffer_length)\n+{\n+ memcpy(m_last[1], buffer, 0x80); // Save current buffer\n+ memcpy(buffer, m_last[0], 0x80); // Load previous buffer\n+ memcpy(m_last[0], m_last[1], 0x80); // Update history\n+\n+ m_lastptr[1] = *buffer_length; // Swap lengths\n+ *buffer_length = m_lastptr[0];\n+ m_lastptr[0] = m_lastptr[1];\n+}\n+\n+int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length)\n+{\n+ const auto& serial_interface = m_system.GetSerialInterface();\n+ u32 buffer_length = ConvertSILengthField(serial_interface.GetInLength());\n+\n+ // Debug logging\n+ ISIDevice::RunBuffer(buffer, buffer_length);\n+\n+ u32 buffer_position = 0;\n+ while (buffer_position < buffer_length)\n+ {\n+ BaseBoardCommand command = static_cast<BaseBoardCommand>(buffer[buffer_position]);\n+ buffer_position++;\n+\n+ switch (command)\n+ {\n+ case BaseBoardCommand::GCAM_Reset: // Returns ID and dip switches\n+ {\n+ const u32 id = Common::swap32(SI_AM_BASEBOARD | 0x100);\n+ std::memcpy(buffer, &id, sizeof(id));\n+ return sizeof(id);\n+ }\n+ break;\n+ case BaseBoardCommand::GCAM_Command:\n+ {\n+ u32 checksum = 0;\n+ for (u32 i = 0; i < buffer_length; ++i)\n+ checksum += buffer[i];\n+\n+ std::array<u8, 0x80> data_out{};\n+ u32 data_offset = 0;\n+\n+ static u32 dip_switch_1 = 0xFE;\n+ static u32 dip_switch_0 = 0xFF;\n+\n+ data_out[data_offset++] = 1;\n+ data_out[data_offset++] = 1;\n+\n+ u8* data_in = buffer + 2;\n+ u8* const data_in_end = buffer + buffer[buffer_position] + 2;', 'path': 'Source/Core/Core/HW/SI/SI_DeviceAMBaseboard.cpp', 'position': 238, 'original_position': 238, 'commit_id': '510fec88be973bc870021f2d0347593cdc285864', 'user': {'login': 'JosJuice', 'id': 6716818, 'node_id': 'MDQ6VXNlcjY3MTY4MTg=', 'avatar_url': 'https://avatars.githubusercontent.com/u/6716818?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/JosJuice', 'html_url': 'https://github.com/JosJuice', 'followers_url': 'https://api.github.com/users/JosJuice/followers', 'following_url': 'https://api.github.com/users/JosJuice/following{/other_user}', 'gists_url': 'https://api.github.com/users/JosJuice/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/JosJuice/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/JosJuice/subscriptions', 'organizations_url': 'https://api.github.com/users/JosJuice/orgs', 'repos_url': 'https://api.github.com/users/JosJuice/repos', 'events_url': 'https://api.github.com/users/JosJuice/events{/privacy}', 'received_events_url': 'https://api.github.com/users/JosJuice/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'body': "This is an older comment, but it hasn't been addressed and GitHub is showing the old comment as outdated, so I'm reposting it.\r\n\r\nYou aren't checking that `buffer_position` is smaller than `128`, and then you aren't checking that `buffer[buffer_position] + 2` is less than or equal to `128`.", 'created_at': '2026-01-31T13:21:16Z', 'updated_at': '2026-01-31T13:33:12Z', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/13844#discussion_r2749557847', 'pull_request_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13844', 'author_association': 'MEMBER', '_links': {'self': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2749557847'}, 'html': {'href': 'https://github.com/dolphin-emu/dolphin/pull/13844#discussion_r2749557847'}, 'pull_request': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13844'}}, 'original_commit_id': '510fec88be973bc870021f2d0347593cdc285864', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2749557847/reactions', 'total_count': 0, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 0, 'confused': 0, 'heart': 0, 'rocket': 0, 'eyes': 0}}, {'id': 2749561110, 'node_id': 'PRRC_kwDOALCn2M6j4vkW', 'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2749561110', 'pull_request_review_id': 3732508353, 'diff_hunk': '@@ -0,0 +1,2682 @@\n+// Copyright 2025 Dolphin Emulator Project\n+// SPDX-License-Identifier: GPL-2.0-or-later\n+\n+#include "Core/HW/SI/SI_DeviceAMBaseboard.h"\n+\n+#include <algorithm>\n+#include <numeric>\n+#include <string>\n+\n+#include <fmt/format.h>\n+\n+#include "Common/Buffer.h"\n+#include "Common/CommonTypes.h"\n+#include "Common/FileUtil.h"\n+#include "Common/IOFile.h"\n+#include "Common/Logging/Log.h"\n+#include "Common/MsgHandler.h"\n+#include "Common/Swap.h"\n+\n+#include "Core/Boot/Boot.h"\n+#include "Core/BootManager.h"\n+#include "Core/Config/MainSettings.h"\n+#include "Core/ConfigManager.h"\n+#include "Core/Core.h"\n+#include "Core/CoreTiming.h"\n+#include "Core/HW/DVD/AMMediaboard.h"\n+#include "Core/HW/DVD/DVDInterface.h"\n+#include "Core/HW/EXI/EXI.h"\n+#include "Core/HW/GCPad.h"\n+#include "Core/HW/MMIO.h"\n+#include "Core/HW/Memmap.h"\n+#include "Core/HW/ProcessorInterface.h"\n+#include "Core/HW/SI/SI.h"\n+#include "Core/HW/SI/SI_Device.h"\n+#include "Core/HW/SI/SI_DeviceGCController.h"\n+#include "Core/HW/SystemTimers.h"\n+#include "Core/Movie.h"\n+#include "Core/NetPlayProto.h"\n+#include "Core/System.h"\n+\n+#include "InputCommon/GCPadStatus.h"\n+\n+namespace SerialInterface\n+{\n+void JVSIOMessage::Start(int node)\n+{\n+ m_last_start = m_pointer;\n+ const u8 header[3] = {0xE0, (u8)node, 0};\n+ m_checksum = 0;\n+ AddData(header, 3, 1);\n+}\n+\n+void JVSIOMessage::AddData(const u8* dst, size_t len, int sync = 0)\n+{\n+ if (m_pointer + len >= sizeof(m_message))\n+ {\n+ PanicAlertFmt("JVSIOMessage overrun!");\n+ return;\n+ }\n+\n+ while (len--)\n+ {\n+ const u8 c = *dst++;\n+ if (!sync && ((c == 0xE0) || (c == 0xD0)))\n+ {\n+ if (m_pointer + 2 > sizeof(m_message))\n+ {\n+ PanicAlertFmt("JVSIOMessage overrun!");\n+ break;\n+ }\n+ m_message[m_pointer++] = 0xD0;\n+ m_message[m_pointer++] = c - 1;\n+ }\n+ else\n+ {\n+ if (m_pointer >= sizeof(m_message))\n+ {\n+ PanicAlertFmt("JVSIOMessage overrun!");\n+ break;\n+ }\n+ m_message[m_pointer++] = c;\n+ }\n+\n+ if (!sync)\n+ m_checksum += c;\n+ sync = 0;\n+ }\n+}\n+\n+void JVSIOMessage::AddData(const void* data, size_t len)\n+{\n+ AddData(static_cast<const u8*>(data), len);\n+}\n+\n+void JVSIOMessage::AddData(const char* data)\n+{\n+ AddData(data, strlen(data));\n+}\n+\n+void JVSIOMessage::AddData(u32 n)\n+{\n+ const u8 cs = n;\n+ AddData(&cs, 1);\n+}\n+\n+void JVSIOMessage::End()\n+{\n+ const u32 len = m_pointer - m_last_start;\n+ if (m_last_start + 2 < sizeof(m_message) && len >= 3)\n+ {\n+ m_message[m_last_start + 2] = len - 2; // assuming len <0xD0\n+ AddData(m_checksum + len - 2);\n+ }\n+ else\n+ {\n+ PanicAlertFmt("JVSIOMessage: Not enough space for checksum!");\n+ }\n+}\n+\n+static constexpr u8 CheckSumXOR(const u8* data, u32 length)\n+{\n+ return std::accumulate(data, data + length, u8{}, std::bit_xor());\n+}\n+\n+static constexpr char s_cdr_program_version[] = {" Version 1.22,2003/09/19,171-8213B"};\n+static constexpr char s_cdr_boot_version[] = {" Version 1.04,2003/06/17,171-8213B"};\n+static constexpr u8 s_cdr_card_data[] = {\n+ 0x00, 0x6E, 0x00, 0x00, 0x01, 0x00, 0x00, 0x06, 0x00, 0x00, 0x07, 0x00, 0x00, 0x0B,\n+ 0x00, 0x00, 0x0E, 0x00, 0x00, 0x10, 0x00, 0x00, 0x17, 0x00, 0x00, 0x19, 0x00, 0x00,\n+ 0x1A, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x20, 0x00,\n+ 0x00, 0x22, 0x00, 0x00, 0x23, 0x00, 0x00, 0x24, 0x00, 0x00, 0x27, 0x00, 0x00, 0x28,\n+ 0x00, 0x00, 0x2C, 0x00, 0x00, 0x2F, 0x00, 0x00, 0x34, 0x00, 0x00, 0x35, 0x00, 0x00,\n+ 0x37, 0x00, 0x00, 0x38, 0x00, 0x00, 0x39, 0x00, 0x00, 0x3D, 0x00};\n+\n+const constexpr u8 s_region_flags[] = "\\x00\\x00\\x30\\x00"\n+ // "\\x01\\xfe\\x00\\x00" // JAPAN\n+ "\\x02\\xfd\\x00\\x00" // USA\n+ //"\\x03\\xfc\\x00\\x00" // export\n+ "\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff";\n+// AM-Baseboard device on SI\n+CSIDevice_AMBaseboard::CSIDevice_AMBaseboard(Core::System& system, SIDevices device,\n+ int device_number)\n+ : ISIDevice(system, device, device_number)\n+{\n+ // Card ID\n+ m_ic_card_data[0x20] = 0x95;\n+ m_ic_card_data[0x21] = 0x71;\n+\n+ if (AMMediaboard::GetGameType() == KeyOfAvalon)\n+ {\n+ m_ic_card_data[0x22] = 0x26;\n+ m_ic_card_data[0x23] = 0x40;\n+ }\n+ else if (AMMediaboard::GetGameType() == VirtuaStriker4)\n+ {\n+ m_ic_card_data[0x22] = 0x44;\n+ m_ic_card_data[0x23] = 0x00;\n+ }\n+\n+ // Use count\n+ m_ic_card_data[0x28] = 0xFF;\n+ m_ic_card_data[0x29] = 0xFF;\n+}\n+\n+constexpr u32 SI_XFER_LENGTH_MASK = 0x7f;\n+\n+// Translate [0,1,2,...,126,127] to [128,1,2,...,126,127]\n+constexpr s32 ConvertSILengthField(u32 field)\n+{\n+ return ((field - 1) & SI_XFER_LENGTH_MASK) + 1;\n+}\n+\n+void CSIDevice_AMBaseboard::ICCardSendReply(ICCommand* iccommand, u8* buffer, u32* length)\n+{\n+ iccommand->status = Common::swap16(iccommand->status);\n+\n+ const auto iccommand_data = reinterpret_cast<const u8*>(iccommand);\n+ const u8 crc = CheckSumXOR(iccommand_data + 2, iccommand->pktlen - 1);\n+\n+ for (u32 i = 0; i < iccommand->pktlen + 1; ++i)\n+ {\n+ buffer[(*length)++] = iccommand_data[i];\n+ }\n+\n+ buffer[(*length)++] = crc;\n+}\n+\n+void CSIDevice_AMBaseboard::SwapBuffers(u8* buffer, u32* buffer_length)\n+{\n+ memcpy(m_last[1], buffer, 0x80); // Save current buffer\n+ memcpy(buffer, m_last[0], 0x80); // Load previous buffer\n+ memcpy(m_last[0], m_last[1], 0x80); // Update history\n+\n+ m_lastptr[1] = *buffer_length; // Swap lengths\n+ *buffer_length = m_lastptr[0];\n+ m_lastptr[0] = m_lastptr[1];\n+}\n+\n+int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length)\n+{\n+ const auto& serial_interface = m_system.GetSerialInterface();\n+ u32 buffer_length = ConvertSILengthField(serial_interface.GetInLength());\n+\n+ // Debug logging\n+ ISIDevice::RunBuffer(buffer, buffer_length);\n+\n+ u32 buffer_position = 0;\n+ while (buffer_position < buffer_length)\n+ {\n+ BaseBoardCommand command = static_cast<BaseBoardCommand>(buffer[buffer_position]);\n+ buffer_position++;\n+\n+ switch (command)\n+ {\n+ case BaseBoardCommand::GCAM_Reset: // Returns ID and dip switches\n+ {\n+ const u32 id = Common::swap32(SI_AM_BASEBOARD | 0x100);\n+ std::memcpy(buffer, &id, sizeof(id));\n+ return sizeof(id);\n+ }\n+ break;\n+ case BaseBoardCommand::GCAM_Command:\n+ {\n+ u32 checksum = 0;\n+ for (u32 i = 0; i < buffer_length; ++i)\n+ checksum += buffer[i];\n+\n+ std::array<u8, 0x80> data_out{};\n+ u32 data_offset = 0;\n+\n+ static u32 dip_switch_1 = 0xFE;\n+ static u32 dip_switch_0 = 0xFF;\n+\n+ data_out[data_offset++] = 1;\n+ data_out[data_offset++] = 1;\n+\n+ u8* data_in = buffer + 2;\n+ u8* const data_in_end = buffer + buffer[buffer_position] + 2;\n+\n+ // Helper to check that iterating over data n times is safe,\n+ // i.e. *data++ at most lead to data.end()\n+ auto validate_data_in_out = [&](u32 n_in, u32 n_out, std::string_view command) -> bool {\n+ if (data_in + n_in > data_in_end)\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: data_in overflow in {}", command);\n+ else if (std::size_t{data_offset} + n_out > data_out.size())', 'path': 'Source/Core/Core/HW/SI/SI_DeviceAMBaseboard.cpp', 'position': 245, 'original_position': 245, 'commit_id': '510fec88be973bc870021f2d0347593cdc285864', 'user': {'login': 'JosJuice', 'id': 6716818, 'node_id': 'MDQ6VXNlcjY3MTY4MTg=', 'avatar_url': 'https://avatars.githubusercontent.com/u/6716818?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/JosJuice', 'html_url': 'https://github.com/JosJuice', 'followers_url': 'https://api.github.com/users/JosJuice/followers', 'following_url': 'https://api.github.com/users/JosJuice/following{/other_user}', 'gists_url': 'https://api.github.com/users/JosJuice/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/JosJuice/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/JosJuice/subscriptions', 'organizations_url': 'https://api.github.com/users/JosJuice/orgs', 'repos_url': 'https://api.github.com/users/JosJuice/repos', 'events_url': 'https://api.github.com/users/JosJuice/events{/privacy}', 'received_events_url': 'https://api.github.com/users/JosJuice/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'body': '```suggestion\r\n else if (u64{data_offset} + n_out > data_out.size())\r\n```', 'created_at': '2026-01-31T13:26:06Z', 'updated_at': '2026-01-31T13:33:12Z', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/13844#discussion_r2749561110', 'pull_request_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13844', 'author_association': 'MEMBER', '_links': {'self': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2749561110'}, 'html': {'href': 'https://github.com/dolphin-emu/dolphin/pull/13844#discussion_r2749561110'}, 'pull_request': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13844'}}, 'original_commit_id': '510fec88be973bc870021f2d0347593cdc285864', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2749561110/reactions', 'total_count': 0, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 0, 'confused': 0, 'heart': 0, 'rocket': 0, 'eyes': 0}}, {'id': 2749561611, 'node_id': 'PRRC_kwDOALCn2M6j4vsL', 'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2749561611', 'pull_request_review_id': 3732508353, 'diff_hunk': '@@ -0,0 +1,2682 @@\n+// Copyright 2025 Dolphin Emulator Project\n+// SPDX-License-Identifier: GPL-2.0-or-later\n+\n+#include "Core/HW/SI/SI_DeviceAMBaseboard.h"\n+\n+#include <algorithm>\n+#include <numeric>\n+#include <string>\n+\n+#include <fmt/format.h>\n+\n+#include "Common/Buffer.h"\n+#include "Common/CommonTypes.h"\n+#include "Common/FileUtil.h"\n+#include "Common/IOFile.h"\n+#include "Common/Logging/Log.h"\n+#include "Common/MsgHandler.h"\n+#include "Common/Swap.h"\n+\n+#include "Core/Boot/Boot.h"\n+#include "Core/BootManager.h"\n+#include "Core/Config/MainSettings.h"\n+#include "Core/ConfigManager.h"\n+#include "Core/Core.h"\n+#include "Core/CoreTiming.h"\n+#include "Core/HW/DVD/AMMediaboard.h"\n+#include "Core/HW/DVD/DVDInterface.h"\n+#include "Core/HW/EXI/EXI.h"\n+#include "Core/HW/GCPad.h"\n+#include "Core/HW/MMIO.h"\n+#include "Core/HW/Memmap.h"\n+#include "Core/HW/ProcessorInterface.h"\n+#include "Core/HW/SI/SI.h"\n+#include "Core/HW/SI/SI_Device.h"\n+#include "Core/HW/SI/SI_DeviceGCController.h"\n+#include "Core/HW/SystemTimers.h"\n+#include "Core/Movie.h"\n+#include "Core/NetPlayProto.h"\n+#include "Core/System.h"\n+\n+#include "InputCommon/GCPadStatus.h"\n+\n+namespace SerialInterface\n+{\n+void JVSIOMessage::Start(int node)\n+{\n+ m_last_start = m_pointer;\n+ const u8 header[3] = {0xE0, (u8)node, 0};\n+ m_checksum = 0;\n+ AddData(header, 3, 1);\n+}\n+\n+void JVSIOMessage::AddData(const u8* dst, size_t len, int sync = 0)\n+{\n+ if (m_pointer + len >= sizeof(m_message))\n+ {\n+ PanicAlertFmt("JVSIOMessage overrun!");\n+ return;\n+ }\n+\n+ while (len--)\n+ {\n+ const u8 c = *dst++;\n+ if (!sync && ((c == 0xE0) || (c == 0xD0)))\n+ {\n+ if (m_pointer + 2 > sizeof(m_message))\n+ {\n+ PanicAlertFmt("JVSIOMessage overrun!");\n+ break;\n+ }\n+ m_message[m_pointer++] = 0xD0;\n+ m_message[m_pointer++] = c - 1;\n+ }\n+ else\n+ {\n+ if (m_pointer >= sizeof(m_message))\n+ {\n+ PanicAlertFmt("JVSIOMessage overrun!");\n+ break;\n+ }\n+ m_message[m_pointer++] = c;\n+ }\n+\n+ if (!sync)\n+ m_checksum += c;\n+ sync = 0;\n+ }\n+}\n+\n+void JVSIOMessage::AddData(const void* data, size_t len)\n+{\n+ AddData(static_cast<const u8*>(data), len);\n+}\n+\n+void JVSIOMessage::AddData(const char* data)\n+{\n+ AddData(data, strlen(data));\n+}\n+\n+void JVSIOMessage::AddData(u32 n)\n+{\n+ const u8 cs = n;\n+ AddData(&cs, 1);\n+}\n+\n+void JVSIOMessage::End()\n+{\n+ const u32 len = m_pointer - m_last_start;\n+ if (m_last_start + 2 < sizeof(m_message) && len >= 3)\n+ {\n+ m_message[m_last_start + 2] = len - 2; // assuming len <0xD0\n+ AddData(m_checksum + len - 2);\n+ }\n+ else\n+ {\n+ PanicAlertFmt("JVSIOMessage: Not enough space for checksum!");\n+ }\n+}\n+\n+static constexpr u8 CheckSumXOR(const u8* data, u32 length)\n+{\n+ return std::accumulate(data, data + length, u8{}, std::bit_xor());\n+}\n+\n+static constexpr char s_cdr_program_version[] = {" Version 1.22,2003/09/19,171-8213B"};\n+static constexpr char s_cdr_boot_version[] = {" Version 1.04,2003/06/17,171-8213B"};\n+static constexpr u8 s_cdr_card_data[] = {\n+ 0x00, 0x6E, 0x00, 0x00, 0x01, 0x00, 0x00, 0x06, 0x00, 0x00, 0x07, 0x00, 0x00, 0x0B,\n+ 0x00, 0x00, 0x0E, 0x00, 0x00, 0x10, 0x00, 0x00, 0x17, 0x00, 0x00, 0x19, 0x00, 0x00,\n+ 0x1A, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x20, 0x00,\n+ 0x00, 0x22, 0x00, 0x00, 0x23, 0x00, 0x00, 0x24, 0x00, 0x00, 0x27, 0x00, 0x00, 0x28,\n+ 0x00, 0x00, 0x2C, 0x00, 0x00, 0x2F, 0x00, 0x00, 0x34, 0x00, 0x00, 0x35, 0x00, 0x00,\n+ 0x37, 0x00, 0x00, 0x38, 0x00, 0x00, 0x39, 0x00, 0x00, 0x3D, 0x00};\n+\n+const constexpr u8 s_region_flags[] = "\\x00\\x00\\x30\\x00"\n+ // "\\x01\\xfe\\x00\\x00" // JAPAN\n+ "\\x02\\xfd\\x00\\x00" // USA\n+ //"\\x03\\xfc\\x00\\x00" // export\n+ "\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff";\n+// AM-Baseboard device on SI\n+CSIDevice_AMBaseboard::CSIDevice_AMBaseboard(Core::System& system, SIDevices device,\n+ int device_number)\n+ : ISIDevice(system, device, device_number)\n+{\n+ // Card ID\n+ m_ic_card_data[0x20] = 0x95;\n+ m_ic_card_data[0x21] = 0x71;\n+\n+ if (AMMediaboard::GetGameType() == KeyOfAvalon)\n+ {\n+ m_ic_card_data[0x22] = 0x26;\n+ m_ic_card_data[0x23] = 0x40;\n+ }\n+ else if (AMMediaboard::GetGameType() == VirtuaStriker4)\n+ {\n+ m_ic_card_data[0x22] = 0x44;\n+ m_ic_card_data[0x23] = 0x00;\n+ }\n+\n+ // Use count\n+ m_ic_card_data[0x28] = 0xFF;\n+ m_ic_card_data[0x29] = 0xFF;\n+}\n+\n+constexpr u32 SI_XFER_LENGTH_MASK = 0x7f;\n+\n+// Translate [0,1,2,...,126,127] to [128,1,2,...,126,127]\n+constexpr s32 ConvertSILengthField(u32 field)\n+{\n+ return ((field - 1) & SI_XFER_LENGTH_MASK) + 1;\n+}\n+\n+void CSIDevice_AMBaseboard::ICCardSendReply(ICCommand* iccommand, u8* buffer, u32* length)\n+{\n+ iccommand->status = Common::swap16(iccommand->status);\n+\n+ const auto iccommand_data = reinterpret_cast<const u8*>(iccommand);\n+ const u8 crc = CheckSumXOR(iccommand_data + 2, iccommand->pktlen - 1);\n+\n+ for (u32 i = 0; i < iccommand->pktlen + 1; ++i)\n+ {\n+ buffer[(*length)++] = iccommand_data[i];\n+ }\n+\n+ buffer[(*length)++] = crc;\n+}\n+\n+void CSIDevice_AMBaseboard::SwapBuffers(u8* buffer, u32* buffer_length)\n+{\n+ memcpy(m_last[1], buffer, 0x80); // Save current buffer\n+ memcpy(buffer, m_last[0], 0x80); // Load previous buffer\n+ memcpy(m_last[0], m_last[1], 0x80); // Update history\n+\n+ m_lastptr[1] = *buffer_length; // Swap lengths\n+ *buffer_length = m_lastptr[0];\n+ m_lastptr[0] = m_lastptr[1];\n+}\n+\n+int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length)\n+{\n+ const auto& serial_interface = m_system.GetSerialInterface();\n+ u32 buffer_length = ConvertSILengthField(serial_interface.GetInLength());\n+\n+ // Debug logging\n+ ISIDevice::RunBuffer(buffer, buffer_length);\n+\n+ u32 buffer_position = 0;\n+ while (buffer_position < buffer_length)\n+ {\n+ BaseBoardCommand command = static_cast<BaseBoardCommand>(buffer[buffer_position]);\n+ buffer_position++;\n+\n+ switch (command)\n+ {\n+ case BaseBoardCommand::GCAM_Reset: // Returns ID and dip switches\n+ {\n+ const u32 id = Common::swap32(SI_AM_BASEBOARD | 0x100);\n+ std::memcpy(buffer, &id, sizeof(id));\n+ return sizeof(id);\n+ }\n+ break;\n+ case BaseBoardCommand::GCAM_Command:\n+ {\n+ u32 checksum = 0;\n+ for (u32 i = 0; i < buffer_length; ++i)\n+ checksum += buffer[i];\n+\n+ std::array<u8, 0x80> data_out{};\n+ u32 data_offset = 0;\n+\n+ static u32 dip_switch_1 = 0xFE;\n+ static u32 dip_switch_0 = 0xFF;\n+\n+ data_out[data_offset++] = 1;\n+ data_out[data_offset++] = 1;\n+\n+ u8* data_in = buffer + 2;\n+ u8* const data_in_end = buffer + buffer[buffer_position] + 2;\n+\n+ // Helper to check that iterating over data n times is safe,\n+ // i.e. *data++ at most lead to data.end()\n+ auto validate_data_in_out = [&](u32 n_in, u32 n_out, std::string_view command) -> bool {\n+ if (data_in + n_in > data_in_end)\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: data_in overflow in {}", command);\n+ else if (std::size_t{data_offset} + n_out > data_out.size())\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: data_out overflow in {}", command);\n+ else\n+ return true;\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "Overflow details:\\n"\n+ " - data_in(begin={}, current={}, end={}, n_in={})\\n"\n+ " - data_out(offset={}, size={}, n_out={})\\n"\n+ " - buffer(position={}, length={})",\n+ fmt::ptr(buffer + 2), fmt::ptr(data_in), fmt::ptr(data_in_end), n_in,\n+ data_offset, data_out.size(), n_out, buffer_position, buffer_length);\n+ data_in = data_in_end;\n+ return false;\n+ };\n+\n+ while (data_in < data_in_end)\n+ {\n+ const u32 gcam_command = *data_in++;\n+ switch (GCAMCommand(gcam_command))\n+ {\n+ case GCAMCommand::StatusSwitches:\n+ {\n+ if (!validate_data_in_out(1, 4, "StatusSwitches"))\n+ break;\n+\n+ const u8 status = *data_in++;\n+ DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x10, {:02x} (READ STATUS&SWITCHES)",\n+ status);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x2;\n+\n+ // We read Test/Service from the JVS-I/O SwitchesInput instead\n+ //\n+ // const GCPadStatus pad_status = Pad::GetStatus(ISIDevice::m_device_number);\n+ // baseboard test/service switches\n+ // if (pad_status.button & PAD_BUTTON_Y)\t// Test\n+ // dip_switch_0 &= ~0x80;\n+ // if (pad_status.button & PAD_BUTTON_X)\t// Service\n+ // dip_switch_0 &= ~0x40;\n+\n+ // Horizontal Scanning Frequency switch\n+ // Required for F-Zero AX booting via Sega Boot\n+ if (AMMediaboard::GetGameType() == FZeroAX ||\n+ AMMediaboard::GetGameType() == FZeroAXMonster)\n+ {\n+ dip_switch_0 &= ~0x20;\n+ }\n+\n+ // Disable camera in MKGP1/2\n+ if (AMMediaboard::GetGameType() == MarioKartGP ||\n+ AMMediaboard::GetGameType() == MarioKartGP2)\n+ {\n+ dip_switch_0 &= ~0x10;\n+ }\n+\n+ data_out[data_offset++] = dip_switch_0;\n+ data_out[data_offset++] = dip_switch_1;\n+ break;\n+ }\n+ case GCAMCommand::SerialNumber:\n+ {\n+ if (!validate_data_in_out(1, 18, "SerialNumber"))\n+ break;\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x11, {:02x} (READ SERIAL NR)",\n+ *data_in);\n+ data_in++;\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 16;\n+ memcpy(data_out.data() + data_offset, "AADE-01B98394904", 16);\n+\n+ data_offset += 16;\n+ break;\n+ }\n+ case GCAMCommand::Unknown_12:\n+ if (!validate_data_in_out(2, 2, "Unknown_12"))\n+ break;\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x12, {:02x} {:02x}", data_in[0],\n+ data_in[1]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x00;\n+\n+ data_in += 2;\n+ break;\n+ case GCAMCommand::Unknown_14:\n+ if (!validate_data_in_out(2, 2, "Unknown_14"))\n+ break;\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x14, {:02x} {:02x}", data_in[0],\n+ data_in[1]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x00;\n+\n+ data_in += 2;\n+ break;\n+ case GCAMCommand::FirmVersion:\n+ if (!validate_data_in_out(1, 4, "FirmVersion"))\n+ break;\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x15, {:02x} (READ FIRM VERSION)",\n+ *data_in);\n+ data_in++;\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x02;\n+ // 00.26\n+ data_out[data_offset++] = 0x00;\n+ data_out[data_offset++] = 0x26;\n+ break;\n+ case GCAMCommand::FPGAVersion:\n+ if (!validate_data_in_out(1, 4, "FPGAVersion"))\n+ break;\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x16, {:02x} (READ FPGA VERSION)",\n+ *data_in);\n+ data_in++;\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x02;\n+ // 07.06\n+ data_out[data_offset++] = 0x07;\n+ data_out[data_offset++] = 0x06;\n+ break;\n+ case GCAMCommand::RegionSettings:\n+ {\n+ if (!validate_data_in_out(5, 0x16, "RegionSettings"))\n+ break;\n+\n+ // Used by SegaBoot for region checks (dev mode skips this check)\n+ // In some games this also controls the displayed language\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x1F, {:02x} {:02x} {:02x} {:02x} {:02x} (REGION)",\n+ data_in[0], data_in[1], data_in[2], data_in[3], data_in[4]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x14;\n+\n+ for (int i = 0; i < 0x14; ++i)\n+ data_out[data_offset++] = s_region_flags[i];\n+\n+ data_in += 5;\n+ }\n+ break;\n+ // No reply\n+ // Note: Always sends three bytes even though size is set to two\n+ case GCAMCommand::Unknown_21:\n+ {\n+ if (!validate_data_in_out(4, 0, "Unknown_21"))\n+ break;\n+\n+ DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x21, {:02x}, {:02x}, {:02x}, {:02x}",\n+ data_in[0], data_in[1], data_in[2], data_in[3]);\n+ data_in += 4;\n+ }\n+ break;\n+ // No reply\n+ // Note: Always sends six bytes\n+ case GCAMCommand::Unknown_22:\n+ {\n+ if (!validate_data_in_out(7, 0, "Unknown_22"))\n+ break;\n+\n+ DEBUG_LOG_FMT(\n+ SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x22, {:02x}, {:02x}, {:02x}, {:02x}, {:02x}, {:02x}, {:02x}",\n+ data_in[0], data_in[1], data_in[2], data_in[3], data_in[4], data_in[5], data_in[6]);\n+\n+ const u32 in_size = data_in[0] + 1;\n+ if (!validate_data_in_out(in_size, 0, "Unknown_22"))\n+ break;\n+ data_in += in_size;\n+ }\n+ break;\n+ case GCAMCommand::Unknown_23:\n+ if (!validate_data_in_out(2, 2, "Unknown_23"))\n+ break;\n+\n+ DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x23, {:02x} {:02x}", data_in[0],\n+ data_in[1]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x00;\n+\n+ data_in += 2;\n+ break;\n+ case GCAMCommand::Unknown_24:\n+ if (!validate_data_in_out(2, 2, "Unknown_24"))\n+ break;\n+\n+ DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x24, {:02x} {:02x}", data_in[0],\n+ data_in[1]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x00;\n+\n+ data_in += 2;\n+ break;\n+ case GCAMCommand::SerialA:\n+ {\n+ if (!validate_data_in_out(1, 0, "SerialA"))\n+ break;\n+\n+ const u32 length = *data_in++;\n+ if (length)\n+ {\n+ if (!validate_data_in_out(length, 0, "SerialA"))\n+ break;\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x31, length=0x{:02x}, hexdump:\\n{}",\n+ length, HexDump(data_in, length));\n+\n+ // Serial - Wheel\n+ if (AMMediaboard::GetGameType() == MarioKartGP ||\n+ AMMediaboard::GetGameType() == MarioKartGP2)\n+ {\n+ if (!validate_data_in_out(10, 2, "SerialA (Wheel)"))\n+ break;\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x31, (WHEEL) {:02x}{:02x} {:02x}{:02x} {:02x} {:02x} "\n+ "{:02x} {:02x} {:02x} {:02x}",\n+ data_in[0], data_in[1], data_in[2], data_in[3], data_in[4], data_in[5],\n+ data_in[6], data_in[7], data_in[8], data_in[9]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x03;\n+\n+ switch (m_wheel_init)\n+ {\n+ case 0:\n+ if (!validate_data_in_out(0, 3, "SerialA (Wheel)"))\n+ break;\n+ data_out[data_offset++] = \'E\'; // Error\n+ data_out[data_offset++] = \'0\';\n+ data_out[data_offset++] = \'0\';\n+ m_wheel_init++;\n+ break;\n+ case 1:\n+ if (!validate_data_in_out(0, 3, "SerialA (Wheel)"))\n+ break;\n+ data_out[data_offset++] = \'C\'; // Power Off\n+ data_out[data_offset++] = \'0\';\n+ data_out[data_offset++] = \'6\';\n+ // Only turn on when a wheel is connected\n+ if (serial_interface.GetDeviceType(1) == SerialInterface::SIDEVICE_GC_STEERING)\n+ {\n+ m_wheel_init++;\n+ }\n+ break;\n+ case 2:\n+ if (!validate_data_in_out(0, 3, "SerialA (Wheel)"))\n+ break;\n+ data_out[data_offset++] = \'C\'; // Power On\n+ data_out[data_offset++] = \'0\';\n+ data_out[data_offset++] = \'1\';\n+ break;\n+ default:\n+ break;\n+ }\n+\n+ // u16 CenteringForce= ptr(6);\n+ // u16 FrictionForce = ptr(8);\n+ // u16 Roll = ptr(10);\n+ if (!validate_data_in_out(length, 0, "SerialA (Wheel)"))\n+ break;\n+ data_in += length;\n+ break;\n+ }\n+\n+ // Serial - Unknown\n+ if (AMMediaboard::GetGameType() == GekitouProYakyuu)\n+ {\n+ if (!validate_data_in_out(sizeof(u32), 0, "SerialA (Unknown)"))\n+ break;\n+ const u32 serial_command = Common::BitCastPtr<u32>(data_in);\n+\n+ if (serial_command == 0x00001000)\n+ {\n+ if (!validate_data_in_out(0, 5, "SerialA (Unknown)"))\n+ break;\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x03;\n+ data_out[data_offset++] = 1;\n+ data_out[data_offset++] = 2;\n+ data_out[data_offset++] = 3;\n+ }\n+\n+ if (!validate_data_in_out(length, 0, "SerialA (Unknown)"))\n+ break;\n+ data_in += length;\n+ break;\n+ }\n+\n+ // Serial IC-CARD / Serial Deck Reader\n+ if (AMMediaboard::GetGameType() == VirtuaStriker4 ||\n+ AMMediaboard::GetGameType() == VirtuaStriker4_2006 ||\n+ AMMediaboard::GetGameType() == KeyOfAvalon)\n+ {\n+ if (!validate_data_in_out(2, 0, "SerialA (IC-CARD)"))\n+ break;\n+ u32 serial_command = data_in[1];\n+\n+ ICCommand icco;\n+\n+ // Set default reply\n+ icco.pktcmd = gcam_command;\n+ icco.pktlen = 7;\n+ icco.fixed = 0x10;\n+ icco.command = serial_command;\n+ icco.flag = 0;\n+ icco.length = 2;\n+ icco.status = 0;\n+ icco.extlen = 0;\n+\n+ // Check for rest of data from the write pages command\n+ if (m_ic_write_size && m_ic_write_offset)\n+ {\n+ const u32 size = data_in[1];\n+\n+ if (!validate_data_in_out(size + 2, 0, "SerialA (IC-CARD)"))\n+ break;\n+ DEBUG_LOG_FMT(SERIALINTERFACE_CARD, "Command: {}", HexDump(data_in, size + 2));\n+\n+ INFO_LOG_FMT(\n+ SERIALINTERFACE_CARD,\n+ "GC-AM: Command 25 (IC-CARD) Write Pages: Off:{:x} Size:{:x} PSize:{:x}",\n+ m_ic_write_offset, m_ic_write_size, size);\n+\n+ if (std::size_t{m_ic_write_offset} + size > sizeof(m_ic_write_buffer))\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 25 (IC-CARD) m_ic_write_buffer overflow:\\n"\n+ " - m_ic_write_buffer(offset={}, size={})\\n"\n+ " - size={}\\n",\n+ m_ic_write_offset, sizeof(m_ic_write_buffer), size);\n+ data_in = data_in_end;\n+ break;\n+ }\n+ memcpy(m_ic_write_buffer + m_ic_write_offset, data_in + 2, size);\n+\n+ m_ic_write_offset += size;\n+\n+ if (m_ic_write_offset > m_ic_write_size)\n+ {\n+ m_ic_write_offset = 0;\n+\n+ const u16 page = m_ic_write_buffer[5];\n+ const u16 count = m_ic_write_buffer[7];\n+\n+ if ((page * 8 + count * 8) > sizeof(m_ic_card_data) ||\n+ (10 + count * 8) > sizeof(m_ic_write_buffer))\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 25 (IC-CARD) Write Pages overflow:\\n"\n+ " - m_ic_card_data(offset={}, size={})\\n"\n+ " - m_ic_write_buffer(offset={}, size={})\\n"\n+ " - size={}, page={}, count={}\\n",\n+ page * 8, sizeof(m_ic_card_data), 10, sizeof(m_ic_write_buffer),\n+ count * 8, page, count);\n+ data_in = data_in_end;\n+ break;\n+ }\n+ memcpy(m_ic_card_data + page * 8, m_ic_write_buffer + 10, count * 8);\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 25 (IC-CARD) Write Pages:{} Count:{}({:x})", page,\n+ count, size);\n+\n+ icco.command = WritePages;\n+\n+ if (!validate_data_in_out(0, icco.pktlen + 2, "SerialA (IC-CARD)"))\n+ break;\n+ ICCardSendReply(&icco, data_out.data(), &data_offset);\n+ }\n+ if (!validate_data_in_out(length, 0, "SerialA (IC-CARD)"))\n+ break;\n+ data_in += length;\n+ break;\n+ }\n+\n+ switch (ICCARDCommand(serial_command))\n+ {\n+ case ICCARDCommand::GetStatus:\n+ icco.status = m_ic_card_state;\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (IC-CARD) Get Status:{:02x}", m_ic_card_state);\n+ break;\n+ case ICCARDCommand::SetBaudrate:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (IC-CARD) Set Baudrate");\n+ break;\n+ case ICCARDCommand::FieldOn:\n+ m_ic_card_state |= 0x10;\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (IC-CARD) Field On");\n+ break;\n+ case ICCARDCommand::InsertCheck:\n+ icco.status = m_ic_card_status;\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (IC-CARD) Insert Check:{:02x}", m_ic_card_status);\n+ break;\n+ case ICCARDCommand::AntiCollision:\n+ icco.extlen = 8;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ // Card ID\n+ icco.extdata[0] = 0x00;\n+ icco.extdata[1] = 0x00;\n+ icco.extdata[2] = 0x54;\n+ icco.extdata[3] = 0x4D;\n+ icco.extdata[4] = 0x50;\n+ icco.extdata[5] = 0x00;\n+ icco.extdata[6] = 0x00;\n+ icco.extdata[7] = 0x00;\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (IC-CARD) Anti Collision");\n+ break;\n+ case ICCARDCommand::SelectCard:\n+ icco.extlen = 8;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ // Session\n+ icco.extdata[0] = 0x00;\n+ icco.extdata[1] = m_ic_card_session;\n+ icco.extdata[2] = 0x00;\n+ icco.extdata[3] = 0x00;\n+ icco.extdata[4] = 0x00;\n+ icco.extdata[5] = 0x00;\n+ icco.extdata[6] = 0x00;\n+ icco.extdata[7] = 0x00;\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (IC-CARD) Select Card:{}",\n+ m_ic_card_session);\n+ break;\n+ case ICCARDCommand::ReadPage:\n+ case ICCARDCommand::ReadUseCount:\n+ {\n+ if (!validate_data_in_out(8, 0, "SerialA (IC-CARD)"))\n+ break;\n+ const u16 page = Common::swap16(data_in + 6) & 0xFF; // 255 is max page\n+\n+ icco.extlen = 8;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ memcpy(icco.extdata, m_ic_card_data + page * 8, 8);\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 31 (IC-CARD) Read Page:{}",\n+ page);\n+ break;\n+ }\n+ case ICCARDCommand::WritePage:\n+ {\n+ if (!validate_data_in_out(10, 0, "SerialA (IC-CARD)"))\n+ break;\n+ const u16 page = Common::swap16(data_in + 8) & 0xFF; // 255 is max page\n+\n+ // Write only one page\n+ if (page == 4)\n+ {\n+ icco.status = 0x80;\n+ }\n+ else\n+ {\n+ if (!validate_data_in_out(18, 0, "SerialA (IC-CARD)"))\n+ break;\n+ memcpy(m_ic_card_data + page * 8, data_in + 10, 8);\n+ }\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (IC-CARD) Write Page:{}",\n+ page);\n+ break;\n+ }\n+ case ICCARDCommand::DecreaseUseCount:\n+ {\n+ if (!validate_data_in_out(8, 0, "SerialA (IC-CARD)"))\n+ break;\n+ const u16 page = Common::swap16(data_in + 6) & 0xFF; // 255 is max page\n+\n+ icco.extlen = 2;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ auto ic_card_data = Common::BitCastPtr<u16>(m_ic_card_data + 0x28);\n+ ic_card_data = ic_card_data - 1;\n+\n+ // Counter\n+ icco.extdata[0] = m_ic_card_data[0x28];\n+ icco.extdata[1] = m_ic_card_data[0x29];\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 31 (IC-CARD) Decrease Use Count:{}", page);\n+ break;\n+ }\n+ case ICCARDCommand::ReadPages:\n+ {\n+ if (!validate_data_in_out(10, 0, "SerialA (IC-CARD)"))\n+ break;\n+ const u16 page = Common::swap16(data_in + 6) & 0xFF; // 255 is max page\n+ const u16 count = Common::swap16(data_in + 8);\n+\n+ const u32 offs = page * 8;\n+ u32 cnt = count * 8;\n+\n+ // Limit read size to not overwrite the reply buffer\n+ const std::size_t reply_buffer_size = sizeof(icco.extdata) - 1;\n+ if (data_offset > reply_buffer_size)\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 31 (IC-CARD) Read Pages overflow:"\n+ " offset={} > buffer_size={}",\n+ data_offset, reply_buffer_size);\n+ data_in = data_in_end;\n+ break;\n+ }\n+ if (cnt > reply_buffer_size - data_offset)\n+ {\n+ cnt = 5 * 8;\n+ }\n+\n+ icco.extlen = cnt;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ if (offs + cnt > sizeof(icco.extdata))\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 31 (IC-CARD) Read Pages overflow:"\n+ " offset={} + count={} > buffer_size={}",\n+ offs, cnt, sizeof(icco.extdata));\n+ data_in = data_in_end;\n+ break;\n+ }\n+ memcpy(icco.extdata, m_ic_card_data + offs, cnt);\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 31 (IC-CARD) Read Pages:{} Count:{}", page, count);\n+ break;\n+ }\n+ case ICCARDCommand::WritePages:\n+ {\n+ if (!validate_data_in_out(10, 0, "SerialA (IC-CARD)"))\n+ break;\n+ const u16 pksize = length;\n+ const u16 size = Common::swap16(data_in + 2);\n+ const u16 page = Common::swap16(data_in + 6) & 0xFF; // 255 is max page\n+ const u16 count = Common::swap16(data_in + 8);\n+\n+ // We got a complete packet\n+ if (pksize - 5 == size)\n+ {\n+ if (page == 4) // Read Only Page, must return error\n+ {\n+ icco.status = 0x80;\n+ }\n+ else\n+ {\n+ if (page * 8 + count * 8 > sizeof(m_ic_card_data))\n+ {\n+ ERROR_LOG_FMT(\n+ SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (IC-CARD) Data overflow: Pages:{} Count:{}({:x})",\n+ page, count, size);\n+ }\n+ else\n+ {\n+ if (!validate_data_in_out(13 + count * 8, 0, "SerialA (IC-CARD)"))\n+ break;\n+ memcpy(m_ic_card_data + page * 8, data_in + 13, count * 8);\n+ }\n+ }\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (IC-CARD) Write Pages:{} Count:{}({:x})", page,\n+ count, size);\n+ }\n+ // VirtuaStriker 4 splits the writes over multiple packets\n+ else\n+ {\n+ if (!validate_data_in_out(2 + pksize, 0, "SerialA (IC-CARD)"))\n+ break;\n+ memcpy(m_ic_write_buffer, data_in + 2, pksize);\n+ m_ic_write_offset += pksize;\n+ m_ic_write_size = size;\n+ }\n+ break;\n+ }\n+ default:\n+ // Handle Deck Reader commands\n+ if (!validate_data_in_out(1, 0, "SerialA (DECK READER)"))\n+ break;\n+ serial_command = data_in[0];\n+ icco.command = serial_command;\n+ icco.flag = 0;\n+ switch (CDReaderCommand(serial_command))\n+ {\n+ case CDReaderCommand::ProgramVersion:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Program Version");\n+\n+ icco.extlen = (u32)strlen(s_cdr_program_version);\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ memcpy(icco.extdata, s_cdr_program_version, icco.extlen);\n+ break;\n+ case CDReaderCommand::BootVersion:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Boot Version");\n+\n+ icco.extlen = (u32)strlen(s_cdr_boot_version);\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ memcpy(icco.extdata, s_cdr_boot_version, icco.extlen);\n+ break;\n+ case CDReaderCommand::ShutterGet:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Shutter Get");\n+\n+ icco.extlen = 4;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ icco.extdata[0] = 0;\n+ icco.extdata[1] = 0;\n+ icco.extdata[2] = 0;\n+ icco.extdata[3] = 0;\n+ break;\n+ case CDReaderCommand::CameraCheck:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Camera Check");\n+\n+ icco.extlen = 6;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ icco.extdata[0] = 0x23;\n+ icco.extdata[1] = 0x28;\n+ icco.extdata[2] = 0x45;\n+ icco.extdata[3] = 0x29;\n+ icco.extdata[4] = 0x45;\n+ icco.extdata[5] = 0x29;\n+ break;\n+ case CDReaderCommand::ProgramChecksum:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Program Checksum");\n+\n+ icco.extlen = 4;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ icco.extdata[0] = 0x23;\n+ icco.extdata[1] = 0x28;\n+ icco.extdata[2] = 0x45;\n+ icco.extdata[3] = 0x29;\n+ break;\n+ case CDReaderCommand::BootChecksum:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Boot Checksum");\n+\n+ icco.extlen = 4;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ icco.extdata[0] = 0x23;\n+ icco.extdata[1] = 0x28;\n+ icco.extdata[2] = 0x45;\n+ icco.extdata[3] = 0x29;\n+ break;\n+ case CDReaderCommand::SelfTest:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (DECK READER) Self Test");\n+ icco.flag = 0x00;\n+ break;\n+ case CDReaderCommand::SensLock:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (DECK READER) Sens Lock");\n+ icco.flag = 0x01;\n+ break;\n+ case CDReaderCommand::SensCard:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (DECK READER) Sens Card");\n+ break;\n+ case CDReaderCommand::ShutterCard:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Shutter Card");\n+ break;\n+ case CDReaderCommand::ReadCard:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (DECK READER) Read Card");\n+\n+ icco.fixed = 0xAA;\n+ icco.flag = 0xAA;\n+ icco.extlen = sizeof(s_cdr_card_data);\n+ icco.length = 0x72;\n+ icco.status = Common::swap16(icco.extlen);\n+\n+ icco.pktlen += icco.extlen;\n+\n+ memcpy(icco.extdata, s_cdr_card_data, sizeof(s_cdr_card_data));\n+\n+ break;\n+ default:\n+ if (!validate_data_in_out(14, 0, "SerialA (DECK READER)"))\n+ break;\n+ WARN_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (IC-Card) {:02x} {:02x} {:02x} {:02x} {:02x} "\n+ "{:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x}",\n+ data_in[2], data_in[3], data_in[4], data_in[5], data_in[6],\n+ data_in[7], data_in[8], data_in[9], data_in[10], data_in[11],\n+ data_in[12], data_in[13]);\n+ break;\n+ }\n+ break;\n+ }\n+\n+ if (!validate_data_in_out(0, icco.pktlen + 2, "SerialA (IC-CARD)"))\n+ break;\n+ ICCardSendReply(&icco, data_out.data(), &data_offset);\n+\n+ if (!validate_data_in_out(2, 0, "SerialA (IC-CARD)"))\n+ break;\n+ data_in += length;\n+ break;\n+ }\n+ }\n+\n+ u32 command_offset = 0;\n+ while (command_offset < length)\n+ {\n+ // All commands are OR\'d with 0x80\n+ // Last byte is checksum which we don\'t care about\n+ if (!validate_data_in_out(command_offset + 4, 0, "SerialA"))\n+ break;\n+ const u32 serial_command = Common::swap32(data_in + command_offset) ^ 0x80000000;\n+\n+ if (AMMediaboard::GetGameType() == FZeroAX ||\n+ AMMediaboard::GetGameType() == FZeroAXMonster)\n+ {\n+ INFO_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x31 (MOTOR) Length:{:02x} Command:{:04x}({:02x})",\n+ length, (serial_command >> 8) & 0xFFFF, serial_command >> 24);\n+ }\n+ else\n+ {\n+ INFO_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x31 (SERIAL) Command:{:06x}",\n+ serial_command);\n+\n+ if (serial_command == 0x801000)\n+ {\n+ if (!validate_data_in_out(0, 4, "SerialA"))\n+ break;\n+ data_out[data_offset++] = 0x31;\n+ data_out[data_offset++] = 0x02;\n+ data_out[data_offset++] = 0xFF;\n+ data_out[data_offset++] = 0x01;\n+ }\n+ }\n+\n+ command_offset += sizeof(u32);\n+\n+ if (AMMediaboard::GetGameType() == FZeroAX ||\n+ AMMediaboard::GetGameType() == FZeroAXMonster)\n+ {\n+ if (command_offset + 5 >= std::size(m_motor_reply))\n+ {\n+ ERROR_LOG_FMT(\n+ SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x31 (MOTOR) overflow: offset={} >= motor_reply_size={}",\n+ command_offset + 5, std::size(m_motor_reply));\n+ data_in = data_in_end;\n+ break;\n+ }\n+\n+ // Status\n+ m_motor_reply[command_offset + 2] = 0;\n+ m_motor_reply[command_offset + 3] = 0;\n+\n+ // Error\n+ m_motor_reply[command_offset + 4] = 0;\n+\n+ switch (serial_command >> 24)\n+ {\n+ case 0:\n+ case 1: // Set Maximum?\n+ case 2:\n+ break;\n+\n+ // 0x00-0x40: left\n+ // 0x40-0x80: right\n+ case 4: // Move Steering Wheel\n+ // Left\n+ if (serial_command & 0x010000)\n+ {\n+ m_motor_force_y = -((s16)serial_command & 0xFF00);\n+ }\n+ else // Right\n+ {\n+ m_motor_force_y = (serial_command - 0x4000) & 0xFF00;\n+ }\n+\n+ m_motor_force_y *= 2;\n+\n+ // FFB\n+ if (m_motor_init == 2)\n+ {\n+ if (serial_interface.GetDeviceType(1) == SerialInterface::SIDEVICE_GC_STEERING)\n+ {\n+ const GCPadStatus pad_status = Pad::GetStatus(1);\n+ if (pad_status.isConnected)\n+ {\n+ const ControlState mapped_strength = (double)(m_motor_force_y >> 8) / 127.f;\n+\n+ Pad::Rumble(1, mapped_strength);\n+ INFO_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x31 (MOTOR) mapped_strength:{}",\n+ mapped_strength);\n+ }\n+ }\n+ }\n+ break;\n+ case 6: // nice\n+ case 9:\n+ default:\n+ break;\n+ // Switch back to normal controls\n+ case 7:\n+ m_motor_init = 2;\n+ break;\n+ // Reset\n+ case 0x7F:\n+ m_motor_init = 1;\n+ memset(m_motor_reply, 0, sizeof(m_motor_reply));\n+ break;\n+ }\n+\n+ // Checksum\n+ m_motor_reply[command_offset + 5] = m_motor_reply[command_offset + 2] ^\n+ m_motor_reply[command_offset + 3] ^\n+ m_motor_reply[command_offset + 4];\n+ }\n+ }\n+\n+ if (length == 0)\n+ {\n+ if (!validate_data_in_out(length, 2, "SerialA"))\n+ break;\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x00;\n+ }\n+ else\n+ {\n+ if (m_motor_init)\n+ {\n+ // Motor\n+ m_motor_reply[0] = gcam_command;\n+ m_motor_reply[1] = length; // Same out as in size\n+\n+ const u32 reply_size = m_motor_reply[1] + 2;\n+ if (!validate_data_in_out(length, reply_size, "SerialA"))\n+ break;\n+\n+ if (reply_size > sizeof(m_motor_reply))\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command SerialA, reply_size={} too big for m_motor_reply",\n+ reply_size);\n+ data_in = data_in_end;\n+ break;\n+ }\n+ memcpy(data_out.data() + data_offset, m_motor_reply, reply_size);\n+ data_offset += reply_size;\n+ }\n+ }\n+\n+ if (!validate_data_in_out(length, 0, "SerialA"))\n+ {\n+ break;\n+ }\n+ data_in += length;\n+ break;\n+ }\n+ case GCAMCommand::SerialB:\n+ {\n+ DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 32 (CARD-Interface)");\n+ if (!validate_data_in_out(1, 0, "SerialB"))\n+ break;\n+ const u32 length = *data_in++;\n+ if (!validate_data_in_out(length, 0, "SerialB"))\n+ break;\n+ if (length)\n+ {\n+ // Send Card Reply\n+ if (length == 1 && data_in[0] == 0x05)\n+ {\n+ if (m_card_read_length)\n+ {\n+ if (!validate_data_in_out(0, 1, "SerialB"))\n+ break;\n+ data_out[data_offset++] = gcam_command;\n+ u32 read_length = m_card_read_length - m_card_read;\n+\n+ if (AMMediaboard::GetGameType() == FZeroAX)\n+ {\n+ read_length = std::min<u32>(read_length, 0x2F);\n+ }\n+\n+ if (!validate_data_in_out(0, 1, "SerialB"))\n+ break;\n+ data_out[data_offset++] = read_length; // 0x2F (max size per packet)\n+\n+ if (!validate_data_in_out(0, read_length, "SerialB"))\n+ break;\n+ if (std::size_t{m_card_read} + read_length > sizeof(m_card_read_packet))', 'path': 'Source/Core/Core/HW/SI/SI_DeviceAMBaseboard.cpp', 'position': 1161, 'original_position': 1161, 'commit_id': '510fec88be973bc870021f2d0347593cdc285864', 'user': {'login': 'JosJuice', 'id': 6716818, 'node_id': 'MDQ6VXNlcjY3MTY4MTg=', 'avatar_url': 'https://avatars.githubusercontent.com/u/6716818?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/JosJuice', 'html_url': 'https://github.com/JosJuice', 'followers_url': 'https://api.github.com/users/JosJuice/followers', 'following_url': 'https://api.github.com/users/JosJuice/following{/other_user}', 'gists_url': 'https://api.github.com/users/JosJuice/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/JosJuice/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/JosJuice/subscriptions', 'organizations_url': 'https://api.github.com/users/JosJuice/orgs', 'repos_url': 'https://api.github.com/users/JosJuice/repos', 'events_url': 'https://api.github.com/users/JosJuice/events{/privacy}', 'received_events_url': 'https://api.github.com/users/JosJuice/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'body': '```suggestion\r\n if (u64{m_card_read} + read_length > sizeof(m_card_read_packet))\r\n```', 'created_at': '2026-01-31T13:26:57Z', 'updated_at': '2026-01-31T13:33:12Z', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/13844#discussion_r2749561611', 'pull_request_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13844', 'author_association': 'MEMBER', '_links': {'self': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2749561611'}, 'html': {'href': 'https://github.com/dolphin-emu/dolphin/pull/13844#discussion_r2749561611'}, 'pull_request': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13844'}}, 'original_commit_id': '510fec88be973bc870021f2d0347593cdc285864', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2749561611/reactions', 'total_count': 0, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 0, 'confused': 0, 'heart': 0, 'rocket': 0, 'eyes': 0}}, {'id': 2749562072, 'node_id': 'PRRC_kwDOALCn2M6j4vzY', 'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2749562072', 'pull_request_review_id': 3732508353, 'diff_hunk': '@@ -0,0 +1,2682 @@\n+// Copyright 2025 Dolphin Emulator Project\n+// SPDX-License-Identifier: GPL-2.0-or-later\n+\n+#include "Core/HW/SI/SI_DeviceAMBaseboard.h"\n+\n+#include <algorithm>\n+#include <numeric>\n+#include <string>\n+\n+#include <fmt/format.h>\n+\n+#include "Common/Buffer.h"\n+#include "Common/CommonTypes.h"\n+#include "Common/FileUtil.h"\n+#include "Common/IOFile.h"\n+#include "Common/Logging/Log.h"\n+#include "Common/MsgHandler.h"\n+#include "Common/Swap.h"\n+\n+#include "Core/Boot/Boot.h"\n+#include "Core/BootManager.h"\n+#include "Core/Config/MainSettings.h"\n+#include "Core/ConfigManager.h"\n+#include "Core/Core.h"\n+#include "Core/CoreTiming.h"\n+#include "Core/HW/DVD/AMMediaboard.h"\n+#include "Core/HW/DVD/DVDInterface.h"\n+#include "Core/HW/EXI/EXI.h"\n+#include "Core/HW/GCPad.h"\n+#include "Core/HW/MMIO.h"\n+#include "Core/HW/Memmap.h"\n+#include "Core/HW/ProcessorInterface.h"\n+#include "Core/HW/SI/SI.h"\n+#include "Core/HW/SI/SI_Device.h"\n+#include "Core/HW/SI/SI_DeviceGCController.h"\n+#include "Core/HW/SystemTimers.h"\n+#include "Core/Movie.h"\n+#include "Core/NetPlayProto.h"\n+#include "Core/System.h"\n+\n+#include "InputCommon/GCPadStatus.h"\n+\n+namespace SerialInterface\n+{\n+void JVSIOMessage::Start(int node)\n+{\n+ m_last_start = m_pointer;\n+ const u8 header[3] = {0xE0, (u8)node, 0};\n+ m_checksum = 0;\n+ AddData(header, 3, 1);\n+}\n+\n+void JVSIOMessage::AddData(const u8* dst, size_t len, int sync = 0)\n+{\n+ if (m_pointer + len >= sizeof(m_message))\n+ {\n+ PanicAlertFmt("JVSIOMessage overrun!");\n+ return;\n+ }\n+\n+ while (len--)\n+ {\n+ const u8 c = *dst++;\n+ if (!sync && ((c == 0xE0) || (c == 0xD0)))\n+ {\n+ if (m_pointer + 2 > sizeof(m_message))\n+ {\n+ PanicAlertFmt("JVSIOMessage overrun!");\n+ break;\n+ }\n+ m_message[m_pointer++] = 0xD0;\n+ m_message[m_pointer++] = c - 1;\n+ }\n+ else\n+ {\n+ if (m_pointer >= sizeof(m_message))\n+ {\n+ PanicAlertFmt("JVSIOMessage overrun!");\n+ break;\n+ }\n+ m_message[m_pointer++] = c;\n+ }\n+\n+ if (!sync)\n+ m_checksum += c;\n+ sync = 0;\n+ }\n+}\n+\n+void JVSIOMessage::AddData(const void* data, size_t len)\n+{\n+ AddData(static_cast<const u8*>(data), len);\n+}\n+\n+void JVSIOMessage::AddData(const char* data)\n+{\n+ AddData(data, strlen(data));\n+}\n+\n+void JVSIOMessage::AddData(u32 n)\n+{\n+ const u8 cs = n;\n+ AddData(&cs, 1);\n+}\n+\n+void JVSIOMessage::End()\n+{\n+ const u32 len = m_pointer - m_last_start;\n+ if (m_last_start + 2 < sizeof(m_message) && len >= 3)\n+ {\n+ m_message[m_last_start + 2] = len - 2; // assuming len <0xD0\n+ AddData(m_checksum + len - 2);\n+ }\n+ else\n+ {\n+ PanicAlertFmt("JVSIOMessage: Not enough space for checksum!");\n+ }\n+}\n+\n+static constexpr u8 CheckSumXOR(const u8* data, u32 length)\n+{\n+ return std::accumulate(data, data + length, u8{}, std::bit_xor());\n+}\n+\n+static constexpr char s_cdr_program_version[] = {" Version 1.22,2003/09/19,171-8213B"};\n+static constexpr char s_cdr_boot_version[] = {" Version 1.04,2003/06/17,171-8213B"};\n+static constexpr u8 s_cdr_card_data[] = {\n+ 0x00, 0x6E, 0x00, 0x00, 0x01, 0x00, 0x00, 0x06, 0x00, 0x00, 0x07, 0x00, 0x00, 0x0B,\n+ 0x00, 0x00, 0x0E, 0x00, 0x00, 0x10, 0x00, 0x00, 0x17, 0x00, 0x00, 0x19, 0x00, 0x00,\n+ 0x1A, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x20, 0x00,\n+ 0x00, 0x22, 0x00, 0x00, 0x23, 0x00, 0x00, 0x24, 0x00, 0x00, 0x27, 0x00, 0x00, 0x28,\n+ 0x00, 0x00, 0x2C, 0x00, 0x00, 0x2F, 0x00, 0x00, 0x34, 0x00, 0x00, 0x35, 0x00, 0x00,\n+ 0x37, 0x00, 0x00, 0x38, 0x00, 0x00, 0x39, 0x00, 0x00, 0x3D, 0x00};\n+\n+const constexpr u8 s_region_flags[] = "\\x00\\x00\\x30\\x00"\n+ // "\\x01\\xfe\\x00\\x00" // JAPAN\n+ "\\x02\\xfd\\x00\\x00" // USA\n+ //"\\x03\\xfc\\x00\\x00" // export\n+ "\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff";\n+// AM-Baseboard device on SI\n+CSIDevice_AMBaseboard::CSIDevice_AMBaseboard(Core::System& system, SIDevices device,\n+ int device_number)\n+ : ISIDevice(system, device, device_number)\n+{\n+ // Card ID\n+ m_ic_card_data[0x20] = 0x95;\n+ m_ic_card_data[0x21] = 0x71;\n+\n+ if (AMMediaboard::GetGameType() == KeyOfAvalon)\n+ {\n+ m_ic_card_data[0x22] = 0x26;\n+ m_ic_card_data[0x23] = 0x40;\n+ }\n+ else if (AMMediaboard::GetGameType() == VirtuaStriker4)\n+ {\n+ m_ic_card_data[0x22] = 0x44;\n+ m_ic_card_data[0x23] = 0x00;\n+ }\n+\n+ // Use count\n+ m_ic_card_data[0x28] = 0xFF;\n+ m_ic_card_data[0x29] = 0xFF;\n+}\n+\n+constexpr u32 SI_XFER_LENGTH_MASK = 0x7f;\n+\n+// Translate [0,1,2,...,126,127] to [128,1,2,...,126,127]\n+constexpr s32 ConvertSILengthField(u32 field)\n+{\n+ return ((field - 1) & SI_XFER_LENGTH_MASK) + 1;\n+}\n+\n+void CSIDevice_AMBaseboard::ICCardSendReply(ICCommand* iccommand, u8* buffer, u32* length)\n+{\n+ iccommand->status = Common::swap16(iccommand->status);\n+\n+ const auto iccommand_data = reinterpret_cast<const u8*>(iccommand);\n+ const u8 crc = CheckSumXOR(iccommand_data + 2, iccommand->pktlen - 1);\n+\n+ for (u32 i = 0; i < iccommand->pktlen + 1; ++i)\n+ {\n+ buffer[(*length)++] = iccommand_data[i];\n+ }\n+\n+ buffer[(*length)++] = crc;\n+}\n+\n+void CSIDevice_AMBaseboard::SwapBuffers(u8* buffer, u32* buffer_length)\n+{\n+ memcpy(m_last[1], buffer, 0x80); // Save current buffer\n+ memcpy(buffer, m_last[0], 0x80); // Load previous buffer\n+ memcpy(m_last[0], m_last[1], 0x80); // Update history\n+\n+ m_lastptr[1] = *buffer_length; // Swap lengths\n+ *buffer_length = m_lastptr[0];\n+ m_lastptr[0] = m_lastptr[1];\n+}\n+\n+int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length)\n+{\n+ const auto& serial_interface = m_system.GetSerialInterface();\n+ u32 buffer_length = ConvertSILengthField(serial_interface.GetInLength());\n+\n+ // Debug logging\n+ ISIDevice::RunBuffer(buffer, buffer_length);\n+\n+ u32 buffer_position = 0;\n+ while (buffer_position < buffer_length)\n+ {\n+ BaseBoardCommand command = static_cast<BaseBoardCommand>(buffer[buffer_position]);\n+ buffer_position++;\n+\n+ switch (command)\n+ {\n+ case BaseBoardCommand::GCAM_Reset: // Returns ID and dip switches\n+ {\n+ const u32 id = Common::swap32(SI_AM_BASEBOARD | 0x100);\n+ std::memcpy(buffer, &id, sizeof(id));\n+ return sizeof(id);\n+ }\n+ break;\n+ case BaseBoardCommand::GCAM_Command:\n+ {\n+ u32 checksum = 0;\n+ for (u32 i = 0; i < buffer_length; ++i)\n+ checksum += buffer[i];\n+\n+ std::array<u8, 0x80> data_out{};\n+ u32 data_offset = 0;\n+\n+ static u32 dip_switch_1 = 0xFE;\n+ static u32 dip_switch_0 = 0xFF;\n+\n+ data_out[data_offset++] = 1;\n+ data_out[data_offset++] = 1;\n+\n+ u8* data_in = buffer + 2;\n+ u8* const data_in_end = buffer + buffer[buffer_position] + 2;\n+\n+ // Helper to check that iterating over data n times is safe,\n+ // i.e. *data++ at most lead to data.end()\n+ auto validate_data_in_out = [&](u32 n_in, u32 n_out, std::string_view command) -> bool {\n+ if (data_in + n_in > data_in_end)\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: data_in overflow in {}", command);\n+ else if (std::size_t{data_offset} + n_out > data_out.size())\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: data_out overflow in {}", command);\n+ else\n+ return true;\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "Overflow details:\\n"\n+ " - data_in(begin={}, current={}, end={}, n_in={})\\n"\n+ " - data_out(offset={}, size={}, n_out={})\\n"\n+ " - buffer(position={}, length={})",\n+ fmt::ptr(buffer + 2), fmt::ptr(data_in), fmt::ptr(data_in_end), n_in,\n+ data_offset, data_out.size(), n_out, buffer_position, buffer_length);\n+ data_in = data_in_end;\n+ return false;\n+ };\n+\n+ while (data_in < data_in_end)\n+ {\n+ const u32 gcam_command = *data_in++;\n+ switch (GCAMCommand(gcam_command))\n+ {\n+ case GCAMCommand::StatusSwitches:\n+ {\n+ if (!validate_data_in_out(1, 4, "StatusSwitches"))\n+ break;\n+\n+ const u8 status = *data_in++;\n+ DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x10, {:02x} (READ STATUS&SWITCHES)",\n+ status);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x2;\n+\n+ // We read Test/Service from the JVS-I/O SwitchesInput instead\n+ //\n+ // const GCPadStatus pad_status = Pad::GetStatus(ISIDevice::m_device_number);\n+ // baseboard test/service switches\n+ // if (pad_status.button & PAD_BUTTON_Y)\t// Test\n+ // dip_switch_0 &= ~0x80;\n+ // if (pad_status.button & PAD_BUTTON_X)\t// Service\n+ // dip_switch_0 &= ~0x40;\n+\n+ // Horizontal Scanning Frequency switch\n+ // Required for F-Zero AX booting via Sega Boot\n+ if (AMMediaboard::GetGameType() == FZeroAX ||\n+ AMMediaboard::GetGameType() == FZeroAXMonster)\n+ {\n+ dip_switch_0 &= ~0x20;\n+ }\n+\n+ // Disable camera in MKGP1/2\n+ if (AMMediaboard::GetGameType() == MarioKartGP ||\n+ AMMediaboard::GetGameType() == MarioKartGP2)\n+ {\n+ dip_switch_0 &= ~0x10;\n+ }\n+\n+ data_out[data_offset++] = dip_switch_0;\n+ data_out[data_offset++] = dip_switch_1;\n+ break;\n+ }\n+ case GCAMCommand::SerialNumber:\n+ {\n+ if (!validate_data_in_out(1, 18, "SerialNumber"))\n+ break;\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x11, {:02x} (READ SERIAL NR)",\n+ *data_in);\n+ data_in++;\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 16;\n+ memcpy(data_out.data() + data_offset, "AADE-01B98394904", 16);\n+\n+ data_offset += 16;\n+ break;\n+ }\n+ case GCAMCommand::Unknown_12:\n+ if (!validate_data_in_out(2, 2, "Unknown_12"))\n+ break;\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x12, {:02x} {:02x}", data_in[0],\n+ data_in[1]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x00;\n+\n+ data_in += 2;\n+ break;\n+ case GCAMCommand::Unknown_14:\n+ if (!validate_data_in_out(2, 2, "Unknown_14"))\n+ break;\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x14, {:02x} {:02x}", data_in[0],\n+ data_in[1]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x00;\n+\n+ data_in += 2;\n+ break;\n+ case GCAMCommand::FirmVersion:\n+ if (!validate_data_in_out(1, 4, "FirmVersion"))\n+ break;\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x15, {:02x} (READ FIRM VERSION)",\n+ *data_in);\n+ data_in++;\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x02;\n+ // 00.26\n+ data_out[data_offset++] = 0x00;\n+ data_out[data_offset++] = 0x26;\n+ break;\n+ case GCAMCommand::FPGAVersion:\n+ if (!validate_data_in_out(1, 4, "FPGAVersion"))\n+ break;\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x16, {:02x} (READ FPGA VERSION)",\n+ *data_in);\n+ data_in++;\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x02;\n+ // 07.06\n+ data_out[data_offset++] = 0x07;\n+ data_out[data_offset++] = 0x06;\n+ break;\n+ case GCAMCommand::RegionSettings:\n+ {\n+ if (!validate_data_in_out(5, 0x16, "RegionSettings"))\n+ break;\n+\n+ // Used by SegaBoot for region checks (dev mode skips this check)\n+ // In some games this also controls the displayed language\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x1F, {:02x} {:02x} {:02x} {:02x} {:02x} (REGION)",\n+ data_in[0], data_in[1], data_in[2], data_in[3], data_in[4]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x14;\n+\n+ for (int i = 0; i < 0x14; ++i)\n+ data_out[data_offset++] = s_region_flags[i];\n+\n+ data_in += 5;\n+ }\n+ break;\n+ // No reply\n+ // Note: Always sends three bytes even though size is set to two\n+ case GCAMCommand::Unknown_21:\n+ {\n+ if (!validate_data_in_out(4, 0, "Unknown_21"))\n+ break;\n+\n+ DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x21, {:02x}, {:02x}, {:02x}, {:02x}",\n+ data_in[0], data_in[1], data_in[2], data_in[3]);\n+ data_in += 4;\n+ }\n+ break;\n+ // No reply\n+ // Note: Always sends six bytes\n+ case GCAMCommand::Unknown_22:\n+ {\n+ if (!validate_data_in_out(7, 0, "Unknown_22"))\n+ break;\n+\n+ DEBUG_LOG_FMT(\n+ SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x22, {:02x}, {:02x}, {:02x}, {:02x}, {:02x}, {:02x}, {:02x}",\n+ data_in[0], data_in[1], data_in[2], data_in[3], data_in[4], data_in[5], data_in[6]);\n+\n+ const u32 in_size = data_in[0] + 1;\n+ if (!validate_data_in_out(in_size, 0, "Unknown_22"))\n+ break;\n+ data_in += in_size;\n+ }\n+ break;\n+ case GCAMCommand::Unknown_23:\n+ if (!validate_data_in_out(2, 2, "Unknown_23"))\n+ break;\n+\n+ DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x23, {:02x} {:02x}", data_in[0],\n+ data_in[1]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x00;\n+\n+ data_in += 2;\n+ break;\n+ case GCAMCommand::Unknown_24:\n+ if (!validate_data_in_out(2, 2, "Unknown_24"))\n+ break;\n+\n+ DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x24, {:02x} {:02x}", data_in[0],\n+ data_in[1]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x00;\n+\n+ data_in += 2;\n+ break;\n+ case GCAMCommand::SerialA:\n+ {\n+ if (!validate_data_in_out(1, 0, "SerialA"))\n+ break;\n+\n+ const u32 length = *data_in++;\n+ if (length)\n+ {\n+ if (!validate_data_in_out(length, 0, "SerialA"))\n+ break;\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x31, length=0x{:02x}, hexdump:\\n{}",\n+ length, HexDump(data_in, length));\n+\n+ // Serial - Wheel\n+ if (AMMediaboard::GetGameType() == MarioKartGP ||\n+ AMMediaboard::GetGameType() == MarioKartGP2)\n+ {\n+ if (!validate_data_in_out(10, 2, "SerialA (Wheel)"))\n+ break;\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x31, (WHEEL) {:02x}{:02x} {:02x}{:02x} {:02x} {:02x} "\n+ "{:02x} {:02x} {:02x} {:02x}",\n+ data_in[0], data_in[1], data_in[2], data_in[3], data_in[4], data_in[5],\n+ data_in[6], data_in[7], data_in[8], data_in[9]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x03;\n+\n+ switch (m_wheel_init)\n+ {\n+ case 0:\n+ if (!validate_data_in_out(0, 3, "SerialA (Wheel)"))\n+ break;\n+ data_out[data_offset++] = \'E\'; // Error\n+ data_out[data_offset++] = \'0\';\n+ data_out[data_offset++] = \'0\';\n+ m_wheel_init++;\n+ break;\n+ case 1:\n+ if (!validate_data_in_out(0, 3, "SerialA (Wheel)"))\n+ break;\n+ data_out[data_offset++] = \'C\'; // Power Off\n+ data_out[data_offset++] = \'0\';\n+ data_out[data_offset++] = \'6\';\n+ // Only turn on when a wheel is connected\n+ if (serial_interface.GetDeviceType(1) == SerialInterface::SIDEVICE_GC_STEERING)\n+ {\n+ m_wheel_init++;\n+ }\n+ break;\n+ case 2:\n+ if (!validate_data_in_out(0, 3, "SerialA (Wheel)"))\n+ break;\n+ data_out[data_offset++] = \'C\'; // Power On\n+ data_out[data_offset++] = \'0\';\n+ data_out[data_offset++] = \'1\';\n+ break;\n+ default:\n+ break;\n+ }\n+\n+ // u16 CenteringForce= ptr(6);\n+ // u16 FrictionForce = ptr(8);\n+ // u16 Roll = ptr(10);\n+ if (!validate_data_in_out(length, 0, "SerialA (Wheel)"))\n+ break;\n+ data_in += length;\n+ break;\n+ }\n+\n+ // Serial - Unknown\n+ if (AMMediaboard::GetGameType() == GekitouProYakyuu)\n+ {\n+ if (!validate_data_in_out(sizeof(u32), 0, "SerialA (Unknown)"))\n+ break;\n+ const u32 serial_command = Common::BitCastPtr<u32>(data_in);\n+\n+ if (serial_command == 0x00001000)\n+ {\n+ if (!validate_data_in_out(0, 5, "SerialA (Unknown)"))\n+ break;\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x03;\n+ data_out[data_offset++] = 1;\n+ data_out[data_offset++] = 2;\n+ data_out[data_offset++] = 3;\n+ }\n+\n+ if (!validate_data_in_out(length, 0, "SerialA (Unknown)"))\n+ break;\n+ data_in += length;\n+ break;\n+ }\n+\n+ // Serial IC-CARD / Serial Deck Reader\n+ if (AMMediaboard::GetGameType() == VirtuaStriker4 ||\n+ AMMediaboard::GetGameType() == VirtuaStriker4_2006 ||\n+ AMMediaboard::GetGameType() == KeyOfAvalon)\n+ {\n+ if (!validate_data_in_out(2, 0, "SerialA (IC-CARD)"))\n+ break;\n+ u32 serial_command = data_in[1];\n+\n+ ICCommand icco;\n+\n+ // Set default reply\n+ icco.pktcmd = gcam_command;\n+ icco.pktlen = 7;\n+ icco.fixed = 0x10;\n+ icco.command = serial_command;\n+ icco.flag = 0;\n+ icco.length = 2;\n+ icco.status = 0;\n+ icco.extlen = 0;\n+\n+ // Check for rest of data from the write pages command\n+ if (m_ic_write_size && m_ic_write_offset)\n+ {\n+ const u32 size = data_in[1];\n+\n+ if (!validate_data_in_out(size + 2, 0, "SerialA (IC-CARD)"))\n+ break;\n+ DEBUG_LOG_FMT(SERIALINTERFACE_CARD, "Command: {}", HexDump(data_in, size + 2));\n+\n+ INFO_LOG_FMT(\n+ SERIALINTERFACE_CARD,\n+ "GC-AM: Command 25 (IC-CARD) Write Pages: Off:{:x} Size:{:x} PSize:{:x}",\n+ m_ic_write_offset, m_ic_write_size, size);\n+\n+ if (std::size_t{m_ic_write_offset} + size > sizeof(m_ic_write_buffer))\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 25 (IC-CARD) m_ic_write_buffer overflow:\\n"\n+ " - m_ic_write_buffer(offset={}, size={})\\n"\n+ " - size={}\\n",\n+ m_ic_write_offset, sizeof(m_ic_write_buffer), size);\n+ data_in = data_in_end;\n+ break;\n+ }\n+ memcpy(m_ic_write_buffer + m_ic_write_offset, data_in + 2, size);\n+\n+ m_ic_write_offset += size;\n+\n+ if (m_ic_write_offset > m_ic_write_size)\n+ {\n+ m_ic_write_offset = 0;\n+\n+ const u16 page = m_ic_write_buffer[5];\n+ const u16 count = m_ic_write_buffer[7];\n+\n+ if ((page * 8 + count * 8) > sizeof(m_ic_card_data) ||\n+ (10 + count * 8) > sizeof(m_ic_write_buffer))\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 25 (IC-CARD) Write Pages overflow:\\n"\n+ " - m_ic_card_data(offset={}, size={})\\n"\n+ " - m_ic_write_buffer(offset={}, size={})\\n"\n+ " - size={}, page={}, count={}\\n",\n+ page * 8, sizeof(m_ic_card_data), 10, sizeof(m_ic_write_buffer),\n+ count * 8, page, count);\n+ data_in = data_in_end;\n+ break;\n+ }\n+ memcpy(m_ic_card_data + page * 8, m_ic_write_buffer + 10, count * 8);\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 25 (IC-CARD) Write Pages:{} Count:{}({:x})", page,\n+ count, size);\n+\n+ icco.command = WritePages;\n+\n+ if (!validate_data_in_out(0, icco.pktlen + 2, "SerialA (IC-CARD)"))\n+ break;\n+ ICCardSendReply(&icco, data_out.data(), &data_offset);\n+ }\n+ if (!validate_data_in_out(length, 0, "SerialA (IC-CARD)"))\n+ break;\n+ data_in += length;\n+ break;\n+ }\n+\n+ switch (ICCARDCommand(serial_command))\n+ {\n+ case ICCARDCommand::GetStatus:\n+ icco.status = m_ic_card_state;\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (IC-CARD) Get Status:{:02x}", m_ic_card_state);\n+ break;\n+ case ICCARDCommand::SetBaudrate:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (IC-CARD) Set Baudrate");\n+ break;\n+ case ICCARDCommand::FieldOn:\n+ m_ic_card_state |= 0x10;\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (IC-CARD) Field On");\n+ break;\n+ case ICCARDCommand::InsertCheck:\n+ icco.status = m_ic_card_status;\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (IC-CARD) Insert Check:{:02x}", m_ic_card_status);\n+ break;\n+ case ICCARDCommand::AntiCollision:\n+ icco.extlen = 8;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ // Card ID\n+ icco.extdata[0] = 0x00;\n+ icco.extdata[1] = 0x00;\n+ icco.extdata[2] = 0x54;\n+ icco.extdata[3] = 0x4D;\n+ icco.extdata[4] = 0x50;\n+ icco.extdata[5] = 0x00;\n+ icco.extdata[6] = 0x00;\n+ icco.extdata[7] = 0x00;\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (IC-CARD) Anti Collision");\n+ break;\n+ case ICCARDCommand::SelectCard:\n+ icco.extlen = 8;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ // Session\n+ icco.extdata[0] = 0x00;\n+ icco.extdata[1] = m_ic_card_session;\n+ icco.extdata[2] = 0x00;\n+ icco.extdata[3] = 0x00;\n+ icco.extdata[4] = 0x00;\n+ icco.extdata[5] = 0x00;\n+ icco.extdata[6] = 0x00;\n+ icco.extdata[7] = 0x00;\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (IC-CARD) Select Card:{}",\n+ m_ic_card_session);\n+ break;\n+ case ICCARDCommand::ReadPage:\n+ case ICCARDCommand::ReadUseCount:\n+ {\n+ if (!validate_data_in_out(8, 0, "SerialA (IC-CARD)"))\n+ break;\n+ const u16 page = Common::swap16(data_in + 6) & 0xFF; // 255 is max page\n+\n+ icco.extlen = 8;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ memcpy(icco.extdata, m_ic_card_data + page * 8, 8);\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 31 (IC-CARD) Read Page:{}",\n+ page);\n+ break;\n+ }\n+ case ICCARDCommand::WritePage:\n+ {\n+ if (!validate_data_in_out(10, 0, "SerialA (IC-CARD)"))\n+ break;\n+ const u16 page = Common::swap16(data_in + 8) & 0xFF; // 255 is max page\n+\n+ // Write only one page\n+ if (page == 4)\n+ {\n+ icco.status = 0x80;\n+ }\n+ else\n+ {\n+ if (!validate_data_in_out(18, 0, "SerialA (IC-CARD)"))\n+ break;\n+ memcpy(m_ic_card_data + page * 8, data_in + 10, 8);\n+ }\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (IC-CARD) Write Page:{}",\n+ page);\n+ break;\n+ }\n+ case ICCARDCommand::DecreaseUseCount:\n+ {\n+ if (!validate_data_in_out(8, 0, "SerialA (IC-CARD)"))\n+ break;\n+ const u16 page = Common::swap16(data_in + 6) & 0xFF; // 255 is max page\n+\n+ icco.extlen = 2;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ auto ic_card_data = Common::BitCastPtr<u16>(m_ic_card_data + 0x28);\n+ ic_card_data = ic_card_data - 1;\n+\n+ // Counter\n+ icco.extdata[0] = m_ic_card_data[0x28];\n+ icco.extdata[1] = m_ic_card_data[0x29];\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 31 (IC-CARD) Decrease Use Count:{}", page);\n+ break;\n+ }\n+ case ICCARDCommand::ReadPages:\n+ {\n+ if (!validate_data_in_out(10, 0, "SerialA (IC-CARD)"))\n+ break;\n+ const u16 page = Common::swap16(data_in + 6) & 0xFF; // 255 is max page\n+ const u16 count = Common::swap16(data_in + 8);\n+\n+ const u32 offs = page * 8;\n+ u32 cnt = count * 8;\n+\n+ // Limit read size to not overwrite the reply buffer\n+ const std::size_t reply_buffer_size = sizeof(icco.extdata) - 1;\n+ if (data_offset > reply_buffer_size)\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 31 (IC-CARD) Read Pages overflow:"\n+ " offset={} > buffer_size={}",\n+ data_offset, reply_buffer_size);\n+ data_in = data_in_end;\n+ break;\n+ }\n+ if (cnt > reply_buffer_size - data_offset)\n+ {\n+ cnt = 5 * 8;\n+ }\n+\n+ icco.extlen = cnt;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ if (offs + cnt > sizeof(icco.extdata))\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 31 (IC-CARD) Read Pages overflow:"\n+ " offset={} + count={} > buffer_size={}",\n+ offs, cnt, sizeof(icco.extdata));\n+ data_in = data_in_end;\n+ break;\n+ }\n+ memcpy(icco.extdata, m_ic_card_data + offs, cnt);\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 31 (IC-CARD) Read Pages:{} Count:{}", page, count);\n+ break;\n+ }\n+ case ICCARDCommand::WritePages:\n+ {\n+ if (!validate_data_in_out(10, 0, "SerialA (IC-CARD)"))\n+ break;\n+ const u16 pksize = length;\n+ const u16 size = Common::swap16(data_in + 2);\n+ const u16 page = Common::swap16(data_in + 6) & 0xFF; // 255 is max page\n+ const u16 count = Common::swap16(data_in + 8);\n+\n+ // We got a complete packet\n+ if (pksize - 5 == size)\n+ {\n+ if (page == 4) // Read Only Page, must return error\n+ {\n+ icco.status = 0x80;\n+ }\n+ else\n+ {\n+ if (page * 8 + count * 8 > sizeof(m_ic_card_data))\n+ {\n+ ERROR_LOG_FMT(\n+ SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (IC-CARD) Data overflow: Pages:{} Count:{}({:x})",\n+ page, count, size);\n+ }\n+ else\n+ {\n+ if (!validate_data_in_out(13 + count * 8, 0, "SerialA (IC-CARD)"))\n+ break;\n+ memcpy(m_ic_card_data + page * 8, data_in + 13, count * 8);\n+ }\n+ }\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (IC-CARD) Write Pages:{} Count:{}({:x})", page,\n+ count, size);\n+ }\n+ // VirtuaStriker 4 splits the writes over multiple packets\n+ else\n+ {\n+ if (!validate_data_in_out(2 + pksize, 0, "SerialA (IC-CARD)"))\n+ break;\n+ memcpy(m_ic_write_buffer, data_in + 2, pksize);\n+ m_ic_write_offset += pksize;\n+ m_ic_write_size = size;\n+ }\n+ break;\n+ }\n+ default:\n+ // Handle Deck Reader commands\n+ if (!validate_data_in_out(1, 0, "SerialA (DECK READER)"))\n+ break;\n+ serial_command = data_in[0];\n+ icco.command = serial_command;\n+ icco.flag = 0;\n+ switch (CDReaderCommand(serial_command))\n+ {\n+ case CDReaderCommand::ProgramVersion:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Program Version");\n+\n+ icco.extlen = (u32)strlen(s_cdr_program_version);\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ memcpy(icco.extdata, s_cdr_program_version, icco.extlen);\n+ break;\n+ case CDReaderCommand::BootVersion:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Boot Version");\n+\n+ icco.extlen = (u32)strlen(s_cdr_boot_version);\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ memcpy(icco.extdata, s_cdr_boot_version, icco.extlen);\n+ break;\n+ case CDReaderCommand::ShutterGet:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Shutter Get");\n+\n+ icco.extlen = 4;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ icco.extdata[0] = 0;\n+ icco.extdata[1] = 0;\n+ icco.extdata[2] = 0;\n+ icco.extdata[3] = 0;\n+ break;\n+ case CDReaderCommand::CameraCheck:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Camera Check");\n+\n+ icco.extlen = 6;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ icco.extdata[0] = 0x23;\n+ icco.extdata[1] = 0x28;\n+ icco.extdata[2] = 0x45;\n+ icco.extdata[3] = 0x29;\n+ icco.extdata[4] = 0x45;\n+ icco.extdata[5] = 0x29;\n+ break;\n+ case CDReaderCommand::ProgramChecksum:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Program Checksum");\n+\n+ icco.extlen = 4;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ icco.extdata[0] = 0x23;\n+ icco.extdata[1] = 0x28;\n+ icco.extdata[2] = 0x45;\n+ icco.extdata[3] = 0x29;\n+ break;\n+ case CDReaderCommand::BootChecksum:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Boot Checksum");\n+\n+ icco.extlen = 4;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ icco.extdata[0] = 0x23;\n+ icco.extdata[1] = 0x28;\n+ icco.extdata[2] = 0x45;\n+ icco.extdata[3] = 0x29;\n+ break;\n+ case CDReaderCommand::SelfTest:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (DECK READER) Self Test");\n+ icco.flag = 0x00;\n+ break;\n+ case CDReaderCommand::SensLock:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (DECK READER) Sens Lock");\n+ icco.flag = 0x01;\n+ break;\n+ case CDReaderCommand::SensCard:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (DECK READER) Sens Card");\n+ break;\n+ case CDReaderCommand::ShutterCard:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Shutter Card");\n+ break;\n+ case CDReaderCommand::ReadCard:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (DECK READER) Read Card");\n+\n+ icco.fixed = 0xAA;\n+ icco.flag = 0xAA;\n+ icco.extlen = sizeof(s_cdr_card_data);\n+ icco.length = 0x72;\n+ icco.status = Common::swap16(icco.extlen);\n+\n+ icco.pktlen += icco.extlen;\n+\n+ memcpy(icco.extdata, s_cdr_card_data, sizeof(s_cdr_card_data));\n+\n+ break;\n+ default:\n+ if (!validate_data_in_out(14, 0, "SerialA (DECK READER)"))\n+ break;\n+ WARN_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (IC-Card) {:02x} {:02x} {:02x} {:02x} {:02x} "\n+ "{:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x}",\n+ data_in[2], data_in[3], data_in[4], data_in[5], data_in[6],\n+ data_in[7], data_in[8], data_in[9], data_in[10], data_in[11],\n+ data_in[12], data_in[13]);\n+ break;\n+ }\n+ break;\n+ }\n+\n+ if (!validate_data_in_out(0, icco.pktlen + 2, "SerialA (IC-CARD)"))\n+ break;\n+ ICCardSendReply(&icco, data_out.data(), &data_offset);\n+\n+ if (!validate_data_in_out(2, 0, "SerialA (IC-CARD)"))\n+ break;\n+ data_in += length;\n+ break;\n+ }\n+ }\n+\n+ u32 command_offset = 0;\n+ while (command_offset < length)\n+ {\n+ // All commands are OR\'d with 0x80\n+ // Last byte is checksum which we don\'t care about\n+ if (!validate_data_in_out(command_offset + 4, 0, "SerialA"))\n+ break;\n+ const u32 serial_command = Common::swap32(data_in + command_offset) ^ 0x80000000;\n+\n+ if (AMMediaboard::GetGameType() == FZeroAX ||\n+ AMMediaboard::GetGameType() == FZeroAXMonster)\n+ {\n+ INFO_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x31 (MOTOR) Length:{:02x} Command:{:04x}({:02x})",\n+ length, (serial_command >> 8) & 0xFFFF, serial_command >> 24);\n+ }\n+ else\n+ {\n+ INFO_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x31 (SERIAL) Command:{:06x}",\n+ serial_command);\n+\n+ if (serial_command == 0x801000)\n+ {\n+ if (!validate_data_in_out(0, 4, "SerialA"))\n+ break;\n+ data_out[data_offset++] = 0x31;\n+ data_out[data_offset++] = 0x02;\n+ data_out[data_offset++] = 0xFF;\n+ data_out[data_offset++] = 0x01;\n+ }\n+ }\n+\n+ command_offset += sizeof(u32);\n+\n+ if (AMMediaboard::GetGameType() == FZeroAX ||\n+ AMMediaboard::GetGameType() == FZeroAXMonster)\n+ {\n+ if (command_offset + 5 >= std::size(m_motor_reply))\n+ {\n+ ERROR_LOG_FMT(\n+ SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x31 (MOTOR) overflow: offset={} >= motor_reply_size={}",\n+ command_offset + 5, std::size(m_motor_reply));\n+ data_in = data_in_end;\n+ break;\n+ }\n+\n+ // Status\n+ m_motor_reply[command_offset + 2] = 0;\n+ m_motor_reply[command_offset + 3] = 0;\n+\n+ // Error\n+ m_motor_reply[command_offset + 4] = 0;\n+\n+ switch (serial_command >> 24)\n+ {\n+ case 0:\n+ case 1: // Set Maximum?\n+ case 2:\n+ break;\n+\n+ // 0x00-0x40: left\n+ // 0x40-0x80: right\n+ case 4: // Move Steering Wheel\n+ // Left\n+ if (serial_command & 0x010000)\n+ {\n+ m_motor_force_y = -((s16)serial_command & 0xFF00);\n+ }\n+ else // Right\n+ {\n+ m_motor_force_y = (serial_command - 0x4000) & 0xFF00;\n+ }\n+\n+ m_motor_force_y *= 2;\n+\n+ // FFB\n+ if (m_motor_init == 2)\n+ {\n+ if (serial_interface.GetDeviceType(1) == SerialInterface::SIDEVICE_GC_STEERING)\n+ {\n+ const GCPadStatus pad_status = Pad::GetStatus(1);\n+ if (pad_status.isConnected)\n+ {\n+ const ControlState mapped_strength = (double)(m_motor_force_y >> 8) / 127.f;\n+\n+ Pad::Rumble(1, mapped_strength);\n+ INFO_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x31 (MOTOR) mapped_strength:{}",\n+ mapped_strength);\n+ }\n+ }\n+ }\n+ break;\n+ case 6: // nice\n+ case 9:\n+ default:\n+ break;\n+ // Switch back to normal controls\n+ case 7:\n+ m_motor_init = 2;\n+ break;\n+ // Reset\n+ case 0x7F:\n+ m_motor_init = 1;\n+ memset(m_motor_reply, 0, sizeof(m_motor_reply));\n+ break;\n+ }\n+\n+ // Checksum\n+ m_motor_reply[command_offset + 5] = m_motor_reply[command_offset + 2] ^\n+ m_motor_reply[command_offset + 3] ^\n+ m_motor_reply[command_offset + 4];\n+ }\n+ }\n+\n+ if (length == 0)\n+ {\n+ if (!validate_data_in_out(length, 2, "SerialA"))\n+ break;\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x00;\n+ }\n+ else\n+ {\n+ if (m_motor_init)\n+ {\n+ // Motor\n+ m_motor_reply[0] = gcam_command;\n+ m_motor_reply[1] = length; // Same out as in size\n+\n+ const u32 reply_size = m_motor_reply[1] + 2;\n+ if (!validate_data_in_out(length, reply_size, "SerialA"))\n+ break;\n+\n+ if (reply_size > sizeof(m_motor_reply))\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command SerialA, reply_size={} too big for m_motor_reply",\n+ reply_size);\n+ data_in = data_in_end;\n+ break;\n+ }\n+ memcpy(data_out.data() + data_offset, m_motor_reply, reply_size);\n+ data_offset += reply_size;\n+ }\n+ }\n+\n+ if (!validate_data_in_out(length, 0, "SerialA"))\n+ {\n+ break;\n+ }\n+ data_in += length;\n+ break;\n+ }\n+ case GCAMCommand::SerialB:\n+ {\n+ DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 32 (CARD-Interface)");\n+ if (!validate_data_in_out(1, 0, "SerialB"))\n+ break;\n+ const u32 length = *data_in++;\n+ if (!validate_data_in_out(length, 0, "SerialB"))\n+ break;\n+ if (length)\n+ {\n+ // Send Card Reply\n+ if (length == 1 && data_in[0] == 0x05)\n+ {\n+ if (m_card_read_length)\n+ {\n+ if (!validate_data_in_out(0, 1, "SerialB"))\n+ break;\n+ data_out[data_offset++] = gcam_command;\n+ u32 read_length = m_card_read_length - m_card_read;\n+\n+ if (AMMediaboard::GetGameType() == FZeroAX)\n+ {\n+ read_length = std::min<u32>(read_length, 0x2F);\n+ }\n+\n+ if (!validate_data_in_out(0, 1, "SerialB"))\n+ break;\n+ data_out[data_offset++] = read_length; // 0x2F (max size per packet)\n+\n+ if (!validate_data_in_out(0, read_length, "SerialB"))\n+ break;\n+ if (std::size_t{m_card_read} + read_length > sizeof(m_card_read_packet))\n+ {\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command SerialB, m_card_read_packet overflow:\\n"\n+ " - m_card_read_packet = {}\\n"\n+ " - m_card_read = {}\\n"\n+ " - read_length = {}",\n+ fmt::ptr(m_card_read_packet), m_card_read, read_length);\n+ data_in = data_in_end;\n+ break;\n+ }\n+ memcpy(data_out.data() + data_offset, m_card_read_packet + m_card_read,\n+ read_length);\n+\n+ data_offset += read_length;\n+ m_card_read += read_length;\n+\n+ if (m_card_read >= m_card_read_length)\n+ m_card_read_length = 0;\n+\n+ data_in += length;\n+ break;\n+ }\n+\n+ if (!validate_data_in_out(0, 5, "SerialB"))\n+ break;\n+\n+ data_out[data_offset++] = gcam_command;\n+ const u32 command_length_offset = data_offset;\n+ data_out[data_offset++] = 0x00; // len\n+\n+ data_out[data_offset++] = 0x02;\n+ const u32 checksum_start = data_offset;\n+\n+ data_out[data_offset++] = 0x00; // 0x00 len\n+\n+ data_out[data_offset++] = m_card_command; // 0x01 command\n+\n+ switch (CARDCommand(m_card_command))\n+ {\n+ case CARDCommand::Init:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x00; // 0x02\n+ data_out[data_offset++] = 0x30; // 0x03\n+ break;\n+ case CARDCommand::GetState:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x20 | m_card_bit; // 0x02\n+\n+ // bit 0: Please take your card\n+ // bit 1: Endless waiting causes UNK_E to be called\n+ data_out[data_offset++] = 0x00; // 0x03\n+ break;\n+ case CARDCommand::Read:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x02; // 0x02\n+ data_out[data_offset++] = 0x53; // 0x03\n+ break;\n+ case CARDCommand::IsPresent:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x22; // 0x02\n+ data_out[data_offset++] = 0x30; // 0x03\n+ break;\n+ case CARDCommand::Write:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x02; // 0x02\n+ data_out[data_offset++] = 0x00; // 0x03\n+ break;\n+ case CARDCommand::SetPrintParam:\n+ case CARDCommand::RegisterFont:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x00; // 0x02\n+ data_out[data_offset++] = 0x00; // 0x03\n+ break;\n+ case CARDCommand::WriteInfo:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x02; // 0x02\n+ data_out[data_offset++] = 0x00; // 0x03\n+ break;\n+ case CARDCommand::Erase:\n+ // TODO: CARDCommand::Erase is not handled.\n+ break;\n+ case CARDCommand::Eject:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ if (AMMediaboard::GetGameType() == FZeroAX)\n+ {\n+ data_out[data_offset++] = 0x01; // 0x02\n+ }\n+ else\n+ {\n+ data_out[data_offset++] = 0x31; // 0x02\n+ }\n+ data_out[data_offset++] = 0x30; // 0x03\n+ break;\n+ case CARDCommand::Clean:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x02; // 0x02\n+ data_out[data_offset++] = 0x00; // 0x03\n+ break;\n+ case CARDCommand::Load:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x02; // 0x02\n+ data_out[data_offset++] = 0x30; // 0x03\n+ break;\n+ case CARDCommand::SetShutter:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x00; // 0x02\n+ data_out[data_offset++] = 0x00; // 0x03\n+ break;\n+ }\n+\n+ if (!validate_data_in_out(0, 3, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x30; // 0x04\n+ data_out[data_offset++] = 0x00; // 0x05\n+\n+ data_out[data_offset++] = 0x03; // 0x06\n+\n+ data_out[checksum_start] = data_offset - checksum_start; // 0x00 len\n+\n+ if (!validate_data_in_out(0, 1, "SerialB"))\n+ break;\n+ data_out[data_offset] = 0; // 0x07\n+ for (u32 i = 0; i < data_out[checksum_start]; ++i)\n+ data_out[data_offset] ^= data_out[checksum_start + i];\n+\n+ if (!validate_data_in_out(0, 1, "SerialB"))\n+ break;\n+ data_offset++;\n+\n+ data_out[command_length_offset] = data_out[checksum_start] + 2;\n+ }\n+ else\n+ {\n+ if (!validate_data_in_out(length, 0, "SerialB"))\n+ break;\n+ if (std::size_t{m_card_offset} + length > std::size(m_card_buffer))', 'path': 'Source/Core/Core/HW/SI/SI_DeviceAMBaseboard.cpp', 'position': 1308, 'original_position': 1308, 'commit_id': '510fec88be973bc870021f2d0347593cdc285864', 'user': {'login': 'JosJuice', 'id': 6716818, 'node_id': 'MDQ6VXNlcjY3MTY4MTg=', 'avatar_url': 'https://avatars.githubusercontent.com/u/6716818?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/JosJuice', 'html_url': 'https://github.com/JosJuice', 'followers_url': 'https://api.github.com/users/JosJuice/followers', 'following_url': 'https://api.github.com/users/JosJuice/following{/other_user}', 'gists_url': 'https://api.github.com/users/JosJuice/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/JosJuice/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/JosJuice/subscriptions', 'organizations_url': 'https://api.github.com/users/JosJuice/orgs', 'repos_url': 'https://api.github.com/users/JosJuice/repos', 'events_url': 'https://api.github.com/users/JosJuice/events{/privacy}', 'received_events_url': 'https://api.github.com/users/JosJuice/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'body': '```suggestion\r\n if (u64{m_card_offset} + length > std::size(m_card_buffer))\r\n```', 'created_at': '2026-01-31T13:27:48Z', 'updated_at': '2026-01-31T13:33:12Z', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/13844#discussion_r2749562072', 'pull_request_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13844', 'author_association': 'MEMBER', '_links': {'self': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2749562072'}, 'html': {'href': 'https://github.com/dolphin-emu/dolphin/pull/13844#discussion_r2749562072'}, 'pull_request': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13844'}}, 'original_commit_id': '510fec88be973bc870021f2d0347593cdc285864', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2749562072/reactions', 'total_count': 0, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 0, 'confused': 0, 'heart': 0, 'rocket': 0, 'eyes': 0}}, {'id': 2749563661, 'node_id': 'PRRC_kwDOALCn2M6j4wMN', 'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2749563661', 'pull_request_review_id': 3732508353, 'diff_hunk': '@@ -0,0 +1,2501 @@\n+// Copyright 2025 Dolphin Emulator Project\n+// SPDX-License-Identifier: GPL-2.0-or-later\n+\n+#include "Core/HW/SI/SI_DeviceAMBaseboard.h"\n+\n+#include <algorithm>\n+#include <numeric>\n+#include <string>\n+\n+#include <fmt/format.h>\n+\n+#include "Common/Buffer.h"\n+#include "Common/CommonTypes.h"\n+#include "Common/FileUtil.h"\n+#include "Common/IOFile.h"\n+#include "Common/Logging/Log.h"\n+#include "Common/MsgHandler.h"\n+#include "Common/Swap.h"\n+\n+#include "Core/Boot/Boot.h"\n+#include "Core/BootManager.h"\n+#include "Core/Config/MainSettings.h"\n+#include "Core/ConfigManager.h"\n+#include "Core/Core.h"\n+#include "Core/CoreTiming.h"\n+#include "Core/HW/DVD/AMMediaboard.h"\n+#include "Core/HW/DVD/DVDInterface.h"\n+#include "Core/HW/EXI/EXI.h"\n+#include "Core/HW/GCPad.h"\n+#include "Core/HW/MMIO.h"\n+#include "Core/HW/Memmap.h"\n+#include "Core/HW/ProcessorInterface.h"\n+#include "Core/HW/SI/SI.h"\n+#include "Core/HW/SI/SI_Device.h"\n+#include "Core/HW/SI/SI_DeviceGCController.h"\n+#include "Core/HW/SystemTimers.h"\n+#include "Core/Movie.h"\n+#include "Core/NetPlayProto.h"\n+#include "Core/System.h"\n+\n+#include "InputCommon/GCPadStatus.h"\n+\n+namespace SerialInterface\n+{\n+void JVSIOMessage::Start(int node)\n+{\n+ m_last_start = m_pointer;\n+ const u8 header[3] = {0xE0, (u8)node, 0};\n+ m_checksum = 0;\n+ AddData(header, 3, 1);\n+}\n+\n+void JVSIOMessage::AddData(const u8* dst, size_t len, int sync = 0)\n+{\n+ if (m_pointer + len >= sizeof(m_message))\n+ {\n+ PanicAlertFmt("JVSIOMessage overrun!");\n+ return;\n+ }\n+\n+ while (len--)\n+ {\n+ const u8 c = *dst++;\n+ if (!sync && ((c == 0xE0) || (c == 0xD0)))\n+ {\n+ if (m_pointer + 2 > sizeof(m_message))\n+ {\n+ PanicAlertFmt("JVSIOMessage overrun!");\n+ break;\n+ }\n+ m_message[m_pointer++] = 0xD0;\n+ m_message[m_pointer++] = c - 1;\n+ }\n+ else\n+ {\n+ if (m_pointer >= sizeof(m_message))\n+ {\n+ PanicAlertFmt("JVSIOMessage overrun!");\n+ break;\n+ }\n+ m_message[m_pointer++] = c;\n+ }\n+\n+ if (!sync)\n+ m_checksum += c;\n+ sync = 0;\n+ }\n+}\n+\n+void JVSIOMessage::AddData(const void* data, size_t len)\n+{\n+ AddData(static_cast<const u8*>(data), len);\n+}\n+\n+void JVSIOMessage::AddData(const char* data)\n+{\n+ AddData(data, strlen(data));\n+}\n+\n+void JVSIOMessage::AddData(u32 n)\n+{\n+ const u8 cs = n;\n+ AddData(&cs, 1);\n+}\n+\n+void JVSIOMessage::End()\n+{\n+ const u32 len = m_pointer - m_last_start;\n+ if (m_last_start + 2 < sizeof(m_message) && len >= 3)\n+ {\n+ m_message[m_last_start + 2] = len - 2; // assuming len <0xD0\n+ AddData(m_checksum + len - 2);\n+ }\n+ else\n+ {\n+ PanicAlertFmt("JVSIOMessage: Not enough space for checksum!");\n+ }\n+}\n+\n+static constexpr u8 CheckSumXOR(const u8* data, u32 length)\n+{\n+ return std::accumulate(data, data + length, u8{}, std::bit_xor());\n+}\n+\n+static constexpr char s_cdr_program_version[] = {" Version 1.22,2003/09/19,171-8213B"};\n+static constexpr char s_cdr_boot_version[] = {" Version 1.04,2003/06/17,171-8213B"};\n+static constexpr u8 s_cdr_card_data[] = {\n+ 0x00, 0x6E, 0x00, 0x00, 0x01, 0x00, 0x00, 0x06, 0x00, 0x00, 0x07, 0x00, 0x00, 0x0B,\n+ 0x00, 0x00, 0x0E, 0x00, 0x00, 0x10, 0x00, 0x00, 0x17, 0x00, 0x00, 0x19, 0x00, 0x00,\n+ 0x1A, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x20, 0x00,\n+ 0x00, 0x22, 0x00, 0x00, 0x23, 0x00, 0x00, 0x24, 0x00, 0x00, 0x27, 0x00, 0x00, 0x28,\n+ 0x00, 0x00, 0x2C, 0x00, 0x00, 0x2F, 0x00, 0x00, 0x34, 0x00, 0x00, 0x35, 0x00, 0x00,\n+ 0x37, 0x00, 0x00, 0x38, 0x00, 0x00, 0x39, 0x00, 0x00, 0x3D, 0x00};\n+\n+const constexpr u8 s_region_flags[] = "\\x00\\x00\\x30\\x00"\n+ // "\\x01\\xfe\\x00\\x00" // JAPAN\n+ "\\x02\\xfd\\x00\\x00" // USA\n+ //"\\x03\\xfc\\x00\\x00" // export\n+ "\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff";\n+// AM-Baseboard device on SI\n+CSIDevice_AMBaseboard::CSIDevice_AMBaseboard(Core::System& system, SIDevices device,\n+ int device_number)\n+ : ISIDevice(system, device, device_number)\n+{\n+ // Card ID\n+ m_ic_card_data[0x20] = 0x95;\n+ m_ic_card_data[0x21] = 0x71;\n+\n+ if (AMMediaboard::GetGameType() == KeyOfAvalon)\n+ {\n+ m_ic_card_data[0x22] = 0x26;\n+ m_ic_card_data[0x23] = 0x40;\n+ }\n+ else if (AMMediaboard::GetGameType() == VirtuaStriker4)\n+ {\n+ m_ic_card_data[0x22] = 0x44;\n+ m_ic_card_data[0x23] = 0x00;\n+ }\n+\n+ // Use count\n+ m_ic_card_data[0x28] = 0xFF;\n+ m_ic_card_data[0x29] = 0xFF;\n+}\n+\n+constexpr u32 SI_XFER_LENGTH_MASK = 0x7f;\n+\n+// Translate [0,1,2,...,126,127] to [128,1,2,...,126,127]\n+constexpr s32 ConvertSILengthField(u32 field)\n+{\n+ return ((field - 1) & SI_XFER_LENGTH_MASK) + 1;\n+}\n+\n+void CSIDevice_AMBaseboard::ICCardSendReply(ICCommand* iccommand, u8* buffer, u32* length)\n+{\n+ iccommand->status = Common::swap16(iccommand->status);\n+\n+ const auto iccommand_data = reinterpret_cast<const u8*>(iccommand);\n+ const u8 crc = CheckSumXOR(iccommand_data + 2, iccommand->pktlen - 1);\n+\n+ for (u32 i = 0; i < iccommand->pktlen + 1; ++i)\n+ {\n+ buffer[(*length)++] = iccommand_data[i];\n+ }\n+\n+ buffer[(*length)++] = crc;\n+}\n+\n+void CSIDevice_AMBaseboard::SwapBuffers(u8* buffer, u32* buffer_length)\n+{\n+ memcpy(m_last[1], buffer, 0x80); // Save current buffer\n+ memcpy(buffer, m_last[0], 0x80); // Load previous buffer\n+ memcpy(m_last[0], m_last[1], 0x80); // Update history\n+\n+ m_lastptr[1] = *buffer_length; // Swap lengths\n+ *buffer_length = m_lastptr[0];\n+ m_lastptr[0] = m_lastptr[1];\n+}\n+\n+int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length)\n+{\n+ const auto& serial_interface = m_system.GetSerialInterface();\n+ u32 buffer_length = ConvertSILengthField(serial_interface.GetInLength());\n+\n+ // Debug logging\n+ ISIDevice::RunBuffer(buffer, buffer_length);\n+\n+ u32 buffer_position = 0;\n+ while (buffer_position < buffer_length)\n+ {\n+ BaseBoardCommand command = static_cast<BaseBoardCommand>(buffer[buffer_position]);\n+ buffer_position++;\n+\n+ switch (command)\n+ {\n+ case BaseBoardCommand::GCAM_Reset: // Returns ID and dip switches\n+ {\n+ const u32 id = Common::swap32(SI_AM_BASEBOARD | 0x100);\n+ std::memcpy(buffer, &id, sizeof(id));\n+ return sizeof(id);\n+ }\n+ break;\n+ case BaseBoardCommand::GCAM_Command:\n+ {\n+ u32 checksum = 0;\n+ for (u32 i = 0; i < buffer_length; ++i)\n+ checksum += buffer[i];\n+\n+ std::array<u8, 0x80> data_out{};\n+ u32 data_offset = 0;\n+\n+ static u32 dip_switch_1 = 0xFE;\n+ static u32 dip_switch_0 = 0xFF;\n+\n+ data_out[data_offset++] = 1;\n+ data_out[data_offset++] = 1;\n+\n+ u8* data_in = buffer + 2;\n+ u8* const data_in_end = buffer + buffer[buffer_position] + 2;\n+\n+ // Helper to check that iterating over data n times is safe,\n+ // i.e. *data++ at most lead to data.end()\n+ auto validate_data_in_out = [&](u32 n_in, u32 n_out, std::string_view command) -> bool {\n+ if (data_in + n_in > data_in_end)\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: data_in overflow in {}", command);\n+ else if (data_offset + n_out > data_out.size())\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: data_out overflow in {}", command);\n+ else\n+ return true;\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "Overflow details:\\n"\n+ " - data_in(begin={}, current={}, end={}, n_in={})\\n"\n+ " - data_out(offset={}, size={}, n_out={})\\n"\n+ " - buffer(position={}, length={})",\n+ fmt::ptr(buffer + 2), fmt::ptr(data_in), fmt::ptr(data_in_end), n_in,\n+ data_offset, data_out.size(), n_out, buffer_position, buffer_length);\n+ data_in = data_in_end;\n+ return false;\n+ };\n+\n+ while (data_in < data_in_end)\n+ {\n+ const u32 gcam_command = *data_in++;\n+ switch (GCAMCommand(gcam_command))\n+ {\n+ case GCAMCommand::StatusSwitches:\n+ {\n+ if (!validate_data_in_out(1, 4, "StatusSwitches"))\n+ break;\n+\n+ const u8 status = *data_in++;\n+ DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x10, {:02x} (READ STATUS&SWITCHES)",\n+ status);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x2;\n+\n+ // We read Test/Service from the JVS-I/O SwitchesInput instead\n+ //\n+ // const GCPadStatus pad_status = Pad::GetStatus(ISIDevice::m_device_number);\n+ // baseboard test/service switches\n+ // if (pad_status.button & PAD_BUTTON_Y)\t// Test\n+ // dip_switch_0 &= ~0x80;\n+ // if (pad_status.button & PAD_BUTTON_X)\t// Service\n+ // dip_switch_0 &= ~0x40;\n+\n+ // Horizontal Scanning Frequency switch\n+ // Required for F-Zero AX booting via Sega Boot\n+ if (AMMediaboard::GetGameType() == FZeroAX ||\n+ AMMediaboard::GetGameType() == FZeroAXMonster)\n+ {\n+ dip_switch_0 &= ~0x20;\n+ }\n+\n+ // Disable camera in MKGP1/2\n+ if (AMMediaboard::GetGameType() == MarioKartGP ||\n+ AMMediaboard::GetGameType() == MarioKartGP2)\n+ {\n+ dip_switch_0 &= ~0x10;\n+ }\n+\n+ data_out[data_offset++] = dip_switch_0;\n+ data_out[data_offset++] = dip_switch_1;\n+ break;\n+ }\n+ case GCAMCommand::SerialNumber:\n+ {\n+ if (!validate_data_in_out(1, 18, "SerialNumber"))\n+ break;\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x11, {:02x} (READ SERIAL NR)",\n+ *data_in);\n+ data_in++;\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 16;\n+ memcpy(data_out.data() + data_offset, "AADE-01B98394904", 16);\n+\n+ data_offset += 16;\n+ break;\n+ }\n+ case GCAMCommand::Unknown_12:\n+ if (!validate_data_in_out(2, 2, "Unknown_12"))\n+ break;\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x12, {:02x} {:02x}", data_in[0],\n+ data_in[1]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x00;\n+\n+ data_in += 2;\n+ break;\n+ case GCAMCommand::Unknown_14:\n+ if (!validate_data_in_out(2, 2, "Unknown_14"))\n+ break;\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x14, {:02x} {:02x}", data_in[0],\n+ data_in[1]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x00;\n+\n+ data_in += 2;\n+ break;\n+ case GCAMCommand::FirmVersion:\n+ if (!validate_data_in_out(1, 4, "FirmVersion"))\n+ break;\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x15, {:02x} (READ FIRM VERSION)",\n+ *data_in);\n+ data_in++;\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x02;\n+ // 00.26\n+ data_out[data_offset++] = 0x00;\n+ data_out[data_offset++] = 0x26;\n+ break;\n+ case GCAMCommand::FPGAVersion:\n+ if (!validate_data_in_out(1, 4, "FPGAVersion"))\n+ break;\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x16, {:02x} (READ FPGA VERSION)",\n+ *data_in);\n+ data_in++;\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x02;\n+ // 07.06\n+ data_out[data_offset++] = 0x07;\n+ data_out[data_offset++] = 0x06;\n+ break;\n+ case GCAMCommand::RegionSettings:\n+ {\n+ if (!validate_data_in_out(5, 0x16, "RegionSettings"))\n+ break;\n+\n+ // Used by SegaBoot for region checks (dev mode skips this check)\n+ // In some games this also controls the displayed language\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x1F, {:02x} {:02x} {:02x} {:02x} {:02x} (REGION)",\n+ data_in[0], data_in[1], data_in[2], data_in[3], data_in[4]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x14;\n+\n+ for (int i = 0; i < 0x14; ++i)\n+ data_out[data_offset++] = s_region_flags[i];\n+\n+ data_in += 5;\n+ }\n+ break;\n+ // No reply\n+ // Note: Always sends three bytes even though size is set to two\n+ case GCAMCommand::Unknown_21:\n+ {\n+ if (!validate_data_in_out(4, 0, "Unknown_21"))\n+ break;\n+\n+ DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x21, {:02x}, {:02x}, {:02x}, {:02x}",\n+ data_in[0], data_in[1], data_in[2], data_in[3]);\n+ data_in += 4;\n+ }\n+ break;\n+ // No reply\n+ // Note: Always sends six bytes\n+ case GCAMCommand::Unknown_22:\n+ {\n+ if (!validate_data_in_out(7, 0, "Unknown_22"))\n+ break;\n+\n+ DEBUG_LOG_FMT(\n+ SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x22, {:02x}, {:02x}, {:02x}, {:02x}, {:02x}, {:02x}, {:02x}",\n+ data_in[0], data_in[1], data_in[2], data_in[3], data_in[4], data_in[5], data_in[6]);\n+\n+ const u32 in_size = data_in[0] + 1;\n+ if (!validate_data_in_out(in_size, 0, "Unknown_22"))\n+ break;\n+ data_in += in_size;\n+ }\n+ break;\n+ case GCAMCommand::Unknown_23:\n+ if (!validate_data_in_out(2, 2, "Unknown_23"))\n+ break;\n+\n+ DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x23, {:02x} {:02x}", data_in[0],\n+ data_in[1]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x00;\n+\n+ data_in += 2;\n+ break;\n+ case GCAMCommand::Unknown_24:\n+ if (!validate_data_in_out(2, 2, "Unknown_24"))\n+ break;\n+\n+ DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x24, {:02x} {:02x}", data_in[0],\n+ data_in[1]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x00;\n+\n+ data_in += 2;\n+ break;\n+ case GCAMCommand::SerialA:\n+ {\n+ if (!validate_data_in_out(1, 0, "SerialA"))\n+ break;\n+\n+ u32 length = *data_in++;\n+ if (length)\n+ {\n+ if (!validate_data_in_out(length, 0, "SerialA"))\n+ break;\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x31, length=0x{:02x}, hexdump:\\n{}",\n+ length, HexDump(data_in, length));\n+\n+ // Serial - Wheel\n+ if (AMMediaboard::GetGameType() == MarioKartGP ||\n+ AMMediaboard::GetGameType() == MarioKartGP2)\n+ {\n+ if (!validate_data_in_out(10, 2, "SerialA (Wheel)"))\n+ break;\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x31, (WHEEL) {:02x}{:02x} {:02x}{:02x} {:02x} {:02x} "\n+ "{:02x} {:02x} {:02x} {:02x}",\n+ data_in[0], data_in[1], data_in[2], data_in[3], data_in[4], data_in[5],\n+ data_in[6], data_in[7], data_in[8], data_in[9]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x03;\n+\n+ switch (m_wheel_init)\n+ {\n+ case 0:\n+ if (!validate_data_in_out(0, 3, "SerialA (Wheel)"))\n+ break;\n+ data_out[data_offset++] = \'E\'; // Error\n+ data_out[data_offset++] = \'0\';\n+ data_out[data_offset++] = \'0\';\n+ m_wheel_init++;\n+ break;\n+ case 1:\n+ if (!validate_data_in_out(0, 3, "SerialA (Wheel)"))\n+ break;\n+ data_out[data_offset++] = \'C\'; // Power Off\n+ data_out[data_offset++] = \'0\';\n+ data_out[data_offset++] = \'6\';\n+ // Only turn on when a wheel is connected\n+ if (serial_interface.GetDeviceType(1) == SerialInterface::SIDEVICE_GC_STEERING)\n+ {\n+ m_wheel_init++;\n+ }\n+ break;\n+ case 2:\n+ if (!validate_data_in_out(0, 3, "SerialA (Wheel)"))\n+ break;\n+ data_out[data_offset++] = \'C\'; // Power On\n+ data_out[data_offset++] = \'0\';\n+ data_out[data_offset++] = \'1\';\n+ break;\n+ default:\n+ break;\n+ }\n+\n+ // u16 CenteringForce= ptr(6);\n+ // u16 FrictionForce = ptr(8);\n+ // u16 Roll = ptr(10);\n+ if (!validate_data_in_out(length, 0, "SerialA (Wheel)"))\n+ break;\n+ data_in += length;\n+ break;\n+ }\n+\n+ // Serial - Unknown\n+ if (AMMediaboard::GetGameType() == GekitouProYakyuu)\n+ {\n+ if (!validate_data_in_out(sizeof(u32), 0, "SerialA (Unknown)"))\n+ break;\n+ const u32 serial_command = Common::BitCastPtr<u32>(data_in);\n+\n+ if (serial_command == 0x00001000)\n+ {\n+ if (!validate_data_in_out(0, 5, "SerialA (Unknown)"))\n+ break;\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x03;\n+ data_out[data_offset++] = 1;\n+ data_out[data_offset++] = 2;\n+ data_out[data_offset++] = 3;\n+ }\n+\n+ if (!validate_data_in_out(length, 0, "SerialA (Unknown)"))\n+ break;\n+ data_in += length;\n+ break;\n+ }\n+\n+ // Serial IC-CARD / Serial Deck Reader\n+ if (AMMediaboard::GetGameType() == VirtuaStriker4 ||\n+ AMMediaboard::GetGameType() == VirtuaStriker4_2006 ||\n+ AMMediaboard::GetGameType() == KeyOfAvalon)\n+ {\n+ if (!validate_data_in_out(2, 0, "SerialA (IC-CARD)"))\n+ break;\n+ u32 serial_command = data_in[1];\n+\n+ ICCommand icco;\n+\n+ // Set default reply\n+ icco.pktcmd = gcam_command;\n+ icco.pktlen = 7;\n+ icco.fixed = 0x10;\n+ icco.command = serial_command;\n+ icco.flag = 0;\n+ icco.length = 2;\n+ icco.status = 0;\n+ icco.extlen = 0;\n+\n+ // Check for rest of data from the write pages command\n+ if (m_ic_write_size && m_ic_write_offset)\n+ {\n+ const u32 size = data_in[1];\n+\n+ if (!validate_data_in_out(size + 2, 0, "SerialA (IC-CARD)"))\n+ break;\n+ DEBUG_LOG_FMT(SERIALINTERFACE_CARD, "Command: {}", HexDump(data_in, size + 2));\n+\n+ INFO_LOG_FMT(\n+ SERIALINTERFACE_CARD,\n+ "GC-AM: Command 25 (IC-CARD) Write Pages: Off:{:x} Size:{:x} PSize:{:x}",\n+ m_ic_write_offset, m_ic_write_size, size);\n+\n+ memcpy(m_ic_write_buffer + m_ic_write_offset, data_in + 2, size);\n+\n+ m_ic_write_offset += size;\n+\n+ if (m_ic_write_offset > m_ic_write_size)\n+ {\n+ m_ic_write_offset = 0;\n+\n+ const u16 page = m_ic_write_buffer[5];\n+ const u16 count = m_ic_write_buffer[7];\n+\n+ memcpy(m_ic_card_data + page * 8, m_ic_write_buffer + 10, count * 8);\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 25 (IC-CARD) Write Pages:{} Count:{}({:x})", page,\n+ count, size);\n+\n+ icco.command = WritePages;\n+\n+ ICCardSendReply(&icco, data_out.data(), &data_offset);\n+ }\n+ if (!validate_data_in_out(length, 0, "SerialA (IC-CARD)"))\n+ break;\n+ data_in += length;\n+ break;\n+ }\n+\n+ switch (ICCARDCommand(serial_command))\n+ {\n+ case ICCARDCommand::GetStatus:\n+ icco.status = m_ic_card_state;\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (IC-CARD) Get Status:{:02x}", m_ic_card_state);\n+ break;\n+ case ICCARDCommand::SetBaudrate:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (IC-CARD) Set Baudrate");\n+ break;\n+ case ICCARDCommand::FieldOn:\n+ m_ic_card_state |= 0x10;\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (IC-CARD) Field On");\n+ break;\n+ case ICCARDCommand::InsertCheck:\n+ icco.status = m_ic_card_status;\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (IC-CARD) Insert Check:{:02x}", m_ic_card_status);\n+ break;\n+ case ICCARDCommand::AntiCollision:\n+ icco.extlen = 8;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ // Card ID\n+ icco.extdata[0] = 0x00;\n+ icco.extdata[1] = 0x00;\n+ icco.extdata[2] = 0x54;\n+ icco.extdata[3] = 0x4D;\n+ icco.extdata[4] = 0x50;\n+ icco.extdata[5] = 0x00;\n+ icco.extdata[6] = 0x00;\n+ icco.extdata[7] = 0x00;\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (IC-CARD) Anti Collision");\n+ break;\n+ case ICCARDCommand::SelectCard:\n+ icco.extlen = 8;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ // Session\n+ icco.extdata[0] = 0x00;\n+ icco.extdata[1] = m_ic_card_session;\n+ icco.extdata[2] = 0x00;\n+ icco.extdata[3] = 0x00;\n+ icco.extdata[4] = 0x00;\n+ icco.extdata[5] = 0x00;\n+ icco.extdata[6] = 0x00;\n+ icco.extdata[7] = 0x00;\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (IC-CARD) Select Card:{}",\n+ m_ic_card_session);\n+ break;\n+ case ICCARDCommand::ReadPage:\n+ case ICCARDCommand::ReadUseCount:\n+ {\n+ if (!validate_data_in_out(8, 0, "SerialA (IC-CARD)"))\n+ break;\n+ const u16 page = Common::swap16(data_in + 6) & 0xFF; // 255 is max page\n+\n+ icco.extlen = 8;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ memcpy(icco.extdata, m_ic_card_data + page * 8, 8);\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 31 (IC-CARD) Read Page:{}",\n+ page);\n+ break;\n+ }\n+ case ICCARDCommand::WritePage:\n+ {\n+ if (!validate_data_in_out(10, 0, "SerialA (IC-CARD)"))\n+ break;\n+ const u16 page = Common::swap16(data_in + 8) & 0xFF; // 255 is max page\n+\n+ // Write only one page\n+ if (page == 4)\n+ {\n+ icco.status = 0x80;\n+ }\n+ else\n+ {\n+ if (!validate_data_in_out(18, 0, "SerialA (IC-CARD)"))\n+ break;\n+ memcpy(m_ic_card_data + page * 8, data_in + 10, 8);\n+ }\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (IC-CARD) Write Page:{}",\n+ page);\n+ break;\n+ }\n+ case ICCARDCommand::DecreaseUseCount:\n+ {\n+ if (!validate_data_in_out(8, 0, "SerialA (IC-CARD)"))\n+ break;\n+ const u16 page = Common::swap16(data_in + 6) & 0xFF; // 255 is max page\n+\n+ icco.extlen = 2;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ auto ic_card_data = Common::BitCastPtr<u16>(m_ic_card_data + 0x28);\n+ ic_card_data = ic_card_data - 1;\n+\n+ // Counter\n+ icco.extdata[0] = m_ic_card_data[0x28];\n+ icco.extdata[1] = m_ic_card_data[0x29];\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 31 (IC-CARD) Decrease Use Count:{}", page);\n+ break;\n+ }\n+ case ICCARDCommand::ReadPages:\n+ {\n+ if (!validate_data_in_out(10, 0, "SerialA (IC-CARD)"))\n+ break;\n+ const u16 page = Common::swap16(data_in + 6) & 0xFF; // 255 is max page\n+ const u16 count = Common::swap16(data_in + 8);\n+\n+ const u32 offs = page * 8;\n+ u32 cnt = count * 8;\n+\n+ // Limit read size to not overwrite the reply buffer\n+ if (cnt > (u32)0x50 - data_offset)\n+ {\n+ cnt = 5 * 8;\n+ }\n+\n+ icco.extlen = cnt;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ memcpy(icco.extdata, m_ic_card_data + offs, cnt);\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 31 (IC-CARD) Read Pages:{} Count:{}", page, count);\n+ break;\n+ }\n+ case ICCARDCommand::WritePages:\n+ {\n+ if (!validate_data_in_out(10, 0, "SerialA (IC-CARD)"))\n+ break;\n+ const u16 pksize = length;\n+ const u16 size = Common::swap16(data_in + 2);\n+ const u16 page = Common::swap16(data_in + 6) & 0xFF; // 255 is max page\n+ const u16 count = Common::swap16(data_in + 8);\n+\n+ // We got a complete packet\n+ if (pksize - 5 == size)\n+ {\n+ if (page == 4) // Read Only Page, must return error\n+ {\n+ icco.status = 0x80;\n+ }\n+ else\n+ {\n+ if (page * 8 + count * 8 > sizeof(m_ic_card_data))\n+ {\n+ ERROR_LOG_FMT(\n+ SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (IC-CARD) Data overflow: Pages:{} Count:{}({:x})",\n+ page, count, size);\n+ }\n+ else\n+ {\n+ if (!validate_data_in_out(13 + count * 8, 0, "SerialA (IC-CARD)"))\n+ break;\n+ memcpy(m_ic_card_data + page * 8, data_in + 13, count * 8);\n+ }\n+ }\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (IC-CARD) Write Pages:{} Count:{}({:x})", page,\n+ count, size);\n+ }\n+ // VirtuaStriker 4 splits the writes over multiple packets\n+ else\n+ {\n+ if (!validate_data_in_out(2 + pksize, 0, "SerialA (IC-CARD)"))\n+ break;\n+ memcpy(m_ic_write_buffer, data_in + 2, pksize);\n+ m_ic_write_offset += pksize;\n+ m_ic_write_size = size;\n+ }\n+ break;\n+ }\n+ default:\n+ // Handle Deck Reader commands\n+ if (!validate_data_in_out(1, 0, "SerialA (DECK READER)"))\n+ break;\n+ serial_command = data_in[0];\n+ icco.command = serial_command;\n+ icco.flag = 0;\n+ switch (CDReaderCommand(serial_command))\n+ {\n+ case CDReaderCommand::ProgramVersion:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Program Version");\n+\n+ icco.extlen = (u32)strlen(s_cdr_program_version);\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ memcpy(icco.extdata, s_cdr_program_version, icco.extlen);\n+ break;\n+ case CDReaderCommand::BootVersion:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Boot Version");\n+\n+ icco.extlen = (u32)strlen(s_cdr_boot_version);\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ memcpy(icco.extdata, s_cdr_boot_version, icco.extlen);\n+ break;\n+ case CDReaderCommand::ShutterGet:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Shutter Get");\n+\n+ icco.extlen = 4;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ icco.extdata[0] = 0;\n+ icco.extdata[1] = 0;\n+ icco.extdata[2] = 0;\n+ icco.extdata[3] = 0;\n+ break;\n+ case CDReaderCommand::CameraCheck:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Camera Check");\n+\n+ icco.extlen = 6;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ icco.extdata[0] = 0x23;\n+ icco.extdata[1] = 0x28;\n+ icco.extdata[2] = 0x45;\n+ icco.extdata[3] = 0x29;\n+ icco.extdata[4] = 0x45;\n+ icco.extdata[5] = 0x29;\n+ break;\n+ case CDReaderCommand::ProgramChecksum:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Program Checksum");\n+\n+ icco.extlen = 4;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ icco.extdata[0] = 0x23;\n+ icco.extdata[1] = 0x28;\n+ icco.extdata[2] = 0x45;\n+ icco.extdata[3] = 0x29;\n+ break;\n+ case CDReaderCommand::BootChecksum:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Boot Checksum");\n+\n+ icco.extlen = 4;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ icco.extdata[0] = 0x23;\n+ icco.extdata[1] = 0x28;\n+ icco.extdata[2] = 0x45;\n+ icco.extdata[3] = 0x29;\n+ break;\n+ case CDReaderCommand::SelfTest:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (DECK READER) Self Test");\n+ icco.flag = 0x00;\n+ break;\n+ case CDReaderCommand::SensLock:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (DECK READER) Sens Lock");\n+ icco.flag = 0x01;\n+ break;\n+ case CDReaderCommand::SensCard:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (DECK READER) Sens Card");\n+ break;\n+ case CDReaderCommand::ShutterCard:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Shutter Card");\n+ break;\n+ case CDReaderCommand::ReadCard:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (DECK READER) Read Card");\n+\n+ icco.fixed = 0xAA;\n+ icco.flag = 0xAA;\n+ icco.extlen = sizeof(s_cdr_card_data);\n+ icco.length = 0x72;\n+ icco.status = Common::swap16(icco.extlen);\n+\n+ icco.pktlen += icco.extlen;\n+\n+ memcpy(icco.extdata, s_cdr_card_data, sizeof(s_cdr_card_data));\n+\n+ break;\n+ default:\n+ if (!validate_data_in_out(14, 0, "SerialA (DECK READER)"))\n+ break;\n+ WARN_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (IC-Card) {:02x} {:02x} {:02x} {:02x} {:02x} "\n+ "{:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x}",\n+ data_in[2], data_in[3], data_in[4], data_in[5], data_in[6],\n+ data_in[7], data_in[8], data_in[9], data_in[10], data_in[11],\n+ data_in[12], data_in[13]);\n+ break;\n+ }\n+ break;\n+ }\n+\n+ if (!validate_data_in_out(0, icco.pktlen + 2, "SerialA (IC-CARD)"))\n+ break;\n+ ICCardSendReply(&icco, data_out.data(), &data_offset);\n+\n+ if (!validate_data_in_out(2, 0, "SerialA (IC-CARD)"))\n+ break;\n+ data_in += length;\n+ break;\n+ }\n+ }\n+\n+ u32 command_offset = 0;\n+ while (command_offset < length)\n+ {\n+ // All commands are OR\'d with 0x80\n+ // Last byte is checksum which we don\'t care about\n+ if (!validate_data_in_out(command_offset + 4, 0, "SerialA"))\n+ break;\n+ const u32 serial_command = Common::swap32(data_in + command_offset) ^ 0x80000000;\n+\n+ if (AMMediaboard::GetGameType() == FZeroAX ||\n+ AMMediaboard::GetGameType() == FZeroAXMonster)\n+ {\n+ INFO_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x31 (MOTOR) Length:{:02x} Command:{:04x}({:02x})",\n+ length, (serial_command >> 8) & 0xFFFF, serial_command >> 24);\n+ }\n+ else\n+ {\n+ INFO_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x31 (SERIAL) Command:{:06x}",\n+ serial_command);\n+\n+ if (serial_command == 0x801000)\n+ {\n+ if (!validate_data_in_out(0, 4, "SerialA"))\n+ break;\n+ data_out[data_offset++] = 0x31;\n+ data_out[data_offset++] = 0x02;\n+ data_out[data_offset++] = 0xFF;\n+ data_out[data_offset++] = 0x01;\n+ }\n+ }\n+\n+ command_offset += sizeof(u32);\n+\n+ if (AMMediaboard::GetGameType() == FZeroAX ||\n+ AMMediaboard::GetGameType() == FZeroAXMonster)\n+ {\n+ // Status\n+ m_motor_reply[command_offset + 2] = 0;\n+ m_motor_reply[command_offset + 3] = 0;\n+\n+ // Error\n+ m_motor_reply[command_offset + 4] = 0;\n+\n+ switch (serial_command >> 24)\n+ {\n+ case 0:\n+ case 1: // Set Maximum?\n+ case 2:\n+ break;\n+\n+ // 0x00-0x40: left\n+ // 0x40-0x80: right\n+ case 4: // Move Steering Wheel\n+ // Left\n+ if (serial_command & 0x010000)\n+ {\n+ m_motor_force_y = -((s16)serial_command & 0xFF00);\n+ }\n+ else // Right\n+ {\n+ m_motor_force_y = (serial_command - 0x4000) & 0xFF00;\n+ }\n+\n+ m_motor_force_y *= 2;\n+\n+ // FFB\n+ if (m_motor_init == 2)\n+ {\n+ if (serial_interface.GetDeviceType(1) == SerialInterface::SIDEVICE_GC_STEERING)\n+ {\n+ const GCPadStatus pad_status = Pad::GetStatus(1);\n+ if (pad_status.isConnected)\n+ {\n+ const ControlState mapped_strength = (double)(m_motor_force_y >> 8) / 127.f;\n+\n+ Pad::Rumble(1, mapped_strength);\n+ INFO_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x31 (MOTOR) mapped_strength:{}",\n+ mapped_strength);\n+ }\n+ }\n+ }\n+ break;\n+ case 6: // nice\n+ case 9:\n+ default:\n+ break;\n+ // Switch back to normal controls\n+ case 7:\n+ m_motor_init = 2;\n+ break;\n+ // Reset\n+ case 0x7F:\n+ m_motor_init = 1;\n+ memset(m_motor_reply, 0, sizeof(m_motor_reply));\n+ break;\n+ }\n+\n+ // Checksum\n+ m_motor_reply[command_offset + 5] = m_motor_reply[command_offset + 2] ^\n+ m_motor_reply[command_offset + 3] ^\n+ m_motor_reply[command_offset + 4];\n+ }\n+ }\n+\n+ if (length == 0)\n+ {\n+ if (!validate_data_in_out(length, 2, "SerialA"))\n+ break;\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x00;\n+ }\n+ else\n+ {\n+ if (m_motor_init)\n+ {\n+ // Motor\n+ m_motor_reply[0] = gcam_command;\n+ m_motor_reply[1] = length; // Same out as in size\n+\n+ const u32 reply_size = m_motor_reply[1] + 2;\n+ if (!validate_data_in_out(length, reply_size, "SerialA"))\n+ break;\n+\n+ memcpy(data_out.data() + data_offset, m_motor_reply, reply_size);\n+ data_offset += reply_size;\n+ }\n+ }\n+\n+ if (!validate_data_in_out(length, 0, "SerialA"))\n+ {\n+ break;\n+ }\n+ data_in += length;\n+ break;\n+ }\n+ case GCAMCommand::SerialB:\n+ {\n+ DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 32 (CARD-Interface)");\n+ if (!validate_data_in_out(1, 0, "SerialB"))\n+ break;\n+ const u32 length = *data_in++;\n+ if (!validate_data_in_out(length, 0, "SerialB"))\n+ break;\n+ if (length)\n+ {\n+ // Send Card Reply\n+ if (length == 1 && data_in[0] == 0x05)\n+ {\n+ if (m_card_read_length)\n+ {\n+ if (!validate_data_in_out(0, 1, "SerialB"))\n+ break;\n+ data_out[data_offset++] = gcam_command;\n+ u32 read_length = m_card_read_length - m_card_read;\n+\n+ if (AMMediaboard::GetGameType() == FZeroAX)\n+ {\n+ read_length = std::min<u32>(read_length, 0x2F);\n+ }\n+\n+ if (!validate_data_in_out(0, 1, "SerialB"))\n+ break;\n+ data_out[data_offset++] = read_length; // 0x2F (max size per packet)\n+\n+ if (!validate_data_in_out(0, read_length, "SerialB"))\n+ break;\n+ memcpy(data_out.data() + data_offset, m_card_read_packet + m_card_read,\n+ read_length);\n+\n+ data_offset += read_length;\n+ m_card_read += read_length;\n+\n+ if (m_card_read >= m_card_read_length)\n+ m_card_read_length = 0;\n+\n+ data_in += length;\n+ break;\n+ }\n+\n+ if (!validate_data_in_out(0, 5, "SerialB"))\n+ break;\n+\n+ data_out[data_offset++] = gcam_command;\n+ const u32 command_length_offset = data_offset;\n+ data_out[data_offset++] = 0x00; // len\n+\n+ data_out[data_offset++] = 0x02;\n+ const u32 checksum_start = data_offset;\n+\n+ data_out[data_offset++] = 0x00; // 0x00 len\n+\n+ data_out[data_offset++] = m_card_command; // 0x01 command\n+\n+ switch (CARDCommand(m_card_command))\n+ {\n+ case CARDCommand::Init:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x00; // 0x02\n+ data_out[data_offset++] = 0x30; // 0x03\n+ break;\n+ case CARDCommand::GetState:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x20 | m_card_bit; // 0x02\n+\n+ // bit 0: Please take your card\n+ // bit 1: Endless waiting causes UNK_E to be called\n+ data_out[data_offset++] = 0x00; // 0x03\n+ break;\n+ case CARDCommand::Read:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x02; // 0x02\n+ data_out[data_offset++] = 0x53; // 0x03\n+ break;\n+ case CARDCommand::IsPresent:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x22; // 0x02\n+ data_out[data_offset++] = 0x30; // 0x03\n+ break;\n+ case CARDCommand::Write:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x02; // 0x02\n+ data_out[data_offset++] = 0x00; // 0x03\n+ break;\n+ case CARDCommand::SetPrintParam:\n+ case CARDCommand::RegisterFont:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x00; // 0x02\n+ data_out[data_offset++] = 0x00; // 0x03\n+ break;\n+ case CARDCommand::WriteInfo:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x02; // 0x02\n+ data_out[data_offset++] = 0x00; // 0x03\n+ break;\n+ case CARDCommand::Erase:\n+ // TODO: CARDCommand::Erase is not handled.\n+ break;\n+ case CARDCommand::Eject:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ if (AMMediaboard::GetGameType() == FZeroAX)\n+ {\n+ data_out[data_offset++] = 0x01; // 0x02\n+ }\n+ else\n+ {\n+ data_out[data_offset++] = 0x31; // 0x02\n+ }\n+ data_out[data_offset++] = 0x30; // 0x03\n+ break;\n+ case CARDCommand::Clean:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x02; // 0x02\n+ data_out[data_offset++] = 0x00; // 0x03\n+ break;\n+ case CARDCommand::Load:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x02; // 0x02\n+ data_out[data_offset++] = 0x30; // 0x03\n+ break;\n+ case CARDCommand::SetShutter:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x00; // 0x02\n+ data_out[data_offset++] = 0x00; // 0x03\n+ break;\n+ }\n+\n+ if (!validate_data_in_out(0, 3, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x30; // 0x04\n+ data_out[data_offset++] = 0x00; // 0x05\n+\n+ data_out[data_offset++] = 0x03; // 0x06\n+\n+ data_out[checksum_start] = data_offset - checksum_start; // 0x00 len\n+\n+ if (!validate_data_in_out(0, 1, "SerialB"))\n+ break;\n+ data_out[data_offset] = 0; // 0x07\n+ for (u32 i = 0; i < data_out[checksum_start]; ++i)\n+ data_out[data_offset] ^= data_out[checksum_start + i];\n+\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_offset++;\n+\n+ data_out[command_length_offset] = data_out[checksum_start] + 2;\n+ }\n+ else\n+ {\n+ for (u32 i = 0; i < length; ++i)\n+ m_card_buffer[m_card_offset + i] = data_in[i];\n+\n+ m_card_offset += length;\n+\n+ // Check if we got a complete command\n+ if (m_card_buffer[0] == 0x02)\n+ {\n+ if (m_card_buffer[1] == m_card_offset - 2)\n+ {\n+ if (m_card_buffer[m_card_offset - 2] == 0x03)\n+ {\n+ m_card_command = m_card_buffer[2];\n+\n+ switch (CARDCommand(m_card_command))\n+ {\n+ case CARDCommand::Init:\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD Init");\n+\n+ m_card_write_length = 0;\n+ m_card_bit = 0;\n+ m_card_memory_size = 0;\n+ m_card_state_call_count = 0;\n+ break;\n+ case CARDCommand::GetState:\n+ {\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD GetState({:02X})",\n+ m_card_bit);\n+\n+ if (m_card_memory_size == 0)\n+ {\n+ const std::string card_filename(\n+ fmt::format("{}tricard_{}.bin", File::GetUserPath(D_TRIUSER_IDX),\n+ SConfig::GetInstance().GetGameID()));\n+\n+ if (File::Exists(card_filename))\n+ {\n+ File::IOFile card(card_filename, "rb+");\n+ m_card_memory_size = (u32)card.GetSize();\n+\n+ card.ReadBytes(m_card_memory, m_card_memory_size);\n+ card.Close();\n+\n+ m_card_is_inserted = true;\n+ }\n+ }\n+\n+ if (AMMediaboard::GetGameType() == FZeroAX && m_card_memory_size)\n+ {\n+ m_card_state_call_count++;\n+ if (m_card_state_call_count > 10)\n+ {\n+ if (m_card_bit & 2)\n+ m_card_bit &= ~2u;\n+ else\n+ m_card_bit |= 2;\n+\n+ m_card_state_call_count = 0;\n+ }\n+ }\n+\n+ if (m_card_clean == 1)\n+ {\n+ m_card_clean = 2;\n+ }\n+ else if (m_card_clean == 2)\n+ {\n+ const std::string card_filename(\n+ fmt::format("{}tricard_{}.bin", File::GetUserPath(D_TRIUSER_IDX),\n+ SConfig::GetInstance().GetGameID()));\n+\n+ if (File::Exists(card_filename))\n+ {\n+ m_card_memory_size = (u32)File::GetSize(card_filename);\n+ if (m_card_memory_size)\n+ {\n+ if (AMMediaboard::GetGameType() == FZeroAX)\n+ {\n+ m_card_bit = 2;\n+ }\n+ else\n+ {\n+ m_card_bit = 1;\n+ }\n+ }\n+ }\n+ m_card_clean = 0;\n+ }\n+ break;\n+ }\n+ case CARDCommand::IsPresent:\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD IsPresent");\n+ break;\n+ case CARDCommand::RegisterFont:\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD RegisterFont");\n+ break;\n+ case CARDCommand::Load:\n+ {\n+ u8 mode = m_card_buffer[6];\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD Load({:02X})",\n+ mode);\n+ break;\n+ }\n+ case CARDCommand::Clean:\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD Clean");\n+ m_card_clean = 1;\n+ break;\n+ case CARDCommand::Read:\n+ {\n+ const u8 mode = m_card_buffer[6];\n+ const u8 bitmode = m_card_buffer[7];\n+ const u8 track = m_card_buffer[8];\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command CARD Read({:02X},{:02X},{:02X})", mode,\n+ bitmode, track);\n+\n+ // Prepare read packet\n+ memset(m_card_read_packet, 0, 0xDB);\n+ u32 packet_offset = 0;\n+\n+ const std::string card_filename(\n+ fmt::format("{}tricard_{}.bin", File::GetUserPath(D_TRIUSER_IDX),\n+ SConfig::GetInstance().GetGameID()));\n+\n+ if (File::Exists(card_filename))\n+ {\n+ File::IOFile card(card_filename, "rb+");\n+ if (m_card_memory_size == 0)\n+ {\n+ m_card_memory_size = (u32)card.GetSize();\n+ }\n+\n+ card.ReadBytes(m_card_memory, m_card_memory_size);\n+ card.Close();\n+\n+ m_card_is_inserted = true;\n+ }\n+\n+ m_card_read_packet[packet_offset++] = 0x02; // SUB CMD\n+ m_card_read_packet[packet_offset++] = 0x00; // SUB CMDLen\n+\n+ m_card_read_packet[packet_offset++] = 0x33; // CARD CMD\n+\n+ if (m_card_is_inserted) // CARD Status\n+ {\n+ m_card_read_packet[packet_offset++] = 0x31;\n+ }\n+ else\n+ {\n+ m_card_read_packet[packet_offset++] = 0x30;\n+ }\n+\n+ m_card_read_packet[packet_offset++] = 0x30;\n+ m_card_read_packet[packet_offset++] = 0x30;\n+\n+ // Data reply\n+ memcpy(m_card_read_packet + packet_offset, m_card_memory, m_card_memory_size);', 'path': 'Source/Core/Core/HW/SI/SI_DeviceAMBaseboard.cpp', 'position': 1498, 'original_position': 1390, 'commit_id': '510fec88be973bc870021f2d0347593cdc285864', 'user': {'login': 'JosJuice', 'id': 6716818, 'node_id': 'MDQ6VXNlcjY3MTY4MTg=', 'avatar_url': 'https://avatars.githubusercontent.com/u/6716818?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/JosJuice', 'html_url': 'https://github.com/JosJuice', 'followers_url': 'https://api.github.com/users/JosJuice/followers', 'following_url': 'https://api.github.com/users/JosJuice/following{/other_user}', 'gists_url': 'https://api.github.com/users/JosJuice/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/JosJuice/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/JosJuice/subscriptions', 'organizations_url': 'https://api.github.com/users/JosJuice/orgs', 'repos_url': 'https://api.github.com/users/JosJuice/repos', 'events_url': 'https://api.github.com/users/JosJuice/events{/privacy}', 'received_events_url': 'https://api.github.com/users/JosJuice/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'body': '`m_card_memory_size` is still not checked.', 'created_at': '2026-01-31T13:30:15Z', 'updated_at': '2026-01-31T13:33:12Z', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/13844#discussion_r2749563661', 'pull_request_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13844', 'author_association': 'MEMBER', '_links': {'self': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2749563661'}, 'html': {'href': 'https://github.com/dolphin-emu/dolphin/pull/13844#discussion_r2749563661'}, 'pull_request': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13844'}}, 'original_commit_id': '2415f0c1c471b2446a2144a1a2b643d8a77d8cc7', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2749563661/reactions', 'total_count': 0, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 0, 'confused': 0, 'heart': 0, 'rocket': 0, 'eyes': 0}, 'in_reply_to_id': 2725629971}, {'id': 2749564419, 'node_id': 'PRRC_kwDOALCn2M6j4wYD', 'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2749564419', 'pull_request_review_id': 3732508353, 'diff_hunk': '@@ -0,0 +1,2501 @@\n+// Copyright 2025 Dolphin Emulator Project\n+// SPDX-License-Identifier: GPL-2.0-or-later\n+\n+#include "Core/HW/SI/SI_DeviceAMBaseboard.h"\n+\n+#include <algorithm>\n+#include <numeric>\n+#include <string>\n+\n+#include <fmt/format.h>\n+\n+#include "Common/Buffer.h"\n+#include "Common/CommonTypes.h"\n+#include "Common/FileUtil.h"\n+#include "Common/IOFile.h"\n+#include "Common/Logging/Log.h"\n+#include "Common/MsgHandler.h"\n+#include "Common/Swap.h"\n+\n+#include "Core/Boot/Boot.h"\n+#include "Core/BootManager.h"\n+#include "Core/Config/MainSettings.h"\n+#include "Core/ConfigManager.h"\n+#include "Core/Core.h"\n+#include "Core/CoreTiming.h"\n+#include "Core/HW/DVD/AMMediaboard.h"\n+#include "Core/HW/DVD/DVDInterface.h"\n+#include "Core/HW/EXI/EXI.h"\n+#include "Core/HW/GCPad.h"\n+#include "Core/HW/MMIO.h"\n+#include "Core/HW/Memmap.h"\n+#include "Core/HW/ProcessorInterface.h"\n+#include "Core/HW/SI/SI.h"\n+#include "Core/HW/SI/SI_Device.h"\n+#include "Core/HW/SI/SI_DeviceGCController.h"\n+#include "Core/HW/SystemTimers.h"\n+#include "Core/Movie.h"\n+#include "Core/NetPlayProto.h"\n+#include "Core/System.h"\n+\n+#include "InputCommon/GCPadStatus.h"\n+\n+namespace SerialInterface\n+{\n+void JVSIOMessage::Start(int node)\n+{\n+ m_last_start = m_pointer;\n+ const u8 header[3] = {0xE0, (u8)node, 0};\n+ m_checksum = 0;\n+ AddData(header, 3, 1);\n+}\n+\n+void JVSIOMessage::AddData(const u8* dst, size_t len, int sync = 0)\n+{\n+ if (m_pointer + len >= sizeof(m_message))\n+ {\n+ PanicAlertFmt("JVSIOMessage overrun!");\n+ return;\n+ }\n+\n+ while (len--)\n+ {\n+ const u8 c = *dst++;\n+ if (!sync && ((c == 0xE0) || (c == 0xD0)))\n+ {\n+ if (m_pointer + 2 > sizeof(m_message))\n+ {\n+ PanicAlertFmt("JVSIOMessage overrun!");\n+ break;\n+ }\n+ m_message[m_pointer++] = 0xD0;\n+ m_message[m_pointer++] = c - 1;\n+ }\n+ else\n+ {\n+ if (m_pointer >= sizeof(m_message))\n+ {\n+ PanicAlertFmt("JVSIOMessage overrun!");\n+ break;\n+ }\n+ m_message[m_pointer++] = c;\n+ }\n+\n+ if (!sync)\n+ m_checksum += c;\n+ sync = 0;\n+ }\n+}\n+\n+void JVSIOMessage::AddData(const void* data, size_t len)\n+{\n+ AddData(static_cast<const u8*>(data), len);\n+}\n+\n+void JVSIOMessage::AddData(const char* data)\n+{\n+ AddData(data, strlen(data));\n+}\n+\n+void JVSIOMessage::AddData(u32 n)\n+{\n+ const u8 cs = n;\n+ AddData(&cs, 1);\n+}\n+\n+void JVSIOMessage::End()\n+{\n+ const u32 len = m_pointer - m_last_start;\n+ if (m_last_start + 2 < sizeof(m_message) && len >= 3)\n+ {\n+ m_message[m_last_start + 2] = len - 2; // assuming len <0xD0\n+ AddData(m_checksum + len - 2);\n+ }\n+ else\n+ {\n+ PanicAlertFmt("JVSIOMessage: Not enough space for checksum!");\n+ }\n+}\n+\n+static constexpr u8 CheckSumXOR(const u8* data, u32 length)\n+{\n+ return std::accumulate(data, data + length, u8{}, std::bit_xor());\n+}\n+\n+static constexpr char s_cdr_program_version[] = {" Version 1.22,2003/09/19,171-8213B"};\n+static constexpr char s_cdr_boot_version[] = {" Version 1.04,2003/06/17,171-8213B"};\n+static constexpr u8 s_cdr_card_data[] = {\n+ 0x00, 0x6E, 0x00, 0x00, 0x01, 0x00, 0x00, 0x06, 0x00, 0x00, 0x07, 0x00, 0x00, 0x0B,\n+ 0x00, 0x00, 0x0E, 0x00, 0x00, 0x10, 0x00, 0x00, 0x17, 0x00, 0x00, 0x19, 0x00, 0x00,\n+ 0x1A, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x20, 0x00,\n+ 0x00, 0x22, 0x00, 0x00, 0x23, 0x00, 0x00, 0x24, 0x00, 0x00, 0x27, 0x00, 0x00, 0x28,\n+ 0x00, 0x00, 0x2C, 0x00, 0x00, 0x2F, 0x00, 0x00, 0x34, 0x00, 0x00, 0x35, 0x00, 0x00,\n+ 0x37, 0x00, 0x00, 0x38, 0x00, 0x00, 0x39, 0x00, 0x00, 0x3D, 0x00};\n+\n+const constexpr u8 s_region_flags[] = "\\x00\\x00\\x30\\x00"\n+ // "\\x01\\xfe\\x00\\x00" // JAPAN\n+ "\\x02\\xfd\\x00\\x00" // USA\n+ //"\\x03\\xfc\\x00\\x00" // export\n+ "\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff";\n+// AM-Baseboard device on SI\n+CSIDevice_AMBaseboard::CSIDevice_AMBaseboard(Core::System& system, SIDevices device,\n+ int device_number)\n+ : ISIDevice(system, device, device_number)\n+{\n+ // Card ID\n+ m_ic_card_data[0x20] = 0x95;\n+ m_ic_card_data[0x21] = 0x71;\n+\n+ if (AMMediaboard::GetGameType() == KeyOfAvalon)\n+ {\n+ m_ic_card_data[0x22] = 0x26;\n+ m_ic_card_data[0x23] = 0x40;\n+ }\n+ else if (AMMediaboard::GetGameType() == VirtuaStriker4)\n+ {\n+ m_ic_card_data[0x22] = 0x44;\n+ m_ic_card_data[0x23] = 0x00;\n+ }\n+\n+ // Use count\n+ m_ic_card_data[0x28] = 0xFF;\n+ m_ic_card_data[0x29] = 0xFF;\n+}\n+\n+constexpr u32 SI_XFER_LENGTH_MASK = 0x7f;\n+\n+// Translate [0,1,2,...,126,127] to [128,1,2,...,126,127]\n+constexpr s32 ConvertSILengthField(u32 field)\n+{\n+ return ((field - 1) & SI_XFER_LENGTH_MASK) + 1;\n+}\n+\n+void CSIDevice_AMBaseboard::ICCardSendReply(ICCommand* iccommand, u8* buffer, u32* length)\n+{\n+ iccommand->status = Common::swap16(iccommand->status);\n+\n+ const auto iccommand_data = reinterpret_cast<const u8*>(iccommand);\n+ const u8 crc = CheckSumXOR(iccommand_data + 2, iccommand->pktlen - 1);\n+\n+ for (u32 i = 0; i < iccommand->pktlen + 1; ++i)\n+ {\n+ buffer[(*length)++] = iccommand_data[i];\n+ }\n+\n+ buffer[(*length)++] = crc;\n+}\n+\n+void CSIDevice_AMBaseboard::SwapBuffers(u8* buffer, u32* buffer_length)\n+{\n+ memcpy(m_last[1], buffer, 0x80); // Save current buffer\n+ memcpy(buffer, m_last[0], 0x80); // Load previous buffer\n+ memcpy(m_last[0], m_last[1], 0x80); // Update history\n+\n+ m_lastptr[1] = *buffer_length; // Swap lengths\n+ *buffer_length = m_lastptr[0];\n+ m_lastptr[0] = m_lastptr[1];\n+}\n+\n+int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length)\n+{\n+ const auto& serial_interface = m_system.GetSerialInterface();\n+ u32 buffer_length = ConvertSILengthField(serial_interface.GetInLength());\n+\n+ // Debug logging\n+ ISIDevice::RunBuffer(buffer, buffer_length);\n+\n+ u32 buffer_position = 0;\n+ while (buffer_position < buffer_length)\n+ {\n+ BaseBoardCommand command = static_cast<BaseBoardCommand>(buffer[buffer_position]);\n+ buffer_position++;\n+\n+ switch (command)\n+ {\n+ case BaseBoardCommand::GCAM_Reset: // Returns ID and dip switches\n+ {\n+ const u32 id = Common::swap32(SI_AM_BASEBOARD | 0x100);\n+ std::memcpy(buffer, &id, sizeof(id));\n+ return sizeof(id);\n+ }\n+ break;\n+ case BaseBoardCommand::GCAM_Command:\n+ {\n+ u32 checksum = 0;\n+ for (u32 i = 0; i < buffer_length; ++i)\n+ checksum += buffer[i];\n+\n+ std::array<u8, 0x80> data_out{};\n+ u32 data_offset = 0;\n+\n+ static u32 dip_switch_1 = 0xFE;\n+ static u32 dip_switch_0 = 0xFF;\n+\n+ data_out[data_offset++] = 1;\n+ data_out[data_offset++] = 1;\n+\n+ u8* data_in = buffer + 2;\n+ u8* const data_in_end = buffer + buffer[buffer_position] + 2;\n+\n+ // Helper to check that iterating over data n times is safe,\n+ // i.e. *data++ at most lead to data.end()\n+ auto validate_data_in_out = [&](u32 n_in, u32 n_out, std::string_view command) -> bool {\n+ if (data_in + n_in > data_in_end)\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: data_in overflow in {}", command);\n+ else if (data_offset + n_out > data_out.size())\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: data_out overflow in {}", command);\n+ else\n+ return true;\n+ ERROR_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "Overflow details:\\n"\n+ " - data_in(begin={}, current={}, end={}, n_in={})\\n"\n+ " - data_out(offset={}, size={}, n_out={})\\n"\n+ " - buffer(position={}, length={})",\n+ fmt::ptr(buffer + 2), fmt::ptr(data_in), fmt::ptr(data_in_end), n_in,\n+ data_offset, data_out.size(), n_out, buffer_position, buffer_length);\n+ data_in = data_in_end;\n+ return false;\n+ };\n+\n+ while (data_in < data_in_end)\n+ {\n+ const u32 gcam_command = *data_in++;\n+ switch (GCAMCommand(gcam_command))\n+ {\n+ case GCAMCommand::StatusSwitches:\n+ {\n+ if (!validate_data_in_out(1, 4, "StatusSwitches"))\n+ break;\n+\n+ const u8 status = *data_in++;\n+ DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x10, {:02x} (READ STATUS&SWITCHES)",\n+ status);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x2;\n+\n+ // We read Test/Service from the JVS-I/O SwitchesInput instead\n+ //\n+ // const GCPadStatus pad_status = Pad::GetStatus(ISIDevice::m_device_number);\n+ // baseboard test/service switches\n+ // if (pad_status.button & PAD_BUTTON_Y)\t// Test\n+ // dip_switch_0 &= ~0x80;\n+ // if (pad_status.button & PAD_BUTTON_X)\t// Service\n+ // dip_switch_0 &= ~0x40;\n+\n+ // Horizontal Scanning Frequency switch\n+ // Required for F-Zero AX booting via Sega Boot\n+ if (AMMediaboard::GetGameType() == FZeroAX ||\n+ AMMediaboard::GetGameType() == FZeroAXMonster)\n+ {\n+ dip_switch_0 &= ~0x20;\n+ }\n+\n+ // Disable camera in MKGP1/2\n+ if (AMMediaboard::GetGameType() == MarioKartGP ||\n+ AMMediaboard::GetGameType() == MarioKartGP2)\n+ {\n+ dip_switch_0 &= ~0x10;\n+ }\n+\n+ data_out[data_offset++] = dip_switch_0;\n+ data_out[data_offset++] = dip_switch_1;\n+ break;\n+ }\n+ case GCAMCommand::SerialNumber:\n+ {\n+ if (!validate_data_in_out(1, 18, "SerialNumber"))\n+ break;\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x11, {:02x} (READ SERIAL NR)",\n+ *data_in);\n+ data_in++;\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 16;\n+ memcpy(data_out.data() + data_offset, "AADE-01B98394904", 16);\n+\n+ data_offset += 16;\n+ break;\n+ }\n+ case GCAMCommand::Unknown_12:\n+ if (!validate_data_in_out(2, 2, "Unknown_12"))\n+ break;\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x12, {:02x} {:02x}", data_in[0],\n+ data_in[1]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x00;\n+\n+ data_in += 2;\n+ break;\n+ case GCAMCommand::Unknown_14:\n+ if (!validate_data_in_out(2, 2, "Unknown_14"))\n+ break;\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x14, {:02x} {:02x}", data_in[0],\n+ data_in[1]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x00;\n+\n+ data_in += 2;\n+ break;\n+ case GCAMCommand::FirmVersion:\n+ if (!validate_data_in_out(1, 4, "FirmVersion"))\n+ break;\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x15, {:02x} (READ FIRM VERSION)",\n+ *data_in);\n+ data_in++;\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x02;\n+ // 00.26\n+ data_out[data_offset++] = 0x00;\n+ data_out[data_offset++] = 0x26;\n+ break;\n+ case GCAMCommand::FPGAVersion:\n+ if (!validate_data_in_out(1, 4, "FPGAVersion"))\n+ break;\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x16, {:02x} (READ FPGA VERSION)",\n+ *data_in);\n+ data_in++;\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x02;\n+ // 07.06\n+ data_out[data_offset++] = 0x07;\n+ data_out[data_offset++] = 0x06;\n+ break;\n+ case GCAMCommand::RegionSettings:\n+ {\n+ if (!validate_data_in_out(5, 0x16, "RegionSettings"))\n+ break;\n+\n+ // Used by SegaBoot for region checks (dev mode skips this check)\n+ // In some games this also controls the displayed language\n+ NOTICE_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x1F, {:02x} {:02x} {:02x} {:02x} {:02x} (REGION)",\n+ data_in[0], data_in[1], data_in[2], data_in[3], data_in[4]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x14;\n+\n+ for (int i = 0; i < 0x14; ++i)\n+ data_out[data_offset++] = s_region_flags[i];\n+\n+ data_in += 5;\n+ }\n+ break;\n+ // No reply\n+ // Note: Always sends three bytes even though size is set to two\n+ case GCAMCommand::Unknown_21:\n+ {\n+ if (!validate_data_in_out(4, 0, "Unknown_21"))\n+ break;\n+\n+ DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x21, {:02x}, {:02x}, {:02x}, {:02x}",\n+ data_in[0], data_in[1], data_in[2], data_in[3]);\n+ data_in += 4;\n+ }\n+ break;\n+ // No reply\n+ // Note: Always sends six bytes\n+ case GCAMCommand::Unknown_22:\n+ {\n+ if (!validate_data_in_out(7, 0, "Unknown_22"))\n+ break;\n+\n+ DEBUG_LOG_FMT(\n+ SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x22, {:02x}, {:02x}, {:02x}, {:02x}, {:02x}, {:02x}, {:02x}",\n+ data_in[0], data_in[1], data_in[2], data_in[3], data_in[4], data_in[5], data_in[6]);\n+\n+ const u32 in_size = data_in[0] + 1;\n+ if (!validate_data_in_out(in_size, 0, "Unknown_22"))\n+ break;\n+ data_in += in_size;\n+ }\n+ break;\n+ case GCAMCommand::Unknown_23:\n+ if (!validate_data_in_out(2, 2, "Unknown_23"))\n+ break;\n+\n+ DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x23, {:02x} {:02x}", data_in[0],\n+ data_in[1]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x00;\n+\n+ data_in += 2;\n+ break;\n+ case GCAMCommand::Unknown_24:\n+ if (!validate_data_in_out(2, 2, "Unknown_24"))\n+ break;\n+\n+ DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x24, {:02x} {:02x}", data_in[0],\n+ data_in[1]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x00;\n+\n+ data_in += 2;\n+ break;\n+ case GCAMCommand::SerialA:\n+ {\n+ if (!validate_data_in_out(1, 0, "SerialA"))\n+ break;\n+\n+ u32 length = *data_in++;\n+ if (length)\n+ {\n+ if (!validate_data_in_out(length, 0, "SerialA"))\n+ break;\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x31, length=0x{:02x}, hexdump:\\n{}",\n+ length, HexDump(data_in, length));\n+\n+ // Serial - Wheel\n+ if (AMMediaboard::GetGameType() == MarioKartGP ||\n+ AMMediaboard::GetGameType() == MarioKartGP2)\n+ {\n+ if (!validate_data_in_out(10, 2, "SerialA (Wheel)"))\n+ break;\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x31, (WHEEL) {:02x}{:02x} {:02x}{:02x} {:02x} {:02x} "\n+ "{:02x} {:02x} {:02x} {:02x}",\n+ data_in[0], data_in[1], data_in[2], data_in[3], data_in[4], data_in[5],\n+ data_in[6], data_in[7], data_in[8], data_in[9]);\n+\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x03;\n+\n+ switch (m_wheel_init)\n+ {\n+ case 0:\n+ if (!validate_data_in_out(0, 3, "SerialA (Wheel)"))\n+ break;\n+ data_out[data_offset++] = \'E\'; // Error\n+ data_out[data_offset++] = \'0\';\n+ data_out[data_offset++] = \'0\';\n+ m_wheel_init++;\n+ break;\n+ case 1:\n+ if (!validate_data_in_out(0, 3, "SerialA (Wheel)"))\n+ break;\n+ data_out[data_offset++] = \'C\'; // Power Off\n+ data_out[data_offset++] = \'0\';\n+ data_out[data_offset++] = \'6\';\n+ // Only turn on when a wheel is connected\n+ if (serial_interface.GetDeviceType(1) == SerialInterface::SIDEVICE_GC_STEERING)\n+ {\n+ m_wheel_init++;\n+ }\n+ break;\n+ case 2:\n+ if (!validate_data_in_out(0, 3, "SerialA (Wheel)"))\n+ break;\n+ data_out[data_offset++] = \'C\'; // Power On\n+ data_out[data_offset++] = \'0\';\n+ data_out[data_offset++] = \'1\';\n+ break;\n+ default:\n+ break;\n+ }\n+\n+ // u16 CenteringForce= ptr(6);\n+ // u16 FrictionForce = ptr(8);\n+ // u16 Roll = ptr(10);\n+ if (!validate_data_in_out(length, 0, "SerialA (Wheel)"))\n+ break;\n+ data_in += length;\n+ break;\n+ }\n+\n+ // Serial - Unknown\n+ if (AMMediaboard::GetGameType() == GekitouProYakyuu)\n+ {\n+ if (!validate_data_in_out(sizeof(u32), 0, "SerialA (Unknown)"))\n+ break;\n+ const u32 serial_command = Common::BitCastPtr<u32>(data_in);\n+\n+ if (serial_command == 0x00001000)\n+ {\n+ if (!validate_data_in_out(0, 5, "SerialA (Unknown)"))\n+ break;\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x03;\n+ data_out[data_offset++] = 1;\n+ data_out[data_offset++] = 2;\n+ data_out[data_offset++] = 3;\n+ }\n+\n+ if (!validate_data_in_out(length, 0, "SerialA (Unknown)"))\n+ break;\n+ data_in += length;\n+ break;\n+ }\n+\n+ // Serial IC-CARD / Serial Deck Reader\n+ if (AMMediaboard::GetGameType() == VirtuaStriker4 ||\n+ AMMediaboard::GetGameType() == VirtuaStriker4_2006 ||\n+ AMMediaboard::GetGameType() == KeyOfAvalon)\n+ {\n+ if (!validate_data_in_out(2, 0, "SerialA (IC-CARD)"))\n+ break;\n+ u32 serial_command = data_in[1];\n+\n+ ICCommand icco;\n+\n+ // Set default reply\n+ icco.pktcmd = gcam_command;\n+ icco.pktlen = 7;\n+ icco.fixed = 0x10;\n+ icco.command = serial_command;\n+ icco.flag = 0;\n+ icco.length = 2;\n+ icco.status = 0;\n+ icco.extlen = 0;\n+\n+ // Check for rest of data from the write pages command\n+ if (m_ic_write_size && m_ic_write_offset)\n+ {\n+ const u32 size = data_in[1];\n+\n+ if (!validate_data_in_out(size + 2, 0, "SerialA (IC-CARD)"))\n+ break;\n+ DEBUG_LOG_FMT(SERIALINTERFACE_CARD, "Command: {}", HexDump(data_in, size + 2));\n+\n+ INFO_LOG_FMT(\n+ SERIALINTERFACE_CARD,\n+ "GC-AM: Command 25 (IC-CARD) Write Pages: Off:{:x} Size:{:x} PSize:{:x}",\n+ m_ic_write_offset, m_ic_write_size, size);\n+\n+ memcpy(m_ic_write_buffer + m_ic_write_offset, data_in + 2, size);\n+\n+ m_ic_write_offset += size;\n+\n+ if (m_ic_write_offset > m_ic_write_size)\n+ {\n+ m_ic_write_offset = 0;\n+\n+ const u16 page = m_ic_write_buffer[5];\n+ const u16 count = m_ic_write_buffer[7];\n+\n+ memcpy(m_ic_card_data + page * 8, m_ic_write_buffer + 10, count * 8);\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 25 (IC-CARD) Write Pages:{} Count:{}({:x})", page,\n+ count, size);\n+\n+ icco.command = WritePages;\n+\n+ ICCardSendReply(&icco, data_out.data(), &data_offset);\n+ }\n+ if (!validate_data_in_out(length, 0, "SerialA (IC-CARD)"))\n+ break;\n+ data_in += length;\n+ break;\n+ }\n+\n+ switch (ICCARDCommand(serial_command))\n+ {\n+ case ICCARDCommand::GetStatus:\n+ icco.status = m_ic_card_state;\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (IC-CARD) Get Status:{:02x}", m_ic_card_state);\n+ break;\n+ case ICCARDCommand::SetBaudrate:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (IC-CARD) Set Baudrate");\n+ break;\n+ case ICCARDCommand::FieldOn:\n+ m_ic_card_state |= 0x10;\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (IC-CARD) Field On");\n+ break;\n+ case ICCARDCommand::InsertCheck:\n+ icco.status = m_ic_card_status;\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (IC-CARD) Insert Check:{:02x}", m_ic_card_status);\n+ break;\n+ case ICCARDCommand::AntiCollision:\n+ icco.extlen = 8;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ // Card ID\n+ icco.extdata[0] = 0x00;\n+ icco.extdata[1] = 0x00;\n+ icco.extdata[2] = 0x54;\n+ icco.extdata[3] = 0x4D;\n+ icco.extdata[4] = 0x50;\n+ icco.extdata[5] = 0x00;\n+ icco.extdata[6] = 0x00;\n+ icco.extdata[7] = 0x00;\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (IC-CARD) Anti Collision");\n+ break;\n+ case ICCARDCommand::SelectCard:\n+ icco.extlen = 8;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ // Session\n+ icco.extdata[0] = 0x00;\n+ icco.extdata[1] = m_ic_card_session;\n+ icco.extdata[2] = 0x00;\n+ icco.extdata[3] = 0x00;\n+ icco.extdata[4] = 0x00;\n+ icco.extdata[5] = 0x00;\n+ icco.extdata[6] = 0x00;\n+ icco.extdata[7] = 0x00;\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (IC-CARD) Select Card:{}",\n+ m_ic_card_session);\n+ break;\n+ case ICCARDCommand::ReadPage:\n+ case ICCARDCommand::ReadUseCount:\n+ {\n+ if (!validate_data_in_out(8, 0, "SerialA (IC-CARD)"))\n+ break;\n+ const u16 page = Common::swap16(data_in + 6) & 0xFF; // 255 is max page\n+\n+ icco.extlen = 8;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ memcpy(icco.extdata, m_ic_card_data + page * 8, 8);\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 31 (IC-CARD) Read Page:{}",\n+ page);\n+ break;\n+ }\n+ case ICCARDCommand::WritePage:\n+ {\n+ if (!validate_data_in_out(10, 0, "SerialA (IC-CARD)"))\n+ break;\n+ const u16 page = Common::swap16(data_in + 8) & 0xFF; // 255 is max page\n+\n+ // Write only one page\n+ if (page == 4)\n+ {\n+ icco.status = 0x80;\n+ }\n+ else\n+ {\n+ if (!validate_data_in_out(18, 0, "SerialA (IC-CARD)"))\n+ break;\n+ memcpy(m_ic_card_data + page * 8, data_in + 10, 8);\n+ }\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (IC-CARD) Write Page:{}",\n+ page);\n+ break;\n+ }\n+ case ICCARDCommand::DecreaseUseCount:\n+ {\n+ if (!validate_data_in_out(8, 0, "SerialA (IC-CARD)"))\n+ break;\n+ const u16 page = Common::swap16(data_in + 6) & 0xFF; // 255 is max page\n+\n+ icco.extlen = 2;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ auto ic_card_data = Common::BitCastPtr<u16>(m_ic_card_data + 0x28);\n+ ic_card_data = ic_card_data - 1;\n+\n+ // Counter\n+ icco.extdata[0] = m_ic_card_data[0x28];\n+ icco.extdata[1] = m_ic_card_data[0x29];\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 31 (IC-CARD) Decrease Use Count:{}", page);\n+ break;\n+ }\n+ case ICCARDCommand::ReadPages:\n+ {\n+ if (!validate_data_in_out(10, 0, "SerialA (IC-CARD)"))\n+ break;\n+ const u16 page = Common::swap16(data_in + 6) & 0xFF; // 255 is max page\n+ const u16 count = Common::swap16(data_in + 8);\n+\n+ const u32 offs = page * 8;\n+ u32 cnt = count * 8;\n+\n+ // Limit read size to not overwrite the reply buffer\n+ if (cnt > (u32)0x50 - data_offset)\n+ {\n+ cnt = 5 * 8;\n+ }\n+\n+ icco.extlen = cnt;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ memcpy(icco.extdata, m_ic_card_data + offs, cnt);\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 31 (IC-CARD) Read Pages:{} Count:{}", page, count);\n+ break;\n+ }\n+ case ICCARDCommand::WritePages:\n+ {\n+ if (!validate_data_in_out(10, 0, "SerialA (IC-CARD)"))\n+ break;\n+ const u16 pksize = length;\n+ const u16 size = Common::swap16(data_in + 2);\n+ const u16 page = Common::swap16(data_in + 6) & 0xFF; // 255 is max page\n+ const u16 count = Common::swap16(data_in + 8);\n+\n+ // We got a complete packet\n+ if (pksize - 5 == size)\n+ {\n+ if (page == 4) // Read Only Page, must return error\n+ {\n+ icco.status = 0x80;\n+ }\n+ else\n+ {\n+ if (page * 8 + count * 8 > sizeof(m_ic_card_data))\n+ {\n+ ERROR_LOG_FMT(\n+ SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (IC-CARD) Data overflow: Pages:{} Count:{}({:x})",\n+ page, count, size);\n+ }\n+ else\n+ {\n+ if (!validate_data_in_out(13 + count * 8, 0, "SerialA (IC-CARD)"))\n+ break;\n+ memcpy(m_ic_card_data + page * 8, data_in + 13, count * 8);\n+ }\n+ }\n+\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (IC-CARD) Write Pages:{} Count:{}({:x})", page,\n+ count, size);\n+ }\n+ // VirtuaStriker 4 splits the writes over multiple packets\n+ else\n+ {\n+ if (!validate_data_in_out(2 + pksize, 0, "SerialA (IC-CARD)"))\n+ break;\n+ memcpy(m_ic_write_buffer, data_in + 2, pksize);\n+ m_ic_write_offset += pksize;\n+ m_ic_write_size = size;\n+ }\n+ break;\n+ }\n+ default:\n+ // Handle Deck Reader commands\n+ if (!validate_data_in_out(1, 0, "SerialA (DECK READER)"))\n+ break;\n+ serial_command = data_in[0];\n+ icco.command = serial_command;\n+ icco.flag = 0;\n+ switch (CDReaderCommand(serial_command))\n+ {\n+ case CDReaderCommand::ProgramVersion:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Program Version");\n+\n+ icco.extlen = (u32)strlen(s_cdr_program_version);\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ memcpy(icco.extdata, s_cdr_program_version, icco.extlen);\n+ break;\n+ case CDReaderCommand::BootVersion:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Boot Version");\n+\n+ icco.extlen = (u32)strlen(s_cdr_boot_version);\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ memcpy(icco.extdata, s_cdr_boot_version, icco.extlen);\n+ break;\n+ case CDReaderCommand::ShutterGet:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Shutter Get");\n+\n+ icco.extlen = 4;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ icco.extdata[0] = 0;\n+ icco.extdata[1] = 0;\n+ icco.extdata[2] = 0;\n+ icco.extdata[3] = 0;\n+ break;\n+ case CDReaderCommand::CameraCheck:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Camera Check");\n+\n+ icco.extlen = 6;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ icco.extdata[0] = 0x23;\n+ icco.extdata[1] = 0x28;\n+ icco.extdata[2] = 0x45;\n+ icco.extdata[3] = 0x29;\n+ icco.extdata[4] = 0x45;\n+ icco.extdata[5] = 0x29;\n+ break;\n+ case CDReaderCommand::ProgramChecksum:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Program Checksum");\n+\n+ icco.extlen = 4;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ icco.extdata[0] = 0x23;\n+ icco.extdata[1] = 0x28;\n+ icco.extdata[2] = 0x45;\n+ icco.extdata[3] = 0x29;\n+ break;\n+ case CDReaderCommand::BootChecksum:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Boot Checksum");\n+\n+ icco.extlen = 4;\n+ icco.length += icco.extlen;\n+ icco.pktlen += icco.extlen;\n+\n+ icco.extdata[0] = 0x23;\n+ icco.extdata[1] = 0x28;\n+ icco.extdata[2] = 0x45;\n+ icco.extdata[3] = 0x29;\n+ break;\n+ case CDReaderCommand::SelfTest:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (DECK READER) Self Test");\n+ icco.flag = 0x00;\n+ break;\n+ case CDReaderCommand::SensLock:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (DECK READER) Sens Lock");\n+ icco.flag = 0x01;\n+ break;\n+ case CDReaderCommand::SensCard:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (DECK READER) Sens Card");\n+ break;\n+ case CDReaderCommand::ShutterCard:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (DECK READER) Shutter Card");\n+ break;\n+ case CDReaderCommand::ReadCard:\n+ INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (DECK READER) Read Card");\n+\n+ icco.fixed = 0xAA;\n+ icco.flag = 0xAA;\n+ icco.extlen = sizeof(s_cdr_card_data);\n+ icco.length = 0x72;\n+ icco.status = Common::swap16(icco.extlen);\n+\n+ icco.pktlen += icco.extlen;\n+\n+ memcpy(icco.extdata, s_cdr_card_data, sizeof(s_cdr_card_data));\n+\n+ break;\n+ default:\n+ if (!validate_data_in_out(14, 0, "SerialA (DECK READER)"))\n+ break;\n+ WARN_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command 0x31 (IC-Card) {:02x} {:02x} {:02x} {:02x} {:02x} "\n+ "{:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x}",\n+ data_in[2], data_in[3], data_in[4], data_in[5], data_in[6],\n+ data_in[7], data_in[8], data_in[9], data_in[10], data_in[11],\n+ data_in[12], data_in[13]);\n+ break;\n+ }\n+ break;\n+ }\n+\n+ if (!validate_data_in_out(0, icco.pktlen + 2, "SerialA (IC-CARD)"))\n+ break;\n+ ICCardSendReply(&icco, data_out.data(), &data_offset);\n+\n+ if (!validate_data_in_out(2, 0, "SerialA (IC-CARD)"))\n+ break;\n+ data_in += length;\n+ break;\n+ }\n+ }\n+\n+ u32 command_offset = 0;\n+ while (command_offset < length)\n+ {\n+ // All commands are OR\'d with 0x80\n+ // Last byte is checksum which we don\'t care about\n+ if (!validate_data_in_out(command_offset + 4, 0, "SerialA"))\n+ break;\n+ const u32 serial_command = Common::swap32(data_in + command_offset) ^ 0x80000000;\n+\n+ if (AMMediaboard::GetGameType() == FZeroAX ||\n+ AMMediaboard::GetGameType() == FZeroAXMonster)\n+ {\n+ INFO_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x31 (MOTOR) Length:{:02x} Command:{:04x}({:02x})",\n+ length, (serial_command >> 8) & 0xFFFF, serial_command >> 24);\n+ }\n+ else\n+ {\n+ INFO_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x31 (SERIAL) Command:{:06x}",\n+ serial_command);\n+\n+ if (serial_command == 0x801000)\n+ {\n+ if (!validate_data_in_out(0, 4, "SerialA"))\n+ break;\n+ data_out[data_offset++] = 0x31;\n+ data_out[data_offset++] = 0x02;\n+ data_out[data_offset++] = 0xFF;\n+ data_out[data_offset++] = 0x01;\n+ }\n+ }\n+\n+ command_offset += sizeof(u32);\n+\n+ if (AMMediaboard::GetGameType() == FZeroAX ||\n+ AMMediaboard::GetGameType() == FZeroAXMonster)\n+ {\n+ // Status\n+ m_motor_reply[command_offset + 2] = 0;\n+ m_motor_reply[command_offset + 3] = 0;\n+\n+ // Error\n+ m_motor_reply[command_offset + 4] = 0;\n+\n+ switch (serial_command >> 24)\n+ {\n+ case 0:\n+ case 1: // Set Maximum?\n+ case 2:\n+ break;\n+\n+ // 0x00-0x40: left\n+ // 0x40-0x80: right\n+ case 4: // Move Steering Wheel\n+ // Left\n+ if (serial_command & 0x010000)\n+ {\n+ m_motor_force_y = -((s16)serial_command & 0xFF00);\n+ }\n+ else // Right\n+ {\n+ m_motor_force_y = (serial_command - 0x4000) & 0xFF00;\n+ }\n+\n+ m_motor_force_y *= 2;\n+\n+ // FFB\n+ if (m_motor_init == 2)\n+ {\n+ if (serial_interface.GetDeviceType(1) == SerialInterface::SIDEVICE_GC_STEERING)\n+ {\n+ const GCPadStatus pad_status = Pad::GetStatus(1);\n+ if (pad_status.isConnected)\n+ {\n+ const ControlState mapped_strength = (double)(m_motor_force_y >> 8) / 127.f;\n+\n+ Pad::Rumble(1, mapped_strength);\n+ INFO_LOG_FMT(SERIALINTERFACE_AMBB,\n+ "GC-AM: Command 0x31 (MOTOR) mapped_strength:{}",\n+ mapped_strength);\n+ }\n+ }\n+ }\n+ break;\n+ case 6: // nice\n+ case 9:\n+ default:\n+ break;\n+ // Switch back to normal controls\n+ case 7:\n+ m_motor_init = 2;\n+ break;\n+ // Reset\n+ case 0x7F:\n+ m_motor_init = 1;\n+ memset(m_motor_reply, 0, sizeof(m_motor_reply));\n+ break;\n+ }\n+\n+ // Checksum\n+ m_motor_reply[command_offset + 5] = m_motor_reply[command_offset + 2] ^\n+ m_motor_reply[command_offset + 3] ^\n+ m_motor_reply[command_offset + 4];\n+ }\n+ }\n+\n+ if (length == 0)\n+ {\n+ if (!validate_data_in_out(length, 2, "SerialA"))\n+ break;\n+ data_out[data_offset++] = gcam_command;\n+ data_out[data_offset++] = 0x00;\n+ }\n+ else\n+ {\n+ if (m_motor_init)\n+ {\n+ // Motor\n+ m_motor_reply[0] = gcam_command;\n+ m_motor_reply[1] = length; // Same out as in size\n+\n+ const u32 reply_size = m_motor_reply[1] + 2;\n+ if (!validate_data_in_out(length, reply_size, "SerialA"))\n+ break;\n+\n+ memcpy(data_out.data() + data_offset, m_motor_reply, reply_size);\n+ data_offset += reply_size;\n+ }\n+ }\n+\n+ if (!validate_data_in_out(length, 0, "SerialA"))\n+ {\n+ break;\n+ }\n+ data_in += length;\n+ break;\n+ }\n+ case GCAMCommand::SerialB:\n+ {\n+ DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 32 (CARD-Interface)");\n+ if (!validate_data_in_out(1, 0, "SerialB"))\n+ break;\n+ const u32 length = *data_in++;\n+ if (!validate_data_in_out(length, 0, "SerialB"))\n+ break;\n+ if (length)\n+ {\n+ // Send Card Reply\n+ if (length == 1 && data_in[0] == 0x05)\n+ {\n+ if (m_card_read_length)\n+ {\n+ if (!validate_data_in_out(0, 1, "SerialB"))\n+ break;\n+ data_out[data_offset++] = gcam_command;\n+ u32 read_length = m_card_read_length - m_card_read;\n+\n+ if (AMMediaboard::GetGameType() == FZeroAX)\n+ {\n+ read_length = std::min<u32>(read_length, 0x2F);\n+ }\n+\n+ if (!validate_data_in_out(0, 1, "SerialB"))\n+ break;\n+ data_out[data_offset++] = read_length; // 0x2F (max size per packet)\n+\n+ if (!validate_data_in_out(0, read_length, "SerialB"))\n+ break;\n+ memcpy(data_out.data() + data_offset, m_card_read_packet + m_card_read,\n+ read_length);\n+\n+ data_offset += read_length;\n+ m_card_read += read_length;\n+\n+ if (m_card_read >= m_card_read_length)\n+ m_card_read_length = 0;\n+\n+ data_in += length;\n+ break;\n+ }\n+\n+ if (!validate_data_in_out(0, 5, "SerialB"))\n+ break;\n+\n+ data_out[data_offset++] = gcam_command;\n+ const u32 command_length_offset = data_offset;\n+ data_out[data_offset++] = 0x00; // len\n+\n+ data_out[data_offset++] = 0x02;\n+ const u32 checksum_start = data_offset;\n+\n+ data_out[data_offset++] = 0x00; // 0x00 len\n+\n+ data_out[data_offset++] = m_card_command; // 0x01 command\n+\n+ switch (CARDCommand(m_card_command))\n+ {\n+ case CARDCommand::Init:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x00; // 0x02\n+ data_out[data_offset++] = 0x30; // 0x03\n+ break;\n+ case CARDCommand::GetState:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x20 | m_card_bit; // 0x02\n+\n+ // bit 0: Please take your card\n+ // bit 1: Endless waiting causes UNK_E to be called\n+ data_out[data_offset++] = 0x00; // 0x03\n+ break;\n+ case CARDCommand::Read:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x02; // 0x02\n+ data_out[data_offset++] = 0x53; // 0x03\n+ break;\n+ case CARDCommand::IsPresent:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x22; // 0x02\n+ data_out[data_offset++] = 0x30; // 0x03\n+ break;\n+ case CARDCommand::Write:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x02; // 0x02\n+ data_out[data_offset++] = 0x00; // 0x03\n+ break;\n+ case CARDCommand::SetPrintParam:\n+ case CARDCommand::RegisterFont:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x00; // 0x02\n+ data_out[data_offset++] = 0x00; // 0x03\n+ break;\n+ case CARDCommand::WriteInfo:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x02; // 0x02\n+ data_out[data_offset++] = 0x00; // 0x03\n+ break;\n+ case CARDCommand::Erase:\n+ // TODO: CARDCommand::Erase is not handled.\n+ break;\n+ case CARDCommand::Eject:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ if (AMMediaboard::GetGameType() == FZeroAX)\n+ {\n+ data_out[data_offset++] = 0x01; // 0x02\n+ }\n+ else\n+ {\n+ data_out[data_offset++] = 0x31; // 0x02\n+ }\n+ data_out[data_offset++] = 0x30; // 0x03\n+ break;\n+ case CARDCommand::Clean:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x02; // 0x02\n+ data_out[data_offset++] = 0x00; // 0x03\n+ break;\n+ case CARDCommand::Load:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x02; // 0x02\n+ data_out[data_offset++] = 0x30; // 0x03\n+ break;\n+ case CARDCommand::SetShutter:\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x00; // 0x02\n+ data_out[data_offset++] = 0x00; // 0x03\n+ break;\n+ }\n+\n+ if (!validate_data_in_out(0, 3, "SerialB"))\n+ break;\n+ data_out[data_offset++] = 0x30; // 0x04\n+ data_out[data_offset++] = 0x00; // 0x05\n+\n+ data_out[data_offset++] = 0x03; // 0x06\n+\n+ data_out[checksum_start] = data_offset - checksum_start; // 0x00 len\n+\n+ if (!validate_data_in_out(0, 1, "SerialB"))\n+ break;\n+ data_out[data_offset] = 0; // 0x07\n+ for (u32 i = 0; i < data_out[checksum_start]; ++i)\n+ data_out[data_offset] ^= data_out[checksum_start + i];\n+\n+ if (!validate_data_in_out(0, 2, "SerialB"))\n+ break;\n+ data_offset++;\n+\n+ data_out[command_length_offset] = data_out[checksum_start] + 2;\n+ }\n+ else\n+ {\n+ for (u32 i = 0; i < length; ++i)\n+ m_card_buffer[m_card_offset + i] = data_in[i];\n+\n+ m_card_offset += length;\n+\n+ // Check if we got a complete command\n+ if (m_card_buffer[0] == 0x02)\n+ {\n+ if (m_card_buffer[1] == m_card_offset - 2)\n+ {\n+ if (m_card_buffer[m_card_offset - 2] == 0x03)\n+ {\n+ m_card_command = m_card_buffer[2];\n+\n+ switch (CARDCommand(m_card_command))\n+ {\n+ case CARDCommand::Init:\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD Init");\n+\n+ m_card_write_length = 0;\n+ m_card_bit = 0;\n+ m_card_memory_size = 0;\n+ m_card_state_call_count = 0;\n+ break;\n+ case CARDCommand::GetState:\n+ {\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD GetState({:02X})",\n+ m_card_bit);\n+\n+ if (m_card_memory_size == 0)\n+ {\n+ const std::string card_filename(\n+ fmt::format("{}tricard_{}.bin", File::GetUserPath(D_TRIUSER_IDX),\n+ SConfig::GetInstance().GetGameID()));\n+\n+ if (File::Exists(card_filename))\n+ {\n+ File::IOFile card(card_filename, "rb+");\n+ m_card_memory_size = (u32)card.GetSize();\n+\n+ card.ReadBytes(m_card_memory, m_card_memory_size);\n+ card.Close();\n+\n+ m_card_is_inserted = true;\n+ }\n+ }\n+\n+ if (AMMediaboard::GetGameType() == FZeroAX && m_card_memory_size)\n+ {\n+ m_card_state_call_count++;\n+ if (m_card_state_call_count > 10)\n+ {\n+ if (m_card_bit & 2)\n+ m_card_bit &= ~2u;\n+ else\n+ m_card_bit |= 2;\n+\n+ m_card_state_call_count = 0;\n+ }\n+ }\n+\n+ if (m_card_clean == 1)\n+ {\n+ m_card_clean = 2;\n+ }\n+ else if (m_card_clean == 2)\n+ {\n+ const std::string card_filename(\n+ fmt::format("{}tricard_{}.bin", File::GetUserPath(D_TRIUSER_IDX),\n+ SConfig::GetInstance().GetGameID()));\n+\n+ if (File::Exists(card_filename))\n+ {\n+ m_card_memory_size = (u32)File::GetSize(card_filename);\n+ if (m_card_memory_size)\n+ {\n+ if (AMMediaboard::GetGameType() == FZeroAX)\n+ {\n+ m_card_bit = 2;\n+ }\n+ else\n+ {\n+ m_card_bit = 1;\n+ }\n+ }\n+ }\n+ m_card_clean = 0;\n+ }\n+ break;\n+ }\n+ case CARDCommand::IsPresent:\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD IsPresent");\n+ break;\n+ case CARDCommand::RegisterFont:\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD RegisterFont");\n+ break;\n+ case CARDCommand::Load:\n+ {\n+ u8 mode = m_card_buffer[6];\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD Load({:02X})",\n+ mode);\n+ break;\n+ }\n+ case CARDCommand::Clean:\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD Clean");\n+ m_card_clean = 1;\n+ break;\n+ case CARDCommand::Read:\n+ {\n+ const u8 mode = m_card_buffer[6];\n+ const u8 bitmode = m_card_buffer[7];\n+ const u8 track = m_card_buffer[8];\n+\n+ NOTICE_LOG_FMT(SERIALINTERFACE_CARD,\n+ "GC-AM: Command CARD Read({:02X},{:02X},{:02X})", mode,\n+ bitmode, track);\n+\n+ // Prepare read packet\n+ memset(m_card_read_packet, 0, 0xDB);\n+ u32 packet_offset = 0;\n+\n+ const std::string card_filename(\n+ fmt::format("{}tricard_{}.bin", File::GetUserPath(D_TRIUSER_IDX),\n+ SConfig::GetInstance().GetGameID()));\n+\n+ if (File::Exists(card_filename))\n+ {\n+ File::IOFile card(card_filename, "rb+");\n+ if (m_card_memory_size == 0)\n+ {\n+ m_card_memory_size = (u32)card.GetSize();\n+ }\n+\n+ card.ReadBytes(m_card_memory, m_card_memory_size);\n+ card.Close();\n+\n+ m_card_is_inserted = true;\n+ }\n+\n+ m_card_read_packet[packet_offset++] = 0x02; // SUB CMD\n+ m_card_read_packet[packet_offset++] = 0x00; // SUB CMDLen\n+\n+ m_card_read_packet[packet_offset++] = 0x33; // CARD CMD\n+\n+ if (m_card_is_inserted) // CARD Status\n+ {\n+ m_card_read_packet[packet_offset++] = 0x31;\n+ }\n+ else\n+ {\n+ m_card_read_packet[packet_offset++] = 0x30;\n+ }\n+\n+ m_card_read_packet[packet_offset++] = 0x30;\n+ m_card_read_packet[packet_offset++] = 0x30;\n+\n+ // Data reply\n+ memcpy(m_card_read_packet + packet_offset, m_card_memory, m_card_memory_size);\n+ packet_offset += m_card_memory_size;\n+\n+ m_card_read_packet[packet_offset++] = 0x03;\n+\n+ m_card_read_packet[1] = packet_offset - 1; // SUB CMDLen\n+\n+ for (u32 i = 0; i < packet_offset - 1; ++i)\n+ m_card_read_packet[packet_offset] ^= m_card_read_packet[1 + i];\n+\n+ packet_offset++;\n+\n+ m_card_read_length = packet_offset;\n+ m_card_read = 0;\n+ break;\n+ }\n+ case CARDCommand::Write:\n+ {\n+ const u8 mode = m_card_buffer[6];\n+ const u8 bitmode = m_card_buffer[7];\n+ const u8 track = m_card_buffer[8];\n+\n+ m_card_memory_size = m_card_buffer[1] - 9;\n+\n+ memcpy(m_card_memory, m_card_buffer + 9, m_card_memory_size);', 'path': 'Source/Core/Core/HW/SI/SI_DeviceAMBaseboard.cpp', 'position': 1536, 'original_position': 1414, 'commit_id': '510fec88be973bc870021f2d0347593cdc285864', 'user': {'login': 'JosJuice', 'id': 6716818, 'node_id': 'MDQ6VXNlcjY3MTY4MTg=', 'avatar_url': 'https://avatars.githubusercontent.com/u/6716818?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/JosJuice', 'html_url': 'https://github.com/JosJuice', 'followers_url': 'https://api.github.com/users/JosJuice/followers', 'following_url': 'https://api.github.com/users/JosJuice/following{/other_user}', 'gists_url': 'https://api.github.com/users/JosJuice/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/JosJuice/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/JosJuice/subscriptions', 'organizations_url': 'https://api.github.com/users/JosJuice/orgs', 'repos_url': 'https://api.github.com/users/JosJuice/repos', 'events_url': 'https://api.github.com/users/JosJuice/events{/privacy}', 'received_events_url': 'https://api.github.com/users/JosJuice/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'body': '`m_card_memory_size` is still not checked.', 'created_at': '2026-01-31T13:31:17Z', 'updated_at': '2026-01-31T13:33:12Z', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/13844#discussion_r2749564419', 'pull_request_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13844', 'author_association': 'MEMBER', '_links': {'self': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2749564419'}, 'html': {'href': 'https://github.com/dolphin-emu/dolphin/pull/13844#discussion_r2749564419'}, 'pull_request': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13844'}}, 'original_commit_id': '2415f0c1c471b2446a2144a1a2b643d8a77d8cc7', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2749564419/reactions', 'total_count': 0, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 0, 'confused': 0, 'heart': 0, 'rocket': 0, 'eyes': 0}, 'in_reply_to_id': 2725631348}], 'type': 'gh_pull_request_review'}
2026-01-31T13:00:47.796532 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'jordan-woyak', 'action': 'submitted', 'pr_id': 14308, 'pr_title': 'StringUtil: Make UTF16ToUTF8 and UTF8ToUTF16 use custom encoding/decoding implementation.', 'state': 'commented', 'url': 'https://github.com/dolphin-emu/dolphin/pull/14308#pullrequestreview-3732505040', 'comments': [{'id': 2749544190, 'node_id': 'PRRC_kwDOALCn2M6j4rb-', 'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2749544190', 'pull_request_review_id': 3732505040, 'diff_hunk': '@@ -391,6 +390,189 @@ size_t StringUTF8CodePointCount(std::string_view str)\n return str.size() - std::ranges::count_if(str, [](char c) -> bool { return (c & 0xC0) == 0x80; });\n }\n \n+enum class DecodeError\n+{\n+ End,\n+ IncompleteSequence,\n+};\n+\n+template <std::integral CharType>\n+requires(sizeof(CharType) == 1)\n+class UTF8Decoder\n+{\n+public:\n+ constexpr explicit UTF8Decoder(std::span<const CharType> chars)\n+ : m_ptr{chars.data()}, m_end_ptr{m_ptr + chars.size()}\n+ {\n+ }\n+\n+ constexpr std::expected<char32_t, DecodeError> operator()()\n+ {\n+ if (m_ptr == m_end_ptr)\n+ return std::unexpected{DecodeError::End};\n+\n+ const u8 first_byte = *m_ptr;\n+ ++m_ptr;\n+\n+ // This is either ASCII or an invalid byte that we will interpret as\n+ // ISO/IEC 8859-1 or "Extended ASCII".\n+ // In particular, this handles 0xa0 (NO-BREAK SPACE).', 'path': 'Source/Core/Common/StringUtil.cpp', 'position': 47, 'original_position': 47, 'commit_id': 'b89301e80b16df4559224d21f11bcbdf314140b1', 'user': {'login': 'jordan-woyak', 'id': 1768214, 'node_id': 'MDQ6VXNlcjE3NjgyMTQ=', 'avatar_url': 'https://avatars.githubusercontent.com/u/1768214?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/jordan-woyak', 'html_url': 'https://github.com/jordan-woyak', 'followers_url': 'https://api.github.com/users/jordan-woyak/followers', 'following_url': 'https://api.github.com/users/jordan-woyak/following{/other_user}', 'gists_url': 'https://api.github.com/users/jordan-woyak/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/jordan-woyak/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/jordan-woyak/subscriptions', 'organizations_url': 'https://api.github.com/users/jordan-woyak/orgs', 'repos_url': 'https://api.github.com/users/jordan-woyak/repos', 'events_url': 'https://api.github.com/users/jordan-woyak/events{/privacy}', 'received_events_url': 'https://api.github.com/users/jordan-woyak/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'body': "Yeah.. you're not wrong.\r\n\r\nAlso, I just re-tested the locale issue and it seems like it fixed itself somehow?\r\nI haven't figured out how yet.. fmt might be translating the `0xa0` to `0x20` somewhere?\r\n", 'created_at': '2026-01-31T13:00:45Z', 'updated_at': '2026-01-31T13:00:45Z', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/14308#discussion_r2749544190', 'pull_request_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/14308', 'author_association': 'MEMBER', '_links': {'self': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2749544190'}, 'html': {'href': 'https://github.com/dolphin-emu/dolphin/pull/14308#discussion_r2749544190'}, 'pull_request': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/14308'}}, 'original_commit_id': 'b89301e80b16df4559224d21f11bcbdf314140b1', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2749544190/reactions', 'total_count': 0, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 0, 'confused': 0, 'heart': 0, 'rocket': 0, 'eyes': 0}, 'in_reply_to_id': 2749402975}], 'type': 'gh_pull_request_review'}
2026-01-31T11:36:10.006954 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'JosJuice', 'action': 'submitted', 'pr_id': 14308, 'pr_title': 'StringUtil: Make UTF16ToUTF8 and UTF8ToUTF16 use custom encoding/decoding implementation.', 'state': 'commented', 'url': 'https://github.com/dolphin-emu/dolphin/pull/14308#pullrequestreview-3732310342', 'comments': [{'id': 2749402975, 'node_id': 'PRRC_kwDOALCn2M6j4I9f', 'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2749402975', 'pull_request_review_id': 3732310342, 'diff_hunk': '@@ -391,6 +390,189 @@ size_t StringUTF8CodePointCount(std::string_view str)\n return str.size() - std::ranges::count_if(str, [](char c) -> bool { return (c & 0xC0) == 0x80; });\n }\n \n+enum class DecodeError\n+{\n+ End,\n+ IncompleteSequence,\n+};\n+\n+template <std::integral CharType>\n+requires(sizeof(CharType) == 1)\n+class UTF8Decoder\n+{\n+public:\n+ constexpr explicit UTF8Decoder(std::span<const CharType> chars)\n+ : m_ptr{chars.data()}, m_end_ptr{m_ptr + chars.size()}\n+ {\n+ }\n+\n+ constexpr std::expected<char32_t, DecodeError> operator()()\n+ {\n+ if (m_ptr == m_end_ptr)\n+ return std::unexpected{DecodeError::End};\n+\n+ const u8 first_byte = *m_ptr;\n+ ++m_ptr;\n+\n+ // This is either ASCII or an invalid byte that we will interpret as\n+ // ISO/IEC 8859-1 or "Extended ASCII".\n+ // In particular, this handles 0xa0 (NO-BREAK SPACE).', 'path': 'Source/Core/Common/StringUtil.cpp', 'position': 47, 'original_position': 47, 'commit_id': 'b89301e80b16df4559224d21f11bcbdf314140b1', 'user': {'login': 'JosJuice', 'id': 6716818, 'node_id': 'MDQ6VXNlcjY3MTY4MTg=', 'avatar_url': 'https://avatars.githubusercontent.com/u/6716818?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/JosJuice', 'html_url': 'https://github.com/JosJuice', 'followers_url': 'https://api.github.com/users/JosJuice/followers', 'following_url': 'https://api.github.com/users/JosJuice/following{/other_user}', 'gists_url': 'https://api.github.com/users/JosJuice/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/JosJuice/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/JosJuice/subscriptions', 'organizations_url': 'https://api.github.com/users/JosJuice/orgs', 'repos_url': 'https://api.github.com/users/JosJuice/repos', 'events_url': 'https://api.github.com/users/JosJuice/events{/privacy}', 'received_events_url': 'https://api.github.com/users/JosJuice/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'body': 'This is in my opinion not desirable behavior. If the input isn\'t valid UTF-8, we shouldn\'t "guess" what we think it might be.\n\nIf you want this because of https://bugs.dolphin-emu.org/issues/12502, I would prefer having special handling for this in the code that deals with file size formatting, not code that can be triggered any time Dolphin decodes UTF-8.', 'created_at': '2026-01-31T11:10:16Z', 'updated_at': '2026-01-31T11:36:07Z', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/14308#discussion_r2749402975', 'pull_request_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/14308', 'author_association': 'MEMBER', '_links': {'self': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2749402975'}, 'html': {'href': 'https://github.com/dolphin-emu/dolphin/pull/14308#discussion_r2749402975'}, 'pull_request': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/14308'}}, 'original_commit_id': 'b89301e80b16df4559224d21f11bcbdf314140b1', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2749402975/reactions', 'total_count': 0, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 0, 'confused': 0, 'heart': 0, 'rocket': 0, 'eyes': 0}}, {'id': 2749404099, 'node_id': 'PRRC_kwDOALCn2M6j4JPD', 'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2749404099', 'pull_request_review_id': 3732310342, 'diff_hunk': '@@ -391,6 +390,189 @@ size_t StringUTF8CodePointCount(std::string_view str)\n return str.size() - std::ranges::count_if(str, [](char c) -> bool { return (c & 0xC0) == 0x80; });\n }\n \n+enum class DecodeError\n+{\n+ End,\n+ IncompleteSequence,\n+};\n+\n+template <std::integral CharType>\n+requires(sizeof(CharType) == 1)\n+class UTF8Decoder\n+{\n+public:\n+ constexpr explicit UTF8Decoder(std::span<const CharType> chars)\n+ : m_ptr{chars.data()}, m_end_ptr{m_ptr + chars.size()}\n+ {\n+ }\n+\n+ constexpr std::expected<char32_t, DecodeError> operator()()\n+ {\n+ if (m_ptr == m_end_ptr)\n+ return std::unexpected{DecodeError::End};\n+\n+ const u8 first_byte = *m_ptr;\n+ ++m_ptr;\n+\n+ // This is either ASCII or an invalid byte that we will interpret as\n+ // ISO/IEC 8859-1 or "Extended ASCII".\n+ // In particular, this handles 0xa0 (NO-BREAK SPACE).\n+ if (first_byte < 0xc0u)\n+ return first_byte;\n+\n+ u32 byte_count = std::countl_one(first_byte);\n+\n+ // Remove leading 1s.\n+ u32 current_value = u8(first_byte << byte_count) >> byte_count;\n+\n+ while (--byte_count != 0)\n+ {\n+ if (m_ptr == m_end_ptr)\n+ return std::unexpected{DecodeError::IncompleteSequence};\n+\n+ current_value = (current_value << 6u) | (u8(*m_ptr) & 0x3fu);\n+ ++m_ptr;\n+ }\n+\n+ return current_value;', 'path': 'Source/Core/Common/StringUtil.cpp', 'position': 65, 'original_position': 65, 'commit_id': 'b89301e80b16df4559224d21f11bcbdf314140b1', 'user': {'login': 'JosJuice', 'id': 6716818, 'node_id': 'MDQ6VXNlcjY3MTY4MTg=', 'avatar_url': 'https://avatars.githubusercontent.com/u/6716818?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/JosJuice', 'html_url': 'https://github.com/JosJuice', 'followers_url': 'https://api.github.com/users/JosJuice/followers', 'following_url': 'https://api.github.com/users/JosJuice/following{/other_user}', 'gists_url': 'https://api.github.com/users/JosJuice/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/JosJuice/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/JosJuice/subscriptions', 'organizations_url': 'https://api.github.com/users/JosJuice/orgs', 'repos_url': 'https://api.github.com/users/JosJuice/repos', 'events_url': 'https://api.github.com/users/JosJuice/events{/privacy}', 'received_events_url': 'https://api.github.com/users/JosJuice/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'body': 'Code points larger than U+10FFFF should be rejected.', 'created_at': '2026-01-31T11:12:13Z', 'updated_at': '2026-01-31T11:36:07Z', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/14308#discussion_r2749404099', 'pull_request_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/14308', 'author_association': 'MEMBER', '_links': {'self': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2749404099'}, 'html': {'href': 'https://github.com/dolphin-emu/dolphin/pull/14308#discussion_r2749404099'}, 'pull_request': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/14308'}}, 'original_commit_id': 'b89301e80b16df4559224d21f11bcbdf314140b1', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2749404099/reactions', 'total_count': 0, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 0, 'confused': 0, 'heart': 0, 'rocket': 0, 'eyes': 0}}, {'id': 2749406314, 'node_id': 'PRRC_kwDOALCn2M6j4Jxq', 'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2749406314', 'pull_request_review_id': 3732310342, 'diff_hunk': '@@ -391,6 +390,189 @@ size_t StringUTF8CodePointCount(std::string_view str)\n return str.size() - std::ranges::count_if(str, [](char c) -> bool { return (c & 0xC0) == 0x80; });\n }\n \n+enum class DecodeError\n+{\n+ End,\n+ IncompleteSequence,\n+};\n+\n+template <std::integral CharType>\n+requires(sizeof(CharType) == 1)\n+class UTF8Decoder\n+{\n+public:\n+ constexpr explicit UTF8Decoder(std::span<const CharType> chars)\n+ : m_ptr{chars.data()}, m_end_ptr{m_ptr + chars.size()}\n+ {\n+ }\n+\n+ constexpr std::expected<char32_t, DecodeError> operator()()\n+ {\n+ if (m_ptr == m_end_ptr)\n+ return std::unexpected{DecodeError::End};\n+\n+ const u8 first_byte = *m_ptr;\n+ ++m_ptr;\n+\n+ // This is either ASCII or an invalid byte that we will interpret as\n+ // ISO/IEC 8859-1 or "Extended ASCII".\n+ // In particular, this handles 0xa0 (NO-BREAK SPACE).\n+ if (first_byte < 0xc0u)\n+ return first_byte;\n+\n+ u32 byte_count = std::countl_one(first_byte);\n+\n+ // Remove leading 1s.\n+ u32 current_value = u8(first_byte << byte_count) >> byte_count;\n+\n+ while (--byte_count != 0)\n+ {\n+ if (m_ptr == m_end_ptr)\n+ return std::unexpected{DecodeError::IncompleteSequence};\n+\n+ current_value = (current_value << 6u) | (u8(*m_ptr) & 0x3fu);', 'path': 'Source/Core/Common/StringUtil.cpp', 'position': 61, 'original_position': 61, 'commit_id': 'b89301e80b16df4559224d21f11bcbdf314140b1', 'user': {'login': 'JosJuice', 'id': 6716818, 'node_id': 'MDQ6VXNlcjY3MTY4MTg=', 'avatar_url': 'https://avatars.githubusercontent.com/u/6716818?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/JosJuice', 'html_url': 'https://github.com/JosJuice', 'followers_url': 'https://api.github.com/users/JosJuice/followers', 'following_url': 'https://api.github.com/users/JosJuice/following{/other_user}', 'gists_url': 'https://api.github.com/users/JosJuice/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/JosJuice/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/JosJuice/subscriptions', 'organizations_url': 'https://api.github.com/users/JosJuice/orgs', 'repos_url': 'https://api.github.com/users/JosJuice/repos', 'events_url': 'https://api.github.com/users/JosJuice/events{/privacy}', 'received_events_url': 'https://api.github.com/users/JosJuice/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'body': 'Overlong encodings should be rejected (i.e. if `current_value` previously was 0, you should return an error).', 'created_at': '2026-01-31T11:16:38Z', 'updated_at': '2026-01-31T11:36:08Z', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/14308#discussion_r2749406314', 'pull_request_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/14308', 'author_association': 'MEMBER', '_links': {'self': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2749406314'}, 'html': {'href': 'https://github.com/dolphin-emu/dolphin/pull/14308#discussion_r2749406314'}, 'pull_request': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/14308'}}, 'original_commit_id': 'b89301e80b16df4559224d21f11bcbdf314140b1', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2749406314/reactions', 'total_count': 0, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 0, 'confused': 0, 'heart': 0, 'rocket': 0, 'eyes': 0}}, {'id': 2749407669, 'node_id': 'PRRC_kwDOALCn2M6j4KG1', 'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2749407669', 'pull_request_review_id': 3732310342, 'diff_hunk': '@@ -391,6 +390,189 @@ size_t StringUTF8CodePointCount(std::string_view str)\n return str.size() - std::ranges::count_if(str, [](char c) -> bool { return (c & 0xC0) == 0x80; });\n }\n \n+enum class DecodeError\n+{\n+ End,\n+ IncompleteSequence,\n+};\n+\n+template <std::integral CharType>\n+requires(sizeof(CharType) == 1)\n+class UTF8Decoder\n+{\n+public:\n+ constexpr explicit UTF8Decoder(std::span<const CharType> chars)\n+ : m_ptr{chars.data()}, m_end_ptr{m_ptr + chars.size()}\n+ {\n+ }\n+\n+ constexpr std::expected<char32_t, DecodeError> operator()()\n+ {\n+ if (m_ptr == m_end_ptr)\n+ return std::unexpected{DecodeError::End};\n+\n+ const u8 first_byte = *m_ptr;\n+ ++m_ptr;\n+\n+ // This is either ASCII or an invalid byte that we will interpret as\n+ // ISO/IEC 8859-1 or "Extended ASCII".\n+ // In particular, this handles 0xa0 (NO-BREAK SPACE).\n+ if (first_byte < 0xc0u)\n+ return first_byte;\n+\n+ u32 byte_count = std::countl_one(first_byte);\n+\n+ // Remove leading 1s.\n+ u32 current_value = u8(first_byte << byte_count) >> byte_count;\n+\n+ while (--byte_count != 0)\n+ {\n+ if (m_ptr == m_end_ptr)\n+ return std::unexpected{DecodeError::IncompleteSequence};\n+\n+ current_value = (current_value << 6u) | (u8(*m_ptr) & 0x3fu);\n+ ++m_ptr;\n+ }\n+\n+ return current_value;\n+ }\n+\n+private:\n+ const CharType* m_ptr;\n+ const CharType* const m_end_ptr;\n+};\n+\n+class UTF8Encoder\n+{\n+public:\n+ static constexpr u32 GetMaxUnitsPerCodePoint() { return 4; }\n+\n+ // `ptr` should point to at least 4 bytes.\n+ // Returns the number of written code units (bytes).\n+ template <std::integral CharType>\n+ requires(sizeof(CharType) == 1)\n+ constexpr u32 operator()(char32_t code_point, CharType* ptr)\n+ {\n+ u32 value = code_point;\n+ if (value < 0x80u)\n+ {\n+ *ptr = u8(value);\n+ return 1;\n+ }\n+\n+ u32 additional_bytes = 1;\n+ if (value < 0x800u)\n+ {\n+ *ptr = u8(0xc0u | (value >> 6u));\n+ }\n+ else if (value < 0x10000u)\n+ {\n+ additional_bytes = 2;\n+ *ptr = u8(0xe0u | (value >> 12u));\n+ }\n+ else\n+ {\n+ additional_bytes = 3;\n+ *ptr = u8(0xf0u | (value >> 18u));\n+ }\n+\n+ for (u32 i = additional_bytes; i != 0; --i)\n+ {\n+ ptr[i] = u8(0x80u | (value & 0x3fu));\n+ value >>= 6;\n+ }\n+\n+ return 1 + additional_bytes;\n+ }\n+};\n+\n+template <std::integral CharType>\n+requires(sizeof(CharType) == 2)\n+class UTF16Decoder\n+{\n+public:\n+ constexpr explicit UTF16Decoder(std::span<const CharType> chars)\n+ : m_ptr{chars.data()}, m_end_ptr{m_ptr + chars.size()}\n+ {\n+ }\n+\n+ constexpr std::expected<char32_t, DecodeError> operator()()\n+ {\n+ if (m_ptr == m_end_ptr)\n+ return std::unexpected{DecodeError::End};\n+\n+ const u32 current_value = u16(*m_ptr);\n+ ++m_ptr;\n+\n+ if (current_value < 0xd800u || current_value > 0xd8ffu)\n+ return current_value;\n+\n+ if (m_ptr == m_end_ptr)\n+ return std::unexpected{DecodeError::IncompleteSequence};\n+\n+ // We assume the surrogate pair is valid.', 'path': 'Source/Core/Common/StringUtil.cpp', 'position': 141, 'original_position': 141, 'commit_id': 'b89301e80b16df4559224d21f11bcbdf314140b1', 'user': {'login': 'JosJuice', 'id': 6716818, 'node_id': 'MDQ6VXNlcjY3MTY4MTg=', 'avatar_url': 'https://avatars.githubusercontent.com/u/6716818?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/JosJuice', 'html_url': 'https://github.com/JosJuice', 'followers_url': 'https://api.github.com/users/JosJuice/followers', 'following_url': 'https://api.github.com/users/JosJuice/following{/other_user}', 'gists_url': 'https://api.github.com/users/JosJuice/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/JosJuice/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/JosJuice/subscriptions', 'organizations_url': 'https://api.github.com/users/JosJuice/orgs', 'repos_url': 'https://api.github.com/users/JosJuice/repos', 'events_url': 'https://api.github.com/users/JosJuice/events{/privacy}', 'received_events_url': 'https://api.github.com/users/JosJuice/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'body': "Don't assume this.", 'created_at': '2026-01-31T11:19:11Z', 'updated_at': '2026-01-31T11:36:08Z', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/14308#discussion_r2749407669', 'pull_request_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/14308', 'author_association': 'MEMBER', '_links': {'self': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2749407669'}, 'html': {'href': 'https://github.com/dolphin-emu/dolphin/pull/14308#discussion_r2749407669'}, 'pull_request': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/14308'}}, 'original_commit_id': 'b89301e80b16df4559224d21f11bcbdf314140b1', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2749407669/reactions', 'total_count': 0, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 0, 'confused': 0, 'heart': 0, 'rocket': 0, 'eyes': 0}}, {'id': 2749408809, 'node_id': 'PRRC_kwDOALCn2M6j4KYp', 'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2749408809', 'pull_request_review_id': 3732310342, 'diff_hunk': '@@ -391,6 +390,189 @@ size_t StringUTF8CodePointCount(std::string_view str)\n return str.size() - std::ranges::count_if(str, [](char c) -> bool { return (c & 0xC0) == 0x80; });\n }\n \n+enum class DecodeError\n+{\n+ End,\n+ IncompleteSequence,\n+};\n+\n+template <std::integral CharType>\n+requires(sizeof(CharType) == 1)\n+class UTF8Decoder\n+{\n+public:\n+ constexpr explicit UTF8Decoder(std::span<const CharType> chars)\n+ : m_ptr{chars.data()}, m_end_ptr{m_ptr + chars.size()}\n+ {\n+ }\n+\n+ constexpr std::expected<char32_t, DecodeError> operator()()\n+ {\n+ if (m_ptr == m_end_ptr)\n+ return std::unexpected{DecodeError::End};\n+\n+ const u8 first_byte = *m_ptr;\n+ ++m_ptr;\n+\n+ // This is either ASCII or an invalid byte that we will interpret as\n+ // ISO/IEC 8859-1 or "Extended ASCII".\n+ // In particular, this handles 0xa0 (NO-BREAK SPACE).\n+ if (first_byte < 0xc0u)\n+ return first_byte;\n+\n+ u32 byte_count = std::countl_one(first_byte);\n+\n+ // Remove leading 1s.\n+ u32 current_value = u8(first_byte << byte_count) >> byte_count;\n+\n+ while (--byte_count != 0)\n+ {\n+ if (m_ptr == m_end_ptr)\n+ return std::unexpected{DecodeError::IncompleteSequence};\n+\n+ current_value = (current_value << 6u) | (u8(*m_ptr) & 0x3fu);\n+ ++m_ptr;\n+ }\n+\n+ return current_value;\n+ }\n+\n+private:\n+ const CharType* m_ptr;\n+ const CharType* const m_end_ptr;\n+};\n+\n+class UTF8Encoder\n+{\n+public:\n+ static constexpr u32 GetMaxUnitsPerCodePoint() { return 4; }\n+\n+ // `ptr` should point to at least 4 bytes.\n+ // Returns the number of written code units (bytes).\n+ template <std::integral CharType>\n+ requires(sizeof(CharType) == 1)\n+ constexpr u32 operator()(char32_t code_point, CharType* ptr)\n+ {\n+ u32 value = code_point;\n+ if (value < 0x80u)\n+ {\n+ *ptr = u8(value);\n+ return 1;\n+ }\n+\n+ u32 additional_bytes = 1;\n+ if (value < 0x800u)\n+ {\n+ *ptr = u8(0xc0u | (value >> 6u));\n+ }\n+ else if (value < 0x10000u)\n+ {\n+ additional_bytes = 2;\n+ *ptr = u8(0xe0u | (value >> 12u));\n+ }\n+ else\n+ {\n+ additional_bytes = 3;\n+ *ptr = u8(0xf0u | (value >> 18u));\n+ }\n+\n+ for (u32 i = additional_bytes; i != 0; --i)\n+ {\n+ ptr[i] = u8(0x80u | (value & 0x3fu));\n+ value >>= 6;\n+ }\n+\n+ return 1 + additional_bytes;\n+ }\n+};\n+\n+template <std::integral CharType>\n+requires(sizeof(CharType) == 2)\n+class UTF16Decoder\n+{\n+public:\n+ constexpr explicit UTF16Decoder(std::span<const CharType> chars)\n+ : m_ptr{chars.data()}, m_end_ptr{m_ptr + chars.size()}\n+ {\n+ }\n+\n+ constexpr std::expected<char32_t, DecodeError> operator()()\n+ {\n+ if (m_ptr == m_end_ptr)\n+ return std::unexpected{DecodeError::End};\n+\n+ const u32 current_value = u16(*m_ptr);\n+ ++m_ptr;\n+\n+ if (current_value < 0xd800u || current_value > 0xd8ffu)\n+ return current_value;\n+\n+ if (m_ptr == m_end_ptr)\n+ return std::unexpected{DecodeError::IncompleteSequence};\n+\n+ // We assume the surrogate pair is valid.\n+ const u32 low_surrogate = u16(*m_ptr);\n+ ++m_ptr;\n+ return ((current_value - 0xd800u) << 10u) + (low_surrogate - 0xdc00u) + 0x10000u;\n+ }\n+\n+private:\n+ const CharType* m_ptr;\n+ const CharType* const m_end_ptr;\n+};\n+\n+class UTF16Encoder\n+{\n+public:\n+ static constexpr u32 GetMaxUnitsPerCodePoint() { return 2; }\n+\n+ // `ptr` should point to at least 2 code units.\n+ // Returns the number of written code units.\n+ template <std::integral CharType>\n+ requires(sizeof(CharType) == 2)\n+ constexpr u32 operator()(char32_t code_point, CharType* ptr)\n+ {\n+ if (code_point < 0x10000u)\n+ {\n+ *ptr = u16(code_point);\n+ return 1;\n+ }\n+\n+ // Create surrogate pair.\n+ const u32 value = code_point - 0x10000u;', 'path': 'Source/Core/Common/StringUtil.cpp', 'position': 170, 'original_position': 170, 'commit_id': 'b89301e80b16df4559224d21f11bcbdf314140b1', 'user': {'login': 'JosJuice', 'id': 6716818, 'node_id': 'MDQ6VXNlcjY3MTY4MTg=', 'avatar_url': 'https://avatars.githubusercontent.com/u/6716818?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/JosJuice', 'html_url': 'https://github.com/JosJuice', 'followers_url': 'https://api.github.com/users/JosJuice/followers', 'following_url': 'https://api.github.com/users/JosJuice/following{/other_user}', 'gists_url': 'https://api.github.com/users/JosJuice/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/JosJuice/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/JosJuice/subscriptions', 'organizations_url': 'https://api.github.com/users/JosJuice/orgs', 'repos_url': 'https://api.github.com/users/JosJuice/repos', 'events_url': 'https://api.github.com/users/JosJuice/events{/privacy}', 'received_events_url': 'https://api.github.com/users/JosJuice/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'body': "I would suggest including a check so code points larger than U+10FFFF are rejected. Though... If this is only used internally by `ReEncodeString`, and we don't expose UTF-32 as an option, I guess it could be fine to not check it.", 'created_at': '2026-01-31T11:21:04Z', 'updated_at': '2026-01-31T11:36:08Z', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/14308#discussion_r2749408809', 'pull_request_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/14308', 'author_association': 'MEMBER', '_links': {'self': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2749408809'}, 'html': {'href': 'https://github.com/dolphin-emu/dolphin/pull/14308#discussion_r2749408809'}, 'pull_request': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/14308'}}, 'original_commit_id': 'b89301e80b16df4559224d21f11bcbdf314140b1', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2749408809/reactions', 'total_count': 0, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 0, 'confused': 0, 'heart': 0, 'rocket': 0, 'eyes': 0}}, {'id': 2749409210, 'node_id': 'PRRC_kwDOALCn2M6j4Ke6', 'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2749409210', 'pull_request_review_id': 3732310342, 'diff_hunk': '@@ -261,6 +261,14 @@ TEST(StringUtil, CaseInsensitiveContains_OverlappingMatches)\n \n TEST(StringUtil, CharacterEncodingConversion)\n {\n+ // Invalid sequences shouldn\'t crash or throw.\n+ WStringToUTF8(L"\\xdbff\\xdbff");\n+ UTF16ToUTF8(u"\\xdbff\\xdbff");\n+ UTF16ToUTF8(u"\\xdbff");\n+ UTF8ToUTF16("\\x80");\n+ UTF8ToUTF16("\\xc2");\n+ UTF8ToSHIFTJIS("\\x80");', 'path': 'Source/UnitTests/Common/StringUtilTest.cpp', 'position': 10, 'original_position': 10, 'commit_id': 'b89301e80b16df4559224d21f11bcbdf314140b1', 'user': {'login': 'JosJuice', 'id': 6716818, 'node_id': 'MDQ6VXNlcjY3MTY4MTg=', 'avatar_url': 'https://avatars.githubusercontent.com/u/6716818?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/JosJuice', 'html_url': 'https://github.com/JosJuice', 'followers_url': 'https://api.github.com/users/JosJuice/followers', 'following_url': 'https://api.github.com/users/JosJuice/following{/other_user}', 'gists_url': 'https://api.github.com/users/JosJuice/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/JosJuice/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/JosJuice/subscriptions', 'organizations_url': 'https://api.github.com/users/JosJuice/orgs', 'repos_url': 'https://api.github.com/users/JosJuice/repos', 'events_url': 'https://api.github.com/users/JosJuice/events{/privacy}', 'received_events_url': 'https://api.github.com/users/JosJuice/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'body': 'Please check the return value of all of these.', 'created_at': '2026-01-31T11:21:56Z', 'updated_at': '2026-01-31T11:36:08Z', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/14308#discussion_r2749409210', 'pull_request_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/14308', 'author_association': 'MEMBER', '_links': {'self': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2749409210'}, 'html': {'href': 'https://github.com/dolphin-emu/dolphin/pull/14308#discussion_r2749409210'}, 'pull_request': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/14308'}}, 'original_commit_id': 'b89301e80b16df4559224d21f11bcbdf314140b1', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2749409210/reactions', 'total_count': 0, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 0, 'confused': 0, 'heart': 0, 'rocket': 0, 'eyes': 0}}, {'id': 2749415165, 'node_id': 'PRRC_kwDOALCn2M6j4L79', 'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2749415165', 'pull_request_review_id': 3732310342, 'diff_hunk': '', 'path': 'Source/UnitTests/Common/StringUtilTest.cpp', 'position': 1, 'original_position': 1, 'commit_id': 'b89301e80b16df4559224d21f11bcbdf314140b1', 'user': {'login': 'JosJuice', 'id': 6716818, 'node_id': 'MDQ6VXNlcjY3MTY4MTg=', 'avatar_url': 'https://avatars.githubusercontent.com/u/6716818?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/JosJuice', 'html_url': 'https://github.com/JosJuice', 'followers_url': 'https://api.github.com/users/JosJuice/followers', 'following_url': 'https://api.github.com/users/JosJuice/following{/other_user}', 'gists_url': 'https://api.github.com/users/JosJuice/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/JosJuice/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/JosJuice/subscriptions', 'organizations_url': 'https://api.github.com/users/JosJuice/orgs', 'repos_url': 'https://api.github.com/users/JosJuice/repos', 'events_url': 'https://api.github.com/users/JosJuice/events{/privacy}', 'received_events_url': 'https://api.github.com/users/JosJuice/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'body': 'Now that we have hand-rolled implementations, we should test more cases. I would suggest:\n\n* Code point between U+0000 and U+007F (already covered here)\n* Code point between U+0080 and U+00FF\n* Code point between U+0800 and U+D7FF\n* High unmatched surrogate half, U+D800 to U+DBFF (already covered by your "invalid sequences" test cases, but only tested in one direction)\n* Low unmatched surrogate half, U+DC00 to U+DFFF\n* Code point between U+E000 and U+FFFF\n* Code point between U+10000 and U+1FFFF (already covered here)\n\nPlus all the edge cases I\'ve left review comments on.', 'created_at': '2026-01-31T11:30:12Z', 'updated_at': '2026-01-31T11:36:08Z', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/14308#discussion_r2749415165', 'pull_request_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/14308', 'author_association': 'MEMBER', '_links': {'self': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2749415165'}, 'html': {'href': 'https://github.com/dolphin-emu/dolphin/pull/14308#discussion_r2749415165'}, 'pull_request': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/14308'}}, 'original_commit_id': 'b89301e80b16df4559224d21f11bcbdf314140b1', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2749415165/reactions', 'total_count': 0, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 0, 'confused': 0, 'heart': 0, 'rocket': 0, 'eyes': 0}}, {'id': 2749417504, 'node_id': 'PRRC_kwDOALCn2M6j4Mgg', 'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2749417504', 'pull_request_review_id': 3732310342, 'diff_hunk': '@@ -391,6 +390,189 @@ size_t StringUTF8CodePointCount(std::string_view str)\n return str.size() - std::ranges::count_if(str, [](char c) -> bool { return (c & 0xC0) == 0x80; });\n }\n \n+enum class DecodeError\n+{\n+ End,\n+ IncompleteSequence,\n+};\n+\n+template <std::integral CharType>\n+requires(sizeof(CharType) == 1)\n+class UTF8Decoder\n+{\n+public:\n+ constexpr explicit UTF8Decoder(std::span<const CharType> chars)\n+ : m_ptr{chars.data()}, m_end_ptr{m_ptr + chars.size()}\n+ {\n+ }\n+\n+ constexpr std::expected<char32_t, DecodeError> operator()()\n+ {\n+ if (m_ptr == m_end_ptr)\n+ return std::unexpected{DecodeError::End};\n+\n+ const u8 first_byte = *m_ptr;\n+ ++m_ptr;\n+\n+ // This is either ASCII or an invalid byte that we will interpret as\n+ // ISO/IEC 8859-1 or "Extended ASCII".\n+ // In particular, this handles 0xa0 (NO-BREAK SPACE).\n+ if (first_byte < 0xc0u)\n+ return first_byte;\n+\n+ u32 byte_count = std::countl_one(first_byte);\n+\n+ // Remove leading 1s.\n+ u32 current_value = u8(first_byte << byte_count) >> byte_count;\n+\n+ while (--byte_count != 0)\n+ {\n+ if (m_ptr == m_end_ptr)\n+ return std::unexpected{DecodeError::IncompleteSequence};\n+\n+ current_value = (current_value << 6u) | (u8(*m_ptr) & 0x3fu);\n+ ++m_ptr;\n+ }\n+\n+ return current_value;\n+ }\n+\n+private:\n+ const CharType* m_ptr;\n+ const CharType* const m_end_ptr;\n+};\n+\n+class UTF8Encoder\n+{\n+public:\n+ static constexpr u32 GetMaxUnitsPerCodePoint() { return 4; }\n+\n+ // `ptr` should point to at least 4 bytes.\n+ // Returns the number of written code units (bytes).\n+ template <std::integral CharType>\n+ requires(sizeof(CharType) == 1)\n+ constexpr u32 operator()(char32_t code_point, CharType* ptr)\n+ {\n+ u32 value = code_point;\n+ if (value < 0x80u)\n+ {\n+ *ptr = u8(value);\n+ return 1;\n+ }\n+\n+ u32 additional_bytes = 1;\n+ if (value < 0x800u)\n+ {\n+ *ptr = u8(0xc0u | (value >> 6u));\n+ }\n+ else if (value < 0x10000u)\n+ {\n+ additional_bytes = 2;\n+ *ptr = u8(0xe0u | (value >> 12u));\n+ }\n+ else\n+ {\n+ additional_bytes = 3;\n+ *ptr = u8(0xf0u | (value >> 18u));\n+ }\n+\n+ for (u32 i = additional_bytes; i != 0; --i)\n+ {\n+ ptr[i] = u8(0x80u | (value & 0x3fu));\n+ value >>= 6;\n+ }\n+\n+ return 1 + additional_bytes;\n+ }\n+};\n+\n+template <std::integral CharType>\n+requires(sizeof(CharType) == 2)\n+class UTF16Decoder\n+{\n+public:\n+ constexpr explicit UTF16Decoder(std::span<const CharType> chars)\n+ : m_ptr{chars.data()}, m_end_ptr{m_ptr + chars.size()}\n+ {\n+ }\n+\n+ constexpr std::expected<char32_t, DecodeError> operator()()\n+ {\n+ if (m_ptr == m_end_ptr)\n+ return std::unexpected{DecodeError::End};\n+\n+ const u32 current_value = u16(*m_ptr);\n+ ++m_ptr;\n+\n+ if (current_value < 0xd800u || current_value > 0xd8ffu)\n+ return current_value;\n+\n+ if (m_ptr == m_end_ptr)\n+ return std::unexpected{DecodeError::IncompleteSequence};\n+\n+ // We assume the surrogate pair is valid.\n+ const u32 low_surrogate = u16(*m_ptr);\n+ ++m_ptr;\n+ return ((current_value - 0xd800u) << 10u) + (low_surrogate - 0xdc00u) + 0x10000u;\n+ }\n+\n+private:\n+ const CharType* m_ptr;\n+ const CharType* const m_end_ptr;\n+};\n+\n+class UTF16Encoder\n+{\n+public:\n+ static constexpr u32 GetMaxUnitsPerCodePoint() { return 2; }\n+\n+ // `ptr` should point to at least 2 code units.\n+ // Returns the number of written code units.\n+ template <std::integral CharType>\n+ requires(sizeof(CharType) == 2)\n+ constexpr u32 operator()(char32_t code_point, CharType* ptr)\n+ {\n+ if (code_point < 0x10000u)\n+ {\n+ *ptr = u16(code_point);\n+ return 1;\n+ }\n+\n+ // Create surrogate pair.\n+ const u32 value = code_point - 0x10000u;\n+ ptr[0] = u16((value >> 10u) + 0xd800u);\n+ ptr[1] = u16((value & 0x3ffu) + 0xdc00u);\n+ return 2;\n+ }\n+};\n+\n+template <typename Decoder, typename Encoder, typename ResultCharType, typename InputCharType>\n+static constexpr std::basic_string<ResultCharType>\n+ReEncodeString(std::basic_string_view<InputCharType> input)\n+{\n+ Decoder decoder{input};\n+ Encoder encoder;\n+\n+ const auto max_code_units = input.size() * encoder.GetMaxUnitsPerCodePoint();\n+\n+ std::basic_string<ResultCharType> result;\n+ result.resize_and_overwrite(max_code_units, [&](ResultCharType* buf, std::size_t) {\n+ std::size_t position = 0;\n+ while (true)\n+ {\n+ const auto code_point = decoder();\n+ if (!code_point.has_value())\n+ break;', 'path': 'Source/Core/Common/StringUtil.cpp', 'position': 193, 'original_position': 193, 'commit_id': 'b89301e80b16df4559224d21f11bcbdf314140b1', 'user': {'login': 'JosJuice', 'id': 6716818, 'node_id': 'MDQ6VXNlcjY3MTY4MTg=', 'avatar_url': 'https://avatars.githubusercontent.com/u/6716818?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/JosJuice', 'html_url': 'https://github.com/JosJuice', 'followers_url': 'https://api.github.com/users/JosJuice/followers', 'following_url': 'https://api.github.com/users/JosJuice/following{/other_user}', 'gists_url': 'https://api.github.com/users/JosJuice/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/JosJuice/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/JosJuice/subscriptions', 'organizations_url': 'https://api.github.com/users/JosJuice/orgs', 'repos_url': 'https://api.github.com/users/JosJuice/repos', 'events_url': 'https://api.github.com/users/JosJuice/events{/privacy}', 'received_events_url': 'https://api.github.com/users/JosJuice/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'body': "It's common practice to replace a decoding failure with a question mark or U+FFFD, not to stop trying to decode the string.", 'created_at': '2026-01-31T11:33:23Z', 'updated_at': '2026-01-31T11:36:08Z', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/14308#discussion_r2749417504', 'pull_request_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/14308', 'author_association': 'MEMBER', '_links': {'self': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2749417504'}, 'html': {'href': 'https://github.com/dolphin-emu/dolphin/pull/14308#discussion_r2749417504'}, 'pull_request': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/14308'}}, 'original_commit_id': 'b89301e80b16df4559224d21f11bcbdf314140b1', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2749417504/reactions', 'total_count': 0, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 0, 'confused': 0, 'heart': 0, 'rocket': 0, 'eyes': 0}}], 'type': 'gh_pull_request_review'}
2026-01-31T11:06:02.576127 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'JosJuice', 'action': 'submitted', 'pr_id': 14311, 'pr_title': 'RetroAchievements - Fix Aliases', 'state': 'approved', 'url': 'https://github.com/dolphin-emu/dolphin/pull/14311#pullrequestreview-3732308741', 'comments': [], 'type': 'gh_pull_request_review'}
2026-01-31T10:21:21.648609 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'oltolm', 'action': 'submitted', 'pr_id': 14003, 'pr_title': 'mingw: enable compilation using mingw compiler', 'state': 'commented', 'url': 'https://github.com/dolphin-emu/dolphin/pull/14003#pullrequestreview-3732221231', 'comments': [{'id': 2749340303, 'node_id': 'PRRC_kwDOALCn2M6j35qP', 'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2749340303', 'pull_request_review_id': 3732221231, 'diff_hunk': '@@ -153,7 +153,8 @@ void Destroy()\n {\n // print out alive objects, but only if we actually have pending references\n // note this will also print out internal live objects to the debug console\n- s_debug->ReportLiveDeviceObjects(D3D11_RLDO_SUMMARY | D3D11_RLDO_DETAIL);', 'path': 'Source/Core/VideoBackends/D3D/D3DBase.cpp', 'position': 4, 'original_position': 4, 'commit_id': '1bdc317e942ddb4309f03e82d22c90bf6532b410', 'user': {'login': 'oltolm', 'id': 1674229, 'node_id': 'MDQ6VXNlcjE2NzQyMjk=', 'avatar_url': 'https://avatars.githubusercontent.com/u/1674229?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/oltolm', 'html_url': 'https://github.com/oltolm', 'followers_url': 'https://api.github.com/users/oltolm/followers', 'following_url': 'https://api.github.com/users/oltolm/following{/other_user}', 'gists_url': 'https://api.github.com/users/oltolm/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/oltolm/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/oltolm/subscriptions', 'organizations_url': 'https://api.github.com/users/oltolm/orgs', 'repos_url': 'https://api.github.com/users/oltolm/repos', 'events_url': 'https://api.github.com/users/oltolm/events{/privacy}', 'received_events_url': 'https://api.github.com/users/oltolm/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'body': 'The latest version of mingw-w64 available today in MSYS2 has added `DEFINE_ENUM_FLAG_OPERATORS(D3D11_RLDO_FLAGS)` to the headers, so that this is not necessary anymore.', 'created_at': '2026-01-31T10:21:18Z', 'updated_at': '2026-01-31T10:21:18Z', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/14003#discussion_r2749340303', 'pull_request_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/14003', 'author_association': 'CONTRIBUTOR', '_links': {'self': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2749340303'}, 'html': {'href': 'https://github.com/dolphin-emu/dolphin/pull/14003#discussion_r2749340303'}, 'pull_request': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/14003'}}, 'original_commit_id': '1bdc317e942ddb4309f03e82d22c90bf6532b410', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2749340303/reactions', 'total_count': 0, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 0, 'confused': 0, 'heart': 0, 'rocket': 0, 'eyes': 0}}], 'type': 'gh_pull_request_review'}
2026-01-31T07:55:55.714075 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'jordan-woyak', 'action': 'submitted', 'pr_id': 14140, 'pr_title': 'VideoCommon: add a graphics mod feature to modify EFBs with a custom material, enhance bloom', 'state': 'commented', 'url': 'https://github.com/dolphin-emu/dolphin/pull/14140#pullrequestreview-3732007325', 'comments': [{'id': 2749173567, 'node_id': 'PRRC_kwDOALCn2M6j3Q8_', 'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2749173567', 'pull_request_review_id': 3732007325, 'diff_hunk': '@@ -93,7 +95,11 @@ class MaterialResource final : public Resource\n // Note: asset cache owns the asset, we access as a reference\n MaterialAsset* m_material_asset = nullptr;\n \n- GXPipelineUid m_uid;\n+ // If provided, denotes this material will be used\n+ // as a custom draw material, if not provided\n+ // denotes this will be used as an efb post\n+ // processing material', 'path': 'Source/Core/VideoCommon/Resources/MaterialResource.h', 'position': 24, 'original_position': 24, 'commit_id': '354b85dddf3c0beb229e700602421c46ce59b4fe', 'user': {'login': 'jordan-woyak', 'id': 1768214, 'node_id': 'MDQ6VXNlcjE3NjgyMTQ=', 'avatar_url': 'https://avatars.githubusercontent.com/u/1768214?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/jordan-woyak', 'html_url': 'https://github.com/jordan-woyak', 'followers_url': 'https://api.github.com/users/jordan-woyak/followers', 'following_url': 'https://api.github.com/users/jordan-woyak/following{/other_user}', 'gists_url': 'https://api.github.com/users/jordan-woyak/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/jordan-woyak/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/jordan-woyak/subscriptions', 'organizations_url': 'https://api.github.com/users/jordan-woyak/orgs', 'repos_url': 'https://api.github.com/users/jordan-woyak/repos', 'events_url': 'https://api.github.com/users/jordan-woyak/events{/privacy}', 'received_events_url': 'https://api.github.com/users/jordan-woyak/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'body': "`processing material` all on it's own line was the main weirdness, but I suppose this works.\r\n```cpp\r\n// If provided, denotes this material will be used as a custom draw material.\r\n// If not provided, denotes this will be used as an efb post processing material.\r\n```", 'created_at': '2026-01-31T07:55:53Z', 'updated_at': '2026-01-31T07:55:54Z', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/14140#discussion_r2749173567', 'pull_request_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/14140', 'author_association': 'MEMBER', '_links': {'self': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2749173567'}, 'html': {'href': 'https://github.com/dolphin-emu/dolphin/pull/14140#discussion_r2749173567'}, 'pull_request': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/14140'}}, 'original_commit_id': '354b85dddf3c0beb229e700602421c46ce59b4fe', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2749173567/reactions', 'total_count': 0, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 0, 'confused': 0, 'heart': 0, 'rocket': 0, 'eyes': 0}, 'in_reply_to_id': 2749167260}], 'type': 'gh_pull_request_review'}
2026-01-31T07:46:08.840044 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'iwubcode', 'action': 'submitted', 'pr_id': 14140, 'pr_title': 'VideoCommon: add a graphics mod feature to modify EFBs with a custom material, enhance bloom', 'state': 'commented', 'url': 'https://github.com/dolphin-emu/dolphin/pull/14140#pullrequestreview-3732001360', 'comments': [{'id': 2749168366, 'node_id': 'PRRC_kwDOALCn2M6j3Pru', 'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2749168366', 'pull_request_review_id': 3732001360, 'diff_hunk': '@@ -93,7 +95,11 @@ class MaterialResource final : public Resource\n // Note: asset cache owns the asset, we access as a reference\n MaterialAsset* m_material_asset = nullptr;\n \n- GXPipelineUid m_uid;\n+ // If provided, denotes this material will be used\n+ // as a custom draw material, if not provided\n+ // denotes this will be used as an efb post\n+ // processing material', 'path': 'Source/Core/VideoCommon/Resources/MaterialResource.h', 'position': 24, 'original_position': 24, 'commit_id': '354b85dddf3c0beb229e700602421c46ce59b4fe', 'user': {'login': 'iwubcode', 'id': 15224722, 'node_id': 'MDQ6VXNlcjE1MjI0NzIy', 'avatar_url': 'https://avatars.githubusercontent.com/u/15224722?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/iwubcode', 'html_url': 'https://github.com/iwubcode', 'followers_url': 'https://api.github.com/users/iwubcode/followers', 'following_url': 'https://api.github.com/users/iwubcode/following{/other_user}', 'gists_url': 'https://api.github.com/users/iwubcode/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/iwubcode/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/iwubcode/subscriptions', 'organizations_url': 'https://api.github.com/users/iwubcode/orgs', 'repos_url': 'https://api.github.com/users/iwubcode/repos', 'events_url': 'https://api.github.com/users/iwubcode/events{/privacy}', 'received_events_url': 'https://api.github.com/users/iwubcode/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'body': 'Any suggestions on how to fix?', 'created_at': '2026-01-31T07:46:06Z', 'updated_at': '2026-01-31T07:46:07Z', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/14140#discussion_r2749168366', 'pull_request_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/14140', 'author_association': 'MEMBER', '_links': {'self': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2749168366'}, 'html': {'href': 'https://github.com/dolphin-emu/dolphin/pull/14140#discussion_r2749168366'}, 'pull_request': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/14140'}}, 'original_commit_id': '354b85dddf3c0beb229e700602421c46ce59b4fe', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2749168366/reactions', 'total_count': 0, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 0, 'confused': 0, 'heart': 0, 'rocket': 0, 'eyes': 0}, 'in_reply_to_id': 2749167260}], 'type': 'gh_pull_request_review'}
2026-01-31T07:44:08.950768 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'jordan-woyak', 'action': 'submitted', 'pr_id': 14140, 'pr_title': 'VideoCommon: add a graphics mod feature to modify EFBs with a custom material, enhance bloom', 'state': 'commented', 'url': 'https://github.com/dolphin-emu/dolphin/pull/14140#pullrequestreview-3731999385', 'comments': [{'id': 2749167260, 'node_id': 'PRRC_kwDOALCn2M6j3Pac', 'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2749167260', 'pull_request_review_id': 3731999385, 'diff_hunk': '@@ -93,7 +95,11 @@ class MaterialResource final : public Resource\n // Note: asset cache owns the asset, we access as a reference\n MaterialAsset* m_material_asset = nullptr;\n \n- GXPipelineUid m_uid;\n+ // If provided, denotes this material will be used\n+ // as a custom draw material, if not provided\n+ // denotes this will be used as an efb post\n+ // processing material', 'path': 'Source/Core/VideoCommon/Resources/MaterialResource.h', 'position': 24, 'original_position': 24, 'commit_id': '354b85dddf3c0beb229e700602421c46ce59b4fe', 'user': {'login': 'jordan-woyak', 'id': 1768214, 'node_id': 'MDQ6VXNlcjE3NjgyMTQ=', 'avatar_url': 'https://avatars.githubusercontent.com/u/1768214?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/jordan-woyak', 'html_url': 'https://github.com/jordan-woyak', 'followers_url': 'https://api.github.com/users/jordan-woyak/followers', 'following_url': 'https://api.github.com/users/jordan-woyak/following{/other_user}', 'gists_url': 'https://api.github.com/users/jordan-woyak/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/jordan-woyak/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/jordan-woyak/subscriptions', 'organizations_url': 'https://api.github.com/users/jordan-woyak/orgs', 'repos_url': 'https://api.github.com/users/jordan-woyak/repos', 'events_url': 'https://api.github.com/users/jordan-woyak/events{/privacy}', 'received_events_url': 'https://api.github.com/users/jordan-woyak/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'body': 'Nitpicky, but this comment spans multiple lines weirdly. :P', 'created_at': '2026-01-31T07:44:06Z', 'updated_at': '2026-01-31T07:44:06Z', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/14140#discussion_r2749167260', 'pull_request_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/14140', 'author_association': 'MEMBER', '_links': {'self': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2749167260'}, 'html': {'href': 'https://github.com/dolphin-emu/dolphin/pull/14140#discussion_r2749167260'}, 'pull_request': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/14140'}}, 'original_commit_id': '354b85dddf3c0beb229e700602421c46ce59b4fe', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2749167260/reactions', 'total_count': 0, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 0, 'confused': 0, 'heart': 0, 'rocket': 0, 'eyes': 0}}], 'type': 'gh_pull_request_review'}
2026-01-31T04:24:31.897282 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'author': 'Geotale', 'action': 'submitted', 'pr_id': 13872, 'pr_title': 'Improve Interpreted Paired Move Accuracy', 'state': 'commented', 'url': 'https://github.com/dolphin-emu/dolphin/pull/13872#pullrequestreview-3731483886', 'comments': [{'id': 2748848295, 'node_id': 'PRRC_kwDOALCn2M6j2Bin', 'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2748848295', 'pull_request_review_id': 3731483886, 'diff_hunk': '@@ -139,51 +139,79 @@ double ApproximateReciprocalSquareRoot(double val)\n }\n \n const std::array<BaseAndDec, 32> fres_expected = {{\n- {0x7ff800, 0x3e1}, {0x783800, 0x3a7}, {0x70ea00, 0x371}, {0x6a0800, 0x340}, {0x638800, 0x313},\n- {0x5d6200, 0x2ea}, {0x579000, 0x2c4}, {0x520800, 0x2a0}, {0x4cc800, 0x27f}, {0x47ca00, 0x261},\n- {0x430800, 0x245}, {0x3e8000, 0x22a}, {0x3a2c00, 0x212}, {0x360800, 0x1fb}, {0x321400, 0x1e5},\n- {0x2e4a00, 0x1d1}, {0x2aa800, 0x1be}, {0x272c00, 0x1ac}, {0x23d600, 0x19b}, {0x209e00, 0x18b},\n- {0x1d8800, 0x17c}, {0x1a9000, 0x16e}, {0x17ae00, 0x15b}, {0x14f800, 0x15b}, {0x124400, 0x143},\n- {0x0fbe00, 0x143}, {0x0d3800, 0x12d}, {0x0ade00, 0x12d}, {0x088400, 0x11a}, {0x065000, 0x11a},\n- {0x041c00, 0x108}, {0x020c00, 0x106},\n+ {0xfff000, -0x3e1}, {0xf07000, -0x3a7}, {0xe1d400, -0x371}, {0xd41000, -0x340},\n+ {0xc71000, -0x313}, {0xbac400, -0x2ea}, {0xaf2000, -0x2c4}, {0xa41000, -0x2a0},\n+ {0x999000, -0x27f}, {0x8f9400, -0x261}, {0x861000, -0x245}, {0x7d0000, -0x22a},\n+ {0x745800, -0x212}, {0x6c1000, -0x1fb}, {0x642800, -0x1e5}, {0x5c9400, -0x1d1},\n+ {0x555000, -0x1be}, {0x4e5800, -0x1ac}, {0x47ac00, -0x19b}, {0x413c00, -0x18b},\n+ {0x3b1000, -0x17c}, {0x352000, -0x16e}, {0x2f5c00, -0x15b}, {0x29f000, -0x15b},\n+ {0x248800, -0x143}, {0x1f7c00, -0x143}, {0x1a7000, -0x12d}, {0x15bc00, -0x12d},\n+ {0x110800, -0x11a}, {0x0ca000, -0x11a}, {0x083800, -0x108}, {0x041800, -0x106},\n }};\n \n // Used by fres and ps_res.\n-double ApproximateReciprocal(double val)\n+double ApproximateReciprocal(const UReg_FPSCR& fpscr, double val)\n {\n- s64 integral = std::bit_cast<s64>(val);\n- const s64 mantissa = integral & ((1LL << 52) - 1);\n- const s64 sign = integral & (1ULL << 63);\n- s64 exponent = integral & (0x7FFLL << 52);\n+ const u64 integral = std::bit_cast<u64>(val);\n+\n+ // Convert into a float when possible\n+ const u64 signless = integral & ~DOUBLE_SIGN;\n+ const u32 mantissa =\n+ static_cast<u32>((integral & DOUBLE_FRAC) >> (DOUBLE_FRAC_WIDTH - FLOAT_FRAC_WIDTH));\n+ const u32 sign = static_cast<u32>((integral >> 32) & FLOAT_SIGN);\n+ const s32 exponent = static_cast<s32>((integral & DOUBLE_EXP) >> DOUBLE_FRAC_WIDTH) - 0x380;\n+\n+ // The largest floats possible just return 0\n+ const u64 huge_float = fpscr.NI ? 0x47d0000000000000ULL : 0x4940000000000000ULL;\n \n // Special case 0\n- if (mantissa == 0 && exponent == 0)\n+ if (signless == 0)\n return std::copysign(std::numeric_limits<double>::infinity(), val);\n \n- // Special case NaN-ish numbers\n- if (exponent == (0x7FFLL << 52))\n+ // Special case huge or NaN-ish numbers\n+ if (signless >= huge_float)\n {\n- if (mantissa == 0)\n+ if (!std::isnan(val))\n return std::copysign(0.0, val);\n return MakeQuiet(val);\n }\n \n // Special case small inputs\n- if (exponent < (895LL << 52))\n+ if (exponent < -1)\n return std::copysign(std::numeric_limits<float>::max(), val);\n \n- // Special case large inputs\n- if (exponent >= (1149LL << 52))\n- return std::copysign(0.0, val);\n+ const s32 new_exponent = 253 - exponent;\n \n- exponent = (0x7FDLL << 52) - exponent;\n-\n- const int i = static_cast<int>(mantissa >> 37);\n+ const u32 i = static_cast<u32>(mantissa >> 8);\n const auto& entry = fres_expected[i / 1024];\n- integral = sign | exponent;\n- integral |= static_cast<s64>(entry.m_base - (entry.m_dec * (i % 1024) + 1) / 2) << 29;\n+ const u32 new_mantissa = static_cast<u32>(entry.m_base + entry.m_dec * (i % 1024)) / 2;\n \n- return std::bit_cast<double>(integral);\n+ u32 result = sign | (static_cast<u32>(new_exponent) << FLOAT_FRAC_WIDTH) | new_mantissa;\n+ if (new_exponent <= 0)\n+ {\n+ // Result is subnormal so format it properly!\n+ if (fpscr.NI)\n+ {\n+ // Flush to 0 if inexact\n+ result = sign;\n+ }\n+ else\n+ {\n+ // Shift by the exponent amount\n+ u32 shift = 1 + static_cast<u32>(-new_exponent);\n+ result = sign | (((1 << FLOAT_FRAC_WIDTH) | new_mantissa) >> shift);\n+ }\n+ }\n+ return static_cast<double>(std::bit_cast<float>(result));\n+}\n+\n+// Variation of `ApproximateReciprocal`, operating on the bits rather than the raw value\n+u64 ApproximateReciprocalBits(const UReg_FPSCR& fpscr, u64 integral)', 'path': 'Source/Core/Core/FloatUtils.cpp', 'position': 1, 'original_position': 130, 'commit_id': 'b8aa7daea89b49f92a0b083307c89ba10a9ad9e5', 'user': {'login': 'Geotale', 'id': 72356786, 'node_id': 'MDQ6VXNlcjcyMzU2Nzg2', 'avatar_url': 'https://avatars.githubusercontent.com/u/72356786?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/Geotale', 'html_url': 'https://github.com/Geotale', 'followers_url': 'https://api.github.com/users/Geotale/followers', 'following_url': 'https://api.github.com/users/Geotale/following{/other_user}', 'gists_url': 'https://api.github.com/users/Geotale/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/Geotale/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/Geotale/subscriptions', 'organizations_url': 'https://api.github.com/users/Geotale/orgs', 'repos_url': 'https://api.github.com/users/Geotale/repos', 'events_url': 'https://api.github.com/users/Geotale/events{/privacy}', 'received_events_url': 'https://api.github.com/users/Geotale/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'body': 'Done ^^b This also gives rise to another optimization which could be done... in the future', 'created_at': '2026-01-31T04:24:29Z', 'updated_at': '2026-01-31T04:24:30Z', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/13872#discussion_r2748848295', 'pull_request_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13872', 'author_association': 'MEMBER', '_links': {'self': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2748848295'}, 'html': {'href': 'https://github.com/dolphin-emu/dolphin/pull/13872#discussion_r2748848295'}, 'pull_request': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13872'}}, 'original_commit_id': 'b8aa7daea89b49f92a0b083307c89ba10a9ad9e5', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2748848295/reactions', 'total_count': 0, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 0, 'confused': 0, 'heart': 0, 'rocket': 0, 'eyes': 0}, 'in_reply_to_id': 2724581105}], 'type': 'gh_pull_request_review'}
Recent 'gh_push' events
2026-02-03T22:50:54.523610 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'pusher': 'jordan-woyak', 'before_sha': 'eac7b290423efea867c2c7f3f2b2792d05418bdd', 'after_sha': 'dd2b94cd4a71fb6abda72f58d27434ef2d8b0a5f', 'commits': [{'author': {'name': 'JosJuice', 'email': 'josjuice@gmail.com', 'date': '2025-04-26T10:33:01+02:00', 'username': 'JosJuice'}, 'distinct': False, 'added': [], 'modified': ['Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/ActivityTracker.kt', 'Source/Android/jni/ActivityTracker.cpp', 'Source/Core/UICommon/UICommon.cpp', 'Source/Core/UICommon/UICommon.h'], 'removed': [], 'message': 'Android: Detect when native code should flush unsaved data', 'url': 'https://github.com/dolphin-emu/dolphin/commit/0ba32f7add97daf1c5ea52cf77b75e81d862676f', 'hash': '0ba32f7add97daf1c5ea52cf77b75e81d862676f'}, {'author': {'name': 'Jordan Woyak', 'email': 'jordan.woyak@gmail.com', 'date': '2025-04-26T17:18:56-05:00', 'username': 'jordan-woyak'}, 'distinct': False, 'added': [], 'modified': ['Source/Core/UICommon/UICommon.cpp', 'Source/Core/UICommon/UICommon.h'], 'removed': [], 'message': 'Core: Add a HookableEvent for UICommon::FlushUnsavedData.', 'url': 'https://github.com/dolphin-emu/dolphin/commit/b0652925faa54b9bfd5a17cfed8870a2b1f2759b', 'hash': 'b0652925faa54b9bfd5a17cfed8870a2b1f2759b'}, {'author': {'name': 'Jordan Woyak', 'email': 'jordan.woyak@gmail.com', 'date': '2025-10-19T20:31:22-05:00', 'username': 'jordan-woyak'}, 'distinct': False, 'added': ['Source/Core/Common/TransferableSharedMutex.h'], 'modified': ['Source/Core/Common/CMakeLists.txt', 'Source/Core/DolphinLib.props', 'Source/UnitTests/Common/MutexTest.cpp'], 'removed': [], 'message': 'Common: Add TransferableSharedMutex class and unit tests.', 'url': 'https://github.com/dolphin-emu/dolphin/commit/1d9e475123346c0e83289e788bdfad9f3345b512', 'hash': '1d9e475123346c0e83289e788bdfad9f3345b512'}, {'author': {'name': 'Jordan Woyak', 'email': 'jordan.woyak@gmail.com', 'date': '2025-11-02T22:21:15-06:00', 'username': 'jordan-woyak'}, 'distinct': False, 'added': [], 'modified': ['Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/NativeLibrary.kt', 'Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.kt', 'Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/fragments/EmulationFragment.kt', 'Source/Android/jni/MainAndroid.cpp', 'Source/Core/Core/State.cpp', 'Source/Core/Core/State.h'], 'removed': [], 'message': 'State: Simplify interthread communication and cleanups. Save/Load calls are now always non-blocking for the caller, but appropriately block the CPU thread as needed.', 'url': 'https://github.com/dolphin-emu/dolphin/commit/2322437f96bc53f1edcd9100daedb5b54c6e0fc4', 'hash': '2322437f96bc53f1edcd9100daedb5b54c6e0fc4'}, {'author': {'name': 'Jordan Woyak', 'email': 'jordan.woyak@gmail.com', 'date': '2026-02-03T16:50:52-06:00', 'username': 'jordan-woyak'}, 'distinct': True, 'added': ['Source/Core/Common/TransferableSharedMutex.h'], 'modified': ['Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/NativeLibrary.kt', 'Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.kt', 'Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/fragments/EmulationFragment.kt', 'Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/ActivityTracker.kt', 'Source/Android/jni/ActivityTracker.cpp', 'Source/Android/jni/MainAndroid.cpp', 'Source/Core/Common/CMakeLists.txt', 'Source/Core/Core/State.cpp', 'Source/Core/Core/State.h', 'Source/Core/DolphinLib.props', 'Source/Core/UICommon/UICommon.cpp', 'Source/Core/UICommon/UICommon.h', 'Source/UnitTests/Common/MutexTest.cpp'], 'removed': [], 'message': 'Merge pull request #13594 from jordan-woyak/state-cleanups\n\nState: Simplify interthread communication and general cleanups.', 'url': 'https://github.com/dolphin-emu/dolphin/commit/dd2b94cd4a71fb6abda72f58d27434ef2d8b0a5f', 'hash': 'dd2b94cd4a71fb6abda72f58d27434ef2d8b0a5f'}], 'base_ref_name': None, 'ref_name': 'master', 'ref_type': 'heads', 'created': False, 'deleted': False, 'forced': False, 'type': 'gh_push'}
2026-02-03T04:26:15.538287 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'pusher': 'jordan-woyak', 'before_sha': 'c7de9162765452fc2787f3ba4f0092394067d603', 'after_sha': 'eac7b290423efea867c2c7f3f2b2792d05418bdd', 'commits': [{'author': {'name': 'JMC47', 'email': 'JMC4789@gmail.com', 'date': '2026-02-01T00:42:27-05:00', 'username': 'JMC47'}, 'distinct': False, 'added': [], 'modified': ['Data/Sys/GameSettings/RSS.ini'], 'removed': [], 'message': 'GameINI: Force Safe Texture Cache to Samurai Shodown Anthology', 'url': 'https://github.com/dolphin-emu/dolphin/commit/ac19ef5452a1abcd7bcfc9d166fcdbe28c714ba1', 'hash': 'ac19ef5452a1abcd7bcfc9d166fcdbe28c714ba1'}, {'author': {'name': 'Jordan Woyak', 'email': 'jordan.woyak@gmail.com', 'date': '2026-02-02T22:26:13-06:00', 'username': 'jordan-woyak'}, 'distinct': True, 'added': [], 'modified': ['Data/Sys/GameSettings/RSS.ini'], 'removed': [], 'message': 'Merge pull request #14314 from JMC47/shodown\n\nGameINI: Force Safe Texture Cache to Samurai Shodown Anthology', 'url': 'https://github.com/dolphin-emu/dolphin/commit/eac7b290423efea867c2c7f3f2b2792d05418bdd', 'hash': 'eac7b290423efea867c2c7f3f2b2792d05418bdd'}], 'base_ref_name': None, 'ref_name': 'master', 'ref_type': 'heads', 'created': False, 'deleted': False, 'forced': False, 'type': 'gh_push'}
2026-02-02T20:05:53.999965 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'pusher': 'JMC47', 'before_sha': '6711d77b9901ed0a4418203db49ed4d8576e6fe0', 'after_sha': 'c7de9162765452fc2787f3ba4f0092394067d603', 'commits': [{'author': {'name': 'Joshua Vandaële', 'email': 'joshua@vandaele.software', 'date': '2026-02-02T20:01:58+01:00', 'username': 'JoshuaVandaele'}, 'distinct': False, 'added': [], 'modified': ['Source/Core/UICommon/ResourcePack/ResourcePack.cpp'], 'removed': [], 'message': "ResourcePack: Fix loading resource packs\n\nThis is something I missed when updating minizip, this mistake made it so resource packs didn't find any textures", 'url': 'https://github.com/dolphin-emu/dolphin/commit/d87e8ab7ff66f3efcedeb5a0ab869b537933aada', 'hash': 'd87e8ab7ff66f3efcedeb5a0ab869b537933aada'}, {'author': {'name': 'JMC47', 'email': 'JMC4789@gmail.com', 'date': '2026-02-02T15:05:51-05:00', 'username': 'JMC47'}, 'distinct': True, 'added': [], 'modified': ['Source/Core/UICommon/ResourcePack/ResourcePack.cpp'], 'removed': [], 'message': 'Merge pull request #14316 from JoshuaVandaele/resourcefix\n\nResourcePack: Fix loading resource packs', 'url': 'https://github.com/dolphin-emu/dolphin/commit/c7de9162765452fc2787f3ba4f0092394067d603', 'hash': 'c7de9162765452fc2787f3ba4f0092394067d603'}], 'base_ref_name': None, 'ref_name': 'master', 'ref_type': 'heads', 'created': False, 'deleted': False, 'forced': False, 'type': 'gh_push'}
2026-01-31T23:02:12.428260 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'pusher': 'Dentomologist', 'before_sha': '16276928ccb6d14e32cd33cde993f70503f0e838', 'after_sha': '6711d77b9901ed0a4418203db49ed4d8576e6fe0', 'commits': [{'author': {'name': 'Nuh Uh', 'email': '72356786+Geotale@users.noreply.github.com', 'date': '2025-12-10T13:40:24-06:00', 'username': 'Geotale'}, 'distinct': False, 'added': [], 'modified': ['Source/Core/VideoCommon/UberShaderPixel.cpp'], 'removed': [], 'message': 'Update Comments Based On Hardware Test\n\nChecked on hardware that this bias was not added because I had assumed the other way around would be true, forgot to ask about making a PR for this when I initially had done so', 'url': 'https://github.com/dolphin-emu/dolphin/commit/f31f11c0a4b37decebd0e0a349bdc33691fdd657', 'hash': 'f31f11c0a4b37decebd0e0a349bdc33691fdd657'}, {'author': {'name': 'Dentomologist', 'email': 'dentomologist@gmail.com', 'date': '2026-01-31T15:02:09-08:00', 'username': 'Dentomologist'}, 'distinct': True, 'added': [], 'modified': ['Source/Core/VideoCommon/UberShaderPixel.cpp'], 'removed': [], 'message': 'Merge pull request #14204 from Geotale/update-comment\n\nUpdate Comments Based On Hardware Test', 'url': 'https://github.com/dolphin-emu/dolphin/commit/6711d77b9901ed0a4418203db49ed4d8576e6fe0', 'hash': '6711d77b9901ed0a4418203db49ed4d8576e6fe0'}], 'base_ref_name': None, 'ref_name': 'master', 'ref_type': 'heads', 'created': False, 'deleted': False, 'forced': False, 'type': 'gh_push'}
2026-01-31T22:45:21.300900 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'pusher': 'jordan-woyak', 'before_sha': 'b1020186519d690a9b64a215f00da7453ab2803b', 'after_sha': '16276928ccb6d14e32cd33cde993f70503f0e838', 'commits': [{'author': {'name': 'Joshua Vandaële', 'email': 'joshua@vandaele.software', 'date': '2026-01-31T10:36:55+01:00', 'username': 'JoshuaVandaele'}, 'distinct': False, 'added': [], 'modified': ['Source/Core/Common/GL/GLInterface/WGL.cpp'], 'removed': [], 'message': 'WGL: Correctly load wglDestroyPbufferARB extension', 'url': 'https://github.com/dolphin-emu/dolphin/commit/e6bc8fb342169d9936645fe1fdebe296d92bc44b', 'hash': 'e6bc8fb342169d9936645fe1fdebe296d92bc44b'}, {'author': {'name': 'Jordan Woyak', 'email': 'jordan.woyak@gmail.com', 'date': '2026-01-31T16:45:19-06:00', 'username': 'jordan-woyak'}, 'distinct': True, 'added': [], 'modified': ['Source/Core/Common/GL/GLInterface/WGL.cpp'], 'removed': [], 'message': 'Merge pull request #14312 from JoshuaVandaele/wglDestroyPbufferARB\n\nWGL: Correctly load wglDestroyPbufferARB extension', 'url': 'https://github.com/dolphin-emu/dolphin/commit/16276928ccb6d14e32cd33cde993f70503f0e838', 'hash': '16276928ccb6d14e32cd33cde993f70503f0e838'}], 'base_ref_name': None, 'ref_name': 'master', 'ref_type': 'heads', 'created': False, 'deleted': False, 'forced': False, 'type': 'gh_push'}
2026-01-30T19:51:11.032299 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'pusher': 'JMC47', 'before_sha': 'bb7e01167346d0e3e4bab426a7b87968d715ee5e', 'after_sha': 'b1020186519d690a9b64a215f00da7453ab2803b', 'commits': [{'author': {'name': 'Sam Belliveau', 'email': 'sam.belliveau@gmail.com', 'date': '2026-01-27T18:48:22-05:00', 'username': 'Sam-Belliveau'}, 'distinct': False, 'added': [], 'modified': ['Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/BooleanSetting.kt', 'Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.kt', 'Source/Android/app/src/main/res/values/strings.xml', 'Source/Core/AudioCommon/Mixer.cpp', 'Source/Core/AudioCommon/Mixer.h', 'Source/Core/Core/Config/MainSettings.cpp', 'Source/Core/Core/Config/MainSettings.h', 'Source/Core/DolphinQt/Settings/AudioPane.cpp', 'Source/Core/DolphinQt/Settings/AudioPane.h'], 'removed': [], 'message': 'feat: Add an option to preserve audio pitch when emulation speed changes, integrating it into core configuration and both Qt and Android UIs.', 'url': 'https://github.com/dolphin-emu/dolphin/commit/51c8f18b73727afaea0991e10789998eaacbe487', 'hash': '51c8f18b73727afaea0991e10789998eaacbe487'}, {'author': {'name': 'JMC47', 'email': 'JMC4789@gmail.com', 'date': '2026-01-30T14:51:08-05:00', 'username': 'JMC47'}, 'distinct': True, 'added': [], 'modified': ['Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/BooleanSetting.kt', 'Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.kt', 'Source/Android/app/src/main/res/values/strings.xml', 'Source/Core/AudioCommon/Mixer.cpp', 'Source/Core/AudioCommon/Mixer.h', 'Source/Core/Core/Config/MainSettings.cpp', 'Source/Core/Core/Config/MainSettings.h', 'Source/Core/DolphinQt/Settings/AudioPane.cpp', 'Source/Core/DolphinQt/Settings/AudioPane.h'], 'removed': [], 'message': 'Merge pull request #14307 from Sam-Belliveau/granular-audio-stretching\n\nAdd an option to preserve audio pitch when emulation speed changes', 'url': 'https://github.com/dolphin-emu/dolphin/commit/b1020186519d690a9b64a215f00da7453ab2803b', 'hash': 'b1020186519d690a9b64a215f00da7453ab2803b'}], 'base_ref_name': None, 'ref_name': 'master', 'ref_type': 'heads', 'created': False, 'deleted': False, 'forced': False, 'type': 'gh_push'}
2026-01-29T22:05:09.716660 {'source': 'ghhookparser', 'repo': 'dolphin-emu/sadm', 'pusher': 'OatmealDome', 'before_sha': '5b6d8c9aa277c5d4c7e23f32eb81f52d26d2f693', 'after_sha': 'aa075152f04a441758db4e72a0468ee2d85df930', 'commits': [{'author': {'name': 'OatmealDome', 'email': 'julian@oatmealdome.me', 'date': '2026-01-29T08:25:10-05:00', 'username': 'OatmealDome'}, 'distinct': True, 'added': [], 'modified': ['flake.lock'], 'removed': [], 'message': "flake.lock: Update\n\nFlake lock file updates:\n\n• Updated input 'nixpkgs':\n 'github:NixOS/nixpkgs/078d69f03934859a181e81ba987c2bb033eebfc5?narHash=sha256-9yA/LIuAVQq0lXelrZPjLuLVuZdm03p8tfmHhnDIkms%3D' (2026-01-22)\n → 'github:NixOS/nixpkgs/fa83fd837f3098e3e678e6cf017b2b36102c7211?narHash=sha256-e7VO/kGLgRMbWtpBqdWl0uFg8Y2XWFMdz0uUJvlML8o%3D' (2026-01-28)\n• Updated input 'rust-overlay':\n 'github:oxalica/rust-overlay/22da29e7f3d8cff75009cbbcf992c7cb66920cfd?narHash=sha256-gu6oZ86zLudBZMq8LL1qdtYt/S69GV5keQVXdvBrVSU%3D' (2026-01-24)\n → 'github:oxalica/rust-overlay/8b94aae763a09749cc153bab0b14e6ad8fc95494?narHash=sha256-Yq4uj%2BRjiE2Bw7C%2B7Mojdiw0kTh9xwJxUo6IjofZV%2Bc%3D' (2026-01-29)", 'url': 'https://github.com/dolphin-emu/sadm/commit/908bde4502959bcc48ef61df34d9005cd203ccda', 'hash': '908bde4502959bcc48ef61df34d9005cd203ccda'}, {'author': {'name': 'OatmealDome', 'email': 'julian@oatmealdome.me', 'date': '2026-01-29T08:25:30-05:00', 'username': 'OatmealDome'}, 'distinct': True, 'added': [], 'modified': ['roles/bug-tracker/default.nix'], 'removed': [], 'message': 'Revert "bug-tracker: Manually patch Redmine with nixpkgs PR 483401"\n\nThis reverts commit 82503abd8f7894e2350676085139de08aa3bde74.', 'url': 'https://github.com/dolphin-emu/sadm/commit/aa075152f04a441758db4e72a0468ee2d85df930', 'hash': 'aa075152f04a441758db4e72a0468ee2d85df930'}], 'base_ref_name': None, 'ref_name': 'master', 'ref_type': 'heads', 'created': False, 'deleted': False, 'forced': False, 'type': 'gh_push'}
2026-01-27T23:32:28.150875 {'source': 'ghhookparser', 'repo': 'dolphin-emu/dolphin', 'pusher': 'jordan-woyak', 'before_sha': '10b9da8cd451875e011ca754ce8c5ff37cff4c0b', 'after_sha': 'bb7e01167346d0e3e4bab426a7b87968d715ee5e', 'commits': [{'author': {'name': 'Ballai Péter', 'email': 'ballaipeterlorant@gmail.com', 'date': '2026-01-18T00:53:08+01:00', 'username': 'droidpeti'}, 'distinct': False, 'added': [], 'modified': ['Data/Sys/GameSettings/GMSE01.ini'], 'removed': [], 'message': 'GMSE01: Added author credits for Gecko Codes', 'url': 'https://github.com/dolphin-emu/dolphin/commit/0afdfd914672695e75fe77b3a1bd4435fc69cc0f', 'hash': '0afdfd914672695e75fe77b3a1bd4435fc69cc0f'}, {'author': {'name': 'Jordan Woyak', 'email': 'jordan.woyak@gmail.com', 'date': '2026-01-27T17:32:25-06:00', 'username': 'jordan-woyak'}, 'distinct': True, 'added': [], 'modified': ['Data/Sys/GameSettings/GMSE01.ini'], 'removed': [], 'message': 'Merge pull request #14254 from droidpeti/master\n\nGMSE01: Added credits to Gecko codes for Super Mario Sunshine (USA)', 'url': 'https://github.com/dolphin-emu/dolphin/commit/bb7e01167346d0e3e4bab426a7b87968d715ee5e', 'hash': 'bb7e01167346d0e3e4bab426a7b87968d715ee5e'}], 'base_ref_name': None, 'ref_name': 'master', 'ref_type': 'heads', 'created': False, 'deleted': False, 'forced': False, 'type': 'gh_push'}
Recent 'internal_log' events
2026-02-03T23:35:02.985655 {'source': 'logging', 'level': 'INFO', 'pathname': '/nix/store/pszlv4r8dapp8a8nw5qjpiz4q8py1bfg-central-env/lib/python3.10/site-packages/central/github/authz.py', 'lineno': 30, 'msg': 'New GH %s: %s', 'args': "('dolphin-emu/trusted-developers', 'marcan,CelestialAmber,aroulin,nullgemm,NanoByte011,mitaclaw,booto,mandar1jn,merryhime,FioraAeterna,Tilka,Sam-Belliveau,endrift,magcius,LPFaint99,moncefmechri,Stevoisiak,rlnilsen,archshift,hdcmeta,mrgreywater,iwubcode,unknownbrackets,RachelBryk,Zopolis4,lioncash,adamdmoss,SirMangler,MayImilae,comex,hthh,yourWaifu,Geotale,phire,RisingFog,AdmiralCurtiss,deReeperJosh,aldelaro5,linkmauve,glennricster,Starsam80,gwicks,smurf3tte,Ebola16,zackhow,Orphis,meffij,Gamer64ytb,JosJuice,Hydr8gon,vladfi1,DacoTaco,cristian64,PatrickFerry,mathieui,ligfx,TryTwo,JMC47,SuperSamus,EmptyChaos,rukai,zopieux,OrN,CasualPokePlayer,sepalani,ColinDTaylor,Pokechu22,neobrain,Techjar,Alcaro,kamiyo,jjdelvalle,delroth,CookiePLMonster,jloehr,Linktothepast,riking,BhaaLseN,OatmealDome,corwin-mcknight,bentley,orbea,TellowKrinkle,Filoppi,jezze,Sintendo,mimimi085181,spxtr,tygyh,galop1n,K0bin,Phatcat,Buddybenj,krnlyng,hrydgard,Parlane,nickbeth,mahdihijazi,ShimmerGlass,jordan-woyak,noahpistilli,JordanTheToaster,Simonx22,Ziek,randomstuff,mmastrac,spycrab,JoshuaVandaele,Tinob,leoetlino,CrossVR,LillyJadeKatrin,degasus,shonumi,dreamsyntax,CrystalGamma,skylersaleh,kayru,mbc07,magumagu,Dentomologist,hackbar,skidau,LAGonauta,malleoz,Helios747,crediar,Lobsterzelda')", 'type': 'internal_log'}
2026-02-03T23:35:01.945872 {'source': 'logging', 'level': 'INFO', 'pathname': '/nix/store/pszlv4r8dapp8a8nw5qjpiz4q8py1bfg-central-env/lib/python3.10/site-packages/central/github/authz.py', 'lineno': 21, 'msg': 'Refreshing list of trusted users (from %s/%s)', 'args': "('dolphin-emu', 'trusted-developers')", 'type': 'internal_log'}
2026-02-03T23:32:40.922058 {'source': 'logging', 'level': 'INFO', 'pathname': '/nix/store/pszlv4r8dapp8a8nw5qjpiz4q8py1bfg-central-env/lib/python3.10/site-packages/central/buildbot.py', 'lineno': 131, 'msg': 'PR %s mergeable: %s (%s)', 'args': "(14318, None, 'unknown')", 'type': 'internal_log'}
2026-02-03T23:30:01.845839 {'source': 'logging', 'level': 'INFO', 'pathname': '/nix/store/pszlv4r8dapp8a8nw5qjpiz4q8py1bfg-central-env/lib/python3.10/site-packages/central/github/authz.py', 'lineno': 30, 'msg': 'New GH %s: %s', 'args': "('dolphin-emu/trusted-developers', 'marcan,CelestialAmber,aroulin,nullgemm,NanoByte011,mitaclaw,booto,mandar1jn,merryhime,FioraAeterna,Tilka,Sam-Belliveau,endrift,magcius,LPFaint99,moncefmechri,Stevoisiak,rlnilsen,archshift,hdcmeta,mrgreywater,iwubcode,unknownbrackets,RachelBryk,Zopolis4,lioncash,adamdmoss,SirMangler,MayImilae,comex,hthh,yourWaifu,Geotale,phire,RisingFog,AdmiralCurtiss,deReeperJosh,aldelaro5,linkmauve,glennricster,Starsam80,gwicks,smurf3tte,Ebola16,zackhow,Orphis,meffij,Gamer64ytb,JosJuice,Hydr8gon,vladfi1,DacoTaco,cristian64,PatrickFerry,mathieui,ligfx,TryTwo,JMC47,SuperSamus,EmptyChaos,rukai,zopieux,OrN,CasualPokePlayer,sepalani,ColinDTaylor,Pokechu22,neobrain,Techjar,Alcaro,kamiyo,jjdelvalle,delroth,CookiePLMonster,jloehr,Linktothepast,riking,BhaaLseN,OatmealDome,corwin-mcknight,bentley,orbea,TellowKrinkle,Filoppi,jezze,Sintendo,mimimi085181,spxtr,tygyh,galop1n,K0bin,Phatcat,Buddybenj,krnlyng,hrydgard,Parlane,nickbeth,mahdihijazi,ShimmerGlass,jordan-woyak,noahpistilli,JordanTheToaster,Simonx22,Ziek,randomstuff,mmastrac,spycrab,JoshuaVandaele,Tinob,leoetlino,CrossVR,LillyJadeKatrin,degasus,shonumi,dreamsyntax,CrystalGamma,skylersaleh,kayru,mbc07,magumagu,Dentomologist,hackbar,skidau,LAGonauta,malleoz,Helios747,crediar,Lobsterzelda')", 'type': 'internal_log'}
2026-02-03T23:30:00.803572 {'source': 'logging', 'level': 'INFO', 'pathname': '/nix/store/pszlv4r8dapp8a8nw5qjpiz4q8py1bfg-central-env/lib/python3.10/site-packages/central/github/authz.py', 'lineno': 21, 'msg': 'Refreshing list of trusted users (from %s/%s)', 'args': "('dolphin-emu', 'trusted-developers')", 'type': 'internal_log'}
2026-02-03T23:27:32.175125 {'source': 'logging', 'level': 'INFO', 'pathname': '/nix/store/pszlv4r8dapp8a8nw5qjpiz4q8py1bfg-central-env/lib/python3.10/site-packages/central/github/authz.py', 'lineno': 30, 'msg': 'New GH %s: %s', 'args': "('dolphin-emu/core-developers', 'AdmiralCurtiss,linkmauve,delroth,jordan-woyak,Tilka,JosJuice,spycrab,OatmealDome,leoetlino,CrossVR,iwubcode,degasus,phire,lioncash,Pokechu22,Dentomologist,skidau,Helios747,hrydgard,Parlane')", 'type': 'internal_log'}
2026-02-03T23:27:31.962733 {'source': 'logging', 'level': 'INFO', 'pathname': '/nix/store/pszlv4r8dapp8a8nw5qjpiz4q8py1bfg-central-env/lib/python3.10/site-packages/central/github/authz.py', 'lineno': 21, 'msg': 'Refreshing list of trusted users (from %s/%s)', 'args': "('dolphin-emu', 'core-developers')", 'type': 'internal_log'}
2026-02-03T23:25:00.704569 {'source': 'logging', 'level': 'INFO', 'pathname': '/nix/store/pszlv4r8dapp8a8nw5qjpiz4q8py1bfg-central-env/lib/python3.10/site-packages/central/github/authz.py', 'lineno': 30, 'msg': 'New GH %s: %s', 'args': "('dolphin-emu/trusted-developers', 'marcan,CelestialAmber,aroulin,nullgemm,NanoByte011,mitaclaw,booto,mandar1jn,merryhime,FioraAeterna,Tilka,Sam-Belliveau,endrift,magcius,LPFaint99,moncefmechri,Stevoisiak,rlnilsen,archshift,hdcmeta,mrgreywater,iwubcode,unknownbrackets,RachelBryk,Zopolis4,lioncash,adamdmoss,SirMangler,MayImilae,comex,hthh,yourWaifu,Geotale,phire,RisingFog,AdmiralCurtiss,deReeperJosh,aldelaro5,linkmauve,glennricster,Starsam80,gwicks,smurf3tte,Ebola16,zackhow,Orphis,meffij,Gamer64ytb,JosJuice,Hydr8gon,vladfi1,DacoTaco,cristian64,PatrickFerry,mathieui,ligfx,TryTwo,JMC47,SuperSamus,EmptyChaos,rukai,zopieux,OrN,CasualPokePlayer,sepalani,ColinDTaylor,Pokechu22,neobrain,Techjar,Alcaro,kamiyo,jjdelvalle,delroth,CookiePLMonster,jloehr,Linktothepast,riking,BhaaLseN,OatmealDome,corwin-mcknight,bentley,orbea,TellowKrinkle,Filoppi,jezze,Sintendo,mimimi085181,spxtr,tygyh,galop1n,K0bin,Phatcat,Buddybenj,krnlyng,hrydgard,Parlane,nickbeth,mahdihijazi,ShimmerGlass,jordan-woyak,noahpistilli,JordanTheToaster,Simonx22,Ziek,randomstuff,mmastrac,spycrab,JoshuaVandaele,Tinob,leoetlino,CrossVR,LillyJadeKatrin,degasus,shonumi,dreamsyntax,CrystalGamma,skylersaleh,kayru,mbc07,magumagu,Dentomologist,hackbar,skidau,LAGonauta,malleoz,Helios747,crediar,Lobsterzelda')", 'type': 'internal_log'}
2026-02-03T23:24:59.266770 {'source': 'logging', 'level': 'INFO', 'pathname': '/nix/store/pszlv4r8dapp8a8nw5qjpiz4q8py1bfg-central-env/lib/python3.10/site-packages/central/github/authz.py', 'lineno': 21, 'msg': 'Refreshing list of trusted users (from %s/%s)', 'args': "('dolphin-emu', 'trusted-developers')", 'type': 'internal_log'}
2026-02-03T23:19:59.166814 {'source': 'logging', 'level': 'INFO', 'pathname': '/nix/store/pszlv4r8dapp8a8nw5qjpiz4q8py1bfg-central-env/lib/python3.10/site-packages/central/github/authz.py', 'lineno': 30, 'msg': 'New GH %s: %s', 'args': "('dolphin-emu/trusted-developers', 'marcan,CelestialAmber,aroulin,nullgemm,NanoByte011,mitaclaw,booto,mandar1jn,merryhime,FioraAeterna,Tilka,Sam-Belliveau,endrift,magcius,LPFaint99,moncefmechri,Stevoisiak,rlnilsen,archshift,hdcmeta,mrgreywater,iwubcode,unknownbrackets,RachelBryk,Zopolis4,lioncash,adamdmoss,SirMangler,MayImilae,comex,hthh,yourWaifu,Geotale,phire,RisingFog,AdmiralCurtiss,deReeperJosh,aldelaro5,linkmauve,glennricster,Starsam80,gwicks,smurf3tte,Ebola16,zackhow,Orphis,meffij,Gamer64ytb,JosJuice,Hydr8gon,vladfi1,DacoTaco,cristian64,PatrickFerry,mathieui,ligfx,TryTwo,JMC47,SuperSamus,EmptyChaos,rukai,zopieux,OrN,CasualPokePlayer,sepalani,ColinDTaylor,Pokechu22,neobrain,Techjar,Alcaro,kamiyo,jjdelvalle,delroth,CookiePLMonster,jloehr,Linktothepast,riking,BhaaLseN,OatmealDome,corwin-mcknight,bentley,orbea,TellowKrinkle,Filoppi,jezze,Sintendo,mimimi085181,spxtr,tygyh,galop1n,K0bin,Phatcat,Buddybenj,krnlyng,hrydgard,Parlane,nickbeth,mahdihijazi,ShimmerGlass,jordan-woyak,noahpistilli,JordanTheToaster,Simonx22,Ziek,randomstuff,mmastrac,spycrab,JoshuaVandaele,Tinob,leoetlino,CrossVR,LillyJadeKatrin,degasus,shonumi,dreamsyntax,CrystalGamma,skylersaleh,kayru,mbc07,magumagu,Dentomologist,hackbar,skidau,LAGonauta,malleoz,Helios747,crediar,Lobsterzelda')", 'type': 'internal_log'}
2026-02-03T23:19:58.121168 {'source': 'logging', 'level': 'INFO', 'pathname': '/nix/store/pszlv4r8dapp8a8nw5qjpiz4q8py1bfg-central-env/lib/python3.10/site-packages/central/github/authz.py', 'lineno': 21, 'msg': 'Refreshing list of trusted users (from %s/%s)', 'args': "('dolphin-emu', 'trusted-developers')", 'type': 'internal_log'}
2026-02-03T23:17:31.862672 {'source': 'logging', 'level': 'INFO', 'pathname': '/nix/store/pszlv4r8dapp8a8nw5qjpiz4q8py1bfg-central-env/lib/python3.10/site-packages/central/github/authz.py', 'lineno': 30, 'msg': 'New GH %s: %s', 'args': "('dolphin-emu/core-developers', 'AdmiralCurtiss,linkmauve,delroth,jordan-woyak,Tilka,JosJuice,spycrab,OatmealDome,leoetlino,CrossVR,iwubcode,degasus,phire,lioncash,Pokechu22,Dentomologist,skidau,Helios747,hrydgard,Parlane')", 'type': 'internal_log'}
2026-02-03T23:17:31.637845 {'source': 'logging', 'level': 'INFO', 'pathname': '/nix/store/pszlv4r8dapp8a8nw5qjpiz4q8py1bfg-central-env/lib/python3.10/site-packages/central/github/authz.py', 'lineno': 21, 'msg': 'Refreshing list of trusted users (from %s/%s)', 'args': "('dolphin-emu', 'core-developers')", 'type': 'internal_log'}
2026-02-03T23:14:58.021237 {'source': 'logging', 'level': 'INFO', 'pathname': '/nix/store/pszlv4r8dapp8a8nw5qjpiz4q8py1bfg-central-env/lib/python3.10/site-packages/central/github/authz.py', 'lineno': 30, 'msg': 'New GH %s: %s', 'args': "('dolphin-emu/trusted-developers', 'marcan,CelestialAmber,aroulin,nullgemm,NanoByte011,mitaclaw,booto,mandar1jn,merryhime,FioraAeterna,Tilka,Sam-Belliveau,endrift,magcius,LPFaint99,moncefmechri,Stevoisiak,rlnilsen,archshift,hdcmeta,mrgreywater,iwubcode,unknownbrackets,RachelBryk,Zopolis4,lioncash,adamdmoss,SirMangler,MayImilae,comex,hthh,yourWaifu,Geotale,phire,RisingFog,AdmiralCurtiss,deReeperJosh,aldelaro5,linkmauve,glennricster,Starsam80,gwicks,smurf3tte,Ebola16,zackhow,Orphis,meffij,Gamer64ytb,JosJuice,Hydr8gon,vladfi1,DacoTaco,cristian64,PatrickFerry,mathieui,ligfx,TryTwo,JMC47,SuperSamus,EmptyChaos,rukai,zopieux,OrN,CasualPokePlayer,sepalani,ColinDTaylor,Pokechu22,neobrain,Techjar,Alcaro,kamiyo,jjdelvalle,delroth,CookiePLMonster,jloehr,Linktothepast,riking,BhaaLseN,OatmealDome,corwin-mcknight,bentley,orbea,TellowKrinkle,Filoppi,jezze,Sintendo,mimimi085181,spxtr,tygyh,galop1n,K0bin,Phatcat,Buddybenj,krnlyng,hrydgard,Parlane,nickbeth,mahdihijazi,ShimmerGlass,jordan-woyak,noahpistilli,JordanTheToaster,Simonx22,Ziek,randomstuff,mmastrac,spycrab,JoshuaVandaele,Tinob,leoetlino,CrossVR,LillyJadeKatrin,degasus,shonumi,dreamsyntax,CrystalGamma,skylersaleh,kayru,mbc07,magumagu,Dentomologist,hackbar,skidau,LAGonauta,malleoz,Helios747,crediar,Lobsterzelda')", 'type': 'internal_log'}
2026-02-03T23:14:56.919765 {'source': 'logging', 'level': 'INFO', 'pathname': '/nix/store/pszlv4r8dapp8a8nw5qjpiz4q8py1bfg-central-env/lib/python3.10/site-packages/central/github/authz.py', 'lineno': 21, 'msg': 'Refreshing list of trusted users (from %s/%s)', 'args': "('dolphin-emu', 'trusted-developers')", 'type': 'internal_log'}
2026-02-03T23:09:56.820171 {'source': 'logging', 'level': 'INFO', 'pathname': '/nix/store/pszlv4r8dapp8a8nw5qjpiz4q8py1bfg-central-env/lib/python3.10/site-packages/central/github/authz.py', 'lineno': 30, 'msg': 'New GH %s: %s', 'args': "('dolphin-emu/trusted-developers', 'marcan,CelestialAmber,aroulin,nullgemm,NanoByte011,mitaclaw,booto,mandar1jn,merryhime,FioraAeterna,Tilka,Sam-Belliveau,endrift,magcius,LPFaint99,moncefmechri,Stevoisiak,rlnilsen,archshift,hdcmeta,mrgreywater,iwubcode,unknownbrackets,RachelBryk,Zopolis4,lioncash,adamdmoss,SirMangler,MayImilae,comex,hthh,yourWaifu,Geotale,phire,RisingFog,AdmiralCurtiss,deReeperJosh,aldelaro5,linkmauve,glennricster,Starsam80,gwicks,smurf3tte,Ebola16,zackhow,Orphis,meffij,Gamer64ytb,JosJuice,Hydr8gon,vladfi1,DacoTaco,cristian64,PatrickFerry,mathieui,ligfx,TryTwo,JMC47,SuperSamus,EmptyChaos,rukai,zopieux,OrN,CasualPokePlayer,sepalani,ColinDTaylor,Pokechu22,neobrain,Techjar,Alcaro,kamiyo,jjdelvalle,delroth,CookiePLMonster,jloehr,Linktothepast,riking,BhaaLseN,OatmealDome,corwin-mcknight,bentley,orbea,TellowKrinkle,Filoppi,jezze,Sintendo,mimimi085181,spxtr,tygyh,galop1n,K0bin,Phatcat,Buddybenj,krnlyng,hrydgard,Parlane,nickbeth,mahdihijazi,ShimmerGlass,jordan-woyak,noahpistilli,JordanTheToaster,Simonx22,Ziek,randomstuff,mmastrac,spycrab,JoshuaVandaele,Tinob,leoetlino,CrossVR,LillyJadeKatrin,degasus,shonumi,dreamsyntax,CrystalGamma,skylersaleh,kayru,mbc07,magumagu,Dentomologist,hackbar,skidau,LAGonauta,malleoz,Helios747,crediar,Lobsterzelda')", 'type': 'internal_log'}
2026-02-03T23:09:55.606760 {'source': 'logging', 'level': 'INFO', 'pathname': '/nix/store/pszlv4r8dapp8a8nw5qjpiz4q8py1bfg-central-env/lib/python3.10/site-packages/central/github/authz.py', 'lineno': 21, 'msg': 'Refreshing list of trusted users (from %s/%s)', 'args': "('dolphin-emu', 'trusted-developers')", 'type': 'internal_log'}
2026-02-03T23:07:31.538382 {'source': 'logging', 'level': 'INFO', 'pathname': '/nix/store/pszlv4r8dapp8a8nw5qjpiz4q8py1bfg-central-env/lib/python3.10/site-packages/central/github/authz.py', 'lineno': 30, 'msg': 'New GH %s: %s', 'args': "('dolphin-emu/core-developers', 'AdmiralCurtiss,linkmauve,delroth,jordan-woyak,Tilka,JosJuice,spycrab,OatmealDome,leoetlino,CrossVR,iwubcode,degasus,phire,lioncash,Pokechu22,Dentomologist,skidau,Helios747,hrydgard,Parlane')", 'type': 'internal_log'}
2026-02-03T23:07:31.308754 {'source': 'logging', 'level': 'INFO', 'pathname': '/nix/store/pszlv4r8dapp8a8nw5qjpiz4q8py1bfg-central-env/lib/python3.10/site-packages/central/github/authz.py', 'lineno': 21, 'msg': 'Refreshing list of trusted users (from %s/%s)', 'args': "('dolphin-emu', 'core-developers')", 'type': 'internal_log'}
2026-02-03T23:04:55.506748 {'source': 'logging', 'level': 'INFO', 'pathname': '/nix/store/pszlv4r8dapp8a8nw5qjpiz4q8py1bfg-central-env/lib/python3.10/site-packages/central/github/authz.py', 'lineno': 30, 'msg': 'New GH %s: %s', 'args': "('dolphin-emu/trusted-developers', 'marcan,CelestialAmber,aroulin,nullgemm,NanoByte011,mitaclaw,booto,mandar1jn,merryhime,FioraAeterna,Tilka,Sam-Belliveau,endrift,magcius,LPFaint99,moncefmechri,Stevoisiak,rlnilsen,archshift,hdcmeta,mrgreywater,iwubcode,unknownbrackets,RachelBryk,Zopolis4,lioncash,adamdmoss,SirMangler,MayImilae,comex,hthh,yourWaifu,Geotale,phire,RisingFog,AdmiralCurtiss,deReeperJosh,aldelaro5,linkmauve,glennricster,Starsam80,gwicks,smurf3tte,Ebola16,zackhow,Orphis,meffij,Gamer64ytb,JosJuice,Hydr8gon,vladfi1,DacoTaco,cristian64,PatrickFerry,mathieui,ligfx,TryTwo,JMC47,SuperSamus,EmptyChaos,rukai,zopieux,OrN,CasualPokePlayer,sepalani,ColinDTaylor,Pokechu22,neobrain,Techjar,Alcaro,kamiyo,jjdelvalle,delroth,CookiePLMonster,jloehr,Linktothepast,riking,BhaaLseN,OatmealDome,corwin-mcknight,bentley,orbea,TellowKrinkle,Filoppi,jezze,Sintendo,mimimi085181,spxtr,tygyh,galop1n,K0bin,Phatcat,Buddybenj,krnlyng,hrydgard,Parlane,nickbeth,mahdihijazi,ShimmerGlass,jordan-woyak,noahpistilli,JordanTheToaster,Simonx22,Ziek,randomstuff,mmastrac,spycrab,JoshuaVandaele,Tinob,leoetlino,CrossVR,LillyJadeKatrin,degasus,shonumi,dreamsyntax,CrystalGamma,skylersaleh,kayru,mbc07,magumagu,Dentomologist,hackbar,skidau,LAGonauta,malleoz,Helios747,crediar,Lobsterzelda')", 'type': 'internal_log'}
2026-02-03T23:04:54.346320 {'source': 'logging', 'level': 'INFO', 'pathname': '/nix/store/pszlv4r8dapp8a8nw5qjpiz4q8py1bfg-central-env/lib/python3.10/site-packages/central/github/authz.py', 'lineno': 21, 'msg': 'Refreshing list of trusted users (from %s/%s)', 'args': "('dolphin-emu', 'trusted-developers')", 'type': 'internal_log'}
2026-02-03T22:59:54.246236 {'source': 'logging', 'level': 'INFO', 'pathname': '/nix/store/pszlv4r8dapp8a8nw5qjpiz4q8py1bfg-central-env/lib/python3.10/site-packages/central/github/authz.py', 'lineno': 30, 'msg': 'New GH %s: %s', 'args': "('dolphin-emu/trusted-developers', 'marcan,CelestialAmber,aroulin,nullgemm,NanoByte011,mitaclaw,booto,mandar1jn,merryhime,FioraAeterna,Tilka,Sam-Belliveau,endrift,magcius,LPFaint99,moncefmechri,Stevoisiak,rlnilsen,archshift,hdcmeta,mrgreywater,iwubcode,unknownbrackets,RachelBryk,Zopolis4,lioncash,adamdmoss,SirMangler,MayImilae,comex,hthh,yourWaifu,Geotale,phire,RisingFog,AdmiralCurtiss,deReeperJosh,aldelaro5,linkmauve,glennricster,Starsam80,gwicks,smurf3tte,Ebola16,zackhow,Orphis,meffij,Gamer64ytb,JosJuice,Hydr8gon,vladfi1,DacoTaco,cristian64,PatrickFerry,mathieui,ligfx,TryTwo,JMC47,SuperSamus,EmptyChaos,rukai,zopieux,OrN,CasualPokePlayer,sepalani,ColinDTaylor,Pokechu22,neobrain,Techjar,Alcaro,kamiyo,jjdelvalle,delroth,CookiePLMonster,jloehr,Linktothepast,riking,BhaaLseN,OatmealDome,corwin-mcknight,bentley,orbea,TellowKrinkle,Filoppi,jezze,Sintendo,mimimi085181,spxtr,tygyh,galop1n,K0bin,Phatcat,Buddybenj,krnlyng,hrydgard,Parlane,nickbeth,mahdihijazi,ShimmerGlass,jordan-woyak,noahpistilli,JordanTheToaster,Simonx22,Ziek,randomstuff,mmastrac,spycrab,JoshuaVandaele,Tinob,leoetlino,CrossVR,LillyJadeKatrin,degasus,shonumi,dreamsyntax,CrystalGamma,skylersaleh,kayru,mbc07,magumagu,Dentomologist,hackbar,skidau,LAGonauta,malleoz,Helios747,crediar,Lobsterzelda')", 'type': 'internal_log'}
2026-02-03T22:59:52.854289 {'source': 'logging', 'level': 'INFO', 'pathname': '/nix/store/pszlv4r8dapp8a8nw5qjpiz4q8py1bfg-central-env/lib/python3.10/site-packages/central/github/authz.py', 'lineno': 21, 'msg': 'Refreshing list of trusted users (from %s/%s)', 'args': "('dolphin-emu', 'trusted-developers')", 'type': 'internal_log'}
2026-02-03T22:57:31.208904 {'source': 'logging', 'level': 'INFO', 'pathname': '/nix/store/pszlv4r8dapp8a8nw5qjpiz4q8py1bfg-central-env/lib/python3.10/site-packages/central/github/authz.py', 'lineno': 30, 'msg': 'New GH %s: %s', 'args': "('dolphin-emu/core-developers', 'AdmiralCurtiss,linkmauve,delroth,jordan-woyak,Tilka,JosJuice,spycrab,OatmealDome,leoetlino,CrossVR,iwubcode,degasus,phire,lioncash,Pokechu22,Dentomologist,skidau,Helios747,hrydgard,Parlane')", 'type': 'internal_log'}
2026-02-03T22:57:30.981835 {'source': 'logging', 'level': 'INFO', 'pathname': '/nix/store/pszlv4r8dapp8a8nw5qjpiz4q8py1bfg-central-env/lib/python3.10/site-packages/central/github/authz.py', 'lineno': 21, 'msg': 'Refreshing list of trusted users (from %s/%s)', 'args': "('dolphin-emu', 'core-developers')", 'type': 'internal_log'}
Recent 'issue' events
2026-02-03T22:35:18.876310 {'source': 'redmine', 'new': False, 'update': 2, 'issue': 13966, 'title': 'Just Dance (SDNP41) will not launch if Progressive Scan is enabled', 'author': 'Someone7272', 'type': 'issue'}
2026-02-03T19:29:56.821630 {'source': 'redmine', 'new': False, 'update': 4, 'issue': 13707, 'title': '.bca data file support for Wii games (Feature Request)', 'author': 'P-chan', 'type': 'issue'}
2026-02-03T04:28:25.991986 {'source': 'redmine', 'new': False, 'update': 2, 'issue': 13971, 'title': 'Samurai Shodown Anthology needs texture cache setting in game INI', 'author': 'Billiard26', 'type': 'issue'}
2026-02-01T13:49:55.259998 {'source': 'redmine', 'new': False, 'update': 3, 'issue': 13970, 'title': 'Majoras Mask (VC) Sound Disappears; Also Softlock; No Savestate before bug', 'author': 'master0fdisaster1', 'type': 'issue'}
2026-01-31T21:27:01.119661 {'source': 'redmine', 'new': False, 'update': 4, 'issue': 13865, 'title': 'fmadds does not match hardware', 'author': 'JMC4789', 'type': 'issue'}
2026-01-31T21:10:38.620105 {'source': 'redmine', 'new': False, 'update': 3, 'issue': 13865, 'title': 'fmadds does not match hardware', 'author': 'JMC4789', 'type': 'issue'}
2026-01-31T15:00:43.901629 {'source': 'redmine', 'new': True, 'update': 0, 'issue': 13971, 'title': 'Samurai Shodown Anthology needs texture cache setting in game INI', 'author': 'JosJuice', 'type': 'issue'}
2026-01-30T05:15:01.064699 {'source': 'redmine', 'new': False, 'update': 6, 'issue': 7743, 'title': "Various Action Replay Codes and Gecko Codes don't work", 'author': 'Billiard26', 'type': 'issue'}
Recent 'new_dev_version' events
2026-02-03T22:50:56.732271 {'source': 'repomanager', 'hash': 'dd2b94cd4a71fb6abda72f58d27434ef2d8b0a5f', 'branch': 'master', 'shortrev': '2512-272', 'author': 'Jordan Woyak', 'message': 'Merge pull request #13594 from jordan-woyak/state-cleanups\n\nState: Simplify interthread communication and general cleanups.', 'url': 'https://github.com/dolphin-emu/dolphin/commit/dd2b94cd4a71fb6abda72f58d27434ef2d8b0a5f', 'type': 'new_dev_version'}
2026-02-03T04:26:17.092079 {'source': 'repomanager', 'hash': 'eac7b290423efea867c2c7f3f2b2792d05418bdd', 'branch': 'master', 'shortrev': '2512-267', 'author': 'Jordan Woyak', 'message': 'Merge pull request #14314 from JMC47/shodown\n\nGameINI: Force Safe Texture Cache to Samurai Shodown Anthology', 'url': 'https://github.com/dolphin-emu/dolphin/commit/eac7b290423efea867c2c7f3f2b2792d05418bdd', 'type': 'new_dev_version'}
2026-02-02T20:05:56.081402 {'source': 'repomanager', 'hash': 'c7de9162765452fc2787f3ba4f0092394067d603', 'branch': 'master', 'shortrev': '2512-265', 'author': 'JMC47', 'message': 'Merge pull request #14316 from JoshuaVandaele/resourcefix\n\nResourcePack: Fix loading resource packs', 'url': 'https://github.com/dolphin-emu/dolphin/commit/c7de9162765452fc2787f3ba4f0092394067d603', 'type': 'new_dev_version'}
2026-01-31T23:02:14.045612 {'source': 'repomanager', 'hash': '6711d77b9901ed0a4418203db49ed4d8576e6fe0', 'branch': 'master', 'shortrev': '2512-263', 'author': 'Dentomologist', 'message': 'Merge pull request #14204 from Geotale/update-comment\n\nUpdate Comments Based On Hardware Test', 'url': 'https://github.com/dolphin-emu/dolphin/commit/6711d77b9901ed0a4418203db49ed4d8576e6fe0', 'type': 'new_dev_version'}
2026-01-31T22:45:22.874684 {'source': 'repomanager', 'hash': '16276928ccb6d14e32cd33cde993f70503f0e838', 'branch': 'master', 'shortrev': '2512-261', 'author': 'Jordan Woyak', 'message': 'Merge pull request #14312 from JoshuaVandaele/wglDestroyPbufferARB\n\nWGL: Correctly load wglDestroyPbufferARB extension', 'url': 'https://github.com/dolphin-emu/dolphin/commit/16276928ccb6d14e32cd33cde993f70503f0e838', 'type': 'new_dev_version'}
2026-01-30T19:51:12.531401 {'source': 'repomanager', 'hash': 'b1020186519d690a9b64a215f00da7453ab2803b', 'branch': 'master', 'shortrev': '2512-259', 'author': 'JMC47', 'message': 'Merge pull request #14307 from Sam-Belliveau/granular-audio-stretching\n\nAdd an option to preserve audio pitch when emulation speed changes', 'url': 'https://github.com/dolphin-emu/dolphin/commit/b1020186519d690a9b64a215f00da7453ab2803b', 'type': 'new_dev_version'}
2026-01-27T23:32:30.190426 {'source': 'repomanager', 'hash': 'bb7e01167346d0e3e4bab426a7b87968d715ee5e', 'branch': 'master', 'shortrev': '2512-257', 'author': 'Jordan Woyak', 'message': 'Merge pull request #14254 from droidpeti/master\n\nGMSE01: Added credits to Gecko codes for Super Mario Sunshine (USA)', 'url': 'https://github.com/dolphin-emu/dolphin/commit/bb7e01167346d0e3e4bab426a7b87968d715ee5e', 'type': 'new_dev_version'}
Recent 'notification' events
2026-02-03T23:32:40.514260 {'source': 'notifications', 'msg': '[\x1f\x0313dolphin-emu/dolphin\x03\x1f] \x0303j\ufeffordan-woyak\x03 opened pull request #14318: Core: Make RunOnCPUThread always non-blocking. (\x0306master\x03...\x0306RunOnCPUThread-always-non-blocking\x03): \x1f\x0302https://dolp.in/pr14318\x03\x1f', 'type': 'notification'}
2026-02-03T22:53:33.532870 {'source': 'notifications', 'msg': '[\x1f\x0313dolphin-emu/dolphin\x03\x1f] build for #14218 \x0304failed\x03 on builders [pr-win-dbg-x64]: \x1f\x0302https://dolphin.ci/#/builders/31/builds/8927\x03\x1f', 'type': 'notification'}
2026-02-03T22:50:54.524181 {'source': 'notifications', 'msg': 'dd2b94 by \x0303J\ufeffordan Woyak\x03 [\x03091\x03|\x030913\x03|\x03040\x03] \x1f\x0302https://dolp.in/rdd2b94cd4a71fb6abda72f58d27434ef2d8b0a5f\x03\x1f Merge pull request #13594 from jordan-woyak/state-cleanups', 'type': 'notification'}
2026-02-03T22:50:54.524010 {'source': 'notifications', 'msg': '[\x1f\x0313dolphin-emu/dolphin\x1f\x03] \x0303j\ufeffordan-woyak\x03 pushed \x021\x02 new commit to \x0306master\x03', 'type': 'notification'}
2026-02-03T22:50:54.190465 {'source': 'notifications', 'msg': '[\x1f\x0313dolphin-emu/dolphin\x03\x1f] \x0303j\ufeffordan-woyak\x03 merged pull request #13594: State: Simplify interthread communication and general cleanups. (\x0306master\x03...\x0306state-cleanups\x03): \x1f\x0302https://dolp.in/pr13594\x03\x1f', 'type': 'notification'}
2026-02-03T22:48:25.698070 {'source': 'notifications', 'msg': '[\x1f\x0313dolphin-emu/dolphin\x03\x1f] \x0303j\ufeffordan-woyak\x03 approved pull request #13768 (Core: Create fastmem mappings for page address translation): \x1f\x0302https://dolp.in/pr13768#pullrequestreview-3748007210\x03\x1f', 'type': 'notification'}
2026-02-03T22:35:19.460599 {'source': 'notifications', 'msg': '[\x1f\x0313dolphin-emu/dolphin\x03\x1f] \x0303J\ufeffosJuice\x03 synchronized pull request #14218: Jit64: Make ABI_CallFunction generic (\x0306master\x03...\x0306jit64-abi-call-function-parameters\x03): \x1f\x0302https://dolp.in/pr14218\x03\x1f', 'type': 'notification'}
2026-02-03T22:35:18.877874 {'source': 'notifications', 'msg': 'Update 2 to issue 13966 ("Just Dance (SDNP41) will not launch if Progressive Scan is enabled") by \x0303S\ufeffomeone7272\x03 - \x1f\x0302https://dolp.in/i13966/2\x03\x1f', 'type': 'notification'}
2026-02-03T22:24:04.646254 {'source': 'notifications', 'msg': '[\x1f\x0313dolphin-emu/dolphin\x03\x1f] build for #13768 \x0304failed\x03 on builders [pr-android]: \x1f\x0302https://dolphin.ci/#/builders/22/builds/8923\x03\x1f', 'type': 'notification'}
2026-02-03T22:19:09.569348 {'source': 'notifications', 'msg': '[\x1f\x0313dolphin-emu/dolphin\x03\x1f] build for #13768 \x0304failed\x03 on builders [pr-win-dbg-x64]: \x1f\x0302https://dolphin.ci/#/builders/31/builds/8925\x03\x1f', 'type': 'notification'}
2026-02-03T22:15:59.569813 {'source': 'notifications', 'msg': '[\x1f\x0313dolphin-emu/dolphin\x03\x1f] build for #13768 \x0304failed\x03 on builders [pr-deb-dbg-x64]: \x1f\x0302https://dolphin.ci/#/builders/1/builds/8923\x03\x1f', 'type': 'notification'}
2026-02-03T22:15:01.768998 {'source': 'notifications', 'msg': '[\x1f\x0313dolphin-emu/dolphin\x03\x1f] build for #13768 \x0304failed\x03 on builders [pr-freebsd-x64]: \x1f\x0302https://dolphin.ci/#/builders/11/builds/8921\x03\x1f', 'type': 'notification'}
2026-02-03T22:13:43.248862 {'source': 'notifications', 'msg': '[\x1f\x0313dolphin-emu/dolphin\x03\x1f] build for #13768 \x0304failed\x03 on builders [pr-win-arm64]: \x1f\x0302https://dolphin.ci/#/builders/13/builds/8918\x03\x1f', 'type': 'notification'}
2026-02-03T22:06:12.690596 {'source': 'notifications', 'msg': '[\x1f\x0313dolphin-emu/dolphin\x03\x1f] \x0303J\ufeffosJuice\x03 synchronized pull request #13768: Core: Create fastmem mappings for page address translation (\x0306master\x03...\x0306page-table-fastmem-2\x03): \x1f\x0302https://dolp.in/pr13768\x03\x1f', 'type': 'notification'}
2026-02-03T22:05:19.920482 {'source': 'notifications', 'msg': '[\x1f\x0313dolphin-emu/dolphin\x03\x1f] build for #13768 \x0304failed\x03 on builders [lint]: \x1f\x0302https://dolphin.ci/#/builders/9/builds/8920\x03\x1f', 'type': 'notification'}
2026-02-03T22:04:37.179304 {'source': 'notifications', 'msg': '[\x1f\x0313dolphin-emu/dolphin\x03\x1f] \x0303J\ufeffosJuice\x03 synchronized pull request #13768: Core: Create fastmem mappings for page address translation (\x0306master\x03...\x0306page-table-fastmem-2\x03): \x1f\x0302https://dolp.in/pr13768\x03\x1f', 'type': 'notification'}
2026-02-03T21:10:07.303726 {'source': 'notifications', 'msg': '[\x1f\x0313dolphin-emu/dolphin\x03\x1f] \x0303j\ufeffordan-woyak\x03 commented on #13970 (WiimoteReal: Windows improvements.): \x1f\x0302https://dolp.in/pr13970#issuecomment-3843701295\x03\x1f', 'type': 'notification'}
2026-02-03T21:01:13.944377 {'source': 'notifications', 'msg': '[\x1f\x0313dolphin-emu/dolphin\x03\x1f] \x0303T\ufeffrihy\x03 edited a comment on #13970 (WiimoteReal: Windows improvements.): \x1f\x0302https://dolp.in/pr13970#issuecomment-3843649310\x03\x1f', 'type': 'notification'}
2026-02-03T20:58:44.456747 {'source': 'notifications', 'msg': '[\x1f\x0313dolphin-emu/dolphin\x03\x1f] \x0303T\ufeffrihy\x03 commented on #13970 (WiimoteReal: Windows improvements.): \x1f\x0302https://dolp.in/pr13970#issuecomment-3843649310\x03\x1f', 'type': 'notification'}
2026-02-03T19:29:56.821900 {'source': 'notifications', 'msg': 'Update 4 to issue 13707 (".bca data file support for Wii games (Feature Request)") by \x0303P\ufeff-chan\x03 - \x1f\x0302https://dolp.in/i13707/4\x03\x1f', 'type': 'notification'}
2026-02-03T17:22:19.677400 {'source': 'notifications', 'msg': '[\x1f\x0313dolphin-emu/dolphin\x03\x1f] \x0303J\ufeffMC47\x03 edited a comment on #13844 (Core: Triforce support): \x1f\x0302https://dolp.in/pr13844#issuecomment-3842626451\x03\x1f', 'type': 'notification'}
2026-02-03T17:22:09.068538 {'source': 'notifications', 'msg': '[\x1f\x0313dolphin-emu/dolphin\x03\x1f] \x0303J\ufeffMC47\x03 commented on #13844 (Core: Triforce support): \x1f\x0302https://dolp.in/pr13844#issuecomment-3842626451\x03\x1f', 'type': 'notification'}
2026-02-03T17:08:19.685165 {'source': 'notifications', 'msg': '[\x1f\x0313dolphin-emu/dolphin\x03\x1f] \x0303p\ufeffizzzza19\x03 commented on #13844 (Core: Triforce support): \x1f\x0302https://dolp.in/pr13844#issuecomment-3842552882\x03\x1f', 'type': 'notification'}
2026-02-03T16:31:29.616745 {'source': 'notifications', 'msg': '[\x1f\x0313dolphin-emu/dolphin\x03\x1f] \x0303s\ufeffepalani\x03 commented on #13844 (Core: Triforce support): \x1f\x0302https://dolp.in/pr13844#issuecomment-3842350109\x03\x1f', 'type': 'notification'}
2026-02-03T16:08:06.874370 {'source': 'notifications', 'msg': '[\x1f\x0313dolphin-emu/dolphin\x03\x1f] \x0303p\ufeffizzzza19\x03 commented on commit 128a6454b6247c0c2a2cafb2d91d03f71fc112e0: \x1f\x0302https://dolp.in/r128a6454b6247c0c2a2cafb2d91d03f71fc112e0#commitcomment-176299891\x03\x1f', 'type': 'notification'}
Recent 'pull_request_fifoci_status' events
2026-02-03T22:57:32.343620 {'source': 'buildbot', 'repo': 'dolphin-emu/dolphin', 'hash': '32856fd15531890d65379376c8cadfc3611b36cb', 'service': 'pr-fifoci-ogl-lin-mesa', 'pr': 13768, 'type': 'pull_request_fifoci_status'}
2026-02-03T22:31:51.810969 {'source': 'buildbot', 'repo': 'dolphin-emu/dolphin', 'hash': 'c8180071a552720e5436e908afc4eda7e87be08c', 'service': 'pr-fifoci-ogl-lin-mesa', 'pr': 14317, 'type': 'pull_request_fifoci_status'}
2026-02-03T22:13:31.207061 {'source': 'buildbot', 'repo': 'dolphin-emu/dolphin', 'hash': 'df85a2c99d667c537a5a896cd81954fb63fab027', 'service': 'pr-fifoci-ogl-lin-mesa', 'pr': 13997, 'type': 'pull_request_fifoci_status'}
2026-02-03T21:54:32.835079 {'source': 'buildbot', 'repo': 'dolphin-emu/dolphin', 'hash': 'df85a2c99d667c537a5a896cd81954fb63fab027', 'service': 'pr-fifoci-sw-lin-mesa', 'pr': 13997, 'type': 'pull_request_fifoci_status'}
2026-02-02T22:27:35.708352 {'source': 'buildbot', 'repo': 'dolphin-emu/dolphin', 'hash': 'c8180071a552720e5436e908afc4eda7e87be08c', 'service': 'pr-fifoci-sw-lin-mesa', 'pr': 14317, 'type': 'pull_request_fifoci_status'}
2026-02-02T22:21:33.739375 {'source': 'buildbot', 'repo': 'dolphin-emu/dolphin', 'hash': 'c8180071a552720e5436e908afc4eda7e87be08c', 'service': 'pr-fifoci-vk-lin-mesa', 'pr': 14317, 'type': 'pull_request_fifoci_status'}
2026-02-02T22:11:18.938675 {'source': 'buildbot', 'repo': 'dolphin-emu/dolphin', 'hash': 'c8180071a552720e5436e908afc4eda7e87be08c', 'service': 'pr-fifoci-vk-lin-mesa', 'pr': 14317, 'type': 'pull_request_fifoci_status'}
2026-02-02T22:00:49.874018 {'source': 'buildbot', 'repo': 'dolphin-emu/dolphin', 'hash': 'c8180071a552720e5436e908afc4eda7e87be08c', 'service': 'pr-fifoci-ogl-lin-mesa', 'pr': 14317, 'type': 'pull_request_fifoci_status'}
2026-02-02T21:48:53.656103 {'source': 'buildbot', 'repo': 'dolphin-emu/dolphin', 'hash': 'c8180071a552720e5436e908afc4eda7e87be08c', 'service': 'pr-fifoci-ogl-lin-mesa', 'pr': 14317, 'type': 'pull_request_fifoci_status'}
2026-02-02T21:34:23.333363 {'source': 'buildbot', 'repo': 'dolphin-emu/dolphin', 'hash': 'c8180071a552720e5436e908afc4eda7e87be08c', 'service': 'pr-fifoci-sw-lin-mesa', 'pr': 14317, 'type': 'pull_request_fifoci_status'}
2026-02-02T21:18:18.371595 {'source': 'buildbot', 'repo': 'dolphin-emu/dolphin', 'hash': '66380234a68eb4d8106aa3004b0e531a06f5cd24', 'service': 'pr-fifoci-vk-lin-mesa', 'pr': 13844, 'type': 'pull_request_fifoci_status'}
2026-02-02T21:01:42.103917 {'source': 'buildbot', 'repo': 'dolphin-emu/dolphin', 'hash': '66380234a68eb4d8106aa3004b0e531a06f5cd24', 'service': 'pr-fifoci-ogl-lin-mesa', 'pr': 13844, 'type': 'pull_request_fifoci_status'}
2026-02-02T20:17:41.642487 {'source': 'buildbot', 'repo': 'dolphin-emu/dolphin', 'hash': '66380234a68eb4d8106aa3004b0e531a06f5cd24', 'service': 'pr-fifoci-sw-lin-mesa', 'pr': 13844, 'type': 'pull_request_fifoci_status'}
2026-02-02T19:36:41.663910 {'source': 'buildbot', 'repo': 'dolphin-emu/dolphin', 'hash': 'd87e8ab7ff66f3efcedeb5a0ab869b537933aada', 'service': 'pr-fifoci-vk-lin-mesa', 'pr': 14316, 'type': 'pull_request_fifoci_status'}
2026-02-02T19:26:23.873938 {'source': 'buildbot', 'repo': 'dolphin-emu/dolphin', 'hash': 'd87e8ab7ff66f3efcedeb5a0ab869b537933aada', 'service': 'pr-fifoci-ogl-lin-mesa', 'pr': 14316, 'type': 'pull_request_fifoci_status'}
2026-02-02T19:12:28.666753 {'source': 'buildbot', 'repo': 'dolphin-emu/dolphin', 'hash': 'd87e8ab7ff66f3efcedeb5a0ab869b537933aada', 'service': 'pr-fifoci-sw-lin-mesa', 'pr': 14316, 'type': 'pull_request_fifoci_status'}
2026-02-02T14:36:19.207571 {'source': 'buildbot', 'repo': 'dolphin-emu/dolphin', 'hash': 'd75794b774d64f50d4bad334660d8f9068f241bd', 'service': 'pr-fifoci-vk-lin-mesa', 'pr': 14260, 'type': 'pull_request_fifoci_status'}
2026-02-02T14:26:00.718968 {'source': 'buildbot', 'repo': 'dolphin-emu/dolphin', 'hash': 'd75794b774d64f50d4bad334660d8f9068f241bd', 'service': 'pr-fifoci-ogl-lin-mesa', 'pr': 14260, 'type': 'pull_request_fifoci_status'}
2026-02-02T14:13:25.995721 {'source': 'buildbot', 'repo': 'dolphin-emu/dolphin', 'hash': 'd75794b774d64f50d4bad334660d8f9068f241bd', 'service': 'pr-fifoci-sw-lin-mesa', 'pr': 14260, 'type': 'pull_request_fifoci_status'}
2026-02-02T09:18:14.519048 {'source': 'buildbot', 'repo': 'dolphin-emu/dolphin', 'hash': '512972c1cd4a10111412d619e464fc72a964101e', 'service': 'pr-fifoci-vk-lin-mesa', 'pr': 14260, 'type': 'pull_request_fifoci_status'}
2026-02-02T09:07:59.865610 {'source': 'buildbot', 'repo': 'dolphin-emu/dolphin', 'hash': '512972c1cd4a10111412d619e464fc72a964101e', 'service': 'pr-fifoci-ogl-lin-mesa', 'pr': 14260, 'type': 'pull_request_fifoci_status'}
2026-02-02T08:55:22.417124 {'source': 'buildbot', 'repo': 'dolphin-emu/dolphin', 'hash': '512972c1cd4a10111412d619e464fc72a964101e', 'service': 'pr-fifoci-sw-lin-mesa', 'pr': 14260, 'type': 'pull_request_fifoci_status'}
2026-02-01T22:40:56.371197 {'source': 'buildbot', 'repo': 'dolphin-emu/dolphin', 'hash': 'f07ac93e55496dbafad9d3d2ad6d36c6f5c016a6', 'service': 'pr-fifoci-sw-lin-mesa', 'pr': 14308, 'type': 'pull_request_fifoci_status'}
2026-02-01T22:34:41.356263 {'source': 'buildbot', 'repo': 'dolphin-emu/dolphin', 'hash': 'f07ac93e55496dbafad9d3d2ad6d36c6f5c016a6', 'service': 'pr-fifoci-vk-lin-mesa', 'pr': 14308, 'type': 'pull_request_fifoci_status'}
2026-02-01T22:24:08.767167 {'source': 'buildbot', 'repo': 'dolphin-emu/dolphin', 'hash': 'f07ac93e55496dbafad9d3d2ad6d36c6f5c016a6', 'service': 'pr-fifoci-ogl-lin-mesa', 'pr': 14308, 'type': 'pull_request_fifoci_status'}
Recent 'raw_bb_hook' events
2026-02-03T23:34:37.470745 {'source': 'webserver', 'raw': {'buildid': 157756, 'number': 8923, 'builderid': 9, 'buildrequestid': 157882, 'workerid': 10, 'masterid': 1, 'started_at': 1770161567, 'complete_at': 1770161571, 'locks_duration_s': 0, 'complete': True, 'state_string': 'build successful', 'results': 0, 'properties': {'branchname': ['pr-14318', 'Change'], 'shortrev': ['201aa6', 'Change'], 'repo': ['dolphin-emu/dolphin', 'Change'], 'scheduler': ['pr', 'Scheduler'], 'workername': ['ubuntu-lts', 'Worker'], 'project': ['', 'Build'], 'buildnumber': [8923, 'Build'], 'branch': ['refs/pull/14318/head', 'Build'], 'revision': ['201aa659066b41a2cfe89b615213bcb093fcbe79', 'Build'], 'headrev': ['201aa659066b41a2cfe89b615213bcb093fcbe79', 'Change'], 'buildername': ['lint', 'Builder'], 'owners': [['Central (on behalf of: jordan-woyak)'], 'Build'], 'baserev': ['dd2b94cd4a71fb6abda72f58d27434ef2d8b0a5f', 'Change'], 'pr_id': [14318, 'Change'], 'builderid': [9, 'Builder'], 'repository': ['', 'Build'], 'codebase': ['', 'Build'], 'basedir': ['/buildbot', 'Worker'], 'builddir': ['/buildbot/lint', 'Worker'], 'got_revision': ['201aa659066b41a2cfe89b615213bcb093fcbe79', 'GitNoBranch']}, 'buildrequest': {'buildrequestid': 157882, 'buildsetid': 36142, 'builderid': 9, 'priority': 0, 'claimed': True, 'claimed_at': 1770161567, 'claimed_by_masterid': 1, 'complete': True, 'results': 0, 'submitted_at': 1770161566, 'complete_at': 1770161571, 'waited_for': False, 'properties': None}, 'buildset': {'bsid': 36142, 'external_idstring': None, 'reason': "The AnyBranchScheduler scheduler named 'pr' triggered this build", 'submitted_at': 1770161566, 'complete': False, 'complete_at': None, 'results': -1, 'parent_buildid': None, 'parent_relationship': None, 'rebuilt_buildid': None, 'sourcestamps': [{'ssid': 10816, 'branch': 'refs/pull/14318/head', 'revision': '201aa659066b41a2cfe89b615213bcb093fcbe79', 'project': '', 'repository': '', 'codebase': '', 'created_at': 1770161566, 'patch': None}]}, 'parentbuild': None, 'parentbuilder': None, 'builder': {'builderid': 9, 'name': 'lint', 'masterids': [1], 'description': None, 'description_format': None, 'description_html': None, 'projectid': None, 'tags': []}, 'url': 'https://dolphin.ci/#/builders/9/builds/8923'}, 'type': 'raw_bb_hook'}
2026-02-03T23:34:17.798758 {'source': 'webserver', 'raw': {'buildid': 157764, 'number': 8923, 'builderid': 27, 'buildrequestid': 157887, 'workerid': 10, 'masterid': 1, 'started_at': 1770161571, 'complete_at': None, 'locks_duration_s': 0, 'complete': False, 'state_string': 'starting', 'results': None, 'properties': {'branchname': ['pr-14318', 'Change'], 'baserev': ['dd2b94cd4a71fb6abda72f58d27434ef2d8b0a5f', 'Change'], 'headrev': ['201aa659066b41a2cfe89b615213bcb093fcbe79', 'Change'], 'shortrev': ['201aa6', 'Change'], 'pr_id': [14318, 'Change'], 'repo': ['dolphin-emu/dolphin', 'Change'], 'scheduler': ['pr', 'Scheduler'], 'buildername': ['pr-ubu-x64', 'Builder'], 'builderid': [27, 'Builder'], 'repository': ['', 'Build'], 'workername': ['ubuntu-lts', 'Worker'], 'buildnumber': [8923, 'Build'], 'branch': ['refs/pull/14318/head', 'Build'], 'revision': ['201aa659066b41a2cfe89b615213bcb093fcbe79', 'Build'], 'codebase': ['', 'Build'], 'project': ['', 'Build'], 'owners': [['Central (on behalf of: jordan-woyak)'], 'Build']}, 'buildrequest': {'buildrequestid': 157887, 'buildsetid': 36142, 'builderid': 27, 'priority': 0, 'claimed': True, 'claimed_at': 1770161571, 'claimed_by_masterid': 1, 'complete': False, 'results': -1, 'submitted_at': 1770161566, 'complete_at': None, 'waited_for': False, 'properties': None}, 'buildset': {'bsid': 36142, 'external_idstring': None, 'reason': "The AnyBranchScheduler scheduler named 'pr' triggered this build", 'submitted_at': 1770161566, 'complete': False, 'complete_at': None, 'results': -1, 'parent_buildid': None, 'parent_relationship': None, 'rebuilt_buildid': None, 'sourcestamps': [{'ssid': 10816, 'branch': 'refs/pull/14318/head', 'revision': '201aa659066b41a2cfe89b615213bcb093fcbe79', 'project': '', 'repository': '', 'codebase': '', 'created_at': 1770161566, 'patch': None}]}, 'parentbuild': None, 'parentbuilder': None, 'builder': {'builderid': 27, 'name': 'pr-ubu-x64', 'masterids': [1], 'description': None, 'description_format': None, 'description_html': None, 'projectid': None, 'tags': []}, 'url': 'https://dolphin.ci/#/builders/27/builds/8923'}, 'type': 'raw_bb_hook'}
2026-02-03T23:34:14.730916 {'source': 'webserver', 'raw': {'buildid': 157762, 'number': 8924, 'builderid': 11, 'buildrequestid': 157883, 'workerid': 1, 'masterid': 1, 'started_at': 1770161567, 'complete_at': None, 'locks_duration_s': 0, 'complete': False, 'state_string': 'starting', 'results': None, 'properties': {'buildername': ['pr-freebsd-x64', 'Builder'], 'builderid': [11, 'Builder'], 'buildnumber': [8924, 'Build'], 'revision': ['201aa659066b41a2cfe89b615213bcb093fcbe79', 'Build'], 'branch': ['refs/pull/14318/head', 'Build'], 'repository': ['', 'Build'], 'baserev': ['dd2b94cd4a71fb6abda72f58d27434ef2d8b0a5f', 'Change'], 'repo': ['dolphin-emu/dolphin', 'Change'], 'scheduler': ['pr', 'Scheduler'], 'workername': ['freebsd', 'Worker'], 'codebase': ['', 'Build'], 'project': ['', 'Build'], 'owners': [['Central (on behalf of: jordan-woyak)'], 'Build'], 'branchname': ['pr-14318', 'Change'], 'pr_id': [14318, 'Change'], 'headrev': ['201aa659066b41a2cfe89b615213bcb093fcbe79', 'Change'], 'shortrev': ['201aa6', 'Change']}, 'buildrequest': {'buildrequestid': 157883, 'buildsetid': 36142, 'builderid': 11, 'priority': 0, 'claimed': True, 'claimed_at': 1770161567, 'claimed_by_masterid': 1, 'complete': False, 'results': -1, 'submitted_at': 1770161566, 'complete_at': None, 'waited_for': False, 'properties': None}, 'buildset': {'bsid': 36142, 'external_idstring': None, 'reason': "The AnyBranchScheduler scheduler named 'pr' triggered this build", 'submitted_at': 1770161566, 'complete': False, 'complete_at': None, 'results': -1, 'parent_buildid': None, 'parent_relationship': None, 'rebuilt_buildid': None, 'sourcestamps': [{'ssid': 10816, 'branch': 'refs/pull/14318/head', 'revision': '201aa659066b41a2cfe89b615213bcb093fcbe79', 'project': '', 'repository': '', 'codebase': '', 'created_at': 1770161566, 'patch': None}]}, 'parentbuild': None, 'parentbuilder': None, 'builder': {'builderid': 11, 'name': 'pr-freebsd-x64', 'masterids': [1], 'description': None, 'description_format': None, 'description_html': None, 'projectid': None, 'tags': []}, 'url': 'https://dolphin.ci/#/builders/11/builds/8924'}, 'type': 'raw_bb_hook'}
2026-02-03T23:34:13.813813 {'source': 'webserver', 'raw': {'buildid': 157757, 'number': 8926, 'builderid': 2, 'buildrequestid': 157880, 'workerid': 4, 'masterid': 1, 'started_at': 1770161567, 'complete_at': None, 'locks_duration_s': 0, 'complete': False, 'state_string': 'starting', 'results': None, 'properties': {'headrev': ['201aa659066b41a2cfe89b615213bcb093fcbe79', 'Change'], 'branch': ['refs/pull/14318/head', 'Build'], 'branchname': ['pr-14318', 'Change'], 'baserev': ['dd2b94cd4a71fb6abda72f58d27434ef2d8b0a5f', 'Change'], 'repo': ['dolphin-emu/dolphin', 'Change'], 'builderid': [2, 'Builder'], 'workername': ['windows', 'Worker'], 'buildnumber': [8926, 'Build'], 'shortrev': ['201aa6', 'Change'], 'pr_id': [14318, 'Change'], 'scheduler': ['pr', 'Scheduler'], 'buildername': ['pr-win-x64', 'Builder'], 'revision': ['201aa659066b41a2cfe89b615213bcb093fcbe79', 'Build'], 'repository': ['', 'Build'], 'codebase': ['', 'Build'], 'project': ['', 'Build']}, 'buildrequest': {'buildrequestid': 157880, 'buildsetid': 36142, 'builderid': 2, 'priority': 0, 'claimed': True, 'claimed_at': 1770161567, 'claimed_by_masterid': 1, 'complete': False, 'results': -1, 'submitted_at': 1770161566, 'complete_at': None, 'waited_for': False, 'properties': None}, 'buildset': {'bsid': 36142, 'external_idstring': None, 'reason': "The AnyBranchScheduler scheduler named 'pr' triggered this build", 'submitted_at': 1770161566, 'complete': False, 'complete_at': None, 'results': -1, 'parent_buildid': None, 'parent_relationship': None, 'rebuilt_buildid': None, 'sourcestamps': [{'ssid': 10816, 'branch': 'refs/pull/14318/head', 'revision': '201aa659066b41a2cfe89b615213bcb093fcbe79', 'project': '', 'repository': '', 'codebase': '', 'created_at': 1770161566, 'patch': None}]}, 'parentbuild': None, 'parentbuilder': None, 'builder': {'builderid': 2, 'name': 'pr-win-x64', 'masterids': [1], 'description': None, 'description_format': None, 'description_html': None, 'projectid': None, 'tags': []}, 'url': 'https://dolphin.ci/#/builders/2/builds/8926'}, 'type': 'raw_bb_hook'}
2026-02-03T23:34:12.465710 {'source': 'webserver', 'raw': {'buildid': 157760, 'number': 8938, 'builderid': 7, 'buildrequestid': 157881, 'workerid': 8, 'masterid': 1, 'started_at': 1770161567, 'complete_at': None, 'locks_duration_s': 0, 'complete': False, 'state_string': 'starting', 'results': None, 'properties': {'buildnumber': [8938, 'Build'], 'revision': ['201aa659066b41a2cfe89b615213bcb093fcbe79', 'Build'], 'codebase': ['', 'Build'], 'branch': ['refs/pull/14318/head', 'Build'], 'repository': ['', 'Build'], 'owners': [['Central (on behalf of: jordan-woyak)'], 'Build'], 'branchname': ['pr-14318', 'Change'], 'baserev': ['dd2b94cd4a71fb6abda72f58d27434ef2d8b0a5f', 'Change'], 'headrev': ['201aa659066b41a2cfe89b615213bcb093fcbe79', 'Change'], 'project': ['', 'Build'], 'shortrev': ['201aa6', 'Change'], 'pr_id': [14318, 'Change'], 'builderid': [7, 'Builder'], 'workername': ['osx-m1', 'Worker'], 'repo': ['dolphin-emu/dolphin', 'Change'], 'scheduler': ['pr', 'Scheduler'], 'buildername': ['pr-osx-universal', 'Builder']}, 'buildrequest': {'buildrequestid': 157881, 'buildsetid': 36142, 'builderid': 7, 'priority': 0, 'claimed': True, 'claimed_at': 1770161567, 'claimed_by_masterid': 1, 'complete': False, 'results': -1, 'submitted_at': 1770161566, 'complete_at': None, 'waited_for': False, 'properties': None}, 'buildset': {'bsid': 36142, 'external_idstring': None, 'reason': "The AnyBranchScheduler scheduler named 'pr' triggered this build", 'submitted_at': 1770161566, 'complete': False, 'complete_at': None, 'results': -1, 'parent_buildid': None, 'parent_relationship': None, 'rebuilt_buildid': None, 'sourcestamps': [{'ssid': 10816, 'branch': 'refs/pull/14318/head', 'revision': '201aa659066b41a2cfe89b615213bcb093fcbe79', 'project': '', 'repository': '', 'codebase': '', 'created_at': 1770161566, 'patch': None}]}, 'parentbuild': None, 'parentbuilder': None, 'builder': {'builderid': 7, 'name': 'pr-osx-universal', 'masterids': [1], 'description': None, 'description_format': None, 'description_html': None, 'projectid': None, 'tags': []}, 'url': 'https://dolphin.ci/#/builders/7/builds/8938'}, 'type': 'raw_bb_hook'}
2026-02-03T23:34:12.373303 {'source': 'webserver', 'raw': {'buildid': 157759, 'number': 8922, 'builderid': 26, 'buildrequestid': 157886, 'workerid': 9, 'masterid': 1, 'started_at': 1770161567, 'complete_at': None, 'locks_duration_s': 0, 'complete': False, 'state_string': 'starting', 'results': None, 'properties': {'headrev': ['201aa659066b41a2cfe89b615213bcb093fcbe79', 'Change'], 'shortrev': ['201aa6', 'Change'], 'pr_id': [14318, 'Change'], 'scheduler': ['pr', 'Scheduler'], 'repo': ['dolphin-emu/dolphin', 'Change'], 'branch': ['refs/pull/14318/head', 'Build'], 'branchname': ['pr-14318', 'Change'], 'baserev': ['dd2b94cd4a71fb6abda72f58d27434ef2d8b0a5f', 'Change'], 'buildername': ['pr-deb-x64', 'Builder'], 'builderid': [26, 'Builder'], 'workername': ['debian', 'Worker'], 'buildnumber': [8922, 'Build'], 'codebase': ['', 'Build'], 'project': ['', 'Build'], 'revision': ['201aa659066b41a2cfe89b615213bcb093fcbe79', 'Build'], 'repository': ['', 'Build'], 'owners': [['Central (on behalf of: jordan-woyak)'], 'Build']}, 'buildrequest': {'buildrequestid': 157886, 'buildsetid': 36142, 'builderid': 26, 'priority': 0, 'claimed': True, 'claimed_at': 1770161567, 'claimed_by_masterid': 1, 'complete': False, 'results': -1, 'submitted_at': 1770161566, 'complete_at': None, 'waited_for': False, 'properties': None}, 'buildset': {'bsid': 36142, 'external_idstring': None, 'reason': "The AnyBranchScheduler scheduler named 'pr' triggered this build", 'submitted_at': 1770161566, 'complete': False, 'complete_at': None, 'results': -1, 'parent_buildid': None, 'parent_relationship': None, 'rebuilt_buildid': None, 'sourcestamps': [{'ssid': 10816, 'branch': 'refs/pull/14318/head', 'revision': '201aa659066b41a2cfe89b615213bcb093fcbe79', 'project': '', 'repository': '', 'codebase': '', 'created_at': 1770161566, 'patch': None}]}, 'parentbuild': None, 'parentbuilder': None, 'builder': {'builderid': 26, 'name': 'pr-deb-x64', 'masterids': [1], 'description': None, 'description_format': None, 'description_html': None, 'projectid': None, 'tags': []}, 'url': 'https://dolphin.ci/#/builders/26/builds/8922'}, 'type': 'raw_bb_hook'}
2026-02-03T23:34:12.003496 {'source': 'webserver', 'raw': {'buildid': 157763, 'number': 8926, 'builderid': 22, 'buildrequestid': 157885, 'workerid': 12, 'masterid': 1, 'started_at': 1770161567, 'complete_at': None, 'locks_duration_s': 0, 'complete': False, 'state_string': 'starting', 'results': None, 'properties': {'headrev': ['201aa659066b41a2cfe89b615213bcb093fcbe79', 'Change'], 'pr_id': [14318, 'Change'], 'buildnumber': [8926, 'Build'], 'buildername': ['pr-android', 'Builder'], 'builderid': [22, 'Builder'], 'repository': ['', 'Build'], 'owners': [['Central (on behalf of: jordan-woyak)'], 'Build'], 'branchname': ['pr-14318', 'Change'], 'branch': ['refs/pull/14318/head', 'Build'], 'codebase': ['', 'Build'], 'project': ['', 'Build'], 'revision': ['201aa659066b41a2cfe89b615213bcb093fcbe79', 'Build'], 'shortrev': ['201aa6', 'Change'], 'scheduler': ['pr', 'Scheduler'], 'baserev': ['dd2b94cd4a71fb6abda72f58d27434ef2d8b0a5f', 'Change'], 'repo': ['dolphin-emu/dolphin', 'Change'], 'workername': ['android', 'Worker']}, 'buildrequest': {'buildrequestid': 157885, 'buildsetid': 36142, 'builderid': 22, 'priority': 0, 'claimed': True, 'claimed_at': 1770161567, 'claimed_by_masterid': 1, 'complete': False, 'results': -1, 'submitted_at': 1770161566, 'complete_at': None, 'waited_for': False, 'properties': None}, 'buildset': {'bsid': 36142, 'external_idstring': None, 'reason': "The AnyBranchScheduler scheduler named 'pr' triggered this build", 'submitted_at': 1770161566, 'complete': False, 'complete_at': None, 'results': -1, 'parent_buildid': None, 'parent_relationship': None, 'rebuilt_buildid': None, 'sourcestamps': [{'ssid': 10816, 'branch': 'refs/pull/14318/head', 'revision': '201aa659066b41a2cfe89b615213bcb093fcbe79', 'project': '', 'repository': '', 'codebase': '', 'created_at': 1770161566, 'patch': None}]}, 'parentbuild': None, 'parentbuilder': None, 'builder': {'builderid': 22, 'name': 'pr-android', 'masterids': [1], 'description': None, 'description_format': None, 'description_html': None, 'projectid': None, 'tags': []}, 'url': 'https://dolphin.ci/#/builders/22/builds/8926'}, 'type': 'raw_bb_hook'}
2026-02-03T23:34:11.174714 {'source': 'webserver', 'raw': {'buildid': 157756, 'number': 8923, 'builderid': 9, 'buildrequestid': 157882, 'workerid': 10, 'masterid': 1, 'started_at': 1770161567, 'complete_at': None, 'locks_duration_s': 0, 'complete': False, 'state_string': 'starting', 'results': None, 'properties': {'branchname': ['pr-14318', 'Change'], 'shortrev': ['201aa6', 'Change'], 'repo': ['dolphin-emu/dolphin', 'Change'], 'scheduler': ['pr', 'Scheduler'], 'workername': ['ubuntu-lts', 'Worker'], 'project': ['', 'Build'], 'buildnumber': [8923, 'Build'], 'branch': ['refs/pull/14318/head', 'Build'], 'revision': ['201aa659066b41a2cfe89b615213bcb093fcbe79', 'Build'], 'headrev': ['201aa659066b41a2cfe89b615213bcb093fcbe79', 'Change'], 'buildername': ['lint', 'Builder'], 'baserev': ['dd2b94cd4a71fb6abda72f58d27434ef2d8b0a5f', 'Change'], 'pr_id': [14318, 'Change'], 'builderid': [9, 'Builder'], 'repository': ['', 'Build'], 'codebase': ['', 'Build']}, 'buildrequest': {'buildrequestid': 157882, 'buildsetid': 36142, 'builderid': 9, 'priority': 0, 'claimed': True, 'claimed_at': 1770161567, 'claimed_by_masterid': 1, 'complete': False, 'results': -1, 'submitted_at': 1770161566, 'complete_at': None, 'waited_for': False, 'properties': None}, 'buildset': {'bsid': 36142, 'external_idstring': None, 'reason': "The AnyBranchScheduler scheduler named 'pr' triggered this build", 'submitted_at': 1770161566, 'complete': False, 'complete_at': None, 'results': -1, 'parent_buildid': None, 'parent_relationship': None, 'rebuilt_buildid': None, 'sourcestamps': [{'ssid': 10816, 'branch': 'refs/pull/14318/head', 'revision': '201aa659066b41a2cfe89b615213bcb093fcbe79', 'project': '', 'repository': '', 'codebase': '', 'created_at': 1770161566, 'patch': None}]}, 'parentbuild': None, 'parentbuilder': None, 'builder': {'builderid': 9, 'name': 'lint', 'masterids': [1], 'description': None, 'description_format': None, 'description_html': None, 'projectid': None, 'tags': []}, 'url': 'https://dolphin.ci/#/builders/9/builds/8923'}, 'type': 'raw_bb_hook'}
2026-02-03T23:33:22.248903 {'source': 'webserver', 'raw': {'buildid': 157761, 'number': 4069, 'builderid': 43, 'buildrequestid': 157889, 'workerid': 13, 'masterid': 1, 'started_at': 1770161567, 'complete_at': None, 'locks_duration_s': 0, 'complete': False, 'state_string': 'starting', 'results': None, 'properties': {'branchname': ['pr-14318', 'Change'], 'headrev': ['201aa659066b41a2cfe89b615213bcb093fcbe79', 'Change'], 'pr_id': [14318, 'Change'], 'buildername': ['pr-flatpak-x64', 'Builder'], 'baserev': ['dd2b94cd4a71fb6abda72f58d27434ef2d8b0a5f', 'Change'], 'branch': ['refs/pull/14318/head', 'Build'], 'owners': [['Central (on behalf of: jordan-woyak)'], 'Build'], 'shortrev': ['201aa6', 'Change'], 'buildnumber': [4069, 'Build'], 'codebase': ['', 'Build'], 'repo': ['dolphin-emu/dolphin', 'Change'], 'scheduler': ['pr', 'Scheduler'], 'builderid': [43, 'Builder'], 'workername': ['altair-flatpak', 'Worker'], 'revision': ['201aa659066b41a2cfe89b615213bcb093fcbe79', 'Build'], 'repository': ['', 'Build'], 'project': ['', 'Build']}, 'buildrequest': {'buildrequestid': 157889, 'buildsetid': 36142, 'builderid': 43, 'priority': 0, 'claimed': True, 'claimed_at': 1770161567, 'claimed_by_masterid': 1, 'complete': False, 'results': -1, 'submitted_at': 1770161566, 'complete_at': None, 'waited_for': False, 'properties': None}, 'buildset': {'bsid': 36142, 'external_idstring': None, 'reason': "The AnyBranchScheduler scheduler named 'pr' triggered this build", 'submitted_at': 1770161566, 'complete': False, 'complete_at': None, 'results': -1, 'parent_buildid': None, 'parent_relationship': None, 'rebuilt_buildid': None, 'sourcestamps': [{'ssid': 10816, 'branch': 'refs/pull/14318/head', 'revision': '201aa659066b41a2cfe89b615213bcb093fcbe79', 'project': '', 'repository': '', 'codebase': '', 'created_at': 1770161566, 'patch': None}]}, 'parentbuild': None, 'parentbuilder': None, 'builder': {'builderid': 43, 'name': 'pr-flatpak-x64', 'masterids': [1], 'description': None, 'description_format': None, 'description_html': None, 'projectid': None, 'tags': []}, 'url': 'https://dolphin.ci/#/builders/43/builds/4069'}, 'type': 'raw_bb_hook'}
2026-02-03T23:33:21.536353 {'source': 'webserver', 'raw': {'buildid': 157758, 'number': 4014, 'builderid': 44, 'buildrequestid': 157890, 'workerid': 14, 'masterid': 1, 'started_at': 1770161567, 'complete_at': None, 'locks_duration_s': 0, 'complete': False, 'state_string': 'starting', 'results': None, 'properties': {'baserev': ['dd2b94cd4a71fb6abda72f58d27434ef2d8b0a5f', 'Change'], 'headrev': ['201aa659066b41a2cfe89b615213bcb093fcbe79', 'Change'], 'branchname': ['pr-14318', 'Change'], 'revision': ['201aa659066b41a2cfe89b615213bcb093fcbe79', 'Build'], 'buildername': ['pr-flatpak-arm64', 'Builder'], 'builderid': [44, 'Builder'], 'scheduler': ['pr', 'Scheduler'], 'shortrev': ['201aa6', 'Change'], 'pr_id': [14318, 'Change'], 'repo': ['dolphin-emu/dolphin', 'Change'], 'workername': ['deneb-flatpak', 'Worker'], 'buildnumber': [4014, 'Build'], 'branch': ['refs/pull/14318/head', 'Build'], 'owners': [['Central (on behalf of: jordan-woyak)'], 'Build'], 'project': ['', 'Build'], 'repository': ['', 'Build'], 'codebase': ['', 'Build']}, 'buildrequest': {'buildrequestid': 157890, 'buildsetid': 36142, 'builderid': 44, 'priority': 0, 'claimed': True, 'claimed_at': 1770161567, 'claimed_by_masterid': 1, 'complete': False, 'results': -1, 'submitted_at': 1770161566, 'complete_at': None, 'waited_for': False, 'properties': None}, 'buildset': {'bsid': 36142, 'external_idstring': None, 'reason': "The AnyBranchScheduler scheduler named 'pr' triggered this build", 'submitted_at': 1770161566, 'complete': False, 'complete_at': None, 'results': -1, 'parent_buildid': None, 'parent_relationship': None, 'rebuilt_buildid': None, 'sourcestamps': [{'ssid': 10816, 'branch': 'refs/pull/14318/head', 'revision': '201aa659066b41a2cfe89b615213bcb093fcbe79', 'project': '', 'repository': '', 'codebase': '', 'created_at': 1770161566, 'patch': None}]}, 'parentbuild': None, 'parentbuilder': None, 'builder': {'builderid': 44, 'name': 'pr-flatpak-arm64', 'masterids': [1], 'description': None, 'description_format': None, 'description_html': None, 'projectid': None, 'tags': []}, 'url': 'https://dolphin.ci/#/builders/44/builds/4014'}, 'type': 'raw_bb_hook'}
2026-02-03T23:30:41.230012 {'source': 'webserver', 'raw': {'buildid': 157755, 'number': 7798, 'builderid': 10, 'buildrequestid': 157863, 'workerid': 5, 'masterid': 1, 'started_at': 1770161440, 'complete_at': None, 'locks_duration_s': 0, 'complete': False, 'state_string': 'starting', 'results': None, 'properties': {'pr_id': [14218, 'Trigger'], 'buildnumber': [7798, 'Build'], 'project': ['', 'Build'], 'branchname': ['pr-14218', 'Trigger'], 'shortrev': ['72a6ad', 'Trigger'], 'builderid': [10, 'Builder'], 'repository': ['', 'Build'], 'headrev': ['72a6ad6fac50dccf5f37921497b92e75e84bbf40', 'Trigger'], 'buildername': ['pr-fifoci-ogl-lin-mesa', 'Builder'], 'revision': ['72a6ad6fac50dccf5f37921497b92e75e84bbf40', 'Build'], 'scheduler': ['pr-fifoci-lin', 'Scheduler'], 'branch': ['refs/pull/14218/head', 'Build'], 'owners': [['Central (on behalf of: JosJuice)'], 'Build'], 'baserev': ['eac7b290423efea867c2c7f3f2b2792d05418bdd', 'Change'], 'repo': ['dolphin-emu/dolphin', 'Trigger'], 'workername': ['altair-fifoci', 'Worker'], 'codebase': ['', 'Build']}, 'buildrequest': {'buildrequestid': 157863, 'buildsetid': 36139, 'builderid': 10, 'priority': 0, 'claimed': True, 'claimed_at': 1770161440, 'claimed_by_masterid': 1, 'complete': False, 'results': -1, 'submitted_at': 1770158877, 'complete_at': None, 'waited_for': False, 'properties': None}, 'buildset': {'bsid': 36139, 'external_idstring': None, 'reason': "The Triggerable scheduler named 'pr-fifoci-lin' triggered this build", 'submitted_at': 1770158877, 'complete': False, 'complete_at': None, 'results': -1, 'parent_buildid': 157729, 'parent_relationship': 'Triggered from', 'rebuilt_buildid': None, 'sourcestamps': [{'ssid': 10814, 'branch': 'refs/pull/14218/head', 'revision': '72a6ad6fac50dccf5f37921497b92e75e84bbf40', 'project': '', 'repository': '', 'codebase': '', 'created_at': 1770158126, 'patch': None}]}, 'parentbuild': {'buildid': 157729, 'number': 8922, 'builderid': 27, 'buildrequestid': 157857, 'workerid': 10, 'masterid': 1, 'started_at': 1770158131, 'complete_at': 1770158877, 'locks_duration_s': 0, 'complete': True, 'state_string': 'build successful', 'results': 0, 'properties': {}}, 'parentbuilder': {'builderid': 27, 'name': 'pr-ubu-x64', 'masterids': [1], 'description': None, 'description_format': None, 'description_html': None, 'projectid': None, 'tags': []}, 'builder': {'builderid': 10, 'name': 'pr-fifoci-ogl-lin-mesa', 'masterids': [1], 'description': None, 'description_format': None, 'description_html': None, 'projectid': None, 'tags': []}, 'url': 'https://dolphin.ci/#/builders/10/builds/7798'}, 'type': 'raw_bb_hook'}
2026-02-03T23:30:40.764575 {'source': 'webserver', 'raw': {'buildid': 157754, 'number': 2115, 'builderid': 5, 'buildrequestid': 157868, 'workerid': 5, 'masterid': 1, 'started_at': 1770160645, 'complete_at': 1770161440, 'locks_duration_s': 0, 'complete': True, 'state_string': 'build successful', 'results': 0, 'properties': {'owners': [['Jordan Woyak'], 'Build'], 'shortrev': ['2512-272', 'Change'], 'basedir': ['/var/lib/fifoci-worker/worker', 'Worker'], 'warnings-count': [0, 'WarningCountingShellCommand'], 'author': ['Jordan Woyak', 'Change'], 'scheduler': ['dev', 'Scheduler'], 'builderid': [5, 'Builder'], 'buildnumber': [2115, 'Build'], 'revision': ['dd2b94cd4a71fb6abda72f58d27434ef2d8b0a5f', 'Build'], 'codebase': ['', 'Build'], 'description': ['Merge pull request #13594 from jordan-woyak/state-cleanups\n\nState: Simplify interthread communication and general cleanups.', 'Change'], 'builddir': ['/var/lib/fifoci-worker/worker/fifoci-ogl-lin-mesa', 'Worker'], 'branchname': ['master', 'Change'], 'buildername': ['fifoci-ogl-lin-mesa', 'Builder'], 'workername': ['altair-fifoci', 'Worker'], 'branch': ['master', 'Build'], 'repository': ['', 'Build'], 'project': ['', 'Build'], 'got_revision': ['dd2b94cd4a71fb6abda72f58d27434ef2d8b0a5f', 'GitNoBranch']}, 'buildrequest': {'buildrequestid': 157868, 'buildsetid': 36141, 'builderid': 5, 'priority': 0, 'claimed': True, 'claimed_at': 1770160645, 'claimed_by_masterid': 1, 'complete': False, 'results': -1, 'submitted_at': 1770159056, 'complete_at': None, 'waited_for': False, 'properties': None}, 'buildset': {'bsid': 36141, 'external_idstring': None, 'reason': "The AnyBranchScheduler scheduler named 'dev' triggered this build", 'submitted_at': 1770159056, 'complete': False, 'complete_at': None, 'results': -1, 'parent_buildid': None, 'parent_relationship': None, 'rebuilt_buildid': None, 'sourcestamps': [{'ssid': 10815, 'branch': 'master', 'revision': 'dd2b94cd4a71fb6abda72f58d27434ef2d8b0a5f', 'project': '', 'repository': '', 'codebase': '', 'created_at': 1770159056, 'patch': None}]}, 'parentbuild': None, 'parentbuilder': None, 'builder': {'builderid': 5, 'name': 'fifoci-ogl-lin-mesa', 'masterids': [1], 'description': None, 'description_format': None, 'description_html': None, 'projectid': None, 'tags': []}, 'url': 'https://dolphin.ci/#/builders/5/builds/2115'}, 'type': 'raw_bb_hook'}
2026-02-03T23:25:18.058501 {'source': 'webserver', 'raw': {'buildid': 157746, 'number': 1136, 'builderid': 39, 'buildrequestid': 157875, 'workerid': 12, 'masterid': 1, 'started_at': 1770159835, 'complete_at': 1770161117, 'locks_duration_s': 0, 'complete': True, 'state_string': 'build successful', 'results': 0, 'properties': {'builderid': [39, 'Builder'], 'branch': ['master', 'Build'], 'codebase': ['', 'Build'], 'branchname': ['master', 'Change'], 'description': ['Merge pull request #13594 from jordan-woyak/state-cleanups\n\nState: Simplify interthread communication and general cleanups.', 'Change'], 'buildername': ['dev-android', 'Builder'], 'buildnumber': [1136, 'Build'], 'repository': ['', 'Build'], 'got_revision': ['dd2b94cd4a71fb6abda72f58d27434ef2d8b0a5f', 'GitNoBranch'], 'shortrev': ['2512-272', 'Change'], 'scheduler': ['dev', 'Scheduler'], 'workername': ['android', 'Worker'], 'revision': ['dd2b94cd4a71fb6abda72f58d27434ef2d8b0a5f', 'Build'], 'project': ['', 'Build'], 'author': ['Jordan Woyak', 'Change'], 'basedir': ['/buildbot', 'Worker'], 'owners': [['Jordan Woyak'], 'Build'], 'builddir': ['/buildbot/dev-android', 'Worker']}, 'buildrequest': {'buildrequestid': 157875, 'buildsetid': 36141, 'builderid': 39, 'priority': 0, 'claimed': True, 'claimed_at': 1770159835, 'claimed_by_masterid': 1, 'complete': False, 'results': -1, 'submitted_at': 1770159056, 'complete_at': None, 'waited_for': False, 'properties': None}, 'buildset': {'bsid': 36141, 'external_idstring': None, 'reason': "The AnyBranchScheduler scheduler named 'dev' triggered this build", 'submitted_at': 1770159056, 'complete': False, 'complete_at': None, 'results': -1, 'parent_buildid': None, 'parent_relationship': None, 'rebuilt_buildid': None, 'sourcestamps': [{'ssid': 10815, 'branch': 'master', 'revision': 'dd2b94cd4a71fb6abda72f58d27434ef2d8b0a5f', 'project': '', 'repository': '', 'codebase': '', 'created_at': 1770159056, 'patch': None}]}, 'parentbuild': None, 'parentbuilder': None, 'builder': {'builderid': 39, 'name': 'dev-android', 'masterids': [1], 'description': None, 'description_format': None, 'description_html': None, 'projectid': None, 'tags': []}, 'url': 'https://dolphin.ci/#/builders/39/builds/1136'}, 'type': 'raw_bb_hook'}
2026-02-03T23:17:25.574672 {'source': 'webserver', 'raw': {'buildid': 157754, 'number': 2115, 'builderid': 5, 'buildrequestid': 157868, 'workerid': 5, 'masterid': 1, 'started_at': 1770160645, 'complete_at': None, 'locks_duration_s': 0, 'complete': False, 'state_string': 'starting', 'results': None, 'properties': {'shortrev': ['2512-272', 'Change'], 'author': ['Jordan Woyak', 'Change'], 'scheduler': ['dev', 'Scheduler'], 'builderid': [5, 'Builder'], 'buildnumber': [2115, 'Build'], 'revision': ['dd2b94cd4a71fb6abda72f58d27434ef2d8b0a5f', 'Build'], 'codebase': ['', 'Build'], 'description': ['Merge pull request #13594 from jordan-woyak/state-cleanups\n\nState: Simplify interthread communication and general cleanups.', 'Change'], 'branchname': ['master', 'Change'], 'buildername': ['fifoci-ogl-lin-mesa', 'Builder'], 'workername': ['altair-fifoci', 'Worker'], 'branch': ['master', 'Build'], 'repository': ['', 'Build'], 'project': ['', 'Build']}, 'buildrequest': {'buildrequestid': 157868, 'buildsetid': 36141, 'builderid': 5, 'priority': 0, 'claimed': True, 'claimed_at': 1770160645, 'claimed_by_masterid': 1, 'complete': False, 'results': -1, 'submitted_at': 1770159056, 'complete_at': None, 'waited_for': False, 'properties': None}, 'buildset': {'bsid': 36141, 'external_idstring': None, 'reason': "The AnyBranchScheduler scheduler named 'dev' triggered this build", 'submitted_at': 1770159056, 'complete': False, 'complete_at': None, 'results': -1, 'parent_buildid': None, 'parent_relationship': None, 'rebuilt_buildid': None, 'sourcestamps': [{'ssid': 10815, 'branch': 'master', 'revision': 'dd2b94cd4a71fb6abda72f58d27434ef2d8b0a5f', 'project': '', 'repository': '', 'codebase': '', 'created_at': 1770159056, 'patch': None}]}, 'parentbuild': None, 'parentbuilder': None, 'builder': {'builderid': 5, 'name': 'fifoci-ogl-lin-mesa', 'masterids': [1], 'description': None, 'description_format': None, 'description_html': None, 'projectid': None, 'tags': []}, 'url': 'https://dolphin.ci/#/builders/5/builds/2115'}, 'type': 'raw_bb_hook'}
2026-02-03T23:17:25.445573 {'source': 'webserver', 'raw': {'buildid': 157747, 'number': 979, 'builderid': 40, 'buildrequestid': 157876, 'workerid': 5, 'masterid': 1, 'started_at': 1770159903, 'complete_at': 1770160645, 'locks_duration_s': 0, 'complete': True, 'state_string': 'build successful', 'results': 0, 'properties': {'buildername': ['fifoci-vk-lin-mesa', 'Builder'], 'revision': ['dd2b94cd4a71fb6abda72f58d27434ef2d8b0a5f', 'Build'], 'builddir': ['/var/lib/fifoci-worker/worker/fifoci-vk-lin-mesa', 'Worker'], 'author': ['Jordan Woyak', 'Change'], 'workername': ['altair-fifoci', 'Worker'], 'codebase': ['', 'Build'], 'description': ['Merge pull request #13594 from jordan-woyak/state-cleanups\n\nState: Simplify interthread communication and general cleanups.', 'Change'], 'buildnumber': [979, 'Build'], 'project': ['', 'Build'], 'basedir': ['/var/lib/fifoci-worker/worker', 'Worker'], 'shortrev': ['2512-272', 'Change'], 'builderid': [40, 'Builder'], 'repository': ['', 'Build'], 'got_revision': ['dd2b94cd4a71fb6abda72f58d27434ef2d8b0a5f', 'GitNoBranch'], 'warnings-count': [0, 'WarningCountingShellCommand'], 'branchname': ['master', 'Change'], 'scheduler': ['dev', 'Scheduler'], 'branch': ['master', 'Build'], 'owners': [['Jordan Woyak'], 'Build']}, 'buildrequest': {'buildrequestid': 157876, 'buildsetid': 36141, 'builderid': 40, 'priority': 0, 'claimed': True, 'claimed_at': 1770159903, 'claimed_by_masterid': 1, 'complete': False, 'results': -1, 'submitted_at': 1770159056, 'complete_at': None, 'waited_for': False, 'properties': None}, 'buildset': {'bsid': 36141, 'external_idstring': None, 'reason': "The AnyBranchScheduler scheduler named 'dev' triggered this build", 'submitted_at': 1770159056, 'complete': False, 'complete_at': None, 'results': -1, 'parent_buildid': None, 'parent_relationship': None, 'rebuilt_buildid': None, 'sourcestamps': [{'ssid': 10815, 'branch': 'master', 'revision': 'dd2b94cd4a71fb6abda72f58d27434ef2d8b0a5f', 'project': '', 'repository': '', 'codebase': '', 'created_at': 1770159056, 'patch': None}]}, 'parentbuild': None, 'parentbuilder': None, 'builder': {'builderid': 40, 'name': 'fifoci-vk-lin-mesa', 'masterids': [1], 'description': None, 'description_format': None, 'description_html': None, 'projectid': None, 'tags': []}, 'url': 'https://dolphin.ci/#/builders/40/builds/979'}, 'type': 'raw_bb_hook'}
2026-02-03T23:11:10.293481 {'source': 'webserver', 'raw': {'buildid': 157753, 'number': 7626, 'builderid': 19, 'buildrequestid': 157866, 'workerid': 8, 'masterid': 1, 'started_at': 1770160251, 'complete_at': 1770160270, 'locks_duration_s': 0, 'complete': True, 'state_string': 'failed build (failure)', 'results': 2, 'properties': {'scheduler': ['pr-fifoci-osx', 'Scheduler'], 'branch': ['refs/pull/14218/head', 'Build'], 'got_revision': ['ca98b3030bcc70e67158ce899a5589aa2ec65d32', 'GitNoBranch'], 'shortrev': ['72a6ad', 'Trigger'], 'builderid': [19, 'Builder'], 'repository': ['', 'Build'], 'basedir': ['/Users/administrator/buildbot-worker/osx_m1', 'Worker'], 'buildername': ['pr-fifoci-mvk-osx-m1', 'Builder'], 'revision': ['72a6ad6fac50dccf5f37921497b92e75e84bbf40', 'Build'], 'owners': [['Central (on behalf of: JosJuice)'], 'Build'], 'builddir': ['/Users/administrator/buildbot-worker/osx_m1/pr-fifoci-mvk-osx-m1', 'Worker'], 'warnings-count': [1, 'WarningCountingShellCommand'], 'baserev': ['eac7b290423efea867c2c7f3f2b2792d05418bdd', 'Change'], 'pr_id': [14218, 'Trigger'], 'workername': ['osx-m1', 'Worker'], 'codebase': ['', 'Build'], 'branchname': ['pr-14218', 'Trigger'], 'headrev': ['72a6ad6fac50dccf5f37921497b92e75e84bbf40', 'Trigger'], 'repo': ['dolphin-emu/dolphin', 'Trigger'], 'buildnumber': [7626, 'Build'], 'project': ['', 'Build']}, 'buildrequest': {'buildrequestid': 157866, 'buildsetid': 36140, 'builderid': 19, 'priority': 0, 'claimed': True, 'claimed_at': 1770160251, 'claimed_by_masterid': 1, 'complete': False, 'results': -1, 'submitted_at': 1770158985, 'complete_at': None, 'waited_for': False, 'properties': None}, 'buildset': {'bsid': 36140, 'external_idstring': None, 'reason': "The Triggerable scheduler named 'pr-fifoci-osx' triggered this build", 'submitted_at': 1770158985, 'complete': False, 'complete_at': None, 'results': -1, 'parent_buildid': 157728, 'parent_relationship': 'Triggered from', 'rebuilt_buildid': None, 'sourcestamps': [{'ssid': 10814, 'branch': 'refs/pull/14218/head', 'revision': '72a6ad6fac50dccf5f37921497b92e75e84bbf40', 'project': '', 'repository': '', 'codebase': '', 'created_at': 1770158126, 'patch': None}]}, 'parentbuild': {'buildid': 157728, 'number': 8937, 'builderid': 7, 'buildrequestid': 157851, 'workerid': 8, 'masterid': 1, 'started_at': 1770158129, 'complete_at': 1770158985, 'locks_duration_s': 0, 'complete': True, 'state_string': 'build successful', 'results': 0, 'properties': {}}, 'parentbuilder': {'builderid': 7, 'name': 'pr-osx-universal', 'masterids': [1], 'description': None, 'description_format': None, 'description_html': None, 'projectid': None, 'tags': []}, 'builder': {'builderid': 19, 'name': 'pr-fifoci-mvk-osx-m1', 'masterids': [1], 'description': None, 'description_format': None, 'description_html': None, 'projectid': None, 'tags': []}, 'url': 'https://dolphin.ci/#/builders/19/builds/7626'}, 'type': 'raw_bb_hook'}
2026-02-03T23:10:51.821221 {'source': 'webserver', 'raw': {'buildid': 157753, 'number': 7626, 'builderid': 19, 'buildrequestid': 157866, 'workerid': 8, 'masterid': 1, 'started_at': 1770160251, 'complete_at': None, 'locks_duration_s': 0, 'complete': False, 'state_string': 'starting', 'results': None, 'properties': {'scheduler': ['pr-fifoci-osx', 'Scheduler'], 'branch': ['refs/pull/14218/head', 'Build'], 'shortrev': ['72a6ad', 'Trigger'], 'builderid': [19, 'Builder'], 'repository': ['', 'Build'], 'buildername': ['pr-fifoci-mvk-osx-m1', 'Builder'], 'revision': ['72a6ad6fac50dccf5f37921497b92e75e84bbf40', 'Build'], 'owners': [['Central (on behalf of: JosJuice)'], 'Build'], 'baserev': ['eac7b290423efea867c2c7f3f2b2792d05418bdd', 'Change'], 'pr_id': [14218, 'Trigger'], 'workername': ['osx-m1', 'Worker'], 'codebase': ['', 'Build'], 'branchname': ['pr-14218', 'Trigger'], 'headrev': ['72a6ad6fac50dccf5f37921497b92e75e84bbf40', 'Trigger'], 'repo': ['dolphin-emu/dolphin', 'Trigger'], 'buildnumber': [7626, 'Build'], 'project': ['', 'Build']}, 'buildrequest': {'buildrequestid': 157866, 'buildsetid': 36140, 'builderid': 19, 'priority': 0, 'claimed': True, 'claimed_at': 1770160251, 'claimed_by_masterid': 1, 'complete': False, 'results': -1, 'submitted_at': 1770158985, 'complete_at': None, 'waited_for': False, 'properties': None}, 'buildset': {'bsid': 36140, 'external_idstring': None, 'reason': "The Triggerable scheduler named 'pr-fifoci-osx' triggered this build", 'submitted_at': 1770158985, 'complete': False, 'complete_at': None, 'results': -1, 'parent_buildid': 157728, 'parent_relationship': 'Triggered from', 'rebuilt_buildid': None, 'sourcestamps': [{'ssid': 10814, 'branch': 'refs/pull/14218/head', 'revision': '72a6ad6fac50dccf5f37921497b92e75e84bbf40', 'project': '', 'repository': '', 'codebase': '', 'created_at': 1770158126, 'patch': None}]}, 'parentbuild': {'buildid': 157728, 'number': 8937, 'builderid': 7, 'buildrequestid': 157851, 'workerid': 8, 'masterid': 1, 'started_at': 1770158129, 'complete_at': 1770158985, 'locks_duration_s': 0, 'complete': True, 'state_string': 'build successful', 'results': 0, 'properties': {}}, 'parentbuilder': {'builderid': 7, 'name': 'pr-osx-universal', 'masterids': [1], 'description': None, 'description_format': None, 'description_html': None, 'projectid': None, 'tags': []}, 'builder': {'builderid': 19, 'name': 'pr-fifoci-mvk-osx-m1', 'masterids': [1], 'description': None, 'description_format': None, 'description_html': None, 'projectid': None, 'tags': []}, 'url': 'https://dolphin.ci/#/builders/19/builds/7626'}, 'type': 'raw_bb_hook'}
2026-02-03T23:10:51.525337 {'source': 'webserver', 'raw': {'buildid': 157752, 'number': 7625, 'builderid': 19, 'buildrequestid': 157861, 'workerid': 8, 'masterid': 1, 'started_at': 1770160237, 'complete_at': 1770160251, 'locks_duration_s': 0, 'complete': True, 'state_string': 'failed build (failure)', 'results': 2, 'properties': {'headrev': ['32856fd15531890d65379376c8cadfc3611b36cb', 'Trigger'], 'pr_id': [13768, 'Trigger'], 'scheduler': ['pr-fifoci-osx', 'Scheduler'], 'buildnumber': [7625, 'Build'], 'repository': ['', 'Build'], 'owners': [['Central (on behalf of: JosJuice)'], 'Build'], 'basedir': ['/Users/administrator/buildbot-worker/osx_m1', 'Worker'], 'buildername': ['pr-fifoci-mvk-osx-m1', 'Builder'], 'project': ['', 'Build'], 'got_revision': ['ca98b3030bcc70e67158ce899a5589aa2ec65d32', 'GitNoBranch'], 'branchname': ['pr-13768', 'Trigger'], 'shortrev': ['32856f', 'Trigger'], 'repo': ['dolphin-emu/dolphin', 'Trigger'], 'builderid': [19, 'Builder'], 'branch': ['refs/pull/13768/head', 'Build'], 'codebase': ['', 'Build'], 'warnings-count': [1, 'WarningCountingShellCommand'], 'baserev': ['eac7b290423efea867c2c7f3f2b2792d05418bdd', 'Change'], 'workername': ['osx-m1', 'Worker'], 'revision': ['32856fd15531890d65379376c8cadfc3611b36cb', 'Build'], 'builddir': ['/Users/administrator/buildbot-worker/osx_m1/pr-fifoci-mvk-osx-m1', 'Worker']}, 'buildrequest': {'buildrequestid': 157861, 'buildsetid': 36137, 'builderid': 19, 'priority': 0, 'claimed': True, 'claimed_at': 1770160237, 'claimed_by_masterid': 1, 'complete': False, 'results': -1, 'submitted_at': 1770158129, 'complete_at': None, 'waited_for': False, 'properties': None}, 'buildset': {'bsid': 36137, 'external_idstring': None, 'reason': "The Triggerable scheduler named 'pr-fifoci-osx' triggered this build", 'submitted_at': 1770158129, 'complete': False, 'complete_at': None, 'results': -1, 'parent_buildid': 157716, 'parent_relationship': 'Triggered from', 'rebuilt_buildid': None, 'sourcestamps': [{'ssid': 10813, 'branch': 'refs/pull/13768/head', 'revision': '32856fd15531890d65379376c8cadfc3611b36cb', 'project': '', 'repository': '', 'codebase': '', 'created_at': 1770156379, 'patch': None}]}, 'parentbuild': {'buildid': 157716, 'number': 8936, 'builderid': 7, 'buildrequestid': 157831, 'workerid': 8, 'masterid': 1, 'started_at': 1770157334, 'complete_at': 1770158129, 'locks_duration_s': 0, 'complete': True, 'state_string': 'build successful', 'results': 0, 'properties': {}}, 'parentbuilder': {'builderid': 7, 'name': 'pr-osx-universal', 'masterids': [1], 'description': None, 'description_format': None, 'description_html': None, 'projectid': None, 'tags': []}, 'builder': {'builderid': 19, 'name': 'pr-fifoci-mvk-osx-m1', 'masterids': [1], 'description': None, 'description_format': None, 'description_html': None, 'projectid': None, 'tags': []}, 'url': 'https://dolphin.ci/#/builders/19/builds/7625'}, 'type': 'raw_bb_hook'}
2026-02-03T23:10:46.791175 {'source': 'webserver', 'raw': {'buildid': 157750, 'number': 7623, 'builderid': 19, 'buildrequestid': 157827, 'workerid': 8, 'masterid': 1, 'started_at': 1770160221, 'complete_at': 1770160236, 'locks_duration_s': 0, 'complete': True, 'state_string': 'failed build (failure)', 'results': 2, 'properties': {'branchname': ['pr-14317', 'Trigger'], 'repo': ['dolphin-emu/dolphin', 'Trigger'], 'buildnumber': [7623, 'Build'], 'project': ['', 'Build'], 'builddir': ['/Users/administrator/buildbot-worker/osx_m1/pr-fifoci-mvk-osx-m1', 'Worker'], 'pr_id': [14317, 'Trigger'], 'workername': ['osx-m1', 'Worker'], 'codebase': ['', 'Build'], 'baserev': ['c7de9162765452fc2787f3ba4f0092394067d603', 'Change'], 'scheduler': ['pr-fifoci-osx', 'Scheduler'], 'branch': ['refs/pull/14317/head', 'Build'], 'owners': [['Central (on behalf of: JosJuice)'], 'Build'], 'got_revision': ['ca98b3030bcc70e67158ce899a5589aa2ec65d32', 'GitNoBranch'], 'warnings-count': [1, 'WarningCountingShellCommand'], 'shortrev': ['c81800', 'Trigger'], 'builderid': [19, 'Builder'], 'repository': ['', 'Build'], 'headrev': ['c8180071a552720e5436e908afc4eda7e87be08c', 'Trigger'], 'buildername': ['pr-fifoci-mvk-osx-m1', 'Builder'], 'revision': ['c8180071a552720e5436e908afc4eda7e87be08c', 'Build'], 'basedir': ['/Users/administrator/buildbot-worker/osx_m1', 'Worker']}, 'buildrequest': {'buildrequestid': 157827, 'buildsetid': 36129, 'builderid': 19, 'priority': 0, 'claimed': True, 'claimed_at': 1770160221, 'claimed_by_masterid': 1, 'complete': False, 'results': -1, 'submitted_at': 1770156358, 'complete_at': None, 'waited_for': False, 'properties': None}, 'buildset': {'bsid': 36129, 'external_idstring': None, 'reason': "The Triggerable scheduler named 'pr-fifoci-osx' triggered this build", 'submitted_at': 1770156358, 'complete': False, 'complete_at': None, 'results': -1, 'parent_buildid': 157688, 'parent_relationship': 'Triggered from', 'rebuilt_buildid': None, 'sourcestamps': [{'ssid': 10809, 'branch': 'refs/pull/14317/head', 'revision': 'c8180071a552720e5436e908afc4eda7e87be08c', 'project': '', 'repository': '', 'codebase': '', 'created_at': 1770066448, 'patch': None}]}, 'parentbuild': {'buildid': 157688, 'number': 8934, 'builderid': 7, 'buildrequestid': 157797, 'workerid': 8, 'masterid': 1, 'started_at': 1770155559, 'complete_at': 1770156358, 'locks_duration_s': 0, 'complete': True, 'state_string': 'build successful', 'results': 0, 'properties': {}}, 'parentbuilder': {'builderid': 7, 'name': 'pr-osx-universal', 'masterids': [1], 'description': None, 'description_format': None, 'description_html': None, 'projectid': None, 'tags': []}, 'builder': {'builderid': 19, 'name': 'pr-fifoci-mvk-osx-m1', 'masterids': [1], 'description': None, 'description_format': None, 'description_html': None, 'projectid': None, 'tags': []}, 'url': 'https://dolphin.ci/#/builders/19/builds/7623'}, 'type': 'raw_bb_hook'}
2026-02-03T23:10:38.030214 {'source': 'webserver', 'raw': {'buildid': 157752, 'number': 7625, 'builderid': 19, 'buildrequestid': 157861, 'workerid': 8, 'masterid': 1, 'started_at': 1770160237, 'complete_at': None, 'locks_duration_s': 0, 'complete': False, 'state_string': 'starting', 'results': None, 'properties': {'headrev': ['32856fd15531890d65379376c8cadfc3611b36cb', 'Trigger'], 'pr_id': [13768, 'Trigger'], 'scheduler': ['pr-fifoci-osx', 'Scheduler'], 'buildnumber': [7625, 'Build'], 'repository': ['', 'Build'], 'owners': [['Central (on behalf of: JosJuice)'], 'Build'], 'buildername': ['pr-fifoci-mvk-osx-m1', 'Builder'], 'project': ['', 'Build'], 'branchname': ['pr-13768', 'Trigger'], 'shortrev': ['32856f', 'Trigger'], 'repo': ['dolphin-emu/dolphin', 'Trigger'], 'builderid': [19, 'Builder'], 'branch': ['refs/pull/13768/head', 'Build'], 'codebase': ['', 'Build'], 'baserev': ['eac7b290423efea867c2c7f3f2b2792d05418bdd', 'Change'], 'workername': ['osx-m1', 'Worker'], 'revision': ['32856fd15531890d65379376c8cadfc3611b36cb', 'Build']}, 'buildrequest': {'buildrequestid': 157861, 'buildsetid': 36137, 'builderid': 19, 'priority': 0, 'claimed': True, 'claimed_at': 1770160237, 'claimed_by_masterid': 1, 'complete': False, 'results': -1, 'submitted_at': 1770158129, 'complete_at': None, 'waited_for': False, 'properties': None}, 'buildset': {'bsid': 36137, 'external_idstring': None, 'reason': "The Triggerable scheduler named 'pr-fifoci-osx' triggered this build", 'submitted_at': 1770158129, 'complete': False, 'complete_at': None, 'results': -1, 'parent_buildid': 157716, 'parent_relationship': 'Triggered from', 'rebuilt_buildid': None, 'sourcestamps': [{'ssid': 10813, 'branch': 'refs/pull/13768/head', 'revision': '32856fd15531890d65379376c8cadfc3611b36cb', 'project': '', 'repository': '', 'codebase': '', 'created_at': 1770156379, 'patch': None}]}, 'parentbuild': {'buildid': 157716, 'number': 8936, 'builderid': 7, 'buildrequestid': 157831, 'workerid': 8, 'masterid': 1, 'started_at': 1770157334, 'complete_at': 1770158129, 'locks_duration_s': 0, 'complete': True, 'state_string': 'build successful', 'results': 0, 'properties': {}}, 'parentbuilder': {'builderid': 7, 'name': 'pr-osx-universal', 'masterids': [1], 'description': None, 'description_format': None, 'description_html': None, 'projectid': None, 'tags': []}, 'builder': {'builderid': 19, 'name': 'pr-fifoci-mvk-osx-m1', 'masterids': [1], 'description': None, 'description_format': None, 'description_html': None, 'projectid': None, 'tags': []}, 'url': 'https://dolphin.ci/#/builders/19/builds/7625'}, 'type': 'raw_bb_hook'}
2026-02-03T23:10:37.899701 {'source': 'webserver', 'raw': {'buildid': 157751, 'number': 7624, 'builderid': 19, 'buildrequestid': 157847, 'workerid': 8, 'masterid': 1, 'started_at': 1770160236, 'complete_at': 1770160237, 'locks_duration_s': 0, 'complete': True, 'state_string': 'failed update (failure)', 'results': 2, 'properties': {'headrev': ['a7301d7d406bc5955303a13e61327c5b17645b7a', 'Trigger'], 'pr_id': [13768, 'Trigger'], 'scheduler': ['pr-fifoci-osx', 'Scheduler'], 'builderid': [19, 'Builder'], 'buildnumber': [7624, 'Build'], 'baserev': ['eac7b290423efea867c2c7f3f2b2792d05418bdd', 'Change'], 'repo': ['dolphin-emu/dolphin', 'Trigger'], 'repository': ['', 'Build'], 'project': ['', 'Build'], 'branchname': ['pr-13768', 'Trigger'], 'shortrev': ['a7301d', 'Trigger'], 'branch': ['refs/pull/13768/head', 'Build'], 'owners': [['Central (on behalf of: JosJuice)'], 'Build'], 'basedir': ['/Users/administrator/buildbot-worker/osx_m1', 'Worker'], 'buildername': ['pr-fifoci-mvk-osx-m1', 'Builder'], 'workername': ['osx-m1', 'Worker'], 'builddir': ['/Users/administrator/buildbot-worker/osx_m1/pr-fifoci-mvk-osx-m1', 'Worker'], 'revision': ['a7301d7d406bc5955303a13e61327c5b17645b7a', 'Build'], 'codebase': ['', 'Build']}, 'buildrequest': {'buildrequestid': 157847, 'buildsetid': 36135, 'builderid': 19, 'priority': 0, 'claimed': True, 'claimed_at': 1770160236, 'claimed_by_masterid': 1, 'complete': False, 'results': -1, 'submitted_at': 1770157334, 'complete_at': None, 'waited_for': False, 'properties': None}, 'buildset': {'bsid': 36135, 'external_idstring': None, 'reason': "The Triggerable scheduler named 'pr-fifoci-osx' triggered this build", 'submitted_at': 1770157334, 'complete': False, 'complete_at': None, 'results': -1, 'parent_buildid': 157701, 'parent_relationship': 'Triggered from', 'rebuilt_buildid': None, 'sourcestamps': [{'ssid': 10812, 'branch': 'refs/pull/13768/head', 'revision': 'a7301d7d406bc5955303a13e61327c5b17645b7a', 'project': '', 'repository': '', 'codebase': '', 'created_at': 1770156283, 'patch': None}]}, 'parentbuild': {'buildid': 157701, 'number': 8935, 'builderid': 7, 'buildrequestid': 157817, 'workerid': 8, 'masterid': 1, 'started_at': 1770156358, 'complete_at': 1770157334, 'locks_duration_s': 0, 'complete': True, 'state_string': 'build successful', 'results': 0, 'properties': {}}, 'parentbuilder': {'builderid': 7, 'name': 'pr-osx-universal', 'masterids': [1], 'description': None, 'description_format': None, 'description_html': None, 'projectid': None, 'tags': []}, 'builder': {'builderid': 19, 'name': 'pr-fifoci-mvk-osx-m1', 'masterids': [1], 'description': None, 'description_format': None, 'description_html': None, 'projectid': None, 'tags': []}, 'url': 'https://dolphin.ci/#/builders/19/builds/7624'}, 'type': 'raw_bb_hook'}
2026-02-03T23:10:36.371211 {'source': 'webserver', 'raw': {'buildid': 157751, 'number': 7624, 'builderid': 19, 'buildrequestid': 157847, 'workerid': 8, 'masterid': 1, 'started_at': 1770160236, 'complete_at': None, 'locks_duration_s': 0, 'complete': False, 'state_string': 'starting', 'results': None, 'properties': {'headrev': ['a7301d7d406bc5955303a13e61327c5b17645b7a', 'Trigger'], 'pr_id': [13768, 'Trigger'], 'scheduler': ['pr-fifoci-osx', 'Scheduler'], 'builderid': [19, 'Builder'], 'buildnumber': [7624, 'Build'], 'baserev': ['eac7b290423efea867c2c7f3f2b2792d05418bdd', 'Change'], 'repo': ['dolphin-emu/dolphin', 'Trigger'], 'repository': ['', 'Build'], 'project': ['', 'Build'], 'branchname': ['pr-13768', 'Trigger'], 'shortrev': ['a7301d', 'Trigger'], 'branch': ['refs/pull/13768/head', 'Build'], 'owners': [['Central (on behalf of: JosJuice)'], 'Build'], 'buildername': ['pr-fifoci-mvk-osx-m1', 'Builder'], 'workername': ['osx-m1', 'Worker'], 'revision': ['a7301d7d406bc5955303a13e61327c5b17645b7a', 'Build'], 'codebase': ['', 'Build']}, 'buildrequest': {'buildrequestid': 157847, 'buildsetid': 36135, 'builderid': 19, 'priority': 0, 'claimed': True, 'claimed_at': 1770160236, 'claimed_by_masterid': 1, 'complete': False, 'results': -1, 'submitted_at': 1770157334, 'complete_at': None, 'waited_for': False, 'properties': None}, 'buildset': {'bsid': 36135, 'external_idstring': None, 'reason': "The Triggerable scheduler named 'pr-fifoci-osx' triggered this build", 'submitted_at': 1770157334, 'complete': False, 'complete_at': None, 'results': -1, 'parent_buildid': 157701, 'parent_relationship': 'Triggered from', 'rebuilt_buildid': None, 'sourcestamps': [{'ssid': 10812, 'branch': 'refs/pull/13768/head', 'revision': 'a7301d7d406bc5955303a13e61327c5b17645b7a', 'project': '', 'repository': '', 'codebase': '', 'created_at': 1770156283, 'patch': None}]}, 'parentbuild': {'buildid': 157701, 'number': 8935, 'builderid': 7, 'buildrequestid': 157817, 'workerid': 8, 'masterid': 1, 'started_at': 1770156358, 'complete_at': 1770157334, 'locks_duration_s': 0, 'complete': True, 'state_string': 'build successful', 'results': 0, 'properties': {}}, 'parentbuilder': {'builderid': 7, 'name': 'pr-osx-universal', 'masterids': [1], 'description': None, 'description_format': None, 'description_html': None, 'projectid': None, 'tags': []}, 'builder': {'builderid': 19, 'name': 'pr-fifoci-mvk-osx-m1', 'masterids': [1], 'description': None, 'description_format': None, 'description_html': None, 'projectid': None, 'tags': []}, 'url': 'https://dolphin.ci/#/builders/19/builds/7624'}, 'type': 'raw_bb_hook'}
2026-02-03T23:10:31.848837 {'source': 'webserver', 'raw': {'buildid': 157750, 'number': 7623, 'builderid': 19, 'buildrequestid': 157827, 'workerid': 8, 'masterid': 1, 'started_at': 1770160221, 'complete_at': None, 'locks_duration_s': 0, 'complete': False, 'state_string': 'starting', 'results': None, 'properties': {'branchname': ['pr-14317', 'Trigger'], 'repo': ['dolphin-emu/dolphin', 'Trigger'], 'buildnumber': [7623, 'Build'], 'project': ['', 'Build'], 'pr_id': [14317, 'Trigger'], 'workername': ['osx-m1', 'Worker'], 'codebase': ['', 'Build'], 'baserev': ['c7de9162765452fc2787f3ba4f0092394067d603', 'Change'], 'scheduler': ['pr-fifoci-osx', 'Scheduler'], 'branch': ['refs/pull/14317/head', 'Build'], 'owners': [['Central (on behalf of: JosJuice)'], 'Build'], 'shortrev': ['c81800', 'Trigger'], 'builderid': [19, 'Builder'], 'repository': ['', 'Build'], 'headrev': ['c8180071a552720e5436e908afc4eda7e87be08c', 'Trigger'], 'buildername': ['pr-fifoci-mvk-osx-m1', 'Builder'], 'revision': ['c8180071a552720e5436e908afc4eda7e87be08c', 'Build']}, 'buildrequest': {'buildrequestid': 157827, 'buildsetid': 36129, 'builderid': 19, 'priority': 0, 'claimed': True, 'claimed_at': 1770160221, 'claimed_by_masterid': 1, 'complete': False, 'results': -1, 'submitted_at': 1770156358, 'complete_at': None, 'waited_for': False, 'properties': None}, 'buildset': {'bsid': 36129, 'external_idstring': None, 'reason': "The Triggerable scheduler named 'pr-fifoci-osx' triggered this build", 'submitted_at': 1770156358, 'complete': False, 'complete_at': None, 'results': -1, 'parent_buildid': 157688, 'parent_relationship': 'Triggered from', 'rebuilt_buildid': None, 'sourcestamps': [{'ssid': 10809, 'branch': 'refs/pull/14317/head', 'revision': 'c8180071a552720e5436e908afc4eda7e87be08c', 'project': '', 'repository': '', 'codebase': '', 'created_at': 1770066448, 'patch': None}]}, 'parentbuild': {'buildid': 157688, 'number': 8934, 'builderid': 7, 'buildrequestid': 157797, 'workerid': 8, 'masterid': 1, 'started_at': 1770155559, 'complete_at': 1770156358, 'locks_duration_s': 0, 'complete': True, 'state_string': 'build successful', 'results': 0, 'properties': {}}, 'parentbuilder': {'builderid': 7, 'name': 'pr-osx-universal', 'masterids': [1], 'description': None, 'description_format': None, 'description_html': None, 'projectid': None, 'tags': []}, 'builder': {'builderid': 19, 'name': 'pr-fifoci-mvk-osx-m1', 'masterids': [1], 'description': None, 'description_format': None, 'description_html': None, 'projectid': None, 'tags': []}, 'url': 'https://dolphin.ci/#/builders/19/builds/7623'}, 'type': 'raw_bb_hook'}
2026-02-03T23:10:21.372530 {'source': 'webserver', 'raw': {'buildid': 157749, 'number': 2168, 'builderid': 17, 'buildrequestid': 157870, 'workerid': 8, 'masterid': 1, 'started_at': 1770160124, 'complete_at': 1770160221, 'locks_duration_s': 0, 'complete': True, 'state_string': 'build successful', 'results': 0, 'properties': {'shortrev': ['2512-272', 'Change'], 'description': ['Merge pull request #13594 from jordan-woyak/state-cleanups\n\nState: Simplify interthread communication and general cleanups.', 'Change'], 'buildername': ['fifoci-mtl-osx-m1', 'Builder'], 'workername': ['osx-m1', 'Worker'], 'branchname': ['master', 'Change'], 'author': ['Jordan Woyak', 'Change'], 'scheduler': ['dev', 'Scheduler'], 'builderid': [17, 'Builder'], 'buildnumber': [2168, 'Build'], 'project': ['', 'Build'], 'basedir': ['/Users/administrator/buildbot-worker/osx_m1', 'Worker'], 'warnings-count': [5, 'WarningCountingShellCommand'], 'branch': ['master', 'Build'], 'builddir': ['/Users/administrator/buildbot-worker/osx_m1/fifoci-mtl-osx-m1', 'Worker'], 'repository': ['', 'Build'], 'owners': [['Jordan Woyak'], 'Build'], 'revision': ['dd2b94cd4a71fb6abda72f58d27434ef2d8b0a5f', 'Build'], 'codebase': ['', 'Build'], 'got_revision': ['ca98b3030bcc70e67158ce899a5589aa2ec65d32', 'GitNoBranch']}, 'buildrequest': {'buildrequestid': 157870, 'buildsetid': 36141, 'builderid': 17, 'priority': 0, 'claimed': True, 'claimed_at': 1770160124, 'claimed_by_masterid': 1, 'complete': False, 'results': -1, 'submitted_at': 1770159056, 'complete_at': None, 'waited_for': False, 'properties': None}, 'buildset': {'bsid': 36141, 'external_idstring': None, 'reason': "The AnyBranchScheduler scheduler named 'dev' triggered this build", 'submitted_at': 1770159056, 'complete': False, 'complete_at': None, 'results': -1, 'parent_buildid': None, 'parent_relationship': None, 'rebuilt_buildid': None, 'sourcestamps': [{'ssid': 10815, 'branch': 'master', 'revision': 'dd2b94cd4a71fb6abda72f58d27434ef2d8b0a5f', 'project': '', 'repository': '', 'codebase': '', 'created_at': 1770159056, 'patch': None}]}, 'parentbuild': None, 'parentbuilder': None, 'builder': {'builderid': 17, 'name': 'fifoci-mtl-osx-m1', 'masterids': [1], 'description': None, 'description_format': None, 'description_html': None, 'projectid': None, 'tags': []}, 'url': 'https://dolphin.ci/#/builders/17/builds/2168'}, 'type': 'raw_bb_hook'}
2026-02-03T23:08:44.615332 {'source': 'webserver', 'raw': {'buildid': 157749, 'number': 2168, 'builderid': 17, 'buildrequestid': 157870, 'workerid': 8, 'masterid': 1, 'started_at': 1770160124, 'complete_at': None, 'locks_duration_s': 0, 'complete': False, 'state_string': 'starting', 'results': None, 'properties': {'shortrev': ['2512-272', 'Change'], 'description': ['Merge pull request #13594 from jordan-woyak/state-cleanups\n\nState: Simplify interthread communication and general cleanups.', 'Change'], 'buildername': ['fifoci-mtl-osx-m1', 'Builder'], 'workername': ['osx-m1', 'Worker'], 'branchname': ['master', 'Change'], 'author': ['Jordan Woyak', 'Change'], 'scheduler': ['dev', 'Scheduler'], 'builderid': [17, 'Builder'], 'buildnumber': [2168, 'Build'], 'project': ['', 'Build'], 'branch': ['master', 'Build'], 'repository': ['', 'Build'], 'revision': ['dd2b94cd4a71fb6abda72f58d27434ef2d8b0a5f', 'Build'], 'codebase': ['', 'Build']}, 'buildrequest': {'buildrequestid': 157870, 'buildsetid': 36141, 'builderid': 17, 'priority': 0, 'claimed': True, 'claimed_at': 1770160124, 'claimed_by_masterid': 1, 'complete': False, 'results': -1, 'submitted_at': 1770159056, 'complete_at': None, 'waited_for': False, 'properties': None}, 'buildset': {'bsid': 36141, 'external_idstring': None, 'reason': "The AnyBranchScheduler scheduler named 'dev' triggered this build", 'submitted_at': 1770159056, 'complete': False, 'complete_at': None, 'results': -1, 'parent_buildid': None, 'parent_relationship': None, 'rebuilt_buildid': None, 'sourcestamps': [{'ssid': 10815, 'branch': 'master', 'revision': 'dd2b94cd4a71fb6abda72f58d27434ef2d8b0a5f', 'project': '', 'repository': '', 'codebase': '', 'created_at': 1770159056, 'patch': None}]}, 'parentbuild': None, 'parentbuilder': None, 'builder': {'builderid': 17, 'name': 'fifoci-mtl-osx-m1', 'masterids': [1], 'description': None, 'description_format': None, 'description_html': None, 'projectid': None, 'tags': []}, 'url': 'https://dolphin.ci/#/builders/17/builds/2168'}, 'type': 'raw_bb_hook'}
Recent 'raw_gh_hook' events
2026-02-03T23:32:40.513253 {'source': 'webserver', 'gh_type': 'pull_request', 'raw': {'action': 'opened', 'number': 14318, 'pull_request': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/14318', 'id': 3243403084, 'node_id': 'PR_kwDOALCn2M7BUmdM', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/14318', 'diff_url': 'https://github.com/dolphin-emu/dolphin/pull/14318.diff', 'patch_url': 'https://github.com/dolphin-emu/dolphin/pull/14318.patch', 'issue_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14318', 'number': 14318, 'state': 'open', 'locked': False, 'title': 'Core: Make RunOnCPUThread always non-blocking.', 'user': {'login': 'jordan-woyak', 'id': 1768214, 'node_id': 'MDQ6VXNlcjE3NjgyMTQ=', 'avatar_url': 'https://avatars.githubusercontent.com/u/1768214?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/jordan-woyak', 'html_url': 'https://github.com/jordan-woyak', 'followers_url': 'https://api.github.com/users/jordan-woyak/followers', 'following_url': 'https://api.github.com/users/jordan-woyak/following{/other_user}', 'gists_url': 'https://api.github.com/users/jordan-woyak/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/jordan-woyak/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/jordan-woyak/subscriptions', 'organizations_url': 'https://api.github.com/users/jordan-woyak/orgs', 'repos_url': 'https://api.github.com/users/jordan-woyak/repos', 'events_url': 'https://api.github.com/users/jordan-woyak/events{/privacy}', 'received_events_url': 'https://api.github.com/users/jordan-woyak/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'body': 'Followup to #14127.\r\n\r\nAll `RunOnCPUThread` calls are already non-blocking.\r\nThis PR removes the `wait_for_completion` parameter.\r\n\r\nThe existing `CPUThreadGuard` is a cleaner way to run code on the CPU thread in a blocking manner.', 'created_at': '2026-02-03T23:32:37Z', 'updated_at': '2026-02-03T23:32:37Z', 'closed_at': None, 'merged_at': None, 'merge_commit_sha': None, 'assignee': None, 'assignees': [], 'requested_reviewers': [], 'requested_teams': [], 'labels': [], 'milestone': None, 'draft': False, 'commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/14318/commits', 'review_comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/14318/comments', 'review_comment_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments{/number}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14318/comments', 'statuses_url': 'https://api.github.com/repos/dolphin-emu/dolphin/statuses/201aa659066b41a2cfe89b615213bcb093fcbe79', 'head': {'label': 'jordan-woyak:RunOnCPUThread-always-non-blocking', 'ref': 'RunOnCPUThread-always-non-blocking', 'sha': '201aa659066b41a2cfe89b615213bcb093fcbe79', 'user': {'login': 'jordan-woyak', 'id': 1768214, 'node_id': 'MDQ6VXNlcjE3NjgyMTQ=', 'avatar_url': 'https://avatars.githubusercontent.com/u/1768214?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/jordan-woyak', 'html_url': 'https://github.com/jordan-woyak', 'followers_url': 'https://api.github.com/users/jordan-woyak/followers', 'following_url': 'https://api.github.com/users/jordan-woyak/following{/other_user}', 'gists_url': 'https://api.github.com/users/jordan-woyak/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/jordan-woyak/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/jordan-woyak/subscriptions', 'organizations_url': 'https://api.github.com/users/jordan-woyak/orgs', 'repos_url': 'https://api.github.com/users/jordan-woyak/repos', 'events_url': 'https://api.github.com/users/jordan-woyak/events{/privacy}', 'received_events_url': 'https://api.github.com/users/jordan-woyak/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'repo': {'id': 16818408, 'node_id': 'MDEwOlJlcG9zaXRvcnkxNjgxODQwOA==', 'name': 'dolphin', 'full_name': 'jordan-woyak/dolphin', 'private': False, 'owner': {'login': 'jordan-woyak', 'id': 1768214, 'node_id': 'MDQ6VXNlcjE3NjgyMTQ=', 'avatar_url': 'https://avatars.githubusercontent.com/u/1768214?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/jordan-woyak', 'html_url': 'https://github.com/jordan-woyak', 'followers_url': 'https://api.github.com/users/jordan-woyak/followers', 'following_url': 'https://api.github.com/users/jordan-woyak/following{/other_user}', 'gists_url': 'https://api.github.com/users/jordan-woyak/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/jordan-woyak/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/jordan-woyak/subscriptions', 'organizations_url': 'https://api.github.com/users/jordan-woyak/orgs', 'repos_url': 'https://api.github.com/users/jordan-woyak/repos', 'events_url': 'https://api.github.com/users/jordan-woyak/events{/privacy}', 'received_events_url': 'https://api.github.com/users/jordan-woyak/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'html_url': 'https://github.com/jordan-woyak/dolphin', 'description': 'Dolphin is a GameCube/Wii emulator, allowing you to play games for these two platforms on PC, with improvements.', 'fork': True, 'url': 'https://api.github.com/repos/jordan-woyak/dolphin', 'forks_url': 'https://api.github.com/repos/jordan-woyak/dolphin/forks', 'keys_url': 'https://api.github.com/repos/jordan-woyak/dolphin/keys{/key_id}', 'collaborators_url': 'https://api.github.com/repos/jordan-woyak/dolphin/collaborators{/collaborator}', 'teams_url': 'https://api.github.com/repos/jordan-woyak/dolphin/teams', 'hooks_url': 'https://api.github.com/repos/jordan-woyak/dolphin/hooks', 'issue_events_url': 'https://api.github.com/repos/jordan-woyak/dolphin/issues/events{/number}', 'events_url': 'https://api.github.com/repos/jordan-woyak/dolphin/events', 'assignees_url': 'https://api.github.com/repos/jordan-woyak/dolphin/assignees{/user}', 'branches_url': 'https://api.github.com/repos/jordan-woyak/dolphin/branches{/branch}', 'tags_url': 'https://api.github.com/repos/jordan-woyak/dolphin/tags', 'blobs_url': 'https://api.github.com/repos/jordan-woyak/dolphin/git/blobs{/sha}', 'git_tags_url': 'https://api.github.com/repos/jordan-woyak/dolphin/git/tags{/sha}', 'git_refs_url': 'https://api.github.com/repos/jordan-woyak/dolphin/git/refs{/sha}', 'trees_url': 'https://api.github.com/repos/jordan-woyak/dolphin/git/trees{/sha}', 'statuses_url': 'https://api.github.com/repos/jordan-woyak/dolphin/statuses/{sha}', 'languages_url': 'https://api.github.com/repos/jordan-woyak/dolphin/languages', 'stargazers_url': 'https://api.github.com/repos/jordan-woyak/dolphin/stargazers', 'contributors_url': 'https://api.github.com/repos/jordan-woyak/dolphin/contributors', 'subscribers_url': 'https://api.github.com/repos/jordan-woyak/dolphin/subscribers', 'subscription_url': 'https://api.github.com/repos/jordan-woyak/dolphin/subscription', 'commits_url': 'https://api.github.com/repos/jordan-woyak/dolphin/commits{/sha}', 'git_commits_url': 'https://api.github.com/repos/jordan-woyak/dolphin/git/commits{/sha}', 'comments_url': 'https://api.github.com/repos/jordan-woyak/dolphin/comments{/number}', 'issue_comment_url': 'https://api.github.com/repos/jordan-woyak/dolphin/issues/comments{/number}', 'contents_url': 'https://api.github.com/repos/jordan-woyak/dolphin/contents/{+path}', 'compare_url': 'https://api.github.com/repos/jordan-woyak/dolphin/compare/{base}...{head}', 'merges_url': 'https://api.github.com/repos/jordan-woyak/dolphin/merges', 'archive_url': 'https://api.github.com/repos/jordan-woyak/dolphin/{archive_format}{/ref}', 'downloads_url': 'https://api.github.com/repos/jordan-woyak/dolphin/downloads', 'issues_url': 'https://api.github.com/repos/jordan-woyak/dolphin/issues{/number}', 'pulls_url': 'https://api.github.com/repos/jordan-woyak/dolphin/pulls{/number}', 'milestones_url': 'https://api.github.com/repos/jordan-woyak/dolphin/milestones{/number}', 'notifications_url': 'https://api.github.com/repos/jordan-woyak/dolphin/notifications{?since,all,participating}', 'labels_url': 'https://api.github.com/repos/jordan-woyak/dolphin/labels{/name}', 'releases_url': 'https://api.github.com/repos/jordan-woyak/dolphin/releases{/id}', 'deployments_url': 'https://api.github.com/repos/jordan-woyak/dolphin/deployments', 'created_at': '2014-02-13T21:50:38Z', 'updated_at': '2026-01-31T21:37:22Z', 'pushed_at': '2026-02-03T23:30:37Z', 'git_url': 'git://github.com/jordan-woyak/dolphin.git', 'ssh_url': 'git@github.com:jordan-woyak/dolphin.git', 'clone_url': 'https://github.com/jordan-woyak/dolphin.git', 'svn_url': 'https://github.com/jordan-woyak/dolphin', 'homepage': None, 'size': 510087, 'stargazers_count': 4, 'watchers_count': 4, 'language': 'C++', 'has_issues': False, 'has_projects': True, 'has_downloads': True, 'has_wiki': False, 'has_pages': False, 'has_discussions': False, 'forks_count': 5, 'mirror_url': None, 'archived': False, 'disabled': False, 'open_issues_count': 2, 'license': {'key': 'other', 'name': 'Other', 'spdx_id': 'NOASSERTION', 'url': None, 'node_id': 'MDc6TGljZW5zZTA='}, 'allow_forking': True, 'is_template': False, 'web_commit_signoff_required': False, 'topics': [], 'visibility': 'public', 'forks': 5, 'open_issues': 2, 'watchers': 4, 'default_branch': 'master', 'allow_squash_merge': True, 'allow_merge_commit': True, 'allow_rebase_merge': True, 'allow_auto_merge': False, 'delete_branch_on_merge': True, 'allow_update_branch': False, 'use_squash_pr_title_as_default': False, 'squash_merge_commit_message': 'COMMIT_MESSAGES', 'squash_merge_commit_title': 'COMMIT_OR_PR_TITLE', 'merge_commit_message': 'PR_TITLE', 'merge_commit_title': 'MERGE_MESSAGE'}}, 'base': {'label': 'dolphin-emu:master', 'ref': 'master', 'sha': 'dd2b94cd4a71fb6abda72f58d27434ef2d8b0a5f', 'user': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/dolphin-emu', 'html_url': 'https://github.com/dolphin-emu', 'followers_url': 'https://api.github.com/users/dolphin-emu/followers', 'following_url': 'https://api.github.com/users/dolphin-emu/following{/other_user}', 'gists_url': 'https://api.github.com/users/dolphin-emu/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/dolphin-emu/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/dolphin-emu/subscriptions', 'organizations_url': 'https://api.github.com/users/dolphin-emu/orgs', 'repos_url': 'https://api.github.com/users/dolphin-emu/repos', 'events_url': 'https://api.github.com/users/dolphin-emu/events{/privacy}', 'received_events_url': 'https://api.github.com/users/dolphin-emu/received_events', 'type': 'Organization', 'user_view_type': 'public', 'site_admin': False}, 'repo': {'id': 11577304, 'node_id': 'MDEwOlJlcG9zaXRvcnkxMTU3NzMwNA==', 'name': 'dolphin', 'full_name': 'dolphin-emu/dolphin', 'private': False, 'owner': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/dolphin-emu', 'html_url': 'https://github.com/dolphin-emu', 'followers_url': 'https://api.github.com/users/dolphin-emu/followers', 'following_url': 'https://api.github.com/users/dolphin-emu/following{/other_user}', 'gists_url': 'https://api.github.com/users/dolphin-emu/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/dolphin-emu/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/dolphin-emu/subscriptions', 'organizations_url': 'https://api.github.com/users/dolphin-emu/orgs', 'repos_url': 'https://api.github.com/users/dolphin-emu/repos', 'events_url': 'https://api.github.com/users/dolphin-emu/events{/privacy}', 'received_events_url': 'https://api.github.com/users/dolphin-emu/received_events', 'type': 'Organization', 'user_view_type': 'public', 'site_admin': False}, 'html_url': 'https://github.com/dolphin-emu/dolphin', 'description': 'Dolphin is a GameCube / Wii emulator, allowing you to play games for these two platforms on PC with improvements.', 'fork': False, 'url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'forks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/forks', 'keys_url': 'https://api.github.com/repos/dolphin-emu/dolphin/keys{/key_id}', 'collaborators_url': 'https://api.github.com/repos/dolphin-emu/dolphin/collaborators{/collaborator}', 'teams_url': 'https://api.github.com/repos/dolphin-emu/dolphin/teams', 'hooks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/hooks', 'issue_events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/events{/number}', 'events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/events', 'assignees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/assignees{/user}', 'branches_url': 'https://api.github.com/repos/dolphin-emu/dolphin/branches{/branch}', 'tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/tags', 'blobs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/blobs{/sha}', 'git_tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/tags{/sha}', 'git_refs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/refs{/sha}', 'trees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/trees{/sha}', 'statuses_url': 'https://api.github.com/repos/dolphin-emu/dolphin/statuses/{sha}', 'languages_url': 'https://api.github.com/repos/dolphin-emu/dolphin/languages', 'stargazers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/stargazers', 'contributors_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contributors', 'subscribers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscribers', 'subscription_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscription', 'commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/commits{/sha}', 'git_commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/commits{/sha}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/comments{/number}', 'issue_comment_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments{/number}', 'contents_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contents/{+path}', 'compare_url': 'https://api.github.com/repos/dolphin-emu/dolphin/compare/{base}...{head}', 'merges_url': 'https://api.github.com/repos/dolphin-emu/dolphin/merges', 'archive_url': 'https://api.github.com/repos/dolphin-emu/dolphin/{archive_format}{/ref}', 'downloads_url': 'https://api.github.com/repos/dolphin-emu/dolphin/downloads', 'issues_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues{/number}', 'pulls_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls{/number}', 'milestones_url': 'https://api.github.com/repos/dolphin-emu/dolphin/milestones{/number}', 'notifications_url': 'https://api.github.com/repos/dolphin-emu/dolphin/notifications{?since,all,participating}', 'labels_url': 'https://api.github.com/repos/dolphin-emu/dolphin/labels{/name}', 'releases_url': 'https://api.github.com/repos/dolphin-emu/dolphin/releases{/id}', 'deployments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/deployments', 'created_at': '2013-07-22T09:07:57Z', 'updated_at': '2026-02-03T22:50:57Z', 'pushed_at': '2026-02-03T22:50:52Z', 'git_url': 'git://github.com/dolphin-emu/dolphin.git', 'ssh_url': 'git@github.com:dolphin-emu/dolphin.git', 'clone_url': 'https://github.com/dolphin-emu/dolphin.git', 'svn_url': 'https://github.com/dolphin-emu/dolphin', 'homepage': 'https://dolphin-emu.org', 'size': 544014, 'stargazers_count': 14582, 'watchers_count': 14582, 'language': 'C++', 'has_issues': False, 'has_projects': False, 'has_downloads': True, 'has_wiki': True, 'has_pages': False, 'has_discussions': False, 'forks_count': 2966, 'mirror_url': None, 'archived': False, 'disabled': False, 'open_issues_count': 360, 'license': {'key': 'other', 'name': 'Other', 'spdx_id': 'NOASSERTION', 'url': None, 'node_id': 'MDc6TGljZW5zZTA='}, 'allow_forking': True, 'is_template': False, 'web_commit_signoff_required': False, 'topics': ['cpp', 'emulation', 'emulator', 'gamecube', 'wii'], 'visibility': 'public', 'forks': 2966, 'open_issues': 360, 'watchers': 14582, 'default_branch': 'master', 'allow_squash_merge': False, 'allow_merge_commit': True, 'allow_rebase_merge': False, 'allow_auto_merge': False, 'delete_branch_on_merge': False, 'allow_update_branch': False, 'use_squash_pr_title_as_default': False, 'squash_merge_commit_message': 'COMMIT_MESSAGES', 'squash_merge_commit_title': 'COMMIT_OR_PR_TITLE', 'merge_commit_message': 'PR_TITLE', 'merge_commit_title': 'MERGE_MESSAGE'}}, '_links': {'self': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/14318'}, 'html': {'href': 'https://github.com/dolphin-emu/dolphin/pull/14318'}, 'issue': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14318'}, 'comments': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14318/comments'}, 'review_comments': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/14318/comments'}, 'review_comment': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments{/number}'}, 'commits': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/14318/commits'}, 'statuses': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/statuses/201aa659066b41a2cfe89b615213bcb093fcbe79'}}, 'author_association': 'MEMBER', 'auto_merge': None, 'active_lock_reason': None, 'merged': False, 'mergeable': None, 'rebaseable': None, 'mergeable_state': 'unknown', 'merged_by': None, 'comments': 0, 'review_comments': 0, 'maintainer_can_modify': True, 'commits': 1, 'additions': 102, 'deletions': 149, 'changed_files': 4}, 'repository': {'id': 11577304, 'node_id': 'MDEwOlJlcG9zaXRvcnkxMTU3NzMwNA==', 'name': 'dolphin', 'full_name': 'dolphin-emu/dolphin', 'private': False, 'owner': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/dolphin-emu', 'html_url': 'https://github.com/dolphin-emu', 'followers_url': 'https://api.github.com/users/dolphin-emu/followers', 'following_url': 'https://api.github.com/users/dolphin-emu/following{/other_user}', 'gists_url': 'https://api.github.com/users/dolphin-emu/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/dolphin-emu/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/dolphin-emu/subscriptions', 'organizations_url': 'https://api.github.com/users/dolphin-emu/orgs', 'repos_url': 'https://api.github.com/users/dolphin-emu/repos', 'events_url': 'https://api.github.com/users/dolphin-emu/events{/privacy}', 'received_events_url': 'https://api.github.com/users/dolphin-emu/received_events', 'type': 'Organization', 'user_view_type': 'public', 'site_admin': False}, 'html_url': 'https://github.com/dolphin-emu/dolphin', 'description': 'Dolphin is a GameCube / Wii emulator, allowing you to play games for these two platforms on PC with improvements.', 'fork': False, 'url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'forks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/forks', 'keys_url': 'https://api.github.com/repos/dolphin-emu/dolphin/keys{/key_id}', 'collaborators_url': 'https://api.github.com/repos/dolphin-emu/dolphin/collaborators{/collaborator}', 'teams_url': 'https://api.github.com/repos/dolphin-emu/dolphin/teams', 'hooks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/hooks', 'issue_events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/events{/number}', 'events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/events', 'assignees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/assignees{/user}', 'branches_url': 'https://api.github.com/repos/dolphin-emu/dolphin/branches{/branch}', 'tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/tags', 'blobs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/blobs{/sha}', 'git_tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/tags{/sha}', 'git_refs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/refs{/sha}', 'trees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/trees{/sha}', 'statuses_url': 'https://api.github.com/repos/dolphin-emu/dolphin/statuses/{sha}', 'languages_url': 'https://api.github.com/repos/dolphin-emu/dolphin/languages', 'stargazers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/stargazers', 'contributors_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contributors', 'subscribers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscribers', 'subscription_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscription', 'commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/commits{/sha}', 'git_commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/commits{/sha}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/comments{/number}', 'issue_comment_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments{/number}', 'contents_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contents/{+path}', 'compare_url': 'https://api.github.com/repos/dolphin-emu/dolphin/compare/{base}...{head}', 'merges_url': 'https://api.github.com/repos/dolphin-emu/dolphin/merges', 'archive_url': 'https://api.github.com/repos/dolphin-emu/dolphin/{archive_format}{/ref}', 'downloads_url': 'https://api.github.com/repos/dolphin-emu/dolphin/downloads', 'issues_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues{/number}', 'pulls_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls{/number}', 'milestones_url': 'https://api.github.com/repos/dolphin-emu/dolphin/milestones{/number}', 'notifications_url': 'https://api.github.com/repos/dolphin-emu/dolphin/notifications{?since,all,participating}', 'labels_url': 'https://api.github.com/repos/dolphin-emu/dolphin/labels{/name}', 'releases_url': 'https://api.github.com/repos/dolphin-emu/dolphin/releases{/id}', 'deployments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/deployments', 'created_at': '2013-07-22T09:07:57Z', 'updated_at': '2026-02-03T22:50:57Z', 'pushed_at': '2026-02-03T22:50:52Z', 'git_url': 'git://github.com/dolphin-emu/dolphin.git', 'ssh_url': 'git@github.com:dolphin-emu/dolphin.git', 'clone_url': 'https://github.com/dolphin-emu/dolphin.git', 'svn_url': 'https://github.com/dolphin-emu/dolphin', 'homepage': 'https://dolphin-emu.org', 'size': 544014, 'stargazers_count': 14582, 'watchers_count': 14582, 'language': 'C++', 'has_issues': False, 'has_projects': False, 'has_downloads': True, 'has_wiki': True, 'has_pages': False, 'has_discussions': False, 'forks_count': 2966, 'mirror_url': None, 'archived': False, 'disabled': False, 'open_issues_count': 360, 'license': {'key': 'other', 'name': 'Other', 'spdx_id': 'NOASSERTION', 'url': None, 'node_id': 'MDc6TGljZW5zZTA='}, 'allow_forking': True, 'is_template': False, 'web_commit_signoff_required': False, 'topics': ['cpp', 'emulation', 'emulator', 'gamecube', 'wii'], 'visibility': 'public', 'forks': 2966, 'open_issues': 360, 'watchers': 14582, 'default_branch': 'master', 'custom_properties': {}}, 'organization': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'url': 'https://api.github.com/orgs/dolphin-emu', 'repos_url': 'https://api.github.com/orgs/dolphin-emu/repos', 'events_url': 'https://api.github.com/orgs/dolphin-emu/events', 'hooks_url': 'https://api.github.com/orgs/dolphin-emu/hooks', 'issues_url': 'https://api.github.com/orgs/dolphin-emu/issues', 'members_url': 'https://api.github.com/orgs/dolphin-emu/members{/member}', 'public_members_url': 'https://api.github.com/orgs/dolphin-emu/public_members{/member}', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'description': ''}, 'sender': {'login': 'jordan-woyak', 'id': 1768214, 'node_id': 'MDQ6VXNlcjE3NjgyMTQ=', 'avatar_url': 'https://avatars.githubusercontent.com/u/1768214?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/jordan-woyak', 'html_url': 'https://github.com/jordan-woyak', 'followers_url': 'https://api.github.com/users/jordan-woyak/followers', 'following_url': 'https://api.github.com/users/jordan-woyak/following{/other_user}', 'gists_url': 'https://api.github.com/users/jordan-woyak/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/jordan-woyak/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/jordan-woyak/subscriptions', 'organizations_url': 'https://api.github.com/users/jordan-woyak/orgs', 'repos_url': 'https://api.github.com/users/jordan-woyak/repos', 'events_url': 'https://api.github.com/users/jordan-woyak/events{/privacy}', 'received_events_url': 'https://api.github.com/users/jordan-woyak/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'installation': {'id': 35437981, 'node_id': 'MDIzOkludGVncmF0aW9uSW5zdGFsbGF0aW9uMzU0Mzc5ODE='}}, 'type': 'raw_gh_hook'}
2026-02-03T22:50:55.572873 {'source': 'webserver', 'gh_type': 'check_suite', 'raw': {'action': 'requested', 'check_suite': {'id': 56318013676, 'node_id': 'CS_kwDOALCn2M8AAAANHNCw7A', 'head_branch': 'master', 'head_sha': 'dd2b94cd4a71fb6abda72f58d27434ef2d8b0a5f', 'status': 'queued', 'conclusion': None, 'url': 'https://api.github.com/repos/dolphin-emu/dolphin/check-suites/56318013676', 'before': 'eac7b290423efea867c2c7f3f2b2792d05418bdd', 'after': 'dd2b94cd4a71fb6abda72f58d27434ef2d8b0a5f', 'pull_requests': [{'url': 'https://api.github.com/repos/MarioPartyNetplay/Dolphin-MPN/pulls/191', 'id': 3229846782, 'number': 191, 'head': {'ref': 'master', 'sha': 'dd2b94cd4a71fb6abda72f58d27434ef2d8b0a5f', 'repo': {'id': 11577304, 'url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'name': 'dolphin'}}, 'base': {'ref': 'master', 'sha': 'bb679f420d6ea68263904009bebf3e8905f5c8e1', 'repo': {'id': 623850328, 'url': 'https://api.github.com/repos/MarioPartyNetplay/Dolphin-MPN', 'name': 'Dolphin-MPN'}}}, {'url': 'https://api.github.com/repos/Faster-Brawl/dolphin/pulls/5', 'id': 1222541325, 'number': 5, 'head': {'ref': 'master', 'sha': 'dd2b94cd4a71fb6abda72f58d27434ef2d8b0a5f', 'repo': {'id': 11577304, 'url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'name': 'dolphin'}}, 'base': {'ref': 'master', 'sha': 'eb9e232680dfdc04ec2adab05dbc530b1e707fc9', 'repo': {'id': 577809188, 'url': 'https://api.github.com/repos/Faster-Brawl/dolphin', 'name': 'dolphin'}}}, {'url': 'https://api.github.com/repos/dirextric-auto/dolphin/pulls/1', 'id': 571486662, 'number': 1, 'head': {'ref': 'master', 'sha': 'dd2b94cd4a71fb6abda72f58d27434ef2d8b0a5f', 'repo': {'id': 11577304, 'url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'name': 'dolphin'}}, 'base': {'ref': 'master', 'sha': '1e71904cb9930eccc036e2ce2360beaa746befa7', 'repo': {'id': 337848960, 'url': 'https://api.github.com/repos/dirextric-auto/dolphin', 'name': 'dolphin'}}}, {'url': 'https://api.github.com/repos/zurgeg/dolphin-vr-no-ovr/pulls/1', 'id': 518053317, 'number': 1, 'head': {'ref': 'master', 'sha': 'dd2b94cd4a71fb6abda72f58d27434ef2d8b0a5f', 'repo': {'id': 11577304, 'url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'name': 'dolphin'}}, 'base': {'ref': 'VR-Hydra', 'sha': 'ea9f95955e5335cc238b2f9c5a0a6dfdc8698aec', 'repo': {'id': 311469400, 'url': 'https://api.github.com/repos/zurgeg/dolphin-vr-no-ovr', 'name': 'dolphin-vr-no-ovr'}}}, {'url': 'https://api.github.com/repos/weihuoya/dolphin/pulls/1', 'id': 400884538, 'number': 1, 'head': {'ref': 'master', 'sha': 'dd2b94cd4a71fb6abda72f58d27434ef2d8b0a5f', 'repo': {'id': 11577304, 'url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'name': 'dolphin'}}, 'base': {'ref': 'master', 'sha': '0f4c971326ae9389b3ad55b0fefacb708d148f4d', 'repo': {'id': 143011855, 'url': 'https://api.github.com/repos/weihuoya/dolphin', 'name': 'dolphin'}}}], 'app': {'id': 49947, 'client_id': 'Iv1.1fcaea7644d8b727', 'slug': 'dolphin-ci', 'node_id': 'MDM6QXBwNDk5NDc=', 'owner': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/dolphin-emu', 'html_url': 'https://github.com/dolphin-emu', 'followers_url': 'https://api.github.com/users/dolphin-emu/followers', 'following_url': 'https://api.github.com/users/dolphin-emu/following{/other_user}', 'gists_url': 'https://api.github.com/users/dolphin-emu/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/dolphin-emu/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/dolphin-emu/subscriptions', 'organizations_url': 'https://api.github.com/users/dolphin-emu/orgs', 'repos_url': 'https://api.github.com/users/dolphin-emu/repos', 'events_url': 'https://api.github.com/users/dolphin-emu/events{/privacy}', 'received_events_url': 'https://api.github.com/users/dolphin-emu/received_events', 'type': 'Organization', 'user_view_type': 'public', 'site_admin': False}, 'name': 'Dolphin CI', 'description': 'Continuous Integration setup for [dolphin-emu.org](https://dolphin-emu.org/).', 'external_url': 'https://github.com/dolphin-emu', 'html_url': 'https://github.com/apps/dolphin-ci', 'created_at': '2019-12-26T22:26:07Z', 'updated_at': '2019-12-26T22:33:19Z', 'permissions': {'checks': 'write', 'contents': 'read', 'issues': 'write', 'members': 'read', 'metadata': 'read', 'pull_requests': 'write', 'statuses': 'write'}, 'events': ['check_run', 'commit_comment', 'issue_comment', 'pull_request', 'pull_request_review', 'pull_request_review_comment', 'push']}, 'created_at': '2026-02-03T22:50:54Z', 'updated_at': '2026-02-03T22:50:54Z', 'rerequestable': True, 'runs_rerequestable': True, 'latest_check_runs_count': 0, 'check_runs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/check-suites/56318013676/check-runs', 'head_commit': {'id': 'dd2b94cd4a71fb6abda72f58d27434ef2d8b0a5f', 'tree_id': '5355c7878fc942b9b9ed79f2b9ca09923c5a8557', 'message': 'Merge pull request #13594 from jordan-woyak/state-cleanups\n\nState: Simplify interthread communication and general cleanups.', 'timestamp': '2026-02-03T22:50:52Z', 'author': {'name': 'Jordan Woyak', 'email': 'jordan.woyak@gmail.com'}, 'committer': {'name': 'GitHub', 'email': 'noreply@github.com'}}}, 'repository': {'id': 11577304, 'node_id': 'MDEwOlJlcG9zaXRvcnkxMTU3NzMwNA==', 'name': 'dolphin', 'full_name': 'dolphin-emu/dolphin', 'private': False, 'owner': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/dolphin-emu', 'html_url': 'https://github.com/dolphin-emu', 'followers_url': 'https://api.github.com/users/dolphin-emu/followers', 'following_url': 'https://api.github.com/users/dolphin-emu/following{/other_user}', 'gists_url': 'https://api.github.com/users/dolphin-emu/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/dolphin-emu/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/dolphin-emu/subscriptions', 'organizations_url': 'https://api.github.com/users/dolphin-emu/orgs', 'repos_url': 'https://api.github.com/users/dolphin-emu/repos', 'events_url': 'https://api.github.com/users/dolphin-emu/events{/privacy}', 'received_events_url': 'https://api.github.com/users/dolphin-emu/received_events', 'type': 'Organization', 'user_view_type': 'public', 'site_admin': False}, 'html_url': 'https://github.com/dolphin-emu/dolphin', 'description': 'Dolphin is a GameCube / Wii emulator, allowing you to play games for these two platforms on PC with improvements.', 'fork': False, 'url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'forks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/forks', 'keys_url': 'https://api.github.com/repos/dolphin-emu/dolphin/keys{/key_id}', 'collaborators_url': 'https://api.github.com/repos/dolphin-emu/dolphin/collaborators{/collaborator}', 'teams_url': 'https://api.github.com/repos/dolphin-emu/dolphin/teams', 'hooks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/hooks', 'issue_events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/events{/number}', 'events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/events', 'assignees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/assignees{/user}', 'branches_url': 'https://api.github.com/repos/dolphin-emu/dolphin/branches{/branch}', 'tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/tags', 'blobs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/blobs{/sha}', 'git_tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/tags{/sha}', 'git_refs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/refs{/sha}', 'trees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/trees{/sha}', 'statuses_url': 'https://api.github.com/repos/dolphin-emu/dolphin/statuses/{sha}', 'languages_url': 'https://api.github.com/repos/dolphin-emu/dolphin/languages', 'stargazers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/stargazers', 'contributors_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contributors', 'subscribers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscribers', 'subscription_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscription', 'commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/commits{/sha}', 'git_commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/commits{/sha}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/comments{/number}', 'issue_comment_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments{/number}', 'contents_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contents/{+path}', 'compare_url': 'https://api.github.com/repos/dolphin-emu/dolphin/compare/{base}...{head}', 'merges_url': 'https://api.github.com/repos/dolphin-emu/dolphin/merges', 'archive_url': 'https://api.github.com/repos/dolphin-emu/dolphin/{archive_format}{/ref}', 'downloads_url': 'https://api.github.com/repos/dolphin-emu/dolphin/downloads', 'issues_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues{/number}', 'pulls_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls{/number}', 'milestones_url': 'https://api.github.com/repos/dolphin-emu/dolphin/milestones{/number}', 'notifications_url': 'https://api.github.com/repos/dolphin-emu/dolphin/notifications{?since,all,participating}', 'labels_url': 'https://api.github.com/repos/dolphin-emu/dolphin/labels{/name}', 'releases_url': 'https://api.github.com/repos/dolphin-emu/dolphin/releases{/id}', 'deployments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/deployments', 'created_at': '2013-07-22T09:07:57Z', 'updated_at': '2026-02-03T18:03:13Z', 'pushed_at': '2026-02-03T22:50:52Z', 'git_url': 'git://github.com/dolphin-emu/dolphin.git', 'ssh_url': 'git@github.com:dolphin-emu/dolphin.git', 'clone_url': 'https://github.com/dolphin-emu/dolphin.git', 'svn_url': 'https://github.com/dolphin-emu/dolphin', 'homepage': 'https://dolphin-emu.org', 'size': 544014, 'stargazers_count': 14582, 'watchers_count': 14582, 'language': 'C++', 'has_issues': False, 'has_projects': False, 'has_downloads': True, 'has_wiki': True, 'has_pages': False, 'has_discussions': False, 'forks_count': 2966, 'mirror_url': None, 'archived': False, 'disabled': False, 'open_issues_count': 359, 'license': {'key': 'other', 'name': 'Other', 'spdx_id': 'NOASSERTION', 'url': None, 'node_id': 'MDc6TGljZW5zZTA='}, 'allow_forking': True, 'is_template': False, 'web_commit_signoff_required': False, 'topics': ['cpp', 'emulation', 'emulator', 'gamecube', 'wii'], 'visibility': 'public', 'forks': 2966, 'open_issues': 359, 'watchers': 14582, 'default_branch': 'master', 'custom_properties': {}}, 'organization': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'url': 'https://api.github.com/orgs/dolphin-emu', 'repos_url': 'https://api.github.com/orgs/dolphin-emu/repos', 'events_url': 'https://api.github.com/orgs/dolphin-emu/events', 'hooks_url': 'https://api.github.com/orgs/dolphin-emu/hooks', 'issues_url': 'https://api.github.com/orgs/dolphin-emu/issues', 'members_url': 'https://api.github.com/orgs/dolphin-emu/members{/member}', 'public_members_url': 'https://api.github.com/orgs/dolphin-emu/public_members{/member}', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'description': ''}, 'sender': {'login': 'jordan-woyak', 'id': 1768214, 'node_id': 'MDQ6VXNlcjE3NjgyMTQ=', 'avatar_url': 'https://avatars.githubusercontent.com/u/1768214?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/jordan-woyak', 'html_url': 'https://github.com/jordan-woyak', 'followers_url': 'https://api.github.com/users/jordan-woyak/followers', 'following_url': 'https://api.github.com/users/jordan-woyak/following{/other_user}', 'gists_url': 'https://api.github.com/users/jordan-woyak/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/jordan-woyak/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/jordan-woyak/subscriptions', 'organizations_url': 'https://api.github.com/users/jordan-woyak/orgs', 'repos_url': 'https://api.github.com/users/jordan-woyak/repos', 'events_url': 'https://api.github.com/users/jordan-woyak/events{/privacy}', 'received_events_url': 'https://api.github.com/users/jordan-woyak/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'installation': {'id': 35437981, 'node_id': 'MDIzOkludGVncmF0aW9uSW5zdGFsbGF0aW9uMzU0Mzc5ODE='}}, 'type': 'raw_gh_hook'}
2026-02-03T22:50:54.523623 {'source': 'webserver', 'gh_type': 'push', 'raw': {'ref': 'refs/heads/master', 'before': 'eac7b290423efea867c2c7f3f2b2792d05418bdd', 'after': 'dd2b94cd4a71fb6abda72f58d27434ef2d8b0a5f', 'repository': {'id': 11577304, 'node_id': 'MDEwOlJlcG9zaXRvcnkxMTU3NzMwNA==', 'name': 'dolphin', 'full_name': 'dolphin-emu/dolphin', 'private': False, 'owner': {'name': 'dolphin-emu', 'email': None, 'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/dolphin-emu', 'html_url': 'https://github.com/dolphin-emu', 'followers_url': 'https://api.github.com/users/dolphin-emu/followers', 'following_url': 'https://api.github.com/users/dolphin-emu/following{/other_user}', 'gists_url': 'https://api.github.com/users/dolphin-emu/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/dolphin-emu/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/dolphin-emu/subscriptions', 'organizations_url': 'https://api.github.com/users/dolphin-emu/orgs', 'repos_url': 'https://api.github.com/users/dolphin-emu/repos', 'events_url': 'https://api.github.com/users/dolphin-emu/events{/privacy}', 'received_events_url': 'https://api.github.com/users/dolphin-emu/received_events', 'type': 'Organization', 'user_view_type': 'public', 'site_admin': False}, 'html_url': 'https://github.com/dolphin-emu/dolphin', 'description': 'Dolphin is a GameCube / Wii emulator, allowing you to play games for these two platforms on PC with improvements.', 'fork': False, 'url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'forks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/forks', 'keys_url': 'https://api.github.com/repos/dolphin-emu/dolphin/keys{/key_id}', 'collaborators_url': 'https://api.github.com/repos/dolphin-emu/dolphin/collaborators{/collaborator}', 'teams_url': 'https://api.github.com/repos/dolphin-emu/dolphin/teams', 'hooks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/hooks', 'issue_events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/events{/number}', 'events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/events', 'assignees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/assignees{/user}', 'branches_url': 'https://api.github.com/repos/dolphin-emu/dolphin/branches{/branch}', 'tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/tags', 'blobs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/blobs{/sha}', 'git_tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/tags{/sha}', 'git_refs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/refs{/sha}', 'trees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/trees{/sha}', 'statuses_url': 'https://api.github.com/repos/dolphin-emu/dolphin/statuses/{sha}', 'languages_url': 'https://api.github.com/repos/dolphin-emu/dolphin/languages', 'stargazers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/stargazers', 'contributors_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contributors', 'subscribers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscribers', 'subscription_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscription', 'commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/commits{/sha}', 'git_commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/commits{/sha}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/comments{/number}', 'issue_comment_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments{/number}', 'contents_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contents/{+path}', 'compare_url': 'https://api.github.com/repos/dolphin-emu/dolphin/compare/{base}...{head}', 'merges_url': 'https://api.github.com/repos/dolphin-emu/dolphin/merges', 'archive_url': 'https://api.github.com/repos/dolphin-emu/dolphin/{archive_format}{/ref}', 'downloads_url': 'https://api.github.com/repos/dolphin-emu/dolphin/downloads', 'issues_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues{/number}', 'pulls_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls{/number}', 'milestones_url': 'https://api.github.com/repos/dolphin-emu/dolphin/milestones{/number}', 'notifications_url': 'https://api.github.com/repos/dolphin-emu/dolphin/notifications{?since,all,participating}', 'labels_url': 'https://api.github.com/repos/dolphin-emu/dolphin/labels{/name}', 'releases_url': 'https://api.github.com/repos/dolphin-emu/dolphin/releases{/id}', 'deployments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/deployments', 'created_at': 1374484077, 'updated_at': '2026-02-03T18:03:13Z', 'pushed_at': 1770159052, 'git_url': 'git://github.com/dolphin-emu/dolphin.git', 'ssh_url': 'git@github.com:dolphin-emu/dolphin.git', 'clone_url': 'https://github.com/dolphin-emu/dolphin.git', 'svn_url': 'https://github.com/dolphin-emu/dolphin', 'homepage': 'https://dolphin-emu.org', 'size': 544014, 'stargazers_count': 14582, 'watchers_count': 14582, 'language': 'C++', 'has_issues': False, 'has_projects': False, 'has_downloads': True, 'has_wiki': True, 'has_pages': False, 'has_discussions': False, 'forks_count': 2966, 'mirror_url': None, 'archived': False, 'disabled': False, 'open_issues_count': 359, 'license': {'key': 'other', 'name': 'Other', 'spdx_id': 'NOASSERTION', 'url': None, 'node_id': 'MDc6TGljZW5zZTA='}, 'allow_forking': True, 'is_template': False, 'web_commit_signoff_required': False, 'topics': ['cpp', 'emulation', 'emulator', 'gamecube', 'wii'], 'visibility': 'public', 'forks': 2966, 'open_issues': 359, 'watchers': 14582, 'default_branch': 'master', 'stargazers': 14582, 'master_branch': 'master', 'organization': 'dolphin-emu', 'custom_properties': {}}, 'pusher': {'name': 'jordan-woyak', 'email': 'jordan.woyak@gmail.com'}, 'forced': False, 'organization': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'url': 'https://api.github.com/orgs/dolphin-emu', 'repos_url': 'https://api.github.com/orgs/dolphin-emu/repos', 'events_url': 'https://api.github.com/orgs/dolphin-emu/events', 'hooks_url': 'https://api.github.com/orgs/dolphin-emu/hooks', 'issues_url': 'https://api.github.com/orgs/dolphin-emu/issues', 'members_url': 'https://api.github.com/orgs/dolphin-emu/members{/member}', 'public_members_url': 'https://api.github.com/orgs/dolphin-emu/public_members{/member}', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'description': ''}, 'sender': {'login': 'jordan-woyak', 'id': 1768214, 'node_id': 'MDQ6VXNlcjE3NjgyMTQ=', 'avatar_url': 'https://avatars.githubusercontent.com/u/1768214?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/jordan-woyak', 'html_url': 'https://github.com/jordan-woyak', 'followers_url': 'https://api.github.com/users/jordan-woyak/followers', 'following_url': 'https://api.github.com/users/jordan-woyak/following{/other_user}', 'gists_url': 'https://api.github.com/users/jordan-woyak/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/jordan-woyak/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/jordan-woyak/subscriptions', 'organizations_url': 'https://api.github.com/users/jordan-woyak/orgs', 'repos_url': 'https://api.github.com/users/jordan-woyak/repos', 'events_url': 'https://api.github.com/users/jordan-woyak/events{/privacy}', 'received_events_url': 'https://api.github.com/users/jordan-woyak/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'installation': {'id': 35437981, 'node_id': 'MDIzOkludGVncmF0aW9uSW5zdGFsbGF0aW9uMzU0Mzc5ODE='}, 'created': False, 'deleted': False, 'base_ref': None, 'compare': 'https://github.com/dolphin-emu/dolphin/compare/eac7b290423e...dd2b94cd4a71', 'commits': [{'id': '0ba32f7add97daf1c5ea52cf77b75e81d862676f', 'tree_id': '88a4225db26349f7b326a0b597725cc2f6ab4299', 'distinct': False, 'message': 'Android: Detect when native code should flush unsaved data', 'timestamp': '2026-01-19T21:56:09-06:00', 'url': 'https://github.com/dolphin-emu/dolphin/commit/0ba32f7add97daf1c5ea52cf77b75e81d862676f', 'author': {'name': 'JosJuice', 'email': 'josjuice@gmail.com', 'date': '2025-04-26T10:33:01+02:00', 'username': 'JosJuice'}, 'committer': {'name': 'Jordan Woyak', 'email': 'jordan.woyak@gmail.com', 'date': '2026-01-19T21:56:09-06:00', 'username': 'jordan-woyak'}, 'added': [], 'removed': [], 'modified': ['Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/ActivityTracker.kt', 'Source/Android/jni/ActivityTracker.cpp', 'Source/Core/UICommon/UICommon.cpp', 'Source/Core/UICommon/UICommon.h']}, {'id': 'b0652925faa54b9bfd5a17cfed8870a2b1f2759b', 'tree_id': '20229c7e3b6735332e4f0938d442c76c720b186c', 'distinct': False, 'message': 'Core: Add a HookableEvent for UICommon::FlushUnsavedData.', 'timestamp': '2026-01-19T21:56:09-06:00', 'url': 'https://github.com/dolphin-emu/dolphin/commit/b0652925faa54b9bfd5a17cfed8870a2b1f2759b', 'author': {'name': 'Jordan Woyak', 'email': 'jordan.woyak@gmail.com', 'date': '2025-04-26T17:18:56-05:00', 'username': 'jordan-woyak'}, 'committer': {'name': 'Jordan Woyak', 'email': 'jordan.woyak@gmail.com', 'date': '2026-01-19T21:56:09-06:00', 'username': 'jordan-woyak'}, 'added': [], 'removed': [], 'modified': ['Source/Core/UICommon/UICommon.cpp', 'Source/Core/UICommon/UICommon.h']}, {'id': '1d9e475123346c0e83289e788bdfad9f3345b512', 'tree_id': 'b81f11362e50f3c1379a757dc655556bdc4749c8', 'distinct': False, 'message': 'Common: Add TransferableSharedMutex class and unit tests.', 'timestamp': '2026-01-19T21:56:59-06:00', 'url': 'https://github.com/dolphin-emu/dolphin/commit/1d9e475123346c0e83289e788bdfad9f3345b512', 'author': {'name': 'Jordan Woyak', 'email': 'jordan.woyak@gmail.com', 'date': '2025-10-19T20:31:22-05:00', 'username': 'jordan-woyak'}, 'committer': {'name': 'Jordan Woyak', 'email': 'jordan.woyak@gmail.com', 'date': '2026-01-19T21:56:59-06:00', 'username': 'jordan-woyak'}, 'added': ['Source/Core/Common/TransferableSharedMutex.h'], 'removed': [], 'modified': ['Source/Core/Common/CMakeLists.txt', 'Source/Core/DolphinLib.props', 'Source/UnitTests/Common/MutexTest.cpp']}, {'id': '2322437f96bc53f1edcd9100daedb5b54c6e0fc4', 'tree_id': '256962a703953a1eb54f978c4bc0fe3e41054806', 'distinct': False, 'message': 'State: Simplify interthread communication and cleanups. Save/Load calls are now always non-blocking for the caller, but appropriately block the CPU thread as needed.', 'timestamp': '2026-01-19T21:56:59-06:00', 'url': 'https://github.com/dolphin-emu/dolphin/commit/2322437f96bc53f1edcd9100daedb5b54c6e0fc4', 'author': {'name': 'Jordan Woyak', 'email': 'jordan.woyak@gmail.com', 'date': '2025-11-02T22:21:15-06:00', 'username': 'jordan-woyak'}, 'committer': {'name': 'Jordan Woyak', 'email': 'jordan.woyak@gmail.com', 'date': '2026-01-19T21:56:59-06:00', 'username': 'jordan-woyak'}, 'added': [], 'removed': [], 'modified': ['Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/NativeLibrary.kt', 'Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.kt', 'Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/fragments/EmulationFragment.kt', 'Source/Android/jni/MainAndroid.cpp', 'Source/Core/Core/State.cpp', 'Source/Core/Core/State.h']}, {'id': 'dd2b94cd4a71fb6abda72f58d27434ef2d8b0a5f', 'tree_id': '5355c7878fc942b9b9ed79f2b9ca09923c5a8557', 'distinct': True, 'message': 'Merge pull request #13594 from jordan-woyak/state-cleanups\n\nState: Simplify interthread communication and general cleanups.', 'timestamp': '2026-02-03T16:50:52-06:00', 'url': 'https://github.com/dolphin-emu/dolphin/commit/dd2b94cd4a71fb6abda72f58d27434ef2d8b0a5f', 'author': {'name': 'Jordan Woyak', 'email': 'jordan.woyak@gmail.com', 'date': '2026-02-03T16:50:52-06:00', 'username': 'jordan-woyak'}, 'committer': {'name': 'GitHub', 'email': 'noreply@github.com', 'date': '2026-02-03T16:50:52-06:00', 'username': 'web-flow'}, 'added': ['Source/Core/Common/TransferableSharedMutex.h'], 'removed': [], 'modified': ['Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/NativeLibrary.kt', 'Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.kt', 'Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/fragments/EmulationFragment.kt', 'Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/ActivityTracker.kt', 'Source/Android/jni/ActivityTracker.cpp', 'Source/Android/jni/MainAndroid.cpp', 'Source/Core/Common/CMakeLists.txt', 'Source/Core/Core/State.cpp', 'Source/Core/Core/State.h', 'Source/Core/DolphinLib.props', 'Source/Core/UICommon/UICommon.cpp', 'Source/Core/UICommon/UICommon.h', 'Source/UnitTests/Common/MutexTest.cpp']}], 'head_commit': {'id': 'dd2b94cd4a71fb6abda72f58d27434ef2d8b0a5f', 'tree_id': '5355c7878fc942b9b9ed79f2b9ca09923c5a8557', 'distinct': True, 'message': 'Merge pull request #13594 from jordan-woyak/state-cleanups\n\nState: Simplify interthread communication and general cleanups.', 'timestamp': '2026-02-03T16:50:52-06:00', 'url': 'https://github.com/dolphin-emu/dolphin/commit/dd2b94cd4a71fb6abda72f58d27434ef2d8b0a5f', 'author': {'name': 'Jordan Woyak', 'email': 'jordan.woyak@gmail.com', 'date': '2026-02-03T16:50:52-06:00', 'username': 'jordan-woyak'}, 'committer': {'name': 'GitHub', 'email': 'noreply@github.com', 'date': '2026-02-03T16:50:52-06:00', 'username': 'web-flow'}, 'added': ['Source/Core/Common/TransferableSharedMutex.h'], 'removed': [], 'modified': ['Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/NativeLibrary.kt', 'Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.kt', 'Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/fragments/EmulationFragment.kt', 'Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/ActivityTracker.kt', 'Source/Android/jni/ActivityTracker.cpp', 'Source/Android/jni/MainAndroid.cpp', 'Source/Core/Common/CMakeLists.txt', 'Source/Core/Core/State.cpp', 'Source/Core/Core/State.h', 'Source/Core/DolphinLib.props', 'Source/Core/UICommon/UICommon.cpp', 'Source/Core/UICommon/UICommon.h', 'Source/UnitTests/Common/MutexTest.cpp']}}, 'type': 'raw_gh_hook'}
2026-02-03T22:50:54.189879 {'source': 'webserver', 'gh_type': 'pull_request', 'raw': {'action': 'closed', 'number': 13594, 'pull_request': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13594', 'id': 2482865278, 'node_id': 'PR_kwDOALCn2M6T_YR-', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/13594', 'diff_url': 'https://github.com/dolphin-emu/dolphin/pull/13594.diff', 'patch_url': 'https://github.com/dolphin-emu/dolphin/pull/13594.patch', 'issue_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13594', 'number': 13594, 'state': 'closed', 'locked': False, 'title': 'State: Simplify interthread communication and general cleanups.', 'user': {'login': 'jordan-woyak', 'id': 1768214, 'node_id': 'MDQ6VXNlcjE3NjgyMTQ=', 'avatar_url': 'https://avatars.githubusercontent.com/u/1768214?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/jordan-woyak', 'html_url': 'https://github.com/jordan-woyak', 'followers_url': 'https://api.github.com/users/jordan-woyak/followers', 'following_url': 'https://api.github.com/users/jordan-woyak/following{/other_user}', 'gists_url': 'https://api.github.com/users/jordan-woyak/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/jordan-woyak/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/jordan-woyak/subscriptions', 'organizations_url': 'https://api.github.com/users/jordan-woyak/orgs', 'repos_url': 'https://api.github.com/users/jordan-woyak/repos', 'events_url': 'https://api.github.com/users/jordan-woyak/events{/privacy}', 'received_events_url': 'https://api.github.com/users/jordan-woyak/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'body': '### Simplified interthread communication\r\nThere was a lot of hard-to-validate interthread communication going on in State.cpp.\r\nIt\'s now less complicated.\r\ne.g. there were four `std::mutex`, now there\'s one mutex.\r\n\r\n### Made saves not block the UI\r\nThere\'s no longer a `wait` parameter on Save functions.\r\nThe calls are now always non-blocking for the UI thread.\r\nWaiting on completion of writes will happen automatically when attempting to read them later.\r\n\r\n### Fixed write-followed-by-read race\r\nFixed the race when saving and immediately trying to use the written data.\r\nSave followed by Load will now make Core wait.\r\nIf the UI tries to read states (for timestamps), it block appropriately. (it doesn\'t block until you open the state menu)\r\n\r\nBecause the tasks generally start on the UI thread, move to the Core thread, and finally a worker thread, I\'ve created a `TransferableSharedMutex` class to track the completion of these tasks.\r\nI\'ve added unit tests for the new class.\r\nA normal `std::shared_mutex` can\'t be locked on one thread and unlocked on another.\r\n\r\n### Made saves marginally faster\r\nThe explicit "Measure" step of `PointerWrap` is now skipped by just doing "Write" on a buffer sized 110% of the last state.\r\n`PointerWrap` conveniently switches to "Measure" mode on buffer overrun.\r\nOn would-be overrun that becomes the measurement for a secondary real "Write".\r\nThat saves like ~0.3ms per state save here. :)\r\n\r\n### Wait for state save flush on Android process termination\r\nThanks to Jos for providing the means to connect this to the android side of things.', 'created_at': '2025-04-26T04:59:35Z', 'updated_at': '2026-02-03T22:50:52Z', 'closed_at': '2026-02-03T22:50:52Z', 'merged_at': '2026-02-03T22:50:52Z', 'merge_commit_sha': 'dd2b94cd4a71fb6abda72f58d27434ef2d8b0a5f', 'assignee': None, 'assignees': [], 'requested_reviewers': [], 'requested_teams': [], 'labels': [], 'milestone': None, 'draft': False, 'commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13594/commits', 'review_comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13594/comments', 'review_comment_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments{/number}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13594/comments', 'statuses_url': 'https://api.github.com/repos/dolphin-emu/dolphin/statuses/2322437f96bc53f1edcd9100daedb5b54c6e0fc4', 'head': {'label': 'jordan-woyak:state-cleanups', 'ref': 'state-cleanups', 'sha': '2322437f96bc53f1edcd9100daedb5b54c6e0fc4', 'user': {'login': 'jordan-woyak', 'id': 1768214, 'node_id': 'MDQ6VXNlcjE3NjgyMTQ=', 'avatar_url': 'https://avatars.githubusercontent.com/u/1768214?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/jordan-woyak', 'html_url': 'https://github.com/jordan-woyak', 'followers_url': 'https://api.github.com/users/jordan-woyak/followers', 'following_url': 'https://api.github.com/users/jordan-woyak/following{/other_user}', 'gists_url': 'https://api.github.com/users/jordan-woyak/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/jordan-woyak/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/jordan-woyak/subscriptions', 'organizations_url': 'https://api.github.com/users/jordan-woyak/orgs', 'repos_url': 'https://api.github.com/users/jordan-woyak/repos', 'events_url': 'https://api.github.com/users/jordan-woyak/events{/privacy}', 'received_events_url': 'https://api.github.com/users/jordan-woyak/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'repo': {'id': 16818408, 'node_id': 'MDEwOlJlcG9zaXRvcnkxNjgxODQwOA==', 'name': 'dolphin', 'full_name': 'jordan-woyak/dolphin', 'private': False, 'owner': {'login': 'jordan-woyak', 'id': 1768214, 'node_id': 'MDQ6VXNlcjE3NjgyMTQ=', 'avatar_url': 'https://avatars.githubusercontent.com/u/1768214?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/jordan-woyak', 'html_url': 'https://github.com/jordan-woyak', 'followers_url': 'https://api.github.com/users/jordan-woyak/followers', 'following_url': 'https://api.github.com/users/jordan-woyak/following{/other_user}', 'gists_url': 'https://api.github.com/users/jordan-woyak/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/jordan-woyak/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/jordan-woyak/subscriptions', 'organizations_url': 'https://api.github.com/users/jordan-woyak/orgs', 'repos_url': 'https://api.github.com/users/jordan-woyak/repos', 'events_url': 'https://api.github.com/users/jordan-woyak/events{/privacy}', 'received_events_url': 'https://api.github.com/users/jordan-woyak/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'html_url': 'https://github.com/jordan-woyak/dolphin', 'description': 'Dolphin is a GameCube/Wii emulator, allowing you to play games for these two platforms on PC, with improvements.', 'fork': True, 'url': 'https://api.github.com/repos/jordan-woyak/dolphin', 'forks_url': 'https://api.github.com/repos/jordan-woyak/dolphin/forks', 'keys_url': 'https://api.github.com/repos/jordan-woyak/dolphin/keys{/key_id}', 'collaborators_url': 'https://api.github.com/repos/jordan-woyak/dolphin/collaborators{/collaborator}', 'teams_url': 'https://api.github.com/repos/jordan-woyak/dolphin/teams', 'hooks_url': 'https://api.github.com/repos/jordan-woyak/dolphin/hooks', 'issue_events_url': 'https://api.github.com/repos/jordan-woyak/dolphin/issues/events{/number}', 'events_url': 'https://api.github.com/repos/jordan-woyak/dolphin/events', 'assignees_url': 'https://api.github.com/repos/jordan-woyak/dolphin/assignees{/user}', 'branches_url': 'https://api.github.com/repos/jordan-woyak/dolphin/branches{/branch}', 'tags_url': 'https://api.github.com/repos/jordan-woyak/dolphin/tags', 'blobs_url': 'https://api.github.com/repos/jordan-woyak/dolphin/git/blobs{/sha}', 'git_tags_url': 'https://api.github.com/repos/jordan-woyak/dolphin/git/tags{/sha}', 'git_refs_url': 'https://api.github.com/repos/jordan-woyak/dolphin/git/refs{/sha}', 'trees_url': 'https://api.github.com/repos/jordan-woyak/dolphin/git/trees{/sha}', 'statuses_url': 'https://api.github.com/repos/jordan-woyak/dolphin/statuses/{sha}', 'languages_url': 'https://api.github.com/repos/jordan-woyak/dolphin/languages', 'stargazers_url': 'https://api.github.com/repos/jordan-woyak/dolphin/stargazers', 'contributors_url': 'https://api.github.com/repos/jordan-woyak/dolphin/contributors', 'subscribers_url': 'https://api.github.com/repos/jordan-woyak/dolphin/subscribers', 'subscription_url': 'https://api.github.com/repos/jordan-woyak/dolphin/subscription', 'commits_url': 'https://api.github.com/repos/jordan-woyak/dolphin/commits{/sha}', 'git_commits_url': 'https://api.github.com/repos/jordan-woyak/dolphin/git/commits{/sha}', 'comments_url': 'https://api.github.com/repos/jordan-woyak/dolphin/comments{/number}', 'issue_comment_url': 'https://api.github.com/repos/jordan-woyak/dolphin/issues/comments{/number}', 'contents_url': 'https://api.github.com/repos/jordan-woyak/dolphin/contents/{+path}', 'compare_url': 'https://api.github.com/repos/jordan-woyak/dolphin/compare/{base}...{head}', 'merges_url': 'https://api.github.com/repos/jordan-woyak/dolphin/merges', 'archive_url': 'https://api.github.com/repos/jordan-woyak/dolphin/{archive_format}{/ref}', 'downloads_url': 'https://api.github.com/repos/jordan-woyak/dolphin/downloads', 'issues_url': 'https://api.github.com/repos/jordan-woyak/dolphin/issues{/number}', 'pulls_url': 'https://api.github.com/repos/jordan-woyak/dolphin/pulls{/number}', 'milestones_url': 'https://api.github.com/repos/jordan-woyak/dolphin/milestones{/number}', 'notifications_url': 'https://api.github.com/repos/jordan-woyak/dolphin/notifications{?since,all,participating}', 'labels_url': 'https://api.github.com/repos/jordan-woyak/dolphin/labels{/name}', 'releases_url': 'https://api.github.com/repos/jordan-woyak/dolphin/releases{/id}', 'deployments_url': 'https://api.github.com/repos/jordan-woyak/dolphin/deployments', 'created_at': '2014-02-13T21:50:38Z', 'updated_at': '2026-01-31T21:37:22Z', 'pushed_at': '2026-02-01T21:57:35Z', 'git_url': 'git://github.com/jordan-woyak/dolphin.git', 'ssh_url': 'git@github.com:jordan-woyak/dolphin.git', 'clone_url': 'https://github.com/jordan-woyak/dolphin.git', 'svn_url': 'https://github.com/jordan-woyak/dolphin', 'homepage': None, 'size': 510087, 'stargazers_count': 4, 'watchers_count': 4, 'language': 'C++', 'has_issues': False, 'has_projects': True, 'has_downloads': True, 'has_wiki': False, 'has_pages': False, 'has_discussions': False, 'forks_count': 5, 'mirror_url': None, 'archived': False, 'disabled': False, 'open_issues_count': 2, 'license': {'key': 'other', 'name': 'Other', 'spdx_id': 'NOASSERTION', 'url': None, 'node_id': 'MDc6TGljZW5zZTA='}, 'allow_forking': True, 'is_template': False, 'web_commit_signoff_required': False, 'topics': [], 'visibility': 'public', 'forks': 5, 'open_issues': 2, 'watchers': 4, 'default_branch': 'master', 'allow_squash_merge': True, 'allow_merge_commit': True, 'allow_rebase_merge': True, 'allow_auto_merge': False, 'delete_branch_on_merge': True, 'allow_update_branch': False, 'use_squash_pr_title_as_default': False, 'squash_merge_commit_message': 'COMMIT_MESSAGES', 'squash_merge_commit_title': 'COMMIT_OR_PR_TITLE', 'merge_commit_message': 'PR_TITLE', 'merge_commit_title': 'MERGE_MESSAGE'}}, 'base': {'label': 'dolphin-emu:master', 'ref': 'master', 'sha': '2a771937cf0a4a45cd466d3eea089a083f73f3c0', 'user': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/dolphin-emu', 'html_url': 'https://github.com/dolphin-emu', 'followers_url': 'https://api.github.com/users/dolphin-emu/followers', 'following_url': 'https://api.github.com/users/dolphin-emu/following{/other_user}', 'gists_url': 'https://api.github.com/users/dolphin-emu/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/dolphin-emu/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/dolphin-emu/subscriptions', 'organizations_url': 'https://api.github.com/users/dolphin-emu/orgs', 'repos_url': 'https://api.github.com/users/dolphin-emu/repos', 'events_url': 'https://api.github.com/users/dolphin-emu/events{/privacy}', 'received_events_url': 'https://api.github.com/users/dolphin-emu/received_events', 'type': 'Organization', 'user_view_type': 'public', 'site_admin': False}, 'repo': {'id': 11577304, 'node_id': 'MDEwOlJlcG9zaXRvcnkxMTU3NzMwNA==', 'name': 'dolphin', 'full_name': 'dolphin-emu/dolphin', 'private': False, 'owner': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/dolphin-emu', 'html_url': 'https://github.com/dolphin-emu', 'followers_url': 'https://api.github.com/users/dolphin-emu/followers', 'following_url': 'https://api.github.com/users/dolphin-emu/following{/other_user}', 'gists_url': 'https://api.github.com/users/dolphin-emu/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/dolphin-emu/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/dolphin-emu/subscriptions', 'organizations_url': 'https://api.github.com/users/dolphin-emu/orgs', 'repos_url': 'https://api.github.com/users/dolphin-emu/repos', 'events_url': 'https://api.github.com/users/dolphin-emu/events{/privacy}', 'received_events_url': 'https://api.github.com/users/dolphin-emu/received_events', 'type': 'Organization', 'user_view_type': 'public', 'site_admin': False}, 'html_url': 'https://github.com/dolphin-emu/dolphin', 'description': 'Dolphin is a GameCube / Wii emulator, allowing you to play games for these two platforms on PC with improvements.', 'fork': False, 'url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'forks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/forks', 'keys_url': 'https://api.github.com/repos/dolphin-emu/dolphin/keys{/key_id}', 'collaborators_url': 'https://api.github.com/repos/dolphin-emu/dolphin/collaborators{/collaborator}', 'teams_url': 'https://api.github.com/repos/dolphin-emu/dolphin/teams', 'hooks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/hooks', 'issue_events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/events{/number}', 'events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/events', 'assignees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/assignees{/user}', 'branches_url': 'https://api.github.com/repos/dolphin-emu/dolphin/branches{/branch}', 'tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/tags', 'blobs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/blobs{/sha}', 'git_tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/tags{/sha}', 'git_refs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/refs{/sha}', 'trees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/trees{/sha}', 'statuses_url': 'https://api.github.com/repos/dolphin-emu/dolphin/statuses/{sha}', 'languages_url': 'https://api.github.com/repos/dolphin-emu/dolphin/languages', 'stargazers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/stargazers', 'contributors_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contributors', 'subscribers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscribers', 'subscription_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscription', 'commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/commits{/sha}', 'git_commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/commits{/sha}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/comments{/number}', 'issue_comment_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments{/number}', 'contents_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contents/{+path}', 'compare_url': 'https://api.github.com/repos/dolphin-emu/dolphin/compare/{base}...{head}', 'merges_url': 'https://api.github.com/repos/dolphin-emu/dolphin/merges', 'archive_url': 'https://api.github.com/repos/dolphin-emu/dolphin/{archive_format}{/ref}', 'downloads_url': 'https://api.github.com/repos/dolphin-emu/dolphin/downloads', 'issues_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues{/number}', 'pulls_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls{/number}', 'milestones_url': 'https://api.github.com/repos/dolphin-emu/dolphin/milestones{/number}', 'notifications_url': 'https://api.github.com/repos/dolphin-emu/dolphin/notifications{?since,all,participating}', 'labels_url': 'https://api.github.com/repos/dolphin-emu/dolphin/labels{/name}', 'releases_url': 'https://api.github.com/repos/dolphin-emu/dolphin/releases{/id}', 'deployments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/deployments', 'created_at': '2013-07-22T09:07:57Z', 'updated_at': '2026-02-03T18:03:13Z', 'pushed_at': '2026-02-03T04:26:13Z', 'git_url': 'git://github.com/dolphin-emu/dolphin.git', 'ssh_url': 'git@github.com:dolphin-emu/dolphin.git', 'clone_url': 'https://github.com/dolphin-emu/dolphin.git', 'svn_url': 'https://github.com/dolphin-emu/dolphin', 'homepage': 'https://dolphin-emu.org', 'size': 544014, 'stargazers_count': 14582, 'watchers_count': 14582, 'language': 'C++', 'has_issues': False, 'has_projects': False, 'has_downloads': True, 'has_wiki': True, 'has_pages': False, 'has_discussions': False, 'forks_count': 2966, 'mirror_url': None, 'archived': False, 'disabled': False, 'open_issues_count': 359, 'license': {'key': 'other', 'name': 'Other', 'spdx_id': 'NOASSERTION', 'url': None, 'node_id': 'MDc6TGljZW5zZTA='}, 'allow_forking': True, 'is_template': False, 'web_commit_signoff_required': False, 'topics': ['cpp', 'emulation', 'emulator', 'gamecube', 'wii'], 'visibility': 'public', 'forks': 2966, 'open_issues': 359, 'watchers': 14582, 'default_branch': 'master', 'allow_squash_merge': False, 'allow_merge_commit': True, 'allow_rebase_merge': False, 'allow_auto_merge': False, 'delete_branch_on_merge': False, 'allow_update_branch': False, 'use_squash_pr_title_as_default': False, 'squash_merge_commit_message': 'COMMIT_MESSAGES', 'squash_merge_commit_title': 'COMMIT_OR_PR_TITLE', 'merge_commit_message': 'PR_TITLE', 'merge_commit_title': 'MERGE_MESSAGE'}}, '_links': {'self': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13594'}, 'html': {'href': 'https://github.com/dolphin-emu/dolphin/pull/13594'}, 'issue': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13594'}, 'comments': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13594/comments'}, 'review_comments': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13594/comments'}, 'review_comment': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments{/number}'}, 'commits': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13594/commits'}, 'statuses': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/statuses/2322437f96bc53f1edcd9100daedb5b54c6e0fc4'}}, 'author_association': 'MEMBER', 'auto_merge': None, 'active_lock_reason': None, 'merged': True, 'mergeable': None, 'rebaseable': None, 'mergeable_state': 'unknown', 'merged_by': {'login': 'jordan-woyak', 'id': 1768214, 'node_id': 'MDQ6VXNlcjE3NjgyMTQ=', 'avatar_url': 'https://avatars.githubusercontent.com/u/1768214?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/jordan-woyak', 'html_url': 'https://github.com/jordan-woyak', 'followers_url': 'https://api.github.com/users/jordan-woyak/followers', 'following_url': 'https://api.github.com/users/jordan-woyak/following{/other_user}', 'gists_url': 'https://api.github.com/users/jordan-woyak/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/jordan-woyak/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/jordan-woyak/subscriptions', 'organizations_url': 'https://api.github.com/users/jordan-woyak/orgs', 'repos_url': 'https://api.github.com/users/jordan-woyak/repos', 'events_url': 'https://api.github.com/users/jordan-woyak/events{/privacy}', 'received_events_url': 'https://api.github.com/users/jordan-woyak/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'comments': 0, 'review_comments': 41, 'maintainer_can_modify': False, 'commits': 4, 'additions': 582, 'deletions': 375, 'changed_files': 14}, 'repository': {'id': 11577304, 'node_id': 'MDEwOlJlcG9zaXRvcnkxMTU3NzMwNA==', 'name': 'dolphin', 'full_name': 'dolphin-emu/dolphin', 'private': False, 'owner': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/dolphin-emu', 'html_url': 'https://github.com/dolphin-emu', 'followers_url': 'https://api.github.com/users/dolphin-emu/followers', 'following_url': 'https://api.github.com/users/dolphin-emu/following{/other_user}', 'gists_url': 'https://api.github.com/users/dolphin-emu/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/dolphin-emu/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/dolphin-emu/subscriptions', 'organizations_url': 'https://api.github.com/users/dolphin-emu/orgs', 'repos_url': 'https://api.github.com/users/dolphin-emu/repos', 'events_url': 'https://api.github.com/users/dolphin-emu/events{/privacy}', 'received_events_url': 'https://api.github.com/users/dolphin-emu/received_events', 'type': 'Organization', 'user_view_type': 'public', 'site_admin': False}, 'html_url': 'https://github.com/dolphin-emu/dolphin', 'description': 'Dolphin is a GameCube / Wii emulator, allowing you to play games for these two platforms on PC with improvements.', 'fork': False, 'url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'forks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/forks', 'keys_url': 'https://api.github.com/repos/dolphin-emu/dolphin/keys{/key_id}', 'collaborators_url': 'https://api.github.com/repos/dolphin-emu/dolphin/collaborators{/collaborator}', 'teams_url': 'https://api.github.com/repos/dolphin-emu/dolphin/teams', 'hooks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/hooks', 'issue_events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/events{/number}', 'events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/events', 'assignees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/assignees{/user}', 'branches_url': 'https://api.github.com/repos/dolphin-emu/dolphin/branches{/branch}', 'tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/tags', 'blobs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/blobs{/sha}', 'git_tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/tags{/sha}', 'git_refs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/refs{/sha}', 'trees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/trees{/sha}', 'statuses_url': 'https://api.github.com/repos/dolphin-emu/dolphin/statuses/{sha}', 'languages_url': 'https://api.github.com/repos/dolphin-emu/dolphin/languages', 'stargazers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/stargazers', 'contributors_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contributors', 'subscribers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscribers', 'subscription_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscription', 'commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/commits{/sha}', 'git_commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/commits{/sha}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/comments{/number}', 'issue_comment_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments{/number}', 'contents_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contents/{+path}', 'compare_url': 'https://api.github.com/repos/dolphin-emu/dolphin/compare/{base}...{head}', 'merges_url': 'https://api.github.com/repos/dolphin-emu/dolphin/merges', 'archive_url': 'https://api.github.com/repos/dolphin-emu/dolphin/{archive_format}{/ref}', 'downloads_url': 'https://api.github.com/repos/dolphin-emu/dolphin/downloads', 'issues_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues{/number}', 'pulls_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls{/number}', 'milestones_url': 'https://api.github.com/repos/dolphin-emu/dolphin/milestones{/number}', 'notifications_url': 'https://api.github.com/repos/dolphin-emu/dolphin/notifications{?since,all,participating}', 'labels_url': 'https://api.github.com/repos/dolphin-emu/dolphin/labels{/name}', 'releases_url': 'https://api.github.com/repos/dolphin-emu/dolphin/releases{/id}', 'deployments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/deployments', 'created_at': '2013-07-22T09:07:57Z', 'updated_at': '2026-02-03T18:03:13Z', 'pushed_at': '2026-02-03T04:26:13Z', 'git_url': 'git://github.com/dolphin-emu/dolphin.git', 'ssh_url': 'git@github.com:dolphin-emu/dolphin.git', 'clone_url': 'https://github.com/dolphin-emu/dolphin.git', 'svn_url': 'https://github.com/dolphin-emu/dolphin', 'homepage': 'https://dolphin-emu.org', 'size': 544014, 'stargazers_count': 14582, 'watchers_count': 14582, 'language': 'C++', 'has_issues': False, 'has_projects': False, 'has_downloads': True, 'has_wiki': True, 'has_pages': False, 'has_discussions': False, 'forks_count': 2966, 'mirror_url': None, 'archived': False, 'disabled': False, 'open_issues_count': 359, 'license': {'key': 'other', 'name': 'Other', 'spdx_id': 'NOASSERTION', 'url': None, 'node_id': 'MDc6TGljZW5zZTA='}, 'allow_forking': True, 'is_template': False, 'web_commit_signoff_required': False, 'topics': ['cpp', 'emulation', 'emulator', 'gamecube', 'wii'], 'visibility': 'public', 'forks': 2966, 'open_issues': 359, 'watchers': 14582, 'default_branch': 'master', 'custom_properties': {}}, 'organization': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'url': 'https://api.github.com/orgs/dolphin-emu', 'repos_url': 'https://api.github.com/orgs/dolphin-emu/repos', 'events_url': 'https://api.github.com/orgs/dolphin-emu/events', 'hooks_url': 'https://api.github.com/orgs/dolphin-emu/hooks', 'issues_url': 'https://api.github.com/orgs/dolphin-emu/issues', 'members_url': 'https://api.github.com/orgs/dolphin-emu/members{/member}', 'public_members_url': 'https://api.github.com/orgs/dolphin-emu/public_members{/member}', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'description': ''}, 'sender': {'login': 'jordan-woyak', 'id': 1768214, 'node_id': 'MDQ6VXNlcjE3NjgyMTQ=', 'avatar_url': 'https://avatars.githubusercontent.com/u/1768214?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/jordan-woyak', 'html_url': 'https://github.com/jordan-woyak', 'followers_url': 'https://api.github.com/users/jordan-woyak/followers', 'following_url': 'https://api.github.com/users/jordan-woyak/following{/other_user}', 'gists_url': 'https://api.github.com/users/jordan-woyak/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/jordan-woyak/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/jordan-woyak/subscriptions', 'organizations_url': 'https://api.github.com/users/jordan-woyak/orgs', 'repos_url': 'https://api.github.com/users/jordan-woyak/repos', 'events_url': 'https://api.github.com/users/jordan-woyak/events{/privacy}', 'received_events_url': 'https://api.github.com/users/jordan-woyak/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'installation': {'id': 35437981, 'node_id': 'MDIzOkludGVncmF0aW9uSW5zdGFsbGF0aW9uMzU0Mzc5ODE='}}, 'type': 'raw_gh_hook'}
2026-02-03T22:48:25.697653 {'source': 'webserver', 'gh_type': 'pull_request_review', 'raw': {'action': 'submitted', 'review': {'id': 3748007210, 'node_id': 'PRR_kwDOALCn2M7fZg0q', 'user': {'login': 'jordan-woyak', 'id': 1768214, 'node_id': 'MDQ6VXNlcjE3NjgyMTQ=', 'avatar_url': 'https://avatars.githubusercontent.com/u/1768214?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/jordan-woyak', 'html_url': 'https://github.com/jordan-woyak', 'followers_url': 'https://api.github.com/users/jordan-woyak/followers', 'following_url': 'https://api.github.com/users/jordan-woyak/following{/other_user}', 'gists_url': 'https://api.github.com/users/jordan-woyak/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/jordan-woyak/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/jordan-woyak/subscriptions', 'organizations_url': 'https://api.github.com/users/jordan-woyak/orgs', 'repos_url': 'https://api.github.com/users/jordan-woyak/repos', 'events_url': 'https://api.github.com/users/jordan-woyak/events{/privacy}', 'received_events_url': 'https://api.github.com/users/jordan-woyak/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'body': "Code LGTM from a C++ standpoint. I can't say anything about the JIT code. Tested.", 'commit_id': '32856fd15531890d65379376c8cadfc3611b36cb', 'submitted_at': '2026-02-03T22:48:22Z', 'state': 'approved', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/13768#pullrequestreview-3748007210', 'pull_request_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13768', '_links': {'html': {'href': 'https://github.com/dolphin-emu/dolphin/pull/13768#pullrequestreview-3748007210'}, 'pull_request': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13768'}}, 'updated_at': '2026-02-03T22:48:22Z', 'author_association': 'MEMBER'}, 'pull_request': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13768', 'id': 2608905795, 'node_id': 'PR_kwDOALCn2M6bgL5D', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/13768', 'diff_url': 'https://github.com/dolphin-emu/dolphin/pull/13768.diff', 'patch_url': 'https://github.com/dolphin-emu/dolphin/pull/13768.patch', 'issue_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13768', 'number': 13768, 'state': 'open', 'locked': False, 'title': 'Core: Create fastmem mappings for page address translation', 'user': {'login': 'JosJuice', 'id': 6716818, 'node_id': 'MDQ6VXNlcjY3MTY4MTg=', 'avatar_url': 'https://avatars.githubusercontent.com/u/6716818?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/JosJuice', 'html_url': 'https://github.com/JosJuice', 'followers_url': 'https://api.github.com/users/JosJuice/followers', 'following_url': 'https://api.github.com/users/JosJuice/following{/other_user}', 'gists_url': 'https://api.github.com/users/JosJuice/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/JosJuice/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/JosJuice/subscriptions', 'organizations_url': 'https://api.github.com/users/JosJuice/orgs', 'repos_url': 'https://api.github.com/users/JosJuice/repos', 'events_url': 'https://api.github.com/users/JosJuice/events{/privacy}', 'received_events_url': 'https://api.github.com/users/JosJuice/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'body': "Previously we've only been setting up fastmem mappings for block address translation, but now we also do it for page address translation. This increases performance when games access memory using page tables, but decreases performance when games set up page tables.\r\n\r\nThe tlbie instruction is used as an indication that the mappings need to be updated.\r\n\r\nThere is the accuracy downside that the TLB is now effectively infinitely large. No games are known to be affected by this, and you still get the old, more accurate behavior if Enable Write-Back Cache is on.\r\n\r\nLeft to do:\r\n\r\n- [x] Support for host page sizes larger than 4K\r\n- [x] Support for host page sizes larger than 4K is untested\r\n- [x] macOS is untested\r\n- [x] Rogue Squadron 3 is super slow due to the pessimistic setting of R and C bits (I guess the game is heavily swapping? DoJit is running very often)\r\n- [x] Unit tests\r\n- [x] Unit tests fail on the Ubuntu and Debian runners", 'created_at': '2025-06-21T09:20:23Z', 'updated_at': '2026-02-03T22:48:22Z', 'closed_at': None, 'merged_at': None, 'merge_commit_sha': '991d76dcffb09f237a458ac540055d13ae1335ad', 'assignee': None, 'assignees': [], 'requested_reviewers': [], 'requested_teams': [], 'labels': [], 'milestone': None, 'draft': False, 'commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13768/commits', 'review_comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13768/comments', 'review_comment_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments{/number}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13768/comments', 'statuses_url': 'https://api.github.com/repos/dolphin-emu/dolphin/statuses/32856fd15531890d65379376c8cadfc3611b36cb', 'head': {'label': 'JosJuice:page-table-fastmem-2', 'ref': 'page-table-fastmem-2', 'sha': '32856fd15531890d65379376c8cadfc3611b36cb', 'user': {'login': 'JosJuice', 'id': 6716818, 'node_id': 'MDQ6VXNlcjY3MTY4MTg=', 'avatar_url': 'https://avatars.githubusercontent.com/u/6716818?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/JosJuice', 'html_url': 'https://github.com/JosJuice', 'followers_url': 'https://api.github.com/users/JosJuice/followers', 'following_url': 'https://api.github.com/users/JosJuice/following{/other_user}', 'gists_url': 'https://api.github.com/users/JosJuice/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/JosJuice/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/JosJuice/subscriptions', 'organizations_url': 'https://api.github.com/users/JosJuice/orgs', 'repos_url': 'https://api.github.com/users/JosJuice/repos', 'events_url': 'https://api.github.com/users/JosJuice/events{/privacy}', 'received_events_url': 'https://api.github.com/users/JosJuice/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'repo': {'id': 26057138, 'node_id': 'MDEwOlJlcG9zaXRvcnkyNjA1NzEzOA==', 'name': 'dolphin', 'full_name': 'JosJuice/dolphin', 'private': False, 'owner': {'login': 'JosJuice', 'id': 6716818, 'node_id': 'MDQ6VXNlcjY3MTY4MTg=', 'avatar_url': 'https://avatars.githubusercontent.com/u/6716818?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/JosJuice', 'html_url': 'https://github.com/JosJuice', 'followers_url': 'https://api.github.com/users/JosJuice/followers', 'following_url': 'https://api.github.com/users/JosJuice/following{/other_user}', 'gists_url': 'https://api.github.com/users/JosJuice/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/JosJuice/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/JosJuice/subscriptions', 'organizations_url': 'https://api.github.com/users/JosJuice/orgs', 'repos_url': 'https://api.github.com/users/JosJuice/repos', 'events_url': 'https://api.github.com/users/JosJuice/events{/privacy}', 'received_events_url': 'https://api.github.com/users/JosJuice/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'html_url': 'https://github.com/JosJuice/dolphin', 'description': 'Dolphin is a GameCube/Wii emulator, allowing you to play games for these two platforms on PC, with improvements.', 'fork': True, 'url': 'https://api.github.com/repos/JosJuice/dolphin', 'forks_url': 'https://api.github.com/repos/JosJuice/dolphin/forks', 'keys_url': 'https://api.github.com/repos/JosJuice/dolphin/keys{/key_id}', 'collaborators_url': 'https://api.github.com/repos/JosJuice/dolphin/collaborators{/collaborator}', 'teams_url': 'https://api.github.com/repos/JosJuice/dolphin/teams', 'hooks_url': 'https://api.github.com/repos/JosJuice/dolphin/hooks', 'issue_events_url': 'https://api.github.com/repos/JosJuice/dolphin/issues/events{/number}', 'events_url': 'https://api.github.com/repos/JosJuice/dolphin/events', 'assignees_url': 'https://api.github.com/repos/JosJuice/dolphin/assignees{/user}', 'branches_url': 'https://api.github.com/repos/JosJuice/dolphin/branches{/branch}', 'tags_url': 'https://api.github.com/repos/JosJuice/dolphin/tags', 'blobs_url': 'https://api.github.com/repos/JosJuice/dolphin/git/blobs{/sha}', 'git_tags_url': 'https://api.github.com/repos/JosJuice/dolphin/git/tags{/sha}', 'git_refs_url': 'https://api.github.com/repos/JosJuice/dolphin/git/refs{/sha}', 'trees_url': 'https://api.github.com/repos/JosJuice/dolphin/git/trees{/sha}', 'statuses_url': 'https://api.github.com/repos/JosJuice/dolphin/statuses/{sha}', 'languages_url': 'https://api.github.com/repos/JosJuice/dolphin/languages', 'stargazers_url': 'https://api.github.com/repos/JosJuice/dolphin/stargazers', 'contributors_url': 'https://api.github.com/repos/JosJuice/dolphin/contributors', 'subscribers_url': 'https://api.github.com/repos/JosJuice/dolphin/subscribers', 'subscription_url': 'https://api.github.com/repos/JosJuice/dolphin/subscription', 'commits_url': 'https://api.github.com/repos/JosJuice/dolphin/commits{/sha}', 'git_commits_url': 'https://api.github.com/repos/JosJuice/dolphin/git/commits{/sha}', 'comments_url': 'https://api.github.com/repos/JosJuice/dolphin/comments{/number}', 'issue_comment_url': 'https://api.github.com/repos/JosJuice/dolphin/issues/comments{/number}', 'contents_url': 'https://api.github.com/repos/JosJuice/dolphin/contents/{+path}', 'compare_url': 'https://api.github.com/repos/JosJuice/dolphin/compare/{base}...{head}', 'merges_url': 'https://api.github.com/repos/JosJuice/dolphin/merges', 'archive_url': 'https://api.github.com/repos/JosJuice/dolphin/{archive_format}{/ref}', 'downloads_url': 'https://api.github.com/repos/JosJuice/dolphin/downloads', 'issues_url': 'https://api.github.com/repos/JosJuice/dolphin/issues{/number}', 'pulls_url': 'https://api.github.com/repos/JosJuice/dolphin/pulls{/number}', 'milestones_url': 'https://api.github.com/repos/JosJuice/dolphin/milestones{/number}', 'notifications_url': 'https://api.github.com/repos/JosJuice/dolphin/notifications{?since,all,participating}', 'labels_url': 'https://api.github.com/repos/JosJuice/dolphin/labels{/name}', 'releases_url': 'https://api.github.com/repos/JosJuice/dolphin/releases{/id}', 'deployments_url': 'https://api.github.com/repos/JosJuice/dolphin/deployments', 'created_at': '2014-11-01T17:12:40Z', 'updated_at': '2026-02-01T11:39:14Z', 'pushed_at': '2026-02-03T22:35:15Z', 'git_url': 'git://github.com/JosJuice/dolphin.git', 'ssh_url': 'git@github.com:JosJuice/dolphin.git', 'clone_url': 'https://github.com/JosJuice/dolphin.git', 'svn_url': 'https://github.com/JosJuice/dolphin', 'homepage': None, 'size': 509732, 'stargazers_count': 6, 'watchers_count': 6, 'language': 'C++', 'has_issues': False, 'has_projects': True, 'has_downloads': True, 'has_wiki': True, 'has_pages': False, 'has_discussions': False, 'forks_count': 1, 'mirror_url': None, 'archived': False, 'disabled': False, 'open_issues_count': 0, 'license': {'key': 'other', 'name': 'Other', 'spdx_id': 'NOASSERTION', 'url': None, 'node_id': 'MDc6TGljZW5zZTA='}, 'allow_forking': True, 'is_template': False, 'web_commit_signoff_required': False, 'topics': [], 'visibility': 'public', 'forks': 1, 'open_issues': 0, 'watchers': 6, 'default_branch': 'master', 'allow_squash_merge': True, 'allow_merge_commit': True, 'allow_rebase_merge': True, 'allow_auto_merge': False, 'delete_branch_on_merge': False, 'allow_update_branch': False, 'use_squash_pr_title_as_default': False, 'squash_merge_commit_message': 'COMMIT_MESSAGES', 'squash_merge_commit_title': 'COMMIT_OR_PR_TITLE', 'merge_commit_message': 'PR_TITLE', 'merge_commit_title': 'MERGE_MESSAGE'}}, 'base': {'label': 'dolphin-emu:master', 'ref': 'master', 'sha': 'eac7b290423efea867c2c7f3f2b2792d05418bdd', 'user': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/dolphin-emu', 'html_url': 'https://github.com/dolphin-emu', 'followers_url': 'https://api.github.com/users/dolphin-emu/followers', 'following_url': 'https://api.github.com/users/dolphin-emu/following{/other_user}', 'gists_url': 'https://api.github.com/users/dolphin-emu/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/dolphin-emu/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/dolphin-emu/subscriptions', 'organizations_url': 'https://api.github.com/users/dolphin-emu/orgs', 'repos_url': 'https://api.github.com/users/dolphin-emu/repos', 'events_url': 'https://api.github.com/users/dolphin-emu/events{/privacy}', 'received_events_url': 'https://api.github.com/users/dolphin-emu/received_events', 'type': 'Organization', 'user_view_type': 'public', 'site_admin': False}, 'repo': {'id': 11577304, 'node_id': 'MDEwOlJlcG9zaXRvcnkxMTU3NzMwNA==', 'name': 'dolphin', 'full_name': 'dolphin-emu/dolphin', 'private': False, 'owner': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/dolphin-emu', 'html_url': 'https://github.com/dolphin-emu', 'followers_url': 'https://api.github.com/users/dolphin-emu/followers', 'following_url': 'https://api.github.com/users/dolphin-emu/following{/other_user}', 'gists_url': 'https://api.github.com/users/dolphin-emu/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/dolphin-emu/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/dolphin-emu/subscriptions', 'organizations_url': 'https://api.github.com/users/dolphin-emu/orgs', 'repos_url': 'https://api.github.com/users/dolphin-emu/repos', 'events_url': 'https://api.github.com/users/dolphin-emu/events{/privacy}', 'received_events_url': 'https://api.github.com/users/dolphin-emu/received_events', 'type': 'Organization', 'user_view_type': 'public', 'site_admin': False}, 'html_url': 'https://github.com/dolphin-emu/dolphin', 'description': 'Dolphin is a GameCube / Wii emulator, allowing you to play games for these two platforms on PC with improvements.', 'fork': False, 'url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'forks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/forks', 'keys_url': 'https://api.github.com/repos/dolphin-emu/dolphin/keys{/key_id}', 'collaborators_url': 'https://api.github.com/repos/dolphin-emu/dolphin/collaborators{/collaborator}', 'teams_url': 'https://api.github.com/repos/dolphin-emu/dolphin/teams', 'hooks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/hooks', 'issue_events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/events{/number}', 'events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/events', 'assignees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/assignees{/user}', 'branches_url': 'https://api.github.com/repos/dolphin-emu/dolphin/branches{/branch}', 'tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/tags', 'blobs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/blobs{/sha}', 'git_tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/tags{/sha}', 'git_refs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/refs{/sha}', 'trees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/trees{/sha}', 'statuses_url': 'https://api.github.com/repos/dolphin-emu/dolphin/statuses/{sha}', 'languages_url': 'https://api.github.com/repos/dolphin-emu/dolphin/languages', 'stargazers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/stargazers', 'contributors_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contributors', 'subscribers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscribers', 'subscription_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscription', 'commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/commits{/sha}', 'git_commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/commits{/sha}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/comments{/number}', 'issue_comment_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments{/number}', 'contents_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contents/{+path}', 'compare_url': 'https://api.github.com/repos/dolphin-emu/dolphin/compare/{base}...{head}', 'merges_url': 'https://api.github.com/repos/dolphin-emu/dolphin/merges', 'archive_url': 'https://api.github.com/repos/dolphin-emu/dolphin/{archive_format}{/ref}', 'downloads_url': 'https://api.github.com/repos/dolphin-emu/dolphin/downloads', 'issues_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues{/number}', 'pulls_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls{/number}', 'milestones_url': 'https://api.github.com/repos/dolphin-emu/dolphin/milestones{/number}', 'notifications_url': 'https://api.github.com/repos/dolphin-emu/dolphin/notifications{?since,all,participating}', 'labels_url': 'https://api.github.com/repos/dolphin-emu/dolphin/labels{/name}', 'releases_url': 'https://api.github.com/repos/dolphin-emu/dolphin/releases{/id}', 'deployments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/deployments', 'created_at': '2013-07-22T09:07:57Z', 'updated_at': '2026-02-03T18:03:13Z', 'pushed_at': '2026-02-03T04:26:13Z', 'git_url': 'git://github.com/dolphin-emu/dolphin.git', 'ssh_url': 'git@github.com:dolphin-emu/dolphin.git', 'clone_url': 'https://github.com/dolphin-emu/dolphin.git', 'svn_url': 'https://github.com/dolphin-emu/dolphin', 'homepage': 'https://dolphin-emu.org', 'size': 544014, 'stargazers_count': 14582, 'watchers_count': 14582, 'language': 'C++', 'has_issues': False, 'has_projects': False, 'has_downloads': True, 'has_wiki': True, 'has_pages': False, 'has_discussions': False, 'forks_count': 2966, 'mirror_url': None, 'archived': False, 'disabled': False, 'open_issues_count': 360, 'license': {'key': 'other', 'name': 'Other', 'spdx_id': 'NOASSERTION', 'url': None, 'node_id': 'MDc6TGljZW5zZTA='}, 'allow_forking': True, 'is_template': False, 'web_commit_signoff_required': False, 'topics': ['cpp', 'emulation', 'emulator', 'gamecube', 'wii'], 'visibility': 'public', 'forks': 2966, 'open_issues': 360, 'watchers': 14582, 'default_branch': 'master', 'allow_squash_merge': False, 'allow_merge_commit': True, 'allow_rebase_merge': False, 'allow_auto_merge': False, 'delete_branch_on_merge': False, 'allow_update_branch': False, 'use_squash_pr_title_as_default': False, 'squash_merge_commit_message': 'COMMIT_MESSAGES', 'squash_merge_commit_title': 'COMMIT_OR_PR_TITLE', 'merge_commit_message': 'PR_TITLE', 'merge_commit_title': 'MERGE_MESSAGE'}}, '_links': {'self': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13768'}, 'html': {'href': 'https://github.com/dolphin-emu/dolphin/pull/13768'}, 'issue': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13768'}, 'comments': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13768/comments'}, 'review_comments': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13768/comments'}, 'review_comment': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments{/number}'}, 'commits': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13768/commits'}, 'statuses': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/statuses/32856fd15531890d65379376c8cadfc3611b36cb'}}, 'author_association': 'MEMBER', 'auto_merge': None, 'active_lock_reason': None}, 'repository': {'id': 11577304, 'node_id': 'MDEwOlJlcG9zaXRvcnkxMTU3NzMwNA==', 'name': 'dolphin', 'full_name': 'dolphin-emu/dolphin', 'private': False, 'owner': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/dolphin-emu', 'html_url': 'https://github.com/dolphin-emu', 'followers_url': 'https://api.github.com/users/dolphin-emu/followers', 'following_url': 'https://api.github.com/users/dolphin-emu/following{/other_user}', 'gists_url': 'https://api.github.com/users/dolphin-emu/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/dolphin-emu/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/dolphin-emu/subscriptions', 'organizations_url': 'https://api.github.com/users/dolphin-emu/orgs', 'repos_url': 'https://api.github.com/users/dolphin-emu/repos', 'events_url': 'https://api.github.com/users/dolphin-emu/events{/privacy}', 'received_events_url': 'https://api.github.com/users/dolphin-emu/received_events', 'type': 'Organization', 'user_view_type': 'public', 'site_admin': False}, 'html_url': 'https://github.com/dolphin-emu/dolphin', 'description': 'Dolphin is a GameCube / Wii emulator, allowing you to play games for these two platforms on PC with improvements.', 'fork': False, 'url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'forks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/forks', 'keys_url': 'https://api.github.com/repos/dolphin-emu/dolphin/keys{/key_id}', 'collaborators_url': 'https://api.github.com/repos/dolphin-emu/dolphin/collaborators{/collaborator}', 'teams_url': 'https://api.github.com/repos/dolphin-emu/dolphin/teams', 'hooks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/hooks', 'issue_events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/events{/number}', 'events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/events', 'assignees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/assignees{/user}', 'branches_url': 'https://api.github.com/repos/dolphin-emu/dolphin/branches{/branch}', 'tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/tags', 'blobs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/blobs{/sha}', 'git_tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/tags{/sha}', 'git_refs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/refs{/sha}', 'trees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/trees{/sha}', 'statuses_url': 'https://api.github.com/repos/dolphin-emu/dolphin/statuses/{sha}', 'languages_url': 'https://api.github.com/repos/dolphin-emu/dolphin/languages', 'stargazers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/stargazers', 'contributors_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contributors', 'subscribers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscribers', 'subscription_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscription', 'commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/commits{/sha}', 'git_commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/commits{/sha}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/comments{/number}', 'issue_comment_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments{/number}', 'contents_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contents/{+path}', 'compare_url': 'https://api.github.com/repos/dolphin-emu/dolphin/compare/{base}...{head}', 'merges_url': 'https://api.github.com/repos/dolphin-emu/dolphin/merges', 'archive_url': 'https://api.github.com/repos/dolphin-emu/dolphin/{archive_format}{/ref}', 'downloads_url': 'https://api.github.com/repos/dolphin-emu/dolphin/downloads', 'issues_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues{/number}', 'pulls_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls{/number}', 'milestones_url': 'https://api.github.com/repos/dolphin-emu/dolphin/milestones{/number}', 'notifications_url': 'https://api.github.com/repos/dolphin-emu/dolphin/notifications{?since,all,participating}', 'labels_url': 'https://api.github.com/repos/dolphin-emu/dolphin/labels{/name}', 'releases_url': 'https://api.github.com/repos/dolphin-emu/dolphin/releases{/id}', 'deployments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/deployments', 'created_at': '2013-07-22T09:07:57Z', 'updated_at': '2026-02-03T18:03:13Z', 'pushed_at': '2026-02-03T04:26:13Z', 'git_url': 'git://github.com/dolphin-emu/dolphin.git', 'ssh_url': 'git@github.com:dolphin-emu/dolphin.git', 'clone_url': 'https://github.com/dolphin-emu/dolphin.git', 'svn_url': 'https://github.com/dolphin-emu/dolphin', 'homepage': 'https://dolphin-emu.org', 'size': 544014, 'stargazers_count': 14582, 'watchers_count': 14582, 'language': 'C++', 'has_issues': False, 'has_projects': False, 'has_downloads': True, 'has_wiki': True, 'has_pages': False, 'has_discussions': False, 'forks_count': 2966, 'mirror_url': None, 'archived': False, 'disabled': False, 'open_issues_count': 360, 'license': {'key': 'other', 'name': 'Other', 'spdx_id': 'NOASSERTION', 'url': None, 'node_id': 'MDc6TGljZW5zZTA='}, 'allow_forking': True, 'is_template': False, 'web_commit_signoff_required': False, 'topics': ['cpp', 'emulation', 'emulator', 'gamecube', 'wii'], 'visibility': 'public', 'forks': 2966, 'open_issues': 360, 'watchers': 14582, 'default_branch': 'master', 'custom_properties': {}}, 'organization': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'url': 'https://api.github.com/orgs/dolphin-emu', 'repos_url': 'https://api.github.com/orgs/dolphin-emu/repos', 'events_url': 'https://api.github.com/orgs/dolphin-emu/events', 'hooks_url': 'https://api.github.com/orgs/dolphin-emu/hooks', 'issues_url': 'https://api.github.com/orgs/dolphin-emu/issues', 'members_url': 'https://api.github.com/orgs/dolphin-emu/members{/member}', 'public_members_url': 'https://api.github.com/orgs/dolphin-emu/public_members{/member}', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'description': ''}, 'sender': {'login': 'jordan-woyak', 'id': 1768214, 'node_id': 'MDQ6VXNlcjE3NjgyMTQ=', 'avatar_url': 'https://avatars.githubusercontent.com/u/1768214?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/jordan-woyak', 'html_url': 'https://github.com/jordan-woyak', 'followers_url': 'https://api.github.com/users/jordan-woyak/followers', 'following_url': 'https://api.github.com/users/jordan-woyak/following{/other_user}', 'gists_url': 'https://api.github.com/users/jordan-woyak/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/jordan-woyak/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/jordan-woyak/subscriptions', 'organizations_url': 'https://api.github.com/users/jordan-woyak/orgs', 'repos_url': 'https://api.github.com/users/jordan-woyak/repos', 'events_url': 'https://api.github.com/users/jordan-woyak/events{/privacy}', 'received_events_url': 'https://api.github.com/users/jordan-woyak/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'installation': {'id': 35437981, 'node_id': 'MDIzOkludGVncmF0aW9uSW5zdGFsbGF0aW9uMzU0Mzc5ODE='}}, 'type': 'raw_gh_hook'}
2026-02-03T22:35:19.459931 {'source': 'webserver', 'gh_type': 'pull_request', 'raw': {'action': 'synchronize', 'number': 14218, 'pull_request': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/14218', 'id': 3099870143, 'node_id': 'PR_kwDOALCn2M64xEO_', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/14218', 'diff_url': 'https://github.com/dolphin-emu/dolphin/pull/14218.diff', 'patch_url': 'https://github.com/dolphin-emu/dolphin/pull/14218.patch', 'issue_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14218', 'number': 14218, 'state': 'open', 'locked': False, 'title': 'Jit64: Make ABI_CallFunction generic', 'user': {'login': 'JosJuice', 'id': 6716818, 'node_id': 'MDQ6VXNlcjY3MTY4MTg=', 'avatar_url': 'https://avatars.githubusercontent.com/u/6716818?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/JosJuice', 'html_url': 'https://github.com/JosJuice', 'followers_url': 'https://api.github.com/users/JosJuice/followers', 'following_url': 'https://api.github.com/users/JosJuice/following{/other_user}', 'gists_url': 'https://api.github.com/users/JosJuice/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/JosJuice/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/JosJuice/subscriptions', 'organizations_url': 'https://api.github.com/users/JosJuice/orgs', 'repos_url': 'https://api.github.com/users/JosJuice/repos', 'events_url': 'https://api.github.com/users/JosJuice/events{/privacy}', 'received_events_url': 'https://api.github.com/users/JosJuice/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'body': 'This replaces all the hand-coded variants of ABI_CallFunction with a single generic function. It handles all existing cases, plus many new cases that would have been annoying to hand-code.\r\n\r\nThe implementation is based on our AArch64 ABI_CallFunction, but adapted for x64. The most notable differences are support for memory operands and a new way for callers to specify the sizes of operands.', 'created_at': '2025-12-14T13:50:33Z', 'updated_at': '2026-02-03T22:35:17Z', 'closed_at': None, 'merged_at': None, 'merge_commit_sha': 'f86a133381f5a524545ebeff9efa0a63c20ca31f', 'assignee': None, 'assignees': [], 'requested_reviewers': [], 'requested_teams': [], 'labels': [], 'milestone': None, 'draft': False, 'commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/14218/commits', 'review_comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/14218/comments', 'review_comment_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments{/number}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14218/comments', 'statuses_url': 'https://api.github.com/repos/dolphin-emu/dolphin/statuses/72a6ad6fac50dccf5f37921497b92e75e84bbf40', 'head': {'label': 'JosJuice:jit64-abi-call-function-parameters', 'ref': 'jit64-abi-call-function-parameters', 'sha': '72a6ad6fac50dccf5f37921497b92e75e84bbf40', 'user': {'login': 'JosJuice', 'id': 6716818, 'node_id': 'MDQ6VXNlcjY3MTY4MTg=', 'avatar_url': 'https://avatars.githubusercontent.com/u/6716818?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/JosJuice', 'html_url': 'https://github.com/JosJuice', 'followers_url': 'https://api.github.com/users/JosJuice/followers', 'following_url': 'https://api.github.com/users/JosJuice/following{/other_user}', 'gists_url': 'https://api.github.com/users/JosJuice/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/JosJuice/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/JosJuice/subscriptions', 'organizations_url': 'https://api.github.com/users/JosJuice/orgs', 'repos_url': 'https://api.github.com/users/JosJuice/repos', 'events_url': 'https://api.github.com/users/JosJuice/events{/privacy}', 'received_events_url': 'https://api.github.com/users/JosJuice/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'repo': {'id': 26057138, 'node_id': 'MDEwOlJlcG9zaXRvcnkyNjA1NzEzOA==', 'name': 'dolphin', 'full_name': 'JosJuice/dolphin', 'private': False, 'owner': {'login': 'JosJuice', 'id': 6716818, 'node_id': 'MDQ6VXNlcjY3MTY4MTg=', 'avatar_url': 'https://avatars.githubusercontent.com/u/6716818?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/JosJuice', 'html_url': 'https://github.com/JosJuice', 'followers_url': 'https://api.github.com/users/JosJuice/followers', 'following_url': 'https://api.github.com/users/JosJuice/following{/other_user}', 'gists_url': 'https://api.github.com/users/JosJuice/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/JosJuice/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/JosJuice/subscriptions', 'organizations_url': 'https://api.github.com/users/JosJuice/orgs', 'repos_url': 'https://api.github.com/users/JosJuice/repos', 'events_url': 'https://api.github.com/users/JosJuice/events{/privacy}', 'received_events_url': 'https://api.github.com/users/JosJuice/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'html_url': 'https://github.com/JosJuice/dolphin', 'description': 'Dolphin is a GameCube/Wii emulator, allowing you to play games for these two platforms on PC, with improvements.', 'fork': True, 'url': 'https://api.github.com/repos/JosJuice/dolphin', 'forks_url': 'https://api.github.com/repos/JosJuice/dolphin/forks', 'keys_url': 'https://api.github.com/repos/JosJuice/dolphin/keys{/key_id}', 'collaborators_url': 'https://api.github.com/repos/JosJuice/dolphin/collaborators{/collaborator}', 'teams_url': 'https://api.github.com/repos/JosJuice/dolphin/teams', 'hooks_url': 'https://api.github.com/repos/JosJuice/dolphin/hooks', 'issue_events_url': 'https://api.github.com/repos/JosJuice/dolphin/issues/events{/number}', 'events_url': 'https://api.github.com/repos/JosJuice/dolphin/events', 'assignees_url': 'https://api.github.com/repos/JosJuice/dolphin/assignees{/user}', 'branches_url': 'https://api.github.com/repos/JosJuice/dolphin/branches{/branch}', 'tags_url': 'https://api.github.com/repos/JosJuice/dolphin/tags', 'blobs_url': 'https://api.github.com/repos/JosJuice/dolphin/git/blobs{/sha}', 'git_tags_url': 'https://api.github.com/repos/JosJuice/dolphin/git/tags{/sha}', 'git_refs_url': 'https://api.github.com/repos/JosJuice/dolphin/git/refs{/sha}', 'trees_url': 'https://api.github.com/repos/JosJuice/dolphin/git/trees{/sha}', 'statuses_url': 'https://api.github.com/repos/JosJuice/dolphin/statuses/{sha}', 'languages_url': 'https://api.github.com/repos/JosJuice/dolphin/languages', 'stargazers_url': 'https://api.github.com/repos/JosJuice/dolphin/stargazers', 'contributors_url': 'https://api.github.com/repos/JosJuice/dolphin/contributors', 'subscribers_url': 'https://api.github.com/repos/JosJuice/dolphin/subscribers', 'subscription_url': 'https://api.github.com/repos/JosJuice/dolphin/subscription', 'commits_url': 'https://api.github.com/repos/JosJuice/dolphin/commits{/sha}', 'git_commits_url': 'https://api.github.com/repos/JosJuice/dolphin/git/commits{/sha}', 'comments_url': 'https://api.github.com/repos/JosJuice/dolphin/comments{/number}', 'issue_comment_url': 'https://api.github.com/repos/JosJuice/dolphin/issues/comments{/number}', 'contents_url': 'https://api.github.com/repos/JosJuice/dolphin/contents/{+path}', 'compare_url': 'https://api.github.com/repos/JosJuice/dolphin/compare/{base}...{head}', 'merges_url': 'https://api.github.com/repos/JosJuice/dolphin/merges', 'archive_url': 'https://api.github.com/repos/JosJuice/dolphin/{archive_format}{/ref}', 'downloads_url': 'https://api.github.com/repos/JosJuice/dolphin/downloads', 'issues_url': 'https://api.github.com/repos/JosJuice/dolphin/issues{/number}', 'pulls_url': 'https://api.github.com/repos/JosJuice/dolphin/pulls{/number}', 'milestones_url': 'https://api.github.com/repos/JosJuice/dolphin/milestones{/number}', 'notifications_url': 'https://api.github.com/repos/JosJuice/dolphin/notifications{?since,all,participating}', 'labels_url': 'https://api.github.com/repos/JosJuice/dolphin/labels{/name}', 'releases_url': 'https://api.github.com/repos/JosJuice/dolphin/releases{/id}', 'deployments_url': 'https://api.github.com/repos/JosJuice/dolphin/deployments', 'created_at': '2014-11-01T17:12:40Z', 'updated_at': '2026-02-01T11:39:14Z', 'pushed_at': '2026-02-03T22:35:15Z', 'git_url': 'git://github.com/JosJuice/dolphin.git', 'ssh_url': 'git@github.com:JosJuice/dolphin.git', 'clone_url': 'https://github.com/JosJuice/dolphin.git', 'svn_url': 'https://github.com/JosJuice/dolphin', 'homepage': None, 'size': 509732, 'stargazers_count': 6, 'watchers_count': 6, 'language': 'C++', 'has_issues': False, 'has_projects': True, 'has_downloads': True, 'has_wiki': True, 'has_pages': False, 'has_discussions': False, 'forks_count': 1, 'mirror_url': None, 'archived': False, 'disabled': False, 'open_issues_count': 0, 'license': {'key': 'other', 'name': 'Other', 'spdx_id': 'NOASSERTION', 'url': None, 'node_id': 'MDc6TGljZW5zZTA='}, 'allow_forking': True, 'is_template': False, 'web_commit_signoff_required': False, 'topics': [], 'visibility': 'public', 'forks': 1, 'open_issues': 0, 'watchers': 6, 'default_branch': 'master', 'allow_squash_merge': True, 'allow_merge_commit': True, 'allow_rebase_merge': True, 'allow_auto_merge': False, 'delete_branch_on_merge': False, 'allow_update_branch': False, 'use_squash_pr_title_as_default': False, 'squash_merge_commit_message': 'COMMIT_MESSAGES', 'squash_merge_commit_title': 'COMMIT_OR_PR_TITLE', 'merge_commit_message': 'PR_TITLE', 'merge_commit_title': 'MERGE_MESSAGE'}}, 'base': {'label': 'dolphin-emu:master', 'ref': 'master', 'sha': 'eac7b290423efea867c2c7f3f2b2792d05418bdd', 'user': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/dolphin-emu', 'html_url': 'https://github.com/dolphin-emu', 'followers_url': 'https://api.github.com/users/dolphin-emu/followers', 'following_url': 'https://api.github.com/users/dolphin-emu/following{/other_user}', 'gists_url': 'https://api.github.com/users/dolphin-emu/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/dolphin-emu/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/dolphin-emu/subscriptions', 'organizations_url': 'https://api.github.com/users/dolphin-emu/orgs', 'repos_url': 'https://api.github.com/users/dolphin-emu/repos', 'events_url': 'https://api.github.com/users/dolphin-emu/events{/privacy}', 'received_events_url': 'https://api.github.com/users/dolphin-emu/received_events', 'type': 'Organization', 'user_view_type': 'public', 'site_admin': False}, 'repo': {'id': 11577304, 'node_id': 'MDEwOlJlcG9zaXRvcnkxMTU3NzMwNA==', 'name': 'dolphin', 'full_name': 'dolphin-emu/dolphin', 'private': False, 'owner': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/dolphin-emu', 'html_url': 'https://github.com/dolphin-emu', 'followers_url': 'https://api.github.com/users/dolphin-emu/followers', 'following_url': 'https://api.github.com/users/dolphin-emu/following{/other_user}', 'gists_url': 'https://api.github.com/users/dolphin-emu/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/dolphin-emu/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/dolphin-emu/subscriptions', 'organizations_url': 'https://api.github.com/users/dolphin-emu/orgs', 'repos_url': 'https://api.github.com/users/dolphin-emu/repos', 'events_url': 'https://api.github.com/users/dolphin-emu/events{/privacy}', 'received_events_url': 'https://api.github.com/users/dolphin-emu/received_events', 'type': 'Organization', 'user_view_type': 'public', 'site_admin': False}, 'html_url': 'https://github.com/dolphin-emu/dolphin', 'description': 'Dolphin is a GameCube / Wii emulator, allowing you to play games for these two platforms on PC with improvements.', 'fork': False, 'url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'forks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/forks', 'keys_url': 'https://api.github.com/repos/dolphin-emu/dolphin/keys{/key_id}', 'collaborators_url': 'https://api.github.com/repos/dolphin-emu/dolphin/collaborators{/collaborator}', 'teams_url': 'https://api.github.com/repos/dolphin-emu/dolphin/teams', 'hooks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/hooks', 'issue_events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/events{/number}', 'events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/events', 'assignees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/assignees{/user}', 'branches_url': 'https://api.github.com/repos/dolphin-emu/dolphin/branches{/branch}', 'tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/tags', 'blobs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/blobs{/sha}', 'git_tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/tags{/sha}', 'git_refs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/refs{/sha}', 'trees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/trees{/sha}', 'statuses_url': 'https://api.github.com/repos/dolphin-emu/dolphin/statuses/{sha}', 'languages_url': 'https://api.github.com/repos/dolphin-emu/dolphin/languages', 'stargazers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/stargazers', 'contributors_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contributors', 'subscribers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscribers', 'subscription_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscription', 'commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/commits{/sha}', 'git_commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/commits{/sha}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/comments{/number}', 'issue_comment_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments{/number}', 'contents_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contents/{+path}', 'compare_url': 'https://api.github.com/repos/dolphin-emu/dolphin/compare/{base}...{head}', 'merges_url': 'https://api.github.com/repos/dolphin-emu/dolphin/merges', 'archive_url': 'https://api.github.com/repos/dolphin-emu/dolphin/{archive_format}{/ref}', 'downloads_url': 'https://api.github.com/repos/dolphin-emu/dolphin/downloads', 'issues_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues{/number}', 'pulls_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls{/number}', 'milestones_url': 'https://api.github.com/repos/dolphin-emu/dolphin/milestones{/number}', 'notifications_url': 'https://api.github.com/repos/dolphin-emu/dolphin/notifications{?since,all,participating}', 'labels_url': 'https://api.github.com/repos/dolphin-emu/dolphin/labels{/name}', 'releases_url': 'https://api.github.com/repos/dolphin-emu/dolphin/releases{/id}', 'deployments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/deployments', 'created_at': '2013-07-22T09:07:57Z', 'updated_at': '2026-02-03T18:03:13Z', 'pushed_at': '2026-02-03T04:26:13Z', 'git_url': 'git://github.com/dolphin-emu/dolphin.git', 'ssh_url': 'git@github.com:dolphin-emu/dolphin.git', 'clone_url': 'https://github.com/dolphin-emu/dolphin.git', 'svn_url': 'https://github.com/dolphin-emu/dolphin', 'homepage': 'https://dolphin-emu.org', 'size': 544014, 'stargazers_count': 14582, 'watchers_count': 14582, 'language': 'C++', 'has_issues': False, 'has_projects': False, 'has_downloads': True, 'has_wiki': True, 'has_pages': False, 'has_discussions': False, 'forks_count': 2966, 'mirror_url': None, 'archived': False, 'disabled': False, 'open_issues_count': 360, 'license': {'key': 'other', 'name': 'Other', 'spdx_id': 'NOASSERTION', 'url': None, 'node_id': 'MDc6TGljZW5zZTA='}, 'allow_forking': True, 'is_template': False, 'web_commit_signoff_required': False, 'topics': ['cpp', 'emulation', 'emulator', 'gamecube', 'wii'], 'visibility': 'public', 'forks': 2966, 'open_issues': 360, 'watchers': 14582, 'default_branch': 'master', 'allow_squash_merge': False, 'allow_merge_commit': True, 'allow_rebase_merge': False, 'allow_auto_merge': False, 'delete_branch_on_merge': False, 'allow_update_branch': False, 'use_squash_pr_title_as_default': False, 'squash_merge_commit_message': 'COMMIT_MESSAGES', 'squash_merge_commit_title': 'COMMIT_OR_PR_TITLE', 'merge_commit_message': 'PR_TITLE', 'merge_commit_title': 'MERGE_MESSAGE'}}, '_links': {'self': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/14218'}, 'html': {'href': 'https://github.com/dolphin-emu/dolphin/pull/14218'}, 'issue': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14218'}, 'comments': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14218/comments'}, 'review_comments': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/14218/comments'}, 'review_comment': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments{/number}'}, 'commits': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/14218/commits'}, 'statuses': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/statuses/72a6ad6fac50dccf5f37921497b92e75e84bbf40'}}, 'author_association': 'MEMBER', 'auto_merge': None, 'active_lock_reason': None, 'merged': False, 'mergeable': None, 'rebaseable': None, 'mergeable_state': 'unknown', 'merged_by': None, 'comments': 1, 'review_comments': 6, 'maintainer_can_modify': True, 'commits': 1, 'additions': 793, 'deletions': 298, 'changed_files': 22}, 'before': '41c26ea85f34764a41e9f4e79211cf9fe6bad48c', 'after': '72a6ad6fac50dccf5f37921497b92e75e84bbf40', 'repository': {'id': 11577304, 'node_id': 'MDEwOlJlcG9zaXRvcnkxMTU3NzMwNA==', 'name': 'dolphin', 'full_name': 'dolphin-emu/dolphin', 'private': False, 'owner': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/dolphin-emu', 'html_url': 'https://github.com/dolphin-emu', 'followers_url': 'https://api.github.com/users/dolphin-emu/followers', 'following_url': 'https://api.github.com/users/dolphin-emu/following{/other_user}', 'gists_url': 'https://api.github.com/users/dolphin-emu/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/dolphin-emu/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/dolphin-emu/subscriptions', 'organizations_url': 'https://api.github.com/users/dolphin-emu/orgs', 'repos_url': 'https://api.github.com/users/dolphin-emu/repos', 'events_url': 'https://api.github.com/users/dolphin-emu/events{/privacy}', 'received_events_url': 'https://api.github.com/users/dolphin-emu/received_events', 'type': 'Organization', 'user_view_type': 'public', 'site_admin': False}, 'html_url': 'https://github.com/dolphin-emu/dolphin', 'description': 'Dolphin is a GameCube / Wii emulator, allowing you to play games for these two platforms on PC with improvements.', 'fork': False, 'url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'forks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/forks', 'keys_url': 'https://api.github.com/repos/dolphin-emu/dolphin/keys{/key_id}', 'collaborators_url': 'https://api.github.com/repos/dolphin-emu/dolphin/collaborators{/collaborator}', 'teams_url': 'https://api.github.com/repos/dolphin-emu/dolphin/teams', 'hooks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/hooks', 'issue_events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/events{/number}', 'events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/events', 'assignees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/assignees{/user}', 'branches_url': 'https://api.github.com/repos/dolphin-emu/dolphin/branches{/branch}', 'tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/tags', 'blobs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/blobs{/sha}', 'git_tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/tags{/sha}', 'git_refs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/refs{/sha}', 'trees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/trees{/sha}', 'statuses_url': 'https://api.github.com/repos/dolphin-emu/dolphin/statuses/{sha}', 'languages_url': 'https://api.github.com/repos/dolphin-emu/dolphin/languages', 'stargazers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/stargazers', 'contributors_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contributors', 'subscribers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscribers', 'subscription_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscription', 'commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/commits{/sha}', 'git_commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/commits{/sha}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/comments{/number}', 'issue_comment_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments{/number}', 'contents_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contents/{+path}', 'compare_url': 'https://api.github.com/repos/dolphin-emu/dolphin/compare/{base}...{head}', 'merges_url': 'https://api.github.com/repos/dolphin-emu/dolphin/merges', 'archive_url': 'https://api.github.com/repos/dolphin-emu/dolphin/{archive_format}{/ref}', 'downloads_url': 'https://api.github.com/repos/dolphin-emu/dolphin/downloads', 'issues_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues{/number}', 'pulls_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls{/number}', 'milestones_url': 'https://api.github.com/repos/dolphin-emu/dolphin/milestones{/number}', 'notifications_url': 'https://api.github.com/repos/dolphin-emu/dolphin/notifications{?since,all,participating}', 'labels_url': 'https://api.github.com/repos/dolphin-emu/dolphin/labels{/name}', 'releases_url': 'https://api.github.com/repos/dolphin-emu/dolphin/releases{/id}', 'deployments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/deployments', 'created_at': '2013-07-22T09:07:57Z', 'updated_at': '2026-02-03T18:03:13Z', 'pushed_at': '2026-02-03T04:26:13Z', 'git_url': 'git://github.com/dolphin-emu/dolphin.git', 'ssh_url': 'git@github.com:dolphin-emu/dolphin.git', 'clone_url': 'https://github.com/dolphin-emu/dolphin.git', 'svn_url': 'https://github.com/dolphin-emu/dolphin', 'homepage': 'https://dolphin-emu.org', 'size': 544014, 'stargazers_count': 14582, 'watchers_count': 14582, 'language': 'C++', 'has_issues': False, 'has_projects': False, 'has_downloads': True, 'has_wiki': True, 'has_pages': False, 'has_discussions': False, 'forks_count': 2966, 'mirror_url': None, 'archived': False, 'disabled': False, 'open_issues_count': 360, 'license': {'key': 'other', 'name': 'Other', 'spdx_id': 'NOASSERTION', 'url': None, 'node_id': 'MDc6TGljZW5zZTA='}, 'allow_forking': True, 'is_template': False, 'web_commit_signoff_required': False, 'topics': ['cpp', 'emulation', 'emulator', 'gamecube', 'wii'], 'visibility': 'public', 'forks': 2966, 'open_issues': 360, 'watchers': 14582, 'default_branch': 'master', 'custom_properties': {}}, 'organization': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'url': 'https://api.github.com/orgs/dolphin-emu', 'repos_url': 'https://api.github.com/orgs/dolphin-emu/repos', 'events_url': 'https://api.github.com/orgs/dolphin-emu/events', 'hooks_url': 'https://api.github.com/orgs/dolphin-emu/hooks', 'issues_url': 'https://api.github.com/orgs/dolphin-emu/issues', 'members_url': 'https://api.github.com/orgs/dolphin-emu/members{/member}', 'public_members_url': 'https://api.github.com/orgs/dolphin-emu/public_members{/member}', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'description': ''}, 'sender': {'login': 'JosJuice', 'id': 6716818, 'node_id': 'MDQ6VXNlcjY3MTY4MTg=', 'avatar_url': 'https://avatars.githubusercontent.com/u/6716818?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/JosJuice', 'html_url': 'https://github.com/JosJuice', 'followers_url': 'https://api.github.com/users/JosJuice/followers', 'following_url': 'https://api.github.com/users/JosJuice/following{/other_user}', 'gists_url': 'https://api.github.com/users/JosJuice/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/JosJuice/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/JosJuice/subscriptions', 'organizations_url': 'https://api.github.com/users/JosJuice/orgs', 'repos_url': 'https://api.github.com/users/JosJuice/repos', 'events_url': 'https://api.github.com/users/JosJuice/events{/privacy}', 'received_events_url': 'https://api.github.com/users/JosJuice/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'installation': {'id': 35437981, 'node_id': 'MDIzOkludGVncmF0aW9uSW5zdGFsbGF0aW9uMzU0Mzc5ODE='}}, 'type': 'raw_gh_hook'}
2026-02-03T22:06:12.686146 {'source': 'webserver', 'gh_type': 'pull_request', 'raw': {'action': 'synchronize', 'number': 13768, 'pull_request': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13768', 'id': 2608905795, 'node_id': 'PR_kwDOALCn2M6bgL5D', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/13768', 'diff_url': 'https://github.com/dolphin-emu/dolphin/pull/13768.diff', 'patch_url': 'https://github.com/dolphin-emu/dolphin/pull/13768.patch', 'issue_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13768', 'number': 13768, 'state': 'open', 'locked': False, 'title': 'Core: Create fastmem mappings for page address translation', 'user': {'login': 'JosJuice', 'id': 6716818, 'node_id': 'MDQ6VXNlcjY3MTY4MTg=', 'avatar_url': 'https://avatars.githubusercontent.com/u/6716818?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/JosJuice', 'html_url': 'https://github.com/JosJuice', 'followers_url': 'https://api.github.com/users/JosJuice/followers', 'following_url': 'https://api.github.com/users/JosJuice/following{/other_user}', 'gists_url': 'https://api.github.com/users/JosJuice/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/JosJuice/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/JosJuice/subscriptions', 'organizations_url': 'https://api.github.com/users/JosJuice/orgs', 'repos_url': 'https://api.github.com/users/JosJuice/repos', 'events_url': 'https://api.github.com/users/JosJuice/events{/privacy}', 'received_events_url': 'https://api.github.com/users/JosJuice/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'body': "Previously we've only been setting up fastmem mappings for block address translation, but now we also do it for page address translation. This increases performance when games access memory using page tables, but decreases performance when games set up page tables.\r\n\r\nThe tlbie instruction is used as an indication that the mappings need to be updated.\r\n\r\nThere is the accuracy downside that the TLB is now effectively infinitely large. No games are known to be affected by this, and you still get the old, more accurate behavior if Enable Write-Back Cache is on.\r\n\r\nLeft to do:\r\n\r\n- [x] Support for host page sizes larger than 4K\r\n- [x] Support for host page sizes larger than 4K is untested\r\n- [x] macOS is untested\r\n- [x] Rogue Squadron 3 is super slow due to the pessimistic setting of R and C bits (I guess the game is heavily swapping? DoJit is running very often)\r\n- [x] Unit tests\r\n- [x] Unit tests fail on the Ubuntu and Debian runners", 'created_at': '2025-06-21T09:20:23Z', 'updated_at': '2026-02-03T22:06:10Z', 'closed_at': None, 'merged_at': None, 'merge_commit_sha': '52a7548520f780d3f393f825e772cba718f412db', 'assignee': None, 'assignees': [], 'requested_reviewers': [], 'requested_teams': [], 'labels': [], 'milestone': None, 'draft': False, 'commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13768/commits', 'review_comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13768/comments', 'review_comment_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments{/number}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13768/comments', 'statuses_url': 'https://api.github.com/repos/dolphin-emu/dolphin/statuses/32856fd15531890d65379376c8cadfc3611b36cb', 'head': {'label': 'JosJuice:page-table-fastmem-2', 'ref': 'page-table-fastmem-2', 'sha': '32856fd15531890d65379376c8cadfc3611b36cb', 'user': {'login': 'JosJuice', 'id': 6716818, 'node_id': 'MDQ6VXNlcjY3MTY4MTg=', 'avatar_url': 'https://avatars.githubusercontent.com/u/6716818?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/JosJuice', 'html_url': 'https://github.com/JosJuice', 'followers_url': 'https://api.github.com/users/JosJuice/followers', 'following_url': 'https://api.github.com/users/JosJuice/following{/other_user}', 'gists_url': 'https://api.github.com/users/JosJuice/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/JosJuice/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/JosJuice/subscriptions', 'organizations_url': 'https://api.github.com/users/JosJuice/orgs', 'repos_url': 'https://api.github.com/users/JosJuice/repos', 'events_url': 'https://api.github.com/users/JosJuice/events{/privacy}', 'received_events_url': 'https://api.github.com/users/JosJuice/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'repo': {'id': 26057138, 'node_id': 'MDEwOlJlcG9zaXRvcnkyNjA1NzEzOA==', 'name': 'dolphin', 'full_name': 'JosJuice/dolphin', 'private': False, 'owner': {'login': 'JosJuice', 'id': 6716818, 'node_id': 'MDQ6VXNlcjY3MTY4MTg=', 'avatar_url': 'https://avatars.githubusercontent.com/u/6716818?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/JosJuice', 'html_url': 'https://github.com/JosJuice', 'followers_url': 'https://api.github.com/users/JosJuice/followers', 'following_url': 'https://api.github.com/users/JosJuice/following{/other_user}', 'gists_url': 'https://api.github.com/users/JosJuice/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/JosJuice/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/JosJuice/subscriptions', 'organizations_url': 'https://api.github.com/users/JosJuice/orgs', 'repos_url': 'https://api.github.com/users/JosJuice/repos', 'events_url': 'https://api.github.com/users/JosJuice/events{/privacy}', 'received_events_url': 'https://api.github.com/users/JosJuice/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'html_url': 'https://github.com/JosJuice/dolphin', 'description': 'Dolphin is a GameCube/Wii emulator, allowing you to play games for these two platforms on PC, with improvements.', 'fork': True, 'url': 'https://api.github.com/repos/JosJuice/dolphin', 'forks_url': 'https://api.github.com/repos/JosJuice/dolphin/forks', 'keys_url': 'https://api.github.com/repos/JosJuice/dolphin/keys{/key_id}', 'collaborators_url': 'https://api.github.com/repos/JosJuice/dolphin/collaborators{/collaborator}', 'teams_url': 'https://api.github.com/repos/JosJuice/dolphin/teams', 'hooks_url': 'https://api.github.com/repos/JosJuice/dolphin/hooks', 'issue_events_url': 'https://api.github.com/repos/JosJuice/dolphin/issues/events{/number}', 'events_url': 'https://api.github.com/repos/JosJuice/dolphin/events', 'assignees_url': 'https://api.github.com/repos/JosJuice/dolphin/assignees{/user}', 'branches_url': 'https://api.github.com/repos/JosJuice/dolphin/branches{/branch}', 'tags_url': 'https://api.github.com/repos/JosJuice/dolphin/tags', 'blobs_url': 'https://api.github.com/repos/JosJuice/dolphin/git/blobs{/sha}', 'git_tags_url': 'https://api.github.com/repos/JosJuice/dolphin/git/tags{/sha}', 'git_refs_url': 'https://api.github.com/repos/JosJuice/dolphin/git/refs{/sha}', 'trees_url': 'https://api.github.com/repos/JosJuice/dolphin/git/trees{/sha}', 'statuses_url': 'https://api.github.com/repos/JosJuice/dolphin/statuses/{sha}', 'languages_url': 'https://api.github.com/repos/JosJuice/dolphin/languages', 'stargazers_url': 'https://api.github.com/repos/JosJuice/dolphin/stargazers', 'contributors_url': 'https://api.github.com/repos/JosJuice/dolphin/contributors', 'subscribers_url': 'https://api.github.com/repos/JosJuice/dolphin/subscribers', 'subscription_url': 'https://api.github.com/repos/JosJuice/dolphin/subscription', 'commits_url': 'https://api.github.com/repos/JosJuice/dolphin/commits{/sha}', 'git_commits_url': 'https://api.github.com/repos/JosJuice/dolphin/git/commits{/sha}', 'comments_url': 'https://api.github.com/repos/JosJuice/dolphin/comments{/number}', 'issue_comment_url': 'https://api.github.com/repos/JosJuice/dolphin/issues/comments{/number}', 'contents_url': 'https://api.github.com/repos/JosJuice/dolphin/contents/{+path}', 'compare_url': 'https://api.github.com/repos/JosJuice/dolphin/compare/{base}...{head}', 'merges_url': 'https://api.github.com/repos/JosJuice/dolphin/merges', 'archive_url': 'https://api.github.com/repos/JosJuice/dolphin/{archive_format}{/ref}', 'downloads_url': 'https://api.github.com/repos/JosJuice/dolphin/downloads', 'issues_url': 'https://api.github.com/repos/JosJuice/dolphin/issues{/number}', 'pulls_url': 'https://api.github.com/repos/JosJuice/dolphin/pulls{/number}', 'milestones_url': 'https://api.github.com/repos/JosJuice/dolphin/milestones{/number}', 'notifications_url': 'https://api.github.com/repos/JosJuice/dolphin/notifications{?since,all,participating}', 'labels_url': 'https://api.github.com/repos/JosJuice/dolphin/labels{/name}', 'releases_url': 'https://api.github.com/repos/JosJuice/dolphin/releases{/id}', 'deployments_url': 'https://api.github.com/repos/JosJuice/dolphin/deployments', 'created_at': '2014-11-01T17:12:40Z', 'updated_at': '2026-02-01T11:39:14Z', 'pushed_at': '2026-02-03T22:06:09Z', 'git_url': 'git://github.com/JosJuice/dolphin.git', 'ssh_url': 'git@github.com:JosJuice/dolphin.git', 'clone_url': 'https://github.com/JosJuice/dolphin.git', 'svn_url': 'https://github.com/JosJuice/dolphin', 'homepage': None, 'size': 509732, 'stargazers_count': 6, 'watchers_count': 6, 'language': 'C++', 'has_issues': False, 'has_projects': True, 'has_downloads': True, 'has_wiki': True, 'has_pages': False, 'has_discussions': False, 'forks_count': 1, 'mirror_url': None, 'archived': False, 'disabled': False, 'open_issues_count': 0, 'license': {'key': 'other', 'name': 'Other', 'spdx_id': 'NOASSERTION', 'url': None, 'node_id': 'MDc6TGljZW5zZTA='}, 'allow_forking': True, 'is_template': False, 'web_commit_signoff_required': False, 'topics': [], 'visibility': 'public', 'forks': 1, 'open_issues': 0, 'watchers': 6, 'default_branch': 'master', 'allow_squash_merge': True, 'allow_merge_commit': True, 'allow_rebase_merge': True, 'allow_auto_merge': False, 'delete_branch_on_merge': False, 'allow_update_branch': False, 'use_squash_pr_title_as_default': False, 'squash_merge_commit_message': 'COMMIT_MESSAGES', 'squash_merge_commit_title': 'COMMIT_OR_PR_TITLE', 'merge_commit_message': 'PR_TITLE', 'merge_commit_title': 'MERGE_MESSAGE'}}, 'base': {'label': 'dolphin-emu:master', 'ref': 'master', 'sha': 'eac7b290423efea867c2c7f3f2b2792d05418bdd', 'user': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/dolphin-emu', 'html_url': 'https://github.com/dolphin-emu', 'followers_url': 'https://api.github.com/users/dolphin-emu/followers', 'following_url': 'https://api.github.com/users/dolphin-emu/following{/other_user}', 'gists_url': 'https://api.github.com/users/dolphin-emu/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/dolphin-emu/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/dolphin-emu/subscriptions', 'organizations_url': 'https://api.github.com/users/dolphin-emu/orgs', 'repos_url': 'https://api.github.com/users/dolphin-emu/repos', 'events_url': 'https://api.github.com/users/dolphin-emu/events{/privacy}', 'received_events_url': 'https://api.github.com/users/dolphin-emu/received_events', 'type': 'Organization', 'user_view_type': 'public', 'site_admin': False}, 'repo': {'id': 11577304, 'node_id': 'MDEwOlJlcG9zaXRvcnkxMTU3NzMwNA==', 'name': 'dolphin', 'full_name': 'dolphin-emu/dolphin', 'private': False, 'owner': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/dolphin-emu', 'html_url': 'https://github.com/dolphin-emu', 'followers_url': 'https://api.github.com/users/dolphin-emu/followers', 'following_url': 'https://api.github.com/users/dolphin-emu/following{/other_user}', 'gists_url': 'https://api.github.com/users/dolphin-emu/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/dolphin-emu/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/dolphin-emu/subscriptions', 'organizations_url': 'https://api.github.com/users/dolphin-emu/orgs', 'repos_url': 'https://api.github.com/users/dolphin-emu/repos', 'events_url': 'https://api.github.com/users/dolphin-emu/events{/privacy}', 'received_events_url': 'https://api.github.com/users/dolphin-emu/received_events', 'type': 'Organization', 'user_view_type': 'public', 'site_admin': False}, 'html_url': 'https://github.com/dolphin-emu/dolphin', 'description': 'Dolphin is a GameCube / Wii emulator, allowing you to play games for these two platforms on PC with improvements.', 'fork': False, 'url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'forks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/forks', 'keys_url': 'https://api.github.com/repos/dolphin-emu/dolphin/keys{/key_id}', 'collaborators_url': 'https://api.github.com/repos/dolphin-emu/dolphin/collaborators{/collaborator}', 'teams_url': 'https://api.github.com/repos/dolphin-emu/dolphin/teams', 'hooks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/hooks', 'issue_events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/events{/number}', 'events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/events', 'assignees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/assignees{/user}', 'branches_url': 'https://api.github.com/repos/dolphin-emu/dolphin/branches{/branch}', 'tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/tags', 'blobs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/blobs{/sha}', 'git_tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/tags{/sha}', 'git_refs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/refs{/sha}', 'trees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/trees{/sha}', 'statuses_url': 'https://api.github.com/repos/dolphin-emu/dolphin/statuses/{sha}', 'languages_url': 'https://api.github.com/repos/dolphin-emu/dolphin/languages', 'stargazers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/stargazers', 'contributors_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contributors', 'subscribers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscribers', 'subscription_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscription', 'commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/commits{/sha}', 'git_commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/commits{/sha}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/comments{/number}', 'issue_comment_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments{/number}', 'contents_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contents/{+path}', 'compare_url': 'https://api.github.com/repos/dolphin-emu/dolphin/compare/{base}...{head}', 'merges_url': 'https://api.github.com/repos/dolphin-emu/dolphin/merges', 'archive_url': 'https://api.github.com/repos/dolphin-emu/dolphin/{archive_format}{/ref}', 'downloads_url': 'https://api.github.com/repos/dolphin-emu/dolphin/downloads', 'issues_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues{/number}', 'pulls_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls{/number}', 'milestones_url': 'https://api.github.com/repos/dolphin-emu/dolphin/milestones{/number}', 'notifications_url': 'https://api.github.com/repos/dolphin-emu/dolphin/notifications{?since,all,participating}', 'labels_url': 'https://api.github.com/repos/dolphin-emu/dolphin/labels{/name}', 'releases_url': 'https://api.github.com/repos/dolphin-emu/dolphin/releases{/id}', 'deployments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/deployments', 'created_at': '2013-07-22T09:07:57Z', 'updated_at': '2026-02-03T18:03:13Z', 'pushed_at': '2026-02-03T04:26:13Z', 'git_url': 'git://github.com/dolphin-emu/dolphin.git', 'ssh_url': 'git@github.com:dolphin-emu/dolphin.git', 'clone_url': 'https://github.com/dolphin-emu/dolphin.git', 'svn_url': 'https://github.com/dolphin-emu/dolphin', 'homepage': 'https://dolphin-emu.org', 'size': 544014, 'stargazers_count': 14582, 'watchers_count': 14582, 'language': 'C++', 'has_issues': False, 'has_projects': False, 'has_downloads': True, 'has_wiki': True, 'has_pages': False, 'has_discussions': False, 'forks_count': 2966, 'mirror_url': None, 'archived': False, 'disabled': False, 'open_issues_count': 360, 'license': {'key': 'other', 'name': 'Other', 'spdx_id': 'NOASSERTION', 'url': None, 'node_id': 'MDc6TGljZW5zZTA='}, 'allow_forking': True, 'is_template': False, 'web_commit_signoff_required': False, 'topics': ['cpp', 'emulation', 'emulator', 'gamecube', 'wii'], 'visibility': 'public', 'forks': 2966, 'open_issues': 360, 'watchers': 14582, 'default_branch': 'master', 'allow_squash_merge': False, 'allow_merge_commit': True, 'allow_rebase_merge': False, 'allow_auto_merge': False, 'delete_branch_on_merge': False, 'allow_update_branch': False, 'use_squash_pr_title_as_default': False, 'squash_merge_commit_message': 'COMMIT_MESSAGES', 'squash_merge_commit_title': 'COMMIT_OR_PR_TITLE', 'merge_commit_message': 'PR_TITLE', 'merge_commit_title': 'MERGE_MESSAGE'}}, '_links': {'self': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13768'}, 'html': {'href': 'https://github.com/dolphin-emu/dolphin/pull/13768'}, 'issue': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13768'}, 'comments': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13768/comments'}, 'review_comments': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13768/comments'}, 'review_comment': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments{/number}'}, 'commits': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13768/commits'}, 'statuses': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/statuses/32856fd15531890d65379376c8cadfc3611b36cb'}}, 'author_association': 'MEMBER', 'auto_merge': None, 'active_lock_reason': None, 'merged': False, 'mergeable': None, 'rebaseable': None, 'mergeable_state': 'unknown', 'merged_by': None, 'comments': 23, 'review_comments': 14, 'maintainer_can_modify': True, 'commits': 10, 'additions': 2043, 'deletions': 153, 'changed_files': 33}, 'before': 'a7301d7d406bc5955303a13e61327c5b17645b7a', 'after': '32856fd15531890d65379376c8cadfc3611b36cb', 'repository': {'id': 11577304, 'node_id': 'MDEwOlJlcG9zaXRvcnkxMTU3NzMwNA==', 'name': 'dolphin', 'full_name': 'dolphin-emu/dolphin', 'private': False, 'owner': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/dolphin-emu', 'html_url': 'https://github.com/dolphin-emu', 'followers_url': 'https://api.github.com/users/dolphin-emu/followers', 'following_url': 'https://api.github.com/users/dolphin-emu/following{/other_user}', 'gists_url': 'https://api.github.com/users/dolphin-emu/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/dolphin-emu/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/dolphin-emu/subscriptions', 'organizations_url': 'https://api.github.com/users/dolphin-emu/orgs', 'repos_url': 'https://api.github.com/users/dolphin-emu/repos', 'events_url': 'https://api.github.com/users/dolphin-emu/events{/privacy}', 'received_events_url': 'https://api.github.com/users/dolphin-emu/received_events', 'type': 'Organization', 'user_view_type': 'public', 'site_admin': False}, 'html_url': 'https://github.com/dolphin-emu/dolphin', 'description': 'Dolphin is a GameCube / Wii emulator, allowing you to play games for these two platforms on PC with improvements.', 'fork': False, 'url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'forks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/forks', 'keys_url': 'https://api.github.com/repos/dolphin-emu/dolphin/keys{/key_id}', 'collaborators_url': 'https://api.github.com/repos/dolphin-emu/dolphin/collaborators{/collaborator}', 'teams_url': 'https://api.github.com/repos/dolphin-emu/dolphin/teams', 'hooks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/hooks', 'issue_events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/events{/number}', 'events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/events', 'assignees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/assignees{/user}', 'branches_url': 'https://api.github.com/repos/dolphin-emu/dolphin/branches{/branch}', 'tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/tags', 'blobs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/blobs{/sha}', 'git_tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/tags{/sha}', 'git_refs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/refs{/sha}', 'trees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/trees{/sha}', 'statuses_url': 'https://api.github.com/repos/dolphin-emu/dolphin/statuses/{sha}', 'languages_url': 'https://api.github.com/repos/dolphin-emu/dolphin/languages', 'stargazers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/stargazers', 'contributors_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contributors', 'subscribers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscribers', 'subscription_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscription', 'commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/commits{/sha}', 'git_commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/commits{/sha}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/comments{/number}', 'issue_comment_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments{/number}', 'contents_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contents/{+path}', 'compare_url': 'https://api.github.com/repos/dolphin-emu/dolphin/compare/{base}...{head}', 'merges_url': 'https://api.github.com/repos/dolphin-emu/dolphin/merges', 'archive_url': 'https://api.github.com/repos/dolphin-emu/dolphin/{archive_format}{/ref}', 'downloads_url': 'https://api.github.com/repos/dolphin-emu/dolphin/downloads', 'issues_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues{/number}', 'pulls_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls{/number}', 'milestones_url': 'https://api.github.com/repos/dolphin-emu/dolphin/milestones{/number}', 'notifications_url': 'https://api.github.com/repos/dolphin-emu/dolphin/notifications{?since,all,participating}', 'labels_url': 'https://api.github.com/repos/dolphin-emu/dolphin/labels{/name}', 'releases_url': 'https://api.github.com/repos/dolphin-emu/dolphin/releases{/id}', 'deployments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/deployments', 'created_at': '2013-07-22T09:07:57Z', 'updated_at': '2026-02-03T18:03:13Z', 'pushed_at': '2026-02-03T04:26:13Z', 'git_url': 'git://github.com/dolphin-emu/dolphin.git', 'ssh_url': 'git@github.com:dolphin-emu/dolphin.git', 'clone_url': 'https://github.com/dolphin-emu/dolphin.git', 'svn_url': 'https://github.com/dolphin-emu/dolphin', 'homepage': 'https://dolphin-emu.org', 'size': 544014, 'stargazers_count': 14582, 'watchers_count': 14582, 'language': 'C++', 'has_issues': False, 'has_projects': False, 'has_downloads': True, 'has_wiki': True, 'has_pages': False, 'has_discussions': False, 'forks_count': 2966, 'mirror_url': None, 'archived': False, 'disabled': False, 'open_issues_count': 360, 'license': {'key': 'other', 'name': 'Other', 'spdx_id': 'NOASSERTION', 'url': None, 'node_id': 'MDc6TGljZW5zZTA='}, 'allow_forking': True, 'is_template': False, 'web_commit_signoff_required': False, 'topics': ['cpp', 'emulation', 'emulator', 'gamecube', 'wii'], 'visibility': 'public', 'forks': 2966, 'open_issues': 360, 'watchers': 14582, 'default_branch': 'master', 'custom_properties': {}}, 'organization': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'url': 'https://api.github.com/orgs/dolphin-emu', 'repos_url': 'https://api.github.com/orgs/dolphin-emu/repos', 'events_url': 'https://api.github.com/orgs/dolphin-emu/events', 'hooks_url': 'https://api.github.com/orgs/dolphin-emu/hooks', 'issues_url': 'https://api.github.com/orgs/dolphin-emu/issues', 'members_url': 'https://api.github.com/orgs/dolphin-emu/members{/member}', 'public_members_url': 'https://api.github.com/orgs/dolphin-emu/public_members{/member}', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'description': ''}, 'sender': {'login': 'JosJuice', 'id': 6716818, 'node_id': 'MDQ6VXNlcjY3MTY4MTg=', 'avatar_url': 'https://avatars.githubusercontent.com/u/6716818?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/JosJuice', 'html_url': 'https://github.com/JosJuice', 'followers_url': 'https://api.github.com/users/JosJuice/followers', 'following_url': 'https://api.github.com/users/JosJuice/following{/other_user}', 'gists_url': 'https://api.github.com/users/JosJuice/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/JosJuice/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/JosJuice/subscriptions', 'organizations_url': 'https://api.github.com/users/JosJuice/orgs', 'repos_url': 'https://api.github.com/users/JosJuice/repos', 'events_url': 'https://api.github.com/users/JosJuice/events{/privacy}', 'received_events_url': 'https://api.github.com/users/JosJuice/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'installation': {'id': 35437981, 'node_id': 'MDIzOkludGVncmF0aW9uSW5zdGFsbGF0aW9uMzU0Mzc5ODE='}}, 'type': 'raw_gh_hook'}
2026-02-03T22:04:37.178288 {'source': 'webserver', 'gh_type': 'pull_request', 'raw': {'action': 'synchronize', 'number': 13768, 'pull_request': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13768', 'id': 2608905795, 'node_id': 'PR_kwDOALCn2M6bgL5D', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/13768', 'diff_url': 'https://github.com/dolphin-emu/dolphin/pull/13768.diff', 'patch_url': 'https://github.com/dolphin-emu/dolphin/pull/13768.patch', 'issue_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13768', 'number': 13768, 'state': 'open', 'locked': False, 'title': 'Core: Create fastmem mappings for page address translation', 'user': {'login': 'JosJuice', 'id': 6716818, 'node_id': 'MDQ6VXNlcjY3MTY4MTg=', 'avatar_url': 'https://avatars.githubusercontent.com/u/6716818?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/JosJuice', 'html_url': 'https://github.com/JosJuice', 'followers_url': 'https://api.github.com/users/JosJuice/followers', 'following_url': 'https://api.github.com/users/JosJuice/following{/other_user}', 'gists_url': 'https://api.github.com/users/JosJuice/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/JosJuice/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/JosJuice/subscriptions', 'organizations_url': 'https://api.github.com/users/JosJuice/orgs', 'repos_url': 'https://api.github.com/users/JosJuice/repos', 'events_url': 'https://api.github.com/users/JosJuice/events{/privacy}', 'received_events_url': 'https://api.github.com/users/JosJuice/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'body': "Previously we've only been setting up fastmem mappings for block address translation, but now we also do it for page address translation. This increases performance when games access memory using page tables, but decreases performance when games set up page tables.\r\n\r\nThe tlbie instruction is used as an indication that the mappings need to be updated.\r\n\r\nThere is the accuracy downside that the TLB is now effectively infinitely large. No games are known to be affected by this, and you still get the old, more accurate behavior if Enable Write-Back Cache is on.\r\n\r\nLeft to do:\r\n\r\n- [x] Support for host page sizes larger than 4K\r\n- [x] Support for host page sizes larger than 4K is untested\r\n- [x] macOS is untested\r\n- [x] Rogue Squadron 3 is super slow due to the pessimistic setting of R and C bits (I guess the game is heavily swapping? DoJit is running very often)\r\n- [x] Unit tests\r\n- [x] Unit tests fail on the Ubuntu and Debian runners", 'created_at': '2025-06-21T09:20:23Z', 'updated_at': '2026-02-03T22:04:35Z', 'closed_at': None, 'merged_at': None, 'merge_commit_sha': '92006fa2785886984eace093208386946a3d5c9f', 'assignee': None, 'assignees': [], 'requested_reviewers': [], 'requested_teams': [], 'labels': [], 'milestone': None, 'draft': False, 'commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13768/commits', 'review_comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13768/comments', 'review_comment_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments{/number}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13768/comments', 'statuses_url': 'https://api.github.com/repos/dolphin-emu/dolphin/statuses/a7301d7d406bc5955303a13e61327c5b17645b7a', 'head': {'label': 'JosJuice:page-table-fastmem-2', 'ref': 'page-table-fastmem-2', 'sha': 'a7301d7d406bc5955303a13e61327c5b17645b7a', 'user': {'login': 'JosJuice', 'id': 6716818, 'node_id': 'MDQ6VXNlcjY3MTY4MTg=', 'avatar_url': 'https://avatars.githubusercontent.com/u/6716818?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/JosJuice', 'html_url': 'https://github.com/JosJuice', 'followers_url': 'https://api.github.com/users/JosJuice/followers', 'following_url': 'https://api.github.com/users/JosJuice/following{/other_user}', 'gists_url': 'https://api.github.com/users/JosJuice/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/JosJuice/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/JosJuice/subscriptions', 'organizations_url': 'https://api.github.com/users/JosJuice/orgs', 'repos_url': 'https://api.github.com/users/JosJuice/repos', 'events_url': 'https://api.github.com/users/JosJuice/events{/privacy}', 'received_events_url': 'https://api.github.com/users/JosJuice/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'repo': {'id': 26057138, 'node_id': 'MDEwOlJlcG9zaXRvcnkyNjA1NzEzOA==', 'name': 'dolphin', 'full_name': 'JosJuice/dolphin', 'private': False, 'owner': {'login': 'JosJuice', 'id': 6716818, 'node_id': 'MDQ6VXNlcjY3MTY4MTg=', 'avatar_url': 'https://avatars.githubusercontent.com/u/6716818?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/JosJuice', 'html_url': 'https://github.com/JosJuice', 'followers_url': 'https://api.github.com/users/JosJuice/followers', 'following_url': 'https://api.github.com/users/JosJuice/following{/other_user}', 'gists_url': 'https://api.github.com/users/JosJuice/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/JosJuice/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/JosJuice/subscriptions', 'organizations_url': 'https://api.github.com/users/JosJuice/orgs', 'repos_url': 'https://api.github.com/users/JosJuice/repos', 'events_url': 'https://api.github.com/users/JosJuice/events{/privacy}', 'received_events_url': 'https://api.github.com/users/JosJuice/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'html_url': 'https://github.com/JosJuice/dolphin', 'description': 'Dolphin is a GameCube/Wii emulator, allowing you to play games for these two platforms on PC, with improvements.', 'fork': True, 'url': 'https://api.github.com/repos/JosJuice/dolphin', 'forks_url': 'https://api.github.com/repos/JosJuice/dolphin/forks', 'keys_url': 'https://api.github.com/repos/JosJuice/dolphin/keys{/key_id}', 'collaborators_url': 'https://api.github.com/repos/JosJuice/dolphin/collaborators{/collaborator}', 'teams_url': 'https://api.github.com/repos/JosJuice/dolphin/teams', 'hooks_url': 'https://api.github.com/repos/JosJuice/dolphin/hooks', 'issue_events_url': 'https://api.github.com/repos/JosJuice/dolphin/issues/events{/number}', 'events_url': 'https://api.github.com/repos/JosJuice/dolphin/events', 'assignees_url': 'https://api.github.com/repos/JosJuice/dolphin/assignees{/user}', 'branches_url': 'https://api.github.com/repos/JosJuice/dolphin/branches{/branch}', 'tags_url': 'https://api.github.com/repos/JosJuice/dolphin/tags', 'blobs_url': 'https://api.github.com/repos/JosJuice/dolphin/git/blobs{/sha}', 'git_tags_url': 'https://api.github.com/repos/JosJuice/dolphin/git/tags{/sha}', 'git_refs_url': 'https://api.github.com/repos/JosJuice/dolphin/git/refs{/sha}', 'trees_url': 'https://api.github.com/repos/JosJuice/dolphin/git/trees{/sha}', 'statuses_url': 'https://api.github.com/repos/JosJuice/dolphin/statuses/{sha}', 'languages_url': 'https://api.github.com/repos/JosJuice/dolphin/languages', 'stargazers_url': 'https://api.github.com/repos/JosJuice/dolphin/stargazers', 'contributors_url': 'https://api.github.com/repos/JosJuice/dolphin/contributors', 'subscribers_url': 'https://api.github.com/repos/JosJuice/dolphin/subscribers', 'subscription_url': 'https://api.github.com/repos/JosJuice/dolphin/subscription', 'commits_url': 'https://api.github.com/repos/JosJuice/dolphin/commits{/sha}', 'git_commits_url': 'https://api.github.com/repos/JosJuice/dolphin/git/commits{/sha}', 'comments_url': 'https://api.github.com/repos/JosJuice/dolphin/comments{/number}', 'issue_comment_url': 'https://api.github.com/repos/JosJuice/dolphin/issues/comments{/number}', 'contents_url': 'https://api.github.com/repos/JosJuice/dolphin/contents/{+path}', 'compare_url': 'https://api.github.com/repos/JosJuice/dolphin/compare/{base}...{head}', 'merges_url': 'https://api.github.com/repos/JosJuice/dolphin/merges', 'archive_url': 'https://api.github.com/repos/JosJuice/dolphin/{archive_format}{/ref}', 'downloads_url': 'https://api.github.com/repos/JosJuice/dolphin/downloads', 'issues_url': 'https://api.github.com/repos/JosJuice/dolphin/issues{/number}', 'pulls_url': 'https://api.github.com/repos/JosJuice/dolphin/pulls{/number}', 'milestones_url': 'https://api.github.com/repos/JosJuice/dolphin/milestones{/number}', 'notifications_url': 'https://api.github.com/repos/JosJuice/dolphin/notifications{?since,all,participating}', 'labels_url': 'https://api.github.com/repos/JosJuice/dolphin/labels{/name}', 'releases_url': 'https://api.github.com/repos/JosJuice/dolphin/releases{/id}', 'deployments_url': 'https://api.github.com/repos/JosJuice/dolphin/deployments', 'created_at': '2014-11-01T17:12:40Z', 'updated_at': '2026-02-01T11:39:14Z', 'pushed_at': '2026-02-03T22:04:34Z', 'git_url': 'git://github.com/JosJuice/dolphin.git', 'ssh_url': 'git@github.com:JosJuice/dolphin.git', 'clone_url': 'https://github.com/JosJuice/dolphin.git', 'svn_url': 'https://github.com/JosJuice/dolphin', 'homepage': None, 'size': 509732, 'stargazers_count': 6, 'watchers_count': 6, 'language': 'C++', 'has_issues': False, 'has_projects': True, 'has_downloads': True, 'has_wiki': True, 'has_pages': False, 'has_discussions': False, 'forks_count': 1, 'mirror_url': None, 'archived': False, 'disabled': False, 'open_issues_count': 0, 'license': {'key': 'other', 'name': 'Other', 'spdx_id': 'NOASSERTION', 'url': None, 'node_id': 'MDc6TGljZW5zZTA='}, 'allow_forking': True, 'is_template': False, 'web_commit_signoff_required': False, 'topics': [], 'visibility': 'public', 'forks': 1, 'open_issues': 0, 'watchers': 6, 'default_branch': 'master', 'allow_squash_merge': True, 'allow_merge_commit': True, 'allow_rebase_merge': True, 'allow_auto_merge': False, 'delete_branch_on_merge': False, 'allow_update_branch': False, 'use_squash_pr_title_as_default': False, 'squash_merge_commit_message': 'COMMIT_MESSAGES', 'squash_merge_commit_title': 'COMMIT_OR_PR_TITLE', 'merge_commit_message': 'PR_TITLE', 'merge_commit_title': 'MERGE_MESSAGE'}}, 'base': {'label': 'dolphin-emu:master', 'ref': 'master', 'sha': 'eac7b290423efea867c2c7f3f2b2792d05418bdd', 'user': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/dolphin-emu', 'html_url': 'https://github.com/dolphin-emu', 'followers_url': 'https://api.github.com/users/dolphin-emu/followers', 'following_url': 'https://api.github.com/users/dolphin-emu/following{/other_user}', 'gists_url': 'https://api.github.com/users/dolphin-emu/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/dolphin-emu/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/dolphin-emu/subscriptions', 'organizations_url': 'https://api.github.com/users/dolphin-emu/orgs', 'repos_url': 'https://api.github.com/users/dolphin-emu/repos', 'events_url': 'https://api.github.com/users/dolphin-emu/events{/privacy}', 'received_events_url': 'https://api.github.com/users/dolphin-emu/received_events', 'type': 'Organization', 'user_view_type': 'public', 'site_admin': False}, 'repo': {'id': 11577304, 'node_id': 'MDEwOlJlcG9zaXRvcnkxMTU3NzMwNA==', 'name': 'dolphin', 'full_name': 'dolphin-emu/dolphin', 'private': False, 'owner': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/dolphin-emu', 'html_url': 'https://github.com/dolphin-emu', 'followers_url': 'https://api.github.com/users/dolphin-emu/followers', 'following_url': 'https://api.github.com/users/dolphin-emu/following{/other_user}', 'gists_url': 'https://api.github.com/users/dolphin-emu/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/dolphin-emu/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/dolphin-emu/subscriptions', 'organizations_url': 'https://api.github.com/users/dolphin-emu/orgs', 'repos_url': 'https://api.github.com/users/dolphin-emu/repos', 'events_url': 'https://api.github.com/users/dolphin-emu/events{/privacy}', 'received_events_url': 'https://api.github.com/users/dolphin-emu/received_events', 'type': 'Organization', 'user_view_type': 'public', 'site_admin': False}, 'html_url': 'https://github.com/dolphin-emu/dolphin', 'description': 'Dolphin is a GameCube / Wii emulator, allowing you to play games for these two platforms on PC with improvements.', 'fork': False, 'url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'forks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/forks', 'keys_url': 'https://api.github.com/repos/dolphin-emu/dolphin/keys{/key_id}', 'collaborators_url': 'https://api.github.com/repos/dolphin-emu/dolphin/collaborators{/collaborator}', 'teams_url': 'https://api.github.com/repos/dolphin-emu/dolphin/teams', 'hooks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/hooks', 'issue_events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/events{/number}', 'events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/events', 'assignees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/assignees{/user}', 'branches_url': 'https://api.github.com/repos/dolphin-emu/dolphin/branches{/branch}', 'tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/tags', 'blobs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/blobs{/sha}', 'git_tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/tags{/sha}', 'git_refs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/refs{/sha}', 'trees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/trees{/sha}', 'statuses_url': 'https://api.github.com/repos/dolphin-emu/dolphin/statuses/{sha}', 'languages_url': 'https://api.github.com/repos/dolphin-emu/dolphin/languages', 'stargazers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/stargazers', 'contributors_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contributors', 'subscribers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscribers', 'subscription_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscription', 'commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/commits{/sha}', 'git_commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/commits{/sha}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/comments{/number}', 'issue_comment_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments{/number}', 'contents_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contents/{+path}', 'compare_url': 'https://api.github.com/repos/dolphin-emu/dolphin/compare/{base}...{head}', 'merges_url': 'https://api.github.com/repos/dolphin-emu/dolphin/merges', 'archive_url': 'https://api.github.com/repos/dolphin-emu/dolphin/{archive_format}{/ref}', 'downloads_url': 'https://api.github.com/repos/dolphin-emu/dolphin/downloads', 'issues_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues{/number}', 'pulls_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls{/number}', 'milestones_url': 'https://api.github.com/repos/dolphin-emu/dolphin/milestones{/number}', 'notifications_url': 'https://api.github.com/repos/dolphin-emu/dolphin/notifications{?since,all,participating}', 'labels_url': 'https://api.github.com/repos/dolphin-emu/dolphin/labels{/name}', 'releases_url': 'https://api.github.com/repos/dolphin-emu/dolphin/releases{/id}', 'deployments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/deployments', 'created_at': '2013-07-22T09:07:57Z', 'updated_at': '2026-02-03T18:03:13Z', 'pushed_at': '2026-02-03T04:26:13Z', 'git_url': 'git://github.com/dolphin-emu/dolphin.git', 'ssh_url': 'git@github.com:dolphin-emu/dolphin.git', 'clone_url': 'https://github.com/dolphin-emu/dolphin.git', 'svn_url': 'https://github.com/dolphin-emu/dolphin', 'homepage': 'https://dolphin-emu.org', 'size': 544014, 'stargazers_count': 14582, 'watchers_count': 14582, 'language': 'C++', 'has_issues': False, 'has_projects': False, 'has_downloads': True, 'has_wiki': True, 'has_pages': False, 'has_discussions': False, 'forks_count': 2966, 'mirror_url': None, 'archived': False, 'disabled': False, 'open_issues_count': 360, 'license': {'key': 'other', 'name': 'Other', 'spdx_id': 'NOASSERTION', 'url': None, 'node_id': 'MDc6TGljZW5zZTA='}, 'allow_forking': True, 'is_template': False, 'web_commit_signoff_required': False, 'topics': ['cpp', 'emulation', 'emulator', 'gamecube', 'wii'], 'visibility': 'public', 'forks': 2966, 'open_issues': 360, 'watchers': 14582, 'default_branch': 'master', 'allow_squash_merge': False, 'allow_merge_commit': True, 'allow_rebase_merge': False, 'allow_auto_merge': False, 'delete_branch_on_merge': False, 'allow_update_branch': False, 'use_squash_pr_title_as_default': False, 'squash_merge_commit_message': 'COMMIT_MESSAGES', 'squash_merge_commit_title': 'COMMIT_OR_PR_TITLE', 'merge_commit_message': 'PR_TITLE', 'merge_commit_title': 'MERGE_MESSAGE'}}, '_links': {'self': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13768'}, 'html': {'href': 'https://github.com/dolphin-emu/dolphin/pull/13768'}, 'issue': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13768'}, 'comments': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13768/comments'}, 'review_comments': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13768/comments'}, 'review_comment': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments{/number}'}, 'commits': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13768/commits'}, 'statuses': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/statuses/a7301d7d406bc5955303a13e61327c5b17645b7a'}}, 'author_association': 'MEMBER', 'auto_merge': None, 'active_lock_reason': None, 'merged': False, 'mergeable': None, 'rebaseable': None, 'mergeable_state': 'unknown', 'merged_by': None, 'comments': 23, 'review_comments': 14, 'maintainer_can_modify': True, 'commits': 10, 'additions': 2042, 'deletions': 153, 'changed_files': 33}, 'before': 'e88ee42d345e0b2a85cc6bd44c7258eb8be3c6b8', 'after': 'a7301d7d406bc5955303a13e61327c5b17645b7a', 'repository': {'id': 11577304, 'node_id': 'MDEwOlJlcG9zaXRvcnkxMTU3NzMwNA==', 'name': 'dolphin', 'full_name': 'dolphin-emu/dolphin', 'private': False, 'owner': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/dolphin-emu', 'html_url': 'https://github.com/dolphin-emu', 'followers_url': 'https://api.github.com/users/dolphin-emu/followers', 'following_url': 'https://api.github.com/users/dolphin-emu/following{/other_user}', 'gists_url': 'https://api.github.com/users/dolphin-emu/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/dolphin-emu/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/dolphin-emu/subscriptions', 'organizations_url': 'https://api.github.com/users/dolphin-emu/orgs', 'repos_url': 'https://api.github.com/users/dolphin-emu/repos', 'events_url': 'https://api.github.com/users/dolphin-emu/events{/privacy}', 'received_events_url': 'https://api.github.com/users/dolphin-emu/received_events', 'type': 'Organization', 'user_view_type': 'public', 'site_admin': False}, 'html_url': 'https://github.com/dolphin-emu/dolphin', 'description': 'Dolphin is a GameCube / Wii emulator, allowing you to play games for these two platforms on PC with improvements.', 'fork': False, 'url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'forks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/forks', 'keys_url': 'https://api.github.com/repos/dolphin-emu/dolphin/keys{/key_id}', 'collaborators_url': 'https://api.github.com/repos/dolphin-emu/dolphin/collaborators{/collaborator}', 'teams_url': 'https://api.github.com/repos/dolphin-emu/dolphin/teams', 'hooks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/hooks', 'issue_events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/events{/number}', 'events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/events', 'assignees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/assignees{/user}', 'branches_url': 'https://api.github.com/repos/dolphin-emu/dolphin/branches{/branch}', 'tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/tags', 'blobs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/blobs{/sha}', 'git_tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/tags{/sha}', 'git_refs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/refs{/sha}', 'trees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/trees{/sha}', 'statuses_url': 'https://api.github.com/repos/dolphin-emu/dolphin/statuses/{sha}', 'languages_url': 'https://api.github.com/repos/dolphin-emu/dolphin/languages', 'stargazers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/stargazers', 'contributors_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contributors', 'subscribers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscribers', 'subscription_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscription', 'commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/commits{/sha}', 'git_commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/commits{/sha}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/comments{/number}', 'issue_comment_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments{/number}', 'contents_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contents/{+path}', 'compare_url': 'https://api.github.com/repos/dolphin-emu/dolphin/compare/{base}...{head}', 'merges_url': 'https://api.github.com/repos/dolphin-emu/dolphin/merges', 'archive_url': 'https://api.github.com/repos/dolphin-emu/dolphin/{archive_format}{/ref}', 'downloads_url': 'https://api.github.com/repos/dolphin-emu/dolphin/downloads', 'issues_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues{/number}', 'pulls_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls{/number}', 'milestones_url': 'https://api.github.com/repos/dolphin-emu/dolphin/milestones{/number}', 'notifications_url': 'https://api.github.com/repos/dolphin-emu/dolphin/notifications{?since,all,participating}', 'labels_url': 'https://api.github.com/repos/dolphin-emu/dolphin/labels{/name}', 'releases_url': 'https://api.github.com/repos/dolphin-emu/dolphin/releases{/id}', 'deployments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/deployments', 'created_at': '2013-07-22T09:07:57Z', 'updated_at': '2026-02-03T18:03:13Z', 'pushed_at': '2026-02-03T04:26:13Z', 'git_url': 'git://github.com/dolphin-emu/dolphin.git', 'ssh_url': 'git@github.com:dolphin-emu/dolphin.git', 'clone_url': 'https://github.com/dolphin-emu/dolphin.git', 'svn_url': 'https://github.com/dolphin-emu/dolphin', 'homepage': 'https://dolphin-emu.org', 'size': 544014, 'stargazers_count': 14582, 'watchers_count': 14582, 'language': 'C++', 'has_issues': False, 'has_projects': False, 'has_downloads': True, 'has_wiki': True, 'has_pages': False, 'has_discussions': False, 'forks_count': 2966, 'mirror_url': None, 'archived': False, 'disabled': False, 'open_issues_count': 360, 'license': {'key': 'other', 'name': 'Other', 'spdx_id': 'NOASSERTION', 'url': None, 'node_id': 'MDc6TGljZW5zZTA='}, 'allow_forking': True, 'is_template': False, 'web_commit_signoff_required': False, 'topics': ['cpp', 'emulation', 'emulator', 'gamecube', 'wii'], 'visibility': 'public', 'forks': 2966, 'open_issues': 360, 'watchers': 14582, 'default_branch': 'master', 'custom_properties': {}}, 'organization': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'url': 'https://api.github.com/orgs/dolphin-emu', 'repos_url': 'https://api.github.com/orgs/dolphin-emu/repos', 'events_url': 'https://api.github.com/orgs/dolphin-emu/events', 'hooks_url': 'https://api.github.com/orgs/dolphin-emu/hooks', 'issues_url': 'https://api.github.com/orgs/dolphin-emu/issues', 'members_url': 'https://api.github.com/orgs/dolphin-emu/members{/member}', 'public_members_url': 'https://api.github.com/orgs/dolphin-emu/public_members{/member}', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'description': ''}, 'sender': {'login': 'JosJuice', 'id': 6716818, 'node_id': 'MDQ6VXNlcjY3MTY4MTg=', 'avatar_url': 'https://avatars.githubusercontent.com/u/6716818?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/JosJuice', 'html_url': 'https://github.com/JosJuice', 'followers_url': 'https://api.github.com/users/JosJuice/followers', 'following_url': 'https://api.github.com/users/JosJuice/following{/other_user}', 'gists_url': 'https://api.github.com/users/JosJuice/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/JosJuice/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/JosJuice/subscriptions', 'organizations_url': 'https://api.github.com/users/JosJuice/orgs', 'repos_url': 'https://api.github.com/users/JosJuice/repos', 'events_url': 'https://api.github.com/users/JosJuice/events{/privacy}', 'received_events_url': 'https://api.github.com/users/JosJuice/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'installation': {'id': 35437981, 'node_id': 'MDIzOkludGVncmF0aW9uSW5zdGFsbGF0aW9uMzU0Mzc5ODE='}}, 'type': 'raw_gh_hook'}
2026-02-03T21:10:07.303456 {'source': 'webserver', 'gh_type': 'issue_comment', 'raw': {'action': 'created', 'issue': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13970', 'repository_url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'labels_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13970/labels{/name}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13970/comments', 'events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13970/events', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/13970', 'id': 3455745903, 'node_id': 'PR_kwDOALCn2M6qnqKV', 'number': 13970, 'title': 'WiimoteReal: Windows improvements.', 'user': {'login': 'jordan-woyak', 'id': 1768214, 'node_id': 'MDQ6VXNlcjE3NjgyMTQ=', 'avatar_url': 'https://avatars.githubusercontent.com/u/1768214?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/jordan-woyak', 'html_url': 'https://github.com/jordan-woyak', 'followers_url': 'https://api.github.com/users/jordan-woyak/followers', 'following_url': 'https://api.github.com/users/jordan-woyak/following{/other_user}', 'gists_url': 'https://api.github.com/users/jordan-woyak/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/jordan-woyak/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/jordan-woyak/subscriptions', 'organizations_url': 'https://api.github.com/users/jordan-woyak/orgs', 'repos_url': 'https://api.github.com/users/jordan-woyak/repos', 'events_url': 'https://api.github.com/users/jordan-woyak/events{/privacy}', 'received_events_url': 'https://api.github.com/users/jordan-woyak/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'labels': [], 'state': 'closed', 'locked': False, 'assignee': None, 'assignees': [], 'milestone': None, 'comments': 27, 'created_at': '2025-09-26T04:25:44Z', 'updated_at': '2026-02-03T21:10:04Z', 'closed_at': '2025-10-13T06:54:54Z', 'author_association': 'MEMBER', 'type': None, 'active_lock_reason': None, 'draft': False, 'pull_request': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13970', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/13970', 'diff_url': 'https://github.com/dolphin-emu/dolphin/pull/13970.diff', 'patch_url': 'https://github.com/dolphin-emu/dolphin/pull/13970.patch', 'merged_at': '2025-10-13T06:54:54Z'}, 'body': 'Majorly overhauled the Windows Wii remote code.\r\n\r\nThis PR should be ready now.\r\n\r\nStatically link with "Bthprops.lib"\r\nDynamic linking was introduced in https://github.com/dolphin-emu/dolphin/commit/3acf0eb3af98009cc4e00bd9bcf43873dc26c4da to make Dolphin run under Wine.\r\nMy search results imply that Wine has long since added stubs for these functions so I\'m hoping it works fine now?\r\nedit: Wine seems to work fine.\r\n\r\nA 10 year old comment describes a workaround needed for the Toshiba Bluetooth stack.\r\nI\'ve removed the workaround.\r\n\r\nRemoved `HidP_SetOutputReport` fallback (Windows 7 workaround).\r\n\r\nFixed buggy overlapped Read/Write event usage (probably my fault from long ago).\r\n\r\nAdded checks for a Wii remote device name on the HID interface to only attempt connecting to actual Wii remotes devices.\r\nThe existing code just throws Wii remote data at every interface and checks for sane replies.\r\n\r\nAdded a periodic write to test for disconnected remotes in a timely manner.\r\n\r\nChanged the `Refresh` button to not remove authenticated remotes.\r\nSome users found it annoying.\r\nIt now only removes unauthenticated disconnected remotes.\r\n\r\nAdded `Sync` and `Reset` actions to establish and remove pairings.\r\nThe `Refresh` button is now a `QToolButton` with additional actions (only on Windows).\r\n<img width="120" height="177" alt="image" src="https://github.com/user-attachments/assets/16e57431-1eab-4465-a4f8-856188b0f942" />\r\n\r\nPairing a remote with the `Sync` button will enable it to reestablish a connection on any button press.\r\nIt currently shows an ugly animation while the task runs async.\r\n<img width="189" height="72" alt="image" src="https://github.com/user-attachments/assets/076191ce-8312-4cdc-8223-14a4bb9eca2d" />\r\nIt\'s better than nothing and can be visually improved later.\r\n\r\n`Refresh` or `Continuous Scanning` are no longer required to connect paired remotes.\r\nPairing with Windows or the DolphinBar "just works" by assigning a slot to `Real Wii Remote`.\r\n\r\nAdditionally, I am constantly bombarded with ControllerInterface / WiimotePool related deadlocks.\r\nThis seems like an existing issue that fixing properly is probably going to take another PR.\r\nIt seems like it\'s SDL?\r\nedit: SDL\'s DirectInput handling has been disabled in #13982 \r\n\r\nTODO\r\n - [x] Make the `Sync` button not require also clicking `Refresh`.\r\n - [x] Make paired + reconnected Wii remotes work without using `Refresh`\r\n - [x] Test Dolphin with Wine (just in general, not Wii remote functionality)\r\n - [x] Clean up log spam from continuous opening of HID interfaces.\r\n - [x] Make the Sync / Reset buttons display some kind kind of feedback.', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13970/reactions', 'total_count': 4, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 1, 'confused': 0, 'heart': 3, 'rocket': 0, 'eyes': 0}, 'timeline_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13970/timeline', 'performed_via_github_app': None, 'state_reason': None}, 'comment': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments/3843701295', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/13970#issuecomment-3843701295', 'issue_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13970', 'id': 3843701295, 'node_id': 'IC_kwDOALCn2M7lGjov', 'user': {'login': 'jordan-woyak', 'id': 1768214, 'node_id': 'MDQ6VXNlcjE3NjgyMTQ=', 'avatar_url': 'https://avatars.githubusercontent.com/u/1768214?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/jordan-woyak', 'html_url': 'https://github.com/jordan-woyak', 'followers_url': 'https://api.github.com/users/jordan-woyak/followers', 'following_url': 'https://api.github.com/users/jordan-woyak/following{/other_user}', 'gists_url': 'https://api.github.com/users/jordan-woyak/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/jordan-woyak/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/jordan-woyak/subscriptions', 'organizations_url': 'https://api.github.com/users/jordan-woyak/orgs', 'repos_url': 'https://api.github.com/users/jordan-woyak/repos', 'events_url': 'https://api.github.com/users/jordan-woyak/events{/privacy}', 'received_events_url': 'https://api.github.com/users/jordan-woyak/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'created_at': '2026-02-03T21:10:04Z', 'updated_at': '2026-02-03T21:10:04Z', 'body': "> Do you know why some bt dongles rejects the reconnection? Probably some security feature on authentication that wiimotes doesn't have.\r\n\r\nI don't know. I haven't experienced that.", 'author_association': 'MEMBER', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments/3843701295/reactions', 'total_count': 0, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 0, 'confused': 0, 'heart': 0, 'rocket': 0, 'eyes': 0}, 'performed_via_github_app': None}, 'repository': {'id': 11577304, 'node_id': 'MDEwOlJlcG9zaXRvcnkxMTU3NzMwNA==', 'name': 'dolphin', 'full_name': 'dolphin-emu/dolphin', 'private': False, 'owner': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/dolphin-emu', 'html_url': 'https://github.com/dolphin-emu', 'followers_url': 'https://api.github.com/users/dolphin-emu/followers', 'following_url': 'https://api.github.com/users/dolphin-emu/following{/other_user}', 'gists_url': 'https://api.github.com/users/dolphin-emu/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/dolphin-emu/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/dolphin-emu/subscriptions', 'organizations_url': 'https://api.github.com/users/dolphin-emu/orgs', 'repos_url': 'https://api.github.com/users/dolphin-emu/repos', 'events_url': 'https://api.github.com/users/dolphin-emu/events{/privacy}', 'received_events_url': 'https://api.github.com/users/dolphin-emu/received_events', 'type': 'Organization', 'user_view_type': 'public', 'site_admin': False}, 'html_url': 'https://github.com/dolphin-emu/dolphin', 'description': 'Dolphin is a GameCube / Wii emulator, allowing you to play games for these two platforms on PC with improvements.', 'fork': False, 'url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'forks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/forks', 'keys_url': 'https://api.github.com/repos/dolphin-emu/dolphin/keys{/key_id}', 'collaborators_url': 'https://api.github.com/repos/dolphin-emu/dolphin/collaborators{/collaborator}', 'teams_url': 'https://api.github.com/repos/dolphin-emu/dolphin/teams', 'hooks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/hooks', 'issue_events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/events{/number}', 'events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/events', 'assignees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/assignees{/user}', 'branches_url': 'https://api.github.com/repos/dolphin-emu/dolphin/branches{/branch}', 'tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/tags', 'blobs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/blobs{/sha}', 'git_tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/tags{/sha}', 'git_refs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/refs{/sha}', 'trees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/trees{/sha}', 'statuses_url': 'https://api.github.com/repos/dolphin-emu/dolphin/statuses/{sha}', 'languages_url': 'https://api.github.com/repos/dolphin-emu/dolphin/languages', 'stargazers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/stargazers', 'contributors_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contributors', 'subscribers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscribers', 'subscription_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscription', 'commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/commits{/sha}', 'git_commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/commits{/sha}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/comments{/number}', 'issue_comment_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments{/number}', 'contents_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contents/{+path}', 'compare_url': 'https://api.github.com/repos/dolphin-emu/dolphin/compare/{base}...{head}', 'merges_url': 'https://api.github.com/repos/dolphin-emu/dolphin/merges', 'archive_url': 'https://api.github.com/repos/dolphin-emu/dolphin/{archive_format}{/ref}', 'downloads_url': 'https://api.github.com/repos/dolphin-emu/dolphin/downloads', 'issues_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues{/number}', 'pulls_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls{/number}', 'milestones_url': 'https://api.github.com/repos/dolphin-emu/dolphin/milestones{/number}', 'notifications_url': 'https://api.github.com/repos/dolphin-emu/dolphin/notifications{?since,all,participating}', 'labels_url': 'https://api.github.com/repos/dolphin-emu/dolphin/labels{/name}', 'releases_url': 'https://api.github.com/repos/dolphin-emu/dolphin/releases{/id}', 'deployments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/deployments', 'created_at': '2013-07-22T09:07:57Z', 'updated_at': '2026-02-03T18:03:13Z', 'pushed_at': '2026-02-03T04:26:13Z', 'git_url': 'git://github.com/dolphin-emu/dolphin.git', 'ssh_url': 'git@github.com:dolphin-emu/dolphin.git', 'clone_url': 'https://github.com/dolphin-emu/dolphin.git', 'svn_url': 'https://github.com/dolphin-emu/dolphin', 'homepage': 'https://dolphin-emu.org', 'size': 544014, 'stargazers_count': 14582, 'watchers_count': 14582, 'language': 'C++', 'has_issues': False, 'has_projects': False, 'has_downloads': True, 'has_wiki': True, 'has_pages': False, 'has_discussions': False, 'forks_count': 2966, 'mirror_url': None, 'archived': False, 'disabled': False, 'open_issues_count': 360, 'license': {'key': 'other', 'name': 'Other', 'spdx_id': 'NOASSERTION', 'url': None, 'node_id': 'MDc6TGljZW5zZTA='}, 'allow_forking': True, 'is_template': False, 'web_commit_signoff_required': False, 'topics': ['cpp', 'emulation', 'emulator', 'gamecube', 'wii'], 'visibility': 'public', 'forks': 2966, 'open_issues': 360, 'watchers': 14582, 'default_branch': 'master', 'custom_properties': {}}, 'organization': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'url': 'https://api.github.com/orgs/dolphin-emu', 'repos_url': 'https://api.github.com/orgs/dolphin-emu/repos', 'events_url': 'https://api.github.com/orgs/dolphin-emu/events', 'hooks_url': 'https://api.github.com/orgs/dolphin-emu/hooks', 'issues_url': 'https://api.github.com/orgs/dolphin-emu/issues', 'members_url': 'https://api.github.com/orgs/dolphin-emu/members{/member}', 'public_members_url': 'https://api.github.com/orgs/dolphin-emu/public_members{/member}', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'description': ''}, 'sender': {'login': 'jordan-woyak', 'id': 1768214, 'node_id': 'MDQ6VXNlcjE3NjgyMTQ=', 'avatar_url': 'https://avatars.githubusercontent.com/u/1768214?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/jordan-woyak', 'html_url': 'https://github.com/jordan-woyak', 'followers_url': 'https://api.github.com/users/jordan-woyak/followers', 'following_url': 'https://api.github.com/users/jordan-woyak/following{/other_user}', 'gists_url': 'https://api.github.com/users/jordan-woyak/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/jordan-woyak/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/jordan-woyak/subscriptions', 'organizations_url': 'https://api.github.com/users/jordan-woyak/orgs', 'repos_url': 'https://api.github.com/users/jordan-woyak/repos', 'events_url': 'https://api.github.com/users/jordan-woyak/events{/privacy}', 'received_events_url': 'https://api.github.com/users/jordan-woyak/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'installation': {'id': 35437981, 'node_id': 'MDIzOkludGVncmF0aW9uSW5zdGFsbGF0aW9uMzU0Mzc5ODE='}}, 'type': 'raw_gh_hook'}
2026-02-03T21:01:13.944102 {'source': 'webserver', 'gh_type': 'issue_comment', 'raw': {'action': 'edited', 'changes': {'body': {'from': "Do you know why some bt dongles rejects the reconnection? Probably some security feature on authentication that wiimotes doesn't have.\r\n\r\nI been testing many built in BT and found the mediatek to have this problem. There are others. Some can fe fixed using another driver\r\n\r\nSo seems to be BT driver related. But maybe the BT dongle expect another response and we can change that. "}}, 'issue': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13970', 'repository_url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'labels_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13970/labels{/name}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13970/comments', 'events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13970/events', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/13970', 'id': 3455745903, 'node_id': 'PR_kwDOALCn2M6qnqKV', 'number': 13970, 'title': 'WiimoteReal: Windows improvements.', 'user': {'login': 'jordan-woyak', 'id': 1768214, 'node_id': 'MDQ6VXNlcjE3NjgyMTQ=', 'avatar_url': 'https://avatars.githubusercontent.com/u/1768214?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/jordan-woyak', 'html_url': 'https://github.com/jordan-woyak', 'followers_url': 'https://api.github.com/users/jordan-woyak/followers', 'following_url': 'https://api.github.com/users/jordan-woyak/following{/other_user}', 'gists_url': 'https://api.github.com/users/jordan-woyak/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/jordan-woyak/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/jordan-woyak/subscriptions', 'organizations_url': 'https://api.github.com/users/jordan-woyak/orgs', 'repos_url': 'https://api.github.com/users/jordan-woyak/repos', 'events_url': 'https://api.github.com/users/jordan-woyak/events{/privacy}', 'received_events_url': 'https://api.github.com/users/jordan-woyak/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'labels': [], 'state': 'closed', 'locked': False, 'assignee': None, 'assignees': [], 'milestone': None, 'comments': 26, 'created_at': '2025-09-26T04:25:44Z', 'updated_at': '2026-02-03T20:58:41Z', 'closed_at': '2025-10-13T06:54:54Z', 'author_association': 'MEMBER', 'type': None, 'active_lock_reason': None, 'draft': False, 'pull_request': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13970', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/13970', 'diff_url': 'https://github.com/dolphin-emu/dolphin/pull/13970.diff', 'patch_url': 'https://github.com/dolphin-emu/dolphin/pull/13970.patch', 'merged_at': '2025-10-13T06:54:54Z'}, 'body': 'Majorly overhauled the Windows Wii remote code.\r\n\r\nThis PR should be ready now.\r\n\r\nStatically link with "Bthprops.lib"\r\nDynamic linking was introduced in https://github.com/dolphin-emu/dolphin/commit/3acf0eb3af98009cc4e00bd9bcf43873dc26c4da to make Dolphin run under Wine.\r\nMy search results imply that Wine has long since added stubs for these functions so I\'m hoping it works fine now?\r\nedit: Wine seems to work fine.\r\n\r\nA 10 year old comment describes a workaround needed for the Toshiba Bluetooth stack.\r\nI\'ve removed the workaround.\r\n\r\nRemoved `HidP_SetOutputReport` fallback (Windows 7 workaround).\r\n\r\nFixed buggy overlapped Read/Write event usage (probably my fault from long ago).\r\n\r\nAdded checks for a Wii remote device name on the HID interface to only attempt connecting to actual Wii remotes devices.\r\nThe existing code just throws Wii remote data at every interface and checks for sane replies.\r\n\r\nAdded a periodic write to test for disconnected remotes in a timely manner.\r\n\r\nChanged the `Refresh` button to not remove authenticated remotes.\r\nSome users found it annoying.\r\nIt now only removes unauthenticated disconnected remotes.\r\n\r\nAdded `Sync` and `Reset` actions to establish and remove pairings.\r\nThe `Refresh` button is now a `QToolButton` with additional actions (only on Windows).\r\n<img width="120" height="177" alt="image" src="https://github.com/user-attachments/assets/16e57431-1eab-4465-a4f8-856188b0f942" />\r\n\r\nPairing a remote with the `Sync` button will enable it to reestablish a connection on any button press.\r\nIt currently shows an ugly animation while the task runs async.\r\n<img width="189" height="72" alt="image" src="https://github.com/user-attachments/assets/076191ce-8312-4cdc-8223-14a4bb9eca2d" />\r\nIt\'s better than nothing and can be visually improved later.\r\n\r\n`Refresh` or `Continuous Scanning` are no longer required to connect paired remotes.\r\nPairing with Windows or the DolphinBar "just works" by assigning a slot to `Real Wii Remote`.\r\n\r\nAdditionally, I am constantly bombarded with ControllerInterface / WiimotePool related deadlocks.\r\nThis seems like an existing issue that fixing properly is probably going to take another PR.\r\nIt seems like it\'s SDL?\r\nedit: SDL\'s DirectInput handling has been disabled in #13982 \r\n\r\nTODO\r\n - [x] Make the `Sync` button not require also clicking `Refresh`.\r\n - [x] Make paired + reconnected Wii remotes work without using `Refresh`\r\n - [x] Test Dolphin with Wine (just in general, not Wii remote functionality)\r\n - [x] Clean up log spam from continuous opening of HID interfaces.\r\n - [x] Make the Sync / Reset buttons display some kind kind of feedback.', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13970/reactions', 'total_count': 4, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 1, 'confused': 0, 'heart': 3, 'rocket': 0, 'eyes': 0}, 'timeline_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13970/timeline', 'performed_via_github_app': None, 'state_reason': None}, 'comment': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments/3843649310', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/13970#issuecomment-3843649310', 'issue_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13970', 'id': 3843649310, 'node_id': 'IC_kwDOALCn2M7lGW8e', 'user': {'login': 'Trihy', 'id': 107453916, 'node_id': 'U_kgDOBmed3A', 'avatar_url': 'https://avatars.githubusercontent.com/u/107453916?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/Trihy', 'html_url': 'https://github.com/Trihy', 'followers_url': 'https://api.github.com/users/Trihy/followers', 'following_url': 'https://api.github.com/users/Trihy/following{/other_user}', 'gists_url': 'https://api.github.com/users/Trihy/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/Trihy/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/Trihy/subscriptions', 'organizations_url': 'https://api.github.com/users/Trihy/orgs', 'repos_url': 'https://api.github.com/users/Trihy/repos', 'events_url': 'https://api.github.com/users/Trihy/events{/privacy}', 'received_events_url': 'https://api.github.com/users/Trihy/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'created_at': '2026-02-03T20:58:41Z', 'updated_at': '2026-02-03T21:01:12Z', 'body': "Do you know why some bt dongles rejects the reconnection? Probably some security feature on authentication that wiimotes doesn't have.\r\n\r\nI been testing many built in BT and found the mediatek to have this problem. There are others. Some can fe fixed using another driver\r\n\r\nSo seems to be BT driver related. But maybe the BT dongle expect another response and we can change that. The wiimote blink 2 or 3 times and turn off. That usually means rejected.", 'author_association': 'NONE', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments/3843649310/reactions', 'total_count': 0, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 0, 'confused': 0, 'heart': 0, 'rocket': 0, 'eyes': 0}, 'performed_via_github_app': None}, 'repository': {'id': 11577304, 'node_id': 'MDEwOlJlcG9zaXRvcnkxMTU3NzMwNA==', 'name': 'dolphin', 'full_name': 'dolphin-emu/dolphin', 'private': False, 'owner': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/dolphin-emu', 'html_url': 'https://github.com/dolphin-emu', 'followers_url': 'https://api.github.com/users/dolphin-emu/followers', 'following_url': 'https://api.github.com/users/dolphin-emu/following{/other_user}', 'gists_url': 'https://api.github.com/users/dolphin-emu/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/dolphin-emu/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/dolphin-emu/subscriptions', 'organizations_url': 'https://api.github.com/users/dolphin-emu/orgs', 'repos_url': 'https://api.github.com/users/dolphin-emu/repos', 'events_url': 'https://api.github.com/users/dolphin-emu/events{/privacy}', 'received_events_url': 'https://api.github.com/users/dolphin-emu/received_events', 'type': 'Organization', 'user_view_type': 'public', 'site_admin': False}, 'html_url': 'https://github.com/dolphin-emu/dolphin', 'description': 'Dolphin is a GameCube / Wii emulator, allowing you to play games for these two platforms on PC with improvements.', 'fork': False, 'url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'forks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/forks', 'keys_url': 'https://api.github.com/repos/dolphin-emu/dolphin/keys{/key_id}', 'collaborators_url': 'https://api.github.com/repos/dolphin-emu/dolphin/collaborators{/collaborator}', 'teams_url': 'https://api.github.com/repos/dolphin-emu/dolphin/teams', 'hooks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/hooks', 'issue_events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/events{/number}', 'events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/events', 'assignees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/assignees{/user}', 'branches_url': 'https://api.github.com/repos/dolphin-emu/dolphin/branches{/branch}', 'tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/tags', 'blobs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/blobs{/sha}', 'git_tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/tags{/sha}', 'git_refs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/refs{/sha}', 'trees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/trees{/sha}', 'statuses_url': 'https://api.github.com/repos/dolphin-emu/dolphin/statuses/{sha}', 'languages_url': 'https://api.github.com/repos/dolphin-emu/dolphin/languages', 'stargazers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/stargazers', 'contributors_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contributors', 'subscribers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscribers', 'subscription_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscription', 'commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/commits{/sha}', 'git_commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/commits{/sha}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/comments{/number}', 'issue_comment_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments{/number}', 'contents_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contents/{+path}', 'compare_url': 'https://api.github.com/repos/dolphin-emu/dolphin/compare/{base}...{head}', 'merges_url': 'https://api.github.com/repos/dolphin-emu/dolphin/merges', 'archive_url': 'https://api.github.com/repos/dolphin-emu/dolphin/{archive_format}{/ref}', 'downloads_url': 'https://api.github.com/repos/dolphin-emu/dolphin/downloads', 'issues_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues{/number}', 'pulls_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls{/number}', 'milestones_url': 'https://api.github.com/repos/dolphin-emu/dolphin/milestones{/number}', 'notifications_url': 'https://api.github.com/repos/dolphin-emu/dolphin/notifications{?since,all,participating}', 'labels_url': 'https://api.github.com/repos/dolphin-emu/dolphin/labels{/name}', 'releases_url': 'https://api.github.com/repos/dolphin-emu/dolphin/releases{/id}', 'deployments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/deployments', 'created_at': '2013-07-22T09:07:57Z', 'updated_at': '2026-02-03T18:03:13Z', 'pushed_at': '2026-02-03T04:26:13Z', 'git_url': 'git://github.com/dolphin-emu/dolphin.git', 'ssh_url': 'git@github.com:dolphin-emu/dolphin.git', 'clone_url': 'https://github.com/dolphin-emu/dolphin.git', 'svn_url': 'https://github.com/dolphin-emu/dolphin', 'homepage': 'https://dolphin-emu.org', 'size': 544014, 'stargazers_count': 14582, 'watchers_count': 14582, 'language': 'C++', 'has_issues': False, 'has_projects': False, 'has_downloads': True, 'has_wiki': True, 'has_pages': False, 'has_discussions': False, 'forks_count': 2966, 'mirror_url': None, 'archived': False, 'disabled': False, 'open_issues_count': 360, 'license': {'key': 'other', 'name': 'Other', 'spdx_id': 'NOASSERTION', 'url': None, 'node_id': 'MDc6TGljZW5zZTA='}, 'allow_forking': True, 'is_template': False, 'web_commit_signoff_required': False, 'topics': ['cpp', 'emulation', 'emulator', 'gamecube', 'wii'], 'visibility': 'public', 'forks': 2966, 'open_issues': 360, 'watchers': 14582, 'default_branch': 'master', 'custom_properties': {}}, 'organization': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'url': 'https://api.github.com/orgs/dolphin-emu', 'repos_url': 'https://api.github.com/orgs/dolphin-emu/repos', 'events_url': 'https://api.github.com/orgs/dolphin-emu/events', 'hooks_url': 'https://api.github.com/orgs/dolphin-emu/hooks', 'issues_url': 'https://api.github.com/orgs/dolphin-emu/issues', 'members_url': 'https://api.github.com/orgs/dolphin-emu/members{/member}', 'public_members_url': 'https://api.github.com/orgs/dolphin-emu/public_members{/member}', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'description': ''}, 'sender': {'login': 'Trihy', 'id': 107453916, 'node_id': 'U_kgDOBmed3A', 'avatar_url': 'https://avatars.githubusercontent.com/u/107453916?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/Trihy', 'html_url': 'https://github.com/Trihy', 'followers_url': 'https://api.github.com/users/Trihy/followers', 'following_url': 'https://api.github.com/users/Trihy/following{/other_user}', 'gists_url': 'https://api.github.com/users/Trihy/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/Trihy/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/Trihy/subscriptions', 'organizations_url': 'https://api.github.com/users/Trihy/orgs', 'repos_url': 'https://api.github.com/users/Trihy/repos', 'events_url': 'https://api.github.com/users/Trihy/events{/privacy}', 'received_events_url': 'https://api.github.com/users/Trihy/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'installation': {'id': 35437981, 'node_id': 'MDIzOkludGVncmF0aW9uSW5zdGFsbGF0aW9uMzU0Mzc5ODE='}}, 'type': 'raw_gh_hook'}
2026-02-03T20:58:44.456501 {'source': 'webserver', 'gh_type': 'issue_comment', 'raw': {'action': 'created', 'issue': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13970', 'repository_url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'labels_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13970/labels{/name}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13970/comments', 'events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13970/events', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/13970', 'id': 3455745903, 'node_id': 'PR_kwDOALCn2M6qnqKV', 'number': 13970, 'title': 'WiimoteReal: Windows improvements.', 'user': {'login': 'jordan-woyak', 'id': 1768214, 'node_id': 'MDQ6VXNlcjE3NjgyMTQ=', 'avatar_url': 'https://avatars.githubusercontent.com/u/1768214?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/jordan-woyak', 'html_url': 'https://github.com/jordan-woyak', 'followers_url': 'https://api.github.com/users/jordan-woyak/followers', 'following_url': 'https://api.github.com/users/jordan-woyak/following{/other_user}', 'gists_url': 'https://api.github.com/users/jordan-woyak/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/jordan-woyak/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/jordan-woyak/subscriptions', 'organizations_url': 'https://api.github.com/users/jordan-woyak/orgs', 'repos_url': 'https://api.github.com/users/jordan-woyak/repos', 'events_url': 'https://api.github.com/users/jordan-woyak/events{/privacy}', 'received_events_url': 'https://api.github.com/users/jordan-woyak/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'labels': [], 'state': 'closed', 'locked': False, 'assignee': None, 'assignees': [], 'milestone': None, 'comments': 26, 'created_at': '2025-09-26T04:25:44Z', 'updated_at': '2026-02-03T20:58:41Z', 'closed_at': '2025-10-13T06:54:54Z', 'author_association': 'MEMBER', 'type': None, 'active_lock_reason': None, 'draft': False, 'pull_request': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13970', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/13970', 'diff_url': 'https://github.com/dolphin-emu/dolphin/pull/13970.diff', 'patch_url': 'https://github.com/dolphin-emu/dolphin/pull/13970.patch', 'merged_at': '2025-10-13T06:54:54Z'}, 'body': 'Majorly overhauled the Windows Wii remote code.\r\n\r\nThis PR should be ready now.\r\n\r\nStatically link with "Bthprops.lib"\r\nDynamic linking was introduced in https://github.com/dolphin-emu/dolphin/commit/3acf0eb3af98009cc4e00bd9bcf43873dc26c4da to make Dolphin run under Wine.\r\nMy search results imply that Wine has long since added stubs for these functions so I\'m hoping it works fine now?\r\nedit: Wine seems to work fine.\r\n\r\nA 10 year old comment describes a workaround needed for the Toshiba Bluetooth stack.\r\nI\'ve removed the workaround.\r\n\r\nRemoved `HidP_SetOutputReport` fallback (Windows 7 workaround).\r\n\r\nFixed buggy overlapped Read/Write event usage (probably my fault from long ago).\r\n\r\nAdded checks for a Wii remote device name on the HID interface to only attempt connecting to actual Wii remotes devices.\r\nThe existing code just throws Wii remote data at every interface and checks for sane replies.\r\n\r\nAdded a periodic write to test for disconnected remotes in a timely manner.\r\n\r\nChanged the `Refresh` button to not remove authenticated remotes.\r\nSome users found it annoying.\r\nIt now only removes unauthenticated disconnected remotes.\r\n\r\nAdded `Sync` and `Reset` actions to establish and remove pairings.\r\nThe `Refresh` button is now a `QToolButton` with additional actions (only on Windows).\r\n<img width="120" height="177" alt="image" src="https://github.com/user-attachments/assets/16e57431-1eab-4465-a4f8-856188b0f942" />\r\n\r\nPairing a remote with the `Sync` button will enable it to reestablish a connection on any button press.\r\nIt currently shows an ugly animation while the task runs async.\r\n<img width="189" height="72" alt="image" src="https://github.com/user-attachments/assets/076191ce-8312-4cdc-8223-14a4bb9eca2d" />\r\nIt\'s better than nothing and can be visually improved later.\r\n\r\n`Refresh` or `Continuous Scanning` are no longer required to connect paired remotes.\r\nPairing with Windows or the DolphinBar "just works" by assigning a slot to `Real Wii Remote`.\r\n\r\nAdditionally, I am constantly bombarded with ControllerInterface / WiimotePool related deadlocks.\r\nThis seems like an existing issue that fixing properly is probably going to take another PR.\r\nIt seems like it\'s SDL?\r\nedit: SDL\'s DirectInput handling has been disabled in #13982 \r\n\r\nTODO\r\n - [x] Make the `Sync` button not require also clicking `Refresh`.\r\n - [x] Make paired + reconnected Wii remotes work without using `Refresh`\r\n - [x] Test Dolphin with Wine (just in general, not Wii remote functionality)\r\n - [x] Clean up log spam from continuous opening of HID interfaces.\r\n - [x] Make the Sync / Reset buttons display some kind kind of feedback.', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13970/reactions', 'total_count': 4, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 1, 'confused': 0, 'heart': 3, 'rocket': 0, 'eyes': 0}, 'timeline_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13970/timeline', 'performed_via_github_app': None, 'state_reason': None}, 'comment': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments/3843649310', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/13970#issuecomment-3843649310', 'issue_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13970', 'id': 3843649310, 'node_id': 'IC_kwDOALCn2M7lGW8e', 'user': {'login': 'Trihy', 'id': 107453916, 'node_id': 'U_kgDOBmed3A', 'avatar_url': 'https://avatars.githubusercontent.com/u/107453916?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/Trihy', 'html_url': 'https://github.com/Trihy', 'followers_url': 'https://api.github.com/users/Trihy/followers', 'following_url': 'https://api.github.com/users/Trihy/following{/other_user}', 'gists_url': 'https://api.github.com/users/Trihy/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/Trihy/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/Trihy/subscriptions', 'organizations_url': 'https://api.github.com/users/Trihy/orgs', 'repos_url': 'https://api.github.com/users/Trihy/repos', 'events_url': 'https://api.github.com/users/Trihy/events{/privacy}', 'received_events_url': 'https://api.github.com/users/Trihy/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'created_at': '2026-02-03T20:58:41Z', 'updated_at': '2026-02-03T20:58:41Z', 'body': "Do you know why some bt dongles rejects the reconnection? Probably some security feature on authentication that wiimotes doesn't have.\r\n\r\nI been testing many built in BT and found the mediatek to have this problem. There are others. Some can fe fixed using another driver\r\n\r\nSo seems to be BT driver related. But maybe the BT dongle expect another response and we can change that. ", 'author_association': 'NONE', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments/3843649310/reactions', 'total_count': 0, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 0, 'confused': 0, 'heart': 0, 'rocket': 0, 'eyes': 0}, 'performed_via_github_app': None}, 'repository': {'id': 11577304, 'node_id': 'MDEwOlJlcG9zaXRvcnkxMTU3NzMwNA==', 'name': 'dolphin', 'full_name': 'dolphin-emu/dolphin', 'private': False, 'owner': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/dolphin-emu', 'html_url': 'https://github.com/dolphin-emu', 'followers_url': 'https://api.github.com/users/dolphin-emu/followers', 'following_url': 'https://api.github.com/users/dolphin-emu/following{/other_user}', 'gists_url': 'https://api.github.com/users/dolphin-emu/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/dolphin-emu/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/dolphin-emu/subscriptions', 'organizations_url': 'https://api.github.com/users/dolphin-emu/orgs', 'repos_url': 'https://api.github.com/users/dolphin-emu/repos', 'events_url': 'https://api.github.com/users/dolphin-emu/events{/privacy}', 'received_events_url': 'https://api.github.com/users/dolphin-emu/received_events', 'type': 'Organization', 'user_view_type': 'public', 'site_admin': False}, 'html_url': 'https://github.com/dolphin-emu/dolphin', 'description': 'Dolphin is a GameCube / Wii emulator, allowing you to play games for these two platforms on PC with improvements.', 'fork': False, 'url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'forks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/forks', 'keys_url': 'https://api.github.com/repos/dolphin-emu/dolphin/keys{/key_id}', 'collaborators_url': 'https://api.github.com/repos/dolphin-emu/dolphin/collaborators{/collaborator}', 'teams_url': 'https://api.github.com/repos/dolphin-emu/dolphin/teams', 'hooks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/hooks', 'issue_events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/events{/number}', 'events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/events', 'assignees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/assignees{/user}', 'branches_url': 'https://api.github.com/repos/dolphin-emu/dolphin/branches{/branch}', 'tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/tags', 'blobs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/blobs{/sha}', 'git_tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/tags{/sha}', 'git_refs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/refs{/sha}', 'trees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/trees{/sha}', 'statuses_url': 'https://api.github.com/repos/dolphin-emu/dolphin/statuses/{sha}', 'languages_url': 'https://api.github.com/repos/dolphin-emu/dolphin/languages', 'stargazers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/stargazers', 'contributors_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contributors', 'subscribers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscribers', 'subscription_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscription', 'commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/commits{/sha}', 'git_commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/commits{/sha}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/comments{/number}', 'issue_comment_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments{/number}', 'contents_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contents/{+path}', 'compare_url': 'https://api.github.com/repos/dolphin-emu/dolphin/compare/{base}...{head}', 'merges_url': 'https://api.github.com/repos/dolphin-emu/dolphin/merges', 'archive_url': 'https://api.github.com/repos/dolphin-emu/dolphin/{archive_format}{/ref}', 'downloads_url': 'https://api.github.com/repos/dolphin-emu/dolphin/downloads', 'issues_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues{/number}', 'pulls_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls{/number}', 'milestones_url': 'https://api.github.com/repos/dolphin-emu/dolphin/milestones{/number}', 'notifications_url': 'https://api.github.com/repos/dolphin-emu/dolphin/notifications{?since,all,participating}', 'labels_url': 'https://api.github.com/repos/dolphin-emu/dolphin/labels{/name}', 'releases_url': 'https://api.github.com/repos/dolphin-emu/dolphin/releases{/id}', 'deployments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/deployments', 'created_at': '2013-07-22T09:07:57Z', 'updated_at': '2026-02-03T18:03:13Z', 'pushed_at': '2026-02-03T04:26:13Z', 'git_url': 'git://github.com/dolphin-emu/dolphin.git', 'ssh_url': 'git@github.com:dolphin-emu/dolphin.git', 'clone_url': 'https://github.com/dolphin-emu/dolphin.git', 'svn_url': 'https://github.com/dolphin-emu/dolphin', 'homepage': 'https://dolphin-emu.org', 'size': 544014, 'stargazers_count': 14582, 'watchers_count': 14582, 'language': 'C++', 'has_issues': False, 'has_projects': False, 'has_downloads': True, 'has_wiki': True, 'has_pages': False, 'has_discussions': False, 'forks_count': 2966, 'mirror_url': None, 'archived': False, 'disabled': False, 'open_issues_count': 360, 'license': {'key': 'other', 'name': 'Other', 'spdx_id': 'NOASSERTION', 'url': None, 'node_id': 'MDc6TGljZW5zZTA='}, 'allow_forking': True, 'is_template': False, 'web_commit_signoff_required': False, 'topics': ['cpp', 'emulation', 'emulator', 'gamecube', 'wii'], 'visibility': 'public', 'forks': 2966, 'open_issues': 360, 'watchers': 14582, 'default_branch': 'master', 'custom_properties': {}}, 'organization': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'url': 'https://api.github.com/orgs/dolphin-emu', 'repos_url': 'https://api.github.com/orgs/dolphin-emu/repos', 'events_url': 'https://api.github.com/orgs/dolphin-emu/events', 'hooks_url': 'https://api.github.com/orgs/dolphin-emu/hooks', 'issues_url': 'https://api.github.com/orgs/dolphin-emu/issues', 'members_url': 'https://api.github.com/orgs/dolphin-emu/members{/member}', 'public_members_url': 'https://api.github.com/orgs/dolphin-emu/public_members{/member}', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'description': ''}, 'sender': {'login': 'Trihy', 'id': 107453916, 'node_id': 'U_kgDOBmed3A', 'avatar_url': 'https://avatars.githubusercontent.com/u/107453916?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/Trihy', 'html_url': 'https://github.com/Trihy', 'followers_url': 'https://api.github.com/users/Trihy/followers', 'following_url': 'https://api.github.com/users/Trihy/following{/other_user}', 'gists_url': 'https://api.github.com/users/Trihy/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/Trihy/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/Trihy/subscriptions', 'organizations_url': 'https://api.github.com/users/Trihy/orgs', 'repos_url': 'https://api.github.com/users/Trihy/repos', 'events_url': 'https://api.github.com/users/Trihy/events{/privacy}', 'received_events_url': 'https://api.github.com/users/Trihy/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'installation': {'id': 35437981, 'node_id': 'MDIzOkludGVncmF0aW9uSW5zdGFsbGF0aW9uMzU0Mzc5ODE='}}, 'type': 'raw_gh_hook'}
2026-02-03T17:22:19.677183 {'source': 'webserver', 'gh_type': 'issue_comment', 'raw': {'action': 'edited', 'changes': {'body': {'from': "All revisions of all games may not be supported initially. We'll continue to work on it as we go, but support should a work in progress."}}, 'issue': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13844', 'repository_url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'labels_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13844/labels{/name}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13844/comments', 'events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13844/events', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/13844', 'id': 3286480944, 'node_id': 'PR_kwDOALCn2M6h2r1s', 'number': 13844, 'title': 'Core: Triforce support', 'user': {'login': 'crediar', 'id': 145270593, 'node_id': 'U_kgDOCKinQQ', 'avatar_url': 'https://avatars.githubusercontent.com/u/145270593?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/crediar', 'html_url': 'https://github.com/crediar', 'followers_url': 'https://api.github.com/users/crediar/followers', 'following_url': 'https://api.github.com/users/crediar/following{/other_user}', 'gists_url': 'https://api.github.com/users/crediar/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/crediar/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/crediar/subscriptions', 'organizations_url': 'https://api.github.com/users/crediar/orgs', 'repos_url': 'https://api.github.com/users/crediar/repos', 'events_url': 'https://api.github.com/users/crediar/events{/privacy}', 'received_events_url': 'https://api.github.com/users/crediar/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'labels': [], 'state': 'open', 'locked': False, 'assignee': None, 'assignees': [], 'milestone': None, 'comments': 102, 'created_at': '2025-08-02T23:06:09Z', 'updated_at': '2026-02-03T17:22:06Z', 'closed_at': None, 'author_association': 'NONE', 'type': None, 'active_lock_reason': None, 'draft': False, 'pull_request': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13844', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/13844', 'diff_url': 'https://github.com/dolphin-emu/dolphin/pull/13844.diff', 'patch_url': 'https://github.com/dolphin-emu/dolphin/pull/13844.patch', 'merged_at': None}, 'body': 'All the required changes to make Triforce games bootable.', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13844/reactions', 'total_count': 35, '+1': 4, '-1': 0, 'laugh': 0, 'hooray': 24, 'confused': 0, 'heart': 2, 'rocket': 5, 'eyes': 0}, 'timeline_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13844/timeline', 'performed_via_github_app': None, 'state_reason': None}, 'comment': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments/3842626451', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/13844#issuecomment-3842626451', 'issue_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13844', 'id': 3842626451, 'node_id': 'IC_kwDOALCn2M7lCdOT', 'user': {'login': 'JMC47', 'id': 6598209, 'node_id': 'MDQ6VXNlcjY1OTgyMDk=', 'avatar_url': 'https://avatars.githubusercontent.com/u/6598209?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/JMC47', 'html_url': 'https://github.com/JMC47', 'followers_url': 'https://api.github.com/users/JMC47/followers', 'following_url': 'https://api.github.com/users/JMC47/following{/other_user}', 'gists_url': 'https://api.github.com/users/JMC47/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/JMC47/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/JMC47/subscriptions', 'organizations_url': 'https://api.github.com/users/JMC47/orgs', 'repos_url': 'https://api.github.com/users/JMC47/repos', 'events_url': 'https://api.github.com/users/JMC47/events{/privacy}', 'received_events_url': 'https://api.github.com/users/JMC47/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'created_at': '2026-02-03T17:22:06Z', 'updated_at': '2026-02-03T17:22:17Z', 'body': "All revisions of all games may not be supported initially. We'll continue to work on it as we go, but support should be considered a work in progress.", 'author_association': 'MEMBER', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments/3842626451/reactions', 'total_count': 0, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 0, 'confused': 0, 'heart': 0, 'rocket': 0, 'eyes': 0}, 'performed_via_github_app': None}, 'repository': {'id': 11577304, 'node_id': 'MDEwOlJlcG9zaXRvcnkxMTU3NzMwNA==', 'name': 'dolphin', 'full_name': 'dolphin-emu/dolphin', 'private': False, 'owner': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/dolphin-emu', 'html_url': 'https://github.com/dolphin-emu', 'followers_url': 'https://api.github.com/users/dolphin-emu/followers', 'following_url': 'https://api.github.com/users/dolphin-emu/following{/other_user}', 'gists_url': 'https://api.github.com/users/dolphin-emu/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/dolphin-emu/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/dolphin-emu/subscriptions', 'organizations_url': 'https://api.github.com/users/dolphin-emu/orgs', 'repos_url': 'https://api.github.com/users/dolphin-emu/repos', 'events_url': 'https://api.github.com/users/dolphin-emu/events{/privacy}', 'received_events_url': 'https://api.github.com/users/dolphin-emu/received_events', 'type': 'Organization', 'user_view_type': 'public', 'site_admin': False}, 'html_url': 'https://github.com/dolphin-emu/dolphin', 'description': 'Dolphin is a GameCube / Wii emulator, allowing you to play games for these two platforms on PC with improvements.', 'fork': False, 'url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'forks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/forks', 'keys_url': 'https://api.github.com/repos/dolphin-emu/dolphin/keys{/key_id}', 'collaborators_url': 'https://api.github.com/repos/dolphin-emu/dolphin/collaborators{/collaborator}', 'teams_url': 'https://api.github.com/repos/dolphin-emu/dolphin/teams', 'hooks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/hooks', 'issue_events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/events{/number}', 'events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/events', 'assignees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/assignees{/user}', 'branches_url': 'https://api.github.com/repos/dolphin-emu/dolphin/branches{/branch}', 'tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/tags', 'blobs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/blobs{/sha}', 'git_tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/tags{/sha}', 'git_refs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/refs{/sha}', 'trees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/trees{/sha}', 'statuses_url': 'https://api.github.com/repos/dolphin-emu/dolphin/statuses/{sha}', 'languages_url': 'https://api.github.com/repos/dolphin-emu/dolphin/languages', 'stargazers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/stargazers', 'contributors_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contributors', 'subscribers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscribers', 'subscription_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscription', 'commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/commits{/sha}', 'git_commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/commits{/sha}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/comments{/number}', 'issue_comment_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments{/number}', 'contents_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contents/{+path}', 'compare_url': 'https://api.github.com/repos/dolphin-emu/dolphin/compare/{base}...{head}', 'merges_url': 'https://api.github.com/repos/dolphin-emu/dolphin/merges', 'archive_url': 'https://api.github.com/repos/dolphin-emu/dolphin/{archive_format}{/ref}', 'downloads_url': 'https://api.github.com/repos/dolphin-emu/dolphin/downloads', 'issues_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues{/number}', 'pulls_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls{/number}', 'milestones_url': 'https://api.github.com/repos/dolphin-emu/dolphin/milestones{/number}', 'notifications_url': 'https://api.github.com/repos/dolphin-emu/dolphin/notifications{?since,all,participating}', 'labels_url': 'https://api.github.com/repos/dolphin-emu/dolphin/labels{/name}', 'releases_url': 'https://api.github.com/repos/dolphin-emu/dolphin/releases{/id}', 'deployments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/deployments', 'created_at': '2013-07-22T09:07:57Z', 'updated_at': '2026-02-03T16:16:52Z', 'pushed_at': '2026-02-03T04:26:13Z', 'git_url': 'git://github.com/dolphin-emu/dolphin.git', 'ssh_url': 'git@github.com:dolphin-emu/dolphin.git', 'clone_url': 'https://github.com/dolphin-emu/dolphin.git', 'svn_url': 'https://github.com/dolphin-emu/dolphin', 'homepage': 'https://dolphin-emu.org', 'size': 544014, 'stargazers_count': 14581, 'watchers_count': 14581, 'language': 'C++', 'has_issues': False, 'has_projects': False, 'has_downloads': True, 'has_wiki': True, 'has_pages': False, 'has_discussions': False, 'forks_count': 2965, 'mirror_url': None, 'archived': False, 'disabled': False, 'open_issues_count': 360, 'license': {'key': 'other', 'name': 'Other', 'spdx_id': 'NOASSERTION', 'url': None, 'node_id': 'MDc6TGljZW5zZTA='}, 'allow_forking': True, 'is_template': False, 'web_commit_signoff_required': False, 'topics': ['cpp', 'emulation', 'emulator', 'gamecube', 'wii'], 'visibility': 'public', 'forks': 2965, 'open_issues': 360, 'watchers': 14581, 'default_branch': 'master', 'custom_properties': {}}, 'organization': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'url': 'https://api.github.com/orgs/dolphin-emu', 'repos_url': 'https://api.github.com/orgs/dolphin-emu/repos', 'events_url': 'https://api.github.com/orgs/dolphin-emu/events', 'hooks_url': 'https://api.github.com/orgs/dolphin-emu/hooks', 'issues_url': 'https://api.github.com/orgs/dolphin-emu/issues', 'members_url': 'https://api.github.com/orgs/dolphin-emu/members{/member}', 'public_members_url': 'https://api.github.com/orgs/dolphin-emu/public_members{/member}', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'description': ''}, 'sender': {'login': 'JMC47', 'id': 6598209, 'node_id': 'MDQ6VXNlcjY1OTgyMDk=', 'avatar_url': 'https://avatars.githubusercontent.com/u/6598209?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/JMC47', 'html_url': 'https://github.com/JMC47', 'followers_url': 'https://api.github.com/users/JMC47/followers', 'following_url': 'https://api.github.com/users/JMC47/following{/other_user}', 'gists_url': 'https://api.github.com/users/JMC47/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/JMC47/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/JMC47/subscriptions', 'organizations_url': 'https://api.github.com/users/JMC47/orgs', 'repos_url': 'https://api.github.com/users/JMC47/repos', 'events_url': 'https://api.github.com/users/JMC47/events{/privacy}', 'received_events_url': 'https://api.github.com/users/JMC47/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'installation': {'id': 35437981, 'node_id': 'MDIzOkludGVncmF0aW9uSW5zdGFsbGF0aW9uMzU0Mzc5ODE='}}, 'type': 'raw_gh_hook'}
2026-02-03T17:22:09.068291 {'source': 'webserver', 'gh_type': 'issue_comment', 'raw': {'action': 'created', 'issue': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13844', 'repository_url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'labels_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13844/labels{/name}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13844/comments', 'events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13844/events', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/13844', 'id': 3286480944, 'node_id': 'PR_kwDOALCn2M6h2r1s', 'number': 13844, 'title': 'Core: Triforce support', 'user': {'login': 'crediar', 'id': 145270593, 'node_id': 'U_kgDOCKinQQ', 'avatar_url': 'https://avatars.githubusercontent.com/u/145270593?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/crediar', 'html_url': 'https://github.com/crediar', 'followers_url': 'https://api.github.com/users/crediar/followers', 'following_url': 'https://api.github.com/users/crediar/following{/other_user}', 'gists_url': 'https://api.github.com/users/crediar/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/crediar/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/crediar/subscriptions', 'organizations_url': 'https://api.github.com/users/crediar/orgs', 'repos_url': 'https://api.github.com/users/crediar/repos', 'events_url': 'https://api.github.com/users/crediar/events{/privacy}', 'received_events_url': 'https://api.github.com/users/crediar/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'labels': [], 'state': 'open', 'locked': False, 'assignee': None, 'assignees': [], 'milestone': None, 'comments': 102, 'created_at': '2025-08-02T23:06:09Z', 'updated_at': '2026-02-03T17:22:06Z', 'closed_at': None, 'author_association': 'NONE', 'type': None, 'active_lock_reason': None, 'draft': False, 'pull_request': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13844', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/13844', 'diff_url': 'https://github.com/dolphin-emu/dolphin/pull/13844.diff', 'patch_url': 'https://github.com/dolphin-emu/dolphin/pull/13844.patch', 'merged_at': None}, 'body': 'All the required changes to make Triforce games bootable.', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13844/reactions', 'total_count': 35, '+1': 4, '-1': 0, 'laugh': 0, 'hooray': 24, 'confused': 0, 'heart': 2, 'rocket': 5, 'eyes': 0}, 'timeline_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13844/timeline', 'performed_via_github_app': None, 'state_reason': None}, 'comment': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments/3842626451', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/13844#issuecomment-3842626451', 'issue_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13844', 'id': 3842626451, 'node_id': 'IC_kwDOALCn2M7lCdOT', 'user': {'login': 'JMC47', 'id': 6598209, 'node_id': 'MDQ6VXNlcjY1OTgyMDk=', 'avatar_url': 'https://avatars.githubusercontent.com/u/6598209?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/JMC47', 'html_url': 'https://github.com/JMC47', 'followers_url': 'https://api.github.com/users/JMC47/followers', 'following_url': 'https://api.github.com/users/JMC47/following{/other_user}', 'gists_url': 'https://api.github.com/users/JMC47/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/JMC47/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/JMC47/subscriptions', 'organizations_url': 'https://api.github.com/users/JMC47/orgs', 'repos_url': 'https://api.github.com/users/JMC47/repos', 'events_url': 'https://api.github.com/users/JMC47/events{/privacy}', 'received_events_url': 'https://api.github.com/users/JMC47/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'created_at': '2026-02-03T17:22:06Z', 'updated_at': '2026-02-03T17:22:06Z', 'body': "All revisions of all games may not be supported initially. We'll continue to work on it as we go, but support should a work in progress.", 'author_association': 'MEMBER', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments/3842626451/reactions', 'total_count': 0, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 0, 'confused': 0, 'heart': 0, 'rocket': 0, 'eyes': 0}, 'performed_via_github_app': None}, 'repository': {'id': 11577304, 'node_id': 'MDEwOlJlcG9zaXRvcnkxMTU3NzMwNA==', 'name': 'dolphin', 'full_name': 'dolphin-emu/dolphin', 'private': False, 'owner': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/dolphin-emu', 'html_url': 'https://github.com/dolphin-emu', 'followers_url': 'https://api.github.com/users/dolphin-emu/followers', 'following_url': 'https://api.github.com/users/dolphin-emu/following{/other_user}', 'gists_url': 'https://api.github.com/users/dolphin-emu/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/dolphin-emu/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/dolphin-emu/subscriptions', 'organizations_url': 'https://api.github.com/users/dolphin-emu/orgs', 'repos_url': 'https://api.github.com/users/dolphin-emu/repos', 'events_url': 'https://api.github.com/users/dolphin-emu/events{/privacy}', 'received_events_url': 'https://api.github.com/users/dolphin-emu/received_events', 'type': 'Organization', 'user_view_type': 'public', 'site_admin': False}, 'html_url': 'https://github.com/dolphin-emu/dolphin', 'description': 'Dolphin is a GameCube / Wii emulator, allowing you to play games for these two platforms on PC with improvements.', 'fork': False, 'url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'forks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/forks', 'keys_url': 'https://api.github.com/repos/dolphin-emu/dolphin/keys{/key_id}', 'collaborators_url': 'https://api.github.com/repos/dolphin-emu/dolphin/collaborators{/collaborator}', 'teams_url': 'https://api.github.com/repos/dolphin-emu/dolphin/teams', 'hooks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/hooks', 'issue_events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/events{/number}', 'events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/events', 'assignees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/assignees{/user}', 'branches_url': 'https://api.github.com/repos/dolphin-emu/dolphin/branches{/branch}', 'tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/tags', 'blobs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/blobs{/sha}', 'git_tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/tags{/sha}', 'git_refs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/refs{/sha}', 'trees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/trees{/sha}', 'statuses_url': 'https://api.github.com/repos/dolphin-emu/dolphin/statuses/{sha}', 'languages_url': 'https://api.github.com/repos/dolphin-emu/dolphin/languages', 'stargazers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/stargazers', 'contributors_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contributors', 'subscribers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscribers', 'subscription_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscription', 'commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/commits{/sha}', 'git_commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/commits{/sha}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/comments{/number}', 'issue_comment_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments{/number}', 'contents_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contents/{+path}', 'compare_url': 'https://api.github.com/repos/dolphin-emu/dolphin/compare/{base}...{head}', 'merges_url': 'https://api.github.com/repos/dolphin-emu/dolphin/merges', 'archive_url': 'https://api.github.com/repos/dolphin-emu/dolphin/{archive_format}{/ref}', 'downloads_url': 'https://api.github.com/repos/dolphin-emu/dolphin/downloads', 'issues_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues{/number}', 'pulls_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls{/number}', 'milestones_url': 'https://api.github.com/repos/dolphin-emu/dolphin/milestones{/number}', 'notifications_url': 'https://api.github.com/repos/dolphin-emu/dolphin/notifications{?since,all,participating}', 'labels_url': 'https://api.github.com/repos/dolphin-emu/dolphin/labels{/name}', 'releases_url': 'https://api.github.com/repos/dolphin-emu/dolphin/releases{/id}', 'deployments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/deployments', 'created_at': '2013-07-22T09:07:57Z', 'updated_at': '2026-02-03T16:16:52Z', 'pushed_at': '2026-02-03T04:26:13Z', 'git_url': 'git://github.com/dolphin-emu/dolphin.git', 'ssh_url': 'git@github.com:dolphin-emu/dolphin.git', 'clone_url': 'https://github.com/dolphin-emu/dolphin.git', 'svn_url': 'https://github.com/dolphin-emu/dolphin', 'homepage': 'https://dolphin-emu.org', 'size': 544014, 'stargazers_count': 14581, 'watchers_count': 14581, 'language': 'C++', 'has_issues': False, 'has_projects': False, 'has_downloads': True, 'has_wiki': True, 'has_pages': False, 'has_discussions': False, 'forks_count': 2965, 'mirror_url': None, 'archived': False, 'disabled': False, 'open_issues_count': 360, 'license': {'key': 'other', 'name': 'Other', 'spdx_id': 'NOASSERTION', 'url': None, 'node_id': 'MDc6TGljZW5zZTA='}, 'allow_forking': True, 'is_template': False, 'web_commit_signoff_required': False, 'topics': ['cpp', 'emulation', 'emulator', 'gamecube', 'wii'], 'visibility': 'public', 'forks': 2965, 'open_issues': 360, 'watchers': 14581, 'default_branch': 'master', 'custom_properties': {}}, 'organization': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'url': 'https://api.github.com/orgs/dolphin-emu', 'repos_url': 'https://api.github.com/orgs/dolphin-emu/repos', 'events_url': 'https://api.github.com/orgs/dolphin-emu/events', 'hooks_url': 'https://api.github.com/orgs/dolphin-emu/hooks', 'issues_url': 'https://api.github.com/orgs/dolphin-emu/issues', 'members_url': 'https://api.github.com/orgs/dolphin-emu/members{/member}', 'public_members_url': 'https://api.github.com/orgs/dolphin-emu/public_members{/member}', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'description': ''}, 'sender': {'login': 'JMC47', 'id': 6598209, 'node_id': 'MDQ6VXNlcjY1OTgyMDk=', 'avatar_url': 'https://avatars.githubusercontent.com/u/6598209?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/JMC47', 'html_url': 'https://github.com/JMC47', 'followers_url': 'https://api.github.com/users/JMC47/followers', 'following_url': 'https://api.github.com/users/JMC47/following{/other_user}', 'gists_url': 'https://api.github.com/users/JMC47/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/JMC47/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/JMC47/subscriptions', 'organizations_url': 'https://api.github.com/users/JMC47/orgs', 'repos_url': 'https://api.github.com/users/JMC47/repos', 'events_url': 'https://api.github.com/users/JMC47/events{/privacy}', 'received_events_url': 'https://api.github.com/users/JMC47/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'installation': {'id': 35437981, 'node_id': 'MDIzOkludGVncmF0aW9uSW5zdGFsbGF0aW9uMzU0Mzc5ODE='}}, 'type': 'raw_gh_hook'}
2026-02-03T17:08:19.684953 {'source': 'webserver', 'gh_type': 'issue_comment', 'raw': {'action': 'created', 'issue': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13844', 'repository_url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'labels_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13844/labels{/name}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13844/comments', 'events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13844/events', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/13844', 'id': 3286480944, 'node_id': 'PR_kwDOALCn2M6h2r1s', 'number': 13844, 'title': 'Core: Triforce support', 'user': {'login': 'crediar', 'id': 145270593, 'node_id': 'U_kgDOCKinQQ', 'avatar_url': 'https://avatars.githubusercontent.com/u/145270593?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/crediar', 'html_url': 'https://github.com/crediar', 'followers_url': 'https://api.github.com/users/crediar/followers', 'following_url': 'https://api.github.com/users/crediar/following{/other_user}', 'gists_url': 'https://api.github.com/users/crediar/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/crediar/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/crediar/subscriptions', 'organizations_url': 'https://api.github.com/users/crediar/orgs', 'repos_url': 'https://api.github.com/users/crediar/repos', 'events_url': 'https://api.github.com/users/crediar/events{/privacy}', 'received_events_url': 'https://api.github.com/users/crediar/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'labels': [], 'state': 'open', 'locked': False, 'assignee': None, 'assignees': [], 'milestone': None, 'comments': 101, 'created_at': '2025-08-02T23:06:09Z', 'updated_at': '2026-02-03T17:08:17Z', 'closed_at': None, 'author_association': 'NONE', 'type': None, 'active_lock_reason': None, 'draft': False, 'pull_request': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13844', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/13844', 'diff_url': 'https://github.com/dolphin-emu/dolphin/pull/13844.diff', 'patch_url': 'https://github.com/dolphin-emu/dolphin/pull/13844.patch', 'merged_at': None}, 'body': 'All the required changes to make Triforce games bootable.', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13844/reactions', 'total_count': 35, '+1': 4, '-1': 0, 'laugh': 0, 'hooray': 24, 'confused': 0, 'heart': 2, 'rocket': 5, 'eyes': 0}, 'timeline_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13844/timeline', 'performed_via_github_app': None, 'state_reason': None}, 'comment': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments/3842552882', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/13844#issuecomment-3842552882', 'issue_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13844', 'id': 3842552882, 'node_id': 'IC_kwDOALCn2M7lCLQy', 'user': {'login': 'pizzzza19', 'id': 183198342, 'node_id': 'U_kgDOCutihg', 'avatar_url': 'https://avatars.githubusercontent.com/u/183198342?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/pizzzza19', 'html_url': 'https://github.com/pizzzza19', 'followers_url': 'https://api.github.com/users/pizzzza19/followers', 'following_url': 'https://api.github.com/users/pizzzza19/following{/other_user}', 'gists_url': 'https://api.github.com/users/pizzzza19/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/pizzzza19/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/pizzzza19/subscriptions', 'organizations_url': 'https://api.github.com/users/pizzzza19/orgs', 'repos_url': 'https://api.github.com/users/pizzzza19/repos', 'events_url': 'https://api.github.com/users/pizzzza19/events{/privacy}', 'received_events_url': 'https://api.github.com/users/pizzzza19/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'created_at': '2026-02-03T17:08:17Z', 'updated_at': '2026-02-03T17:08:17Z', 'body': 'Tested with manually built exe as guided by official dolphin readme.\nPrevious PR also had a same error. It is a known issue and may have discussed by other people outside this PR.', 'author_association': 'NONE', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments/3842552882/reactions', 'total_count': 0, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 0, 'confused': 0, 'heart': 0, 'rocket': 0, 'eyes': 0}, 'performed_via_github_app': None}, 'repository': {'id': 11577304, 'node_id': 'MDEwOlJlcG9zaXRvcnkxMTU3NzMwNA==', 'name': 'dolphin', 'full_name': 'dolphin-emu/dolphin', 'private': False, 'owner': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/dolphin-emu', 'html_url': 'https://github.com/dolphin-emu', 'followers_url': 'https://api.github.com/users/dolphin-emu/followers', 'following_url': 'https://api.github.com/users/dolphin-emu/following{/other_user}', 'gists_url': 'https://api.github.com/users/dolphin-emu/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/dolphin-emu/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/dolphin-emu/subscriptions', 'organizations_url': 'https://api.github.com/users/dolphin-emu/orgs', 'repos_url': 'https://api.github.com/users/dolphin-emu/repos', 'events_url': 'https://api.github.com/users/dolphin-emu/events{/privacy}', 'received_events_url': 'https://api.github.com/users/dolphin-emu/received_events', 'type': 'Organization', 'user_view_type': 'public', 'site_admin': False}, 'html_url': 'https://github.com/dolphin-emu/dolphin', 'description': 'Dolphin is a GameCube / Wii emulator, allowing you to play games for these two platforms on PC with improvements.', 'fork': False, 'url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'forks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/forks', 'keys_url': 'https://api.github.com/repos/dolphin-emu/dolphin/keys{/key_id}', 'collaborators_url': 'https://api.github.com/repos/dolphin-emu/dolphin/collaborators{/collaborator}', 'teams_url': 'https://api.github.com/repos/dolphin-emu/dolphin/teams', 'hooks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/hooks', 'issue_events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/events{/number}', 'events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/events', 'assignees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/assignees{/user}', 'branches_url': 'https://api.github.com/repos/dolphin-emu/dolphin/branches{/branch}', 'tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/tags', 'blobs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/blobs{/sha}', 'git_tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/tags{/sha}', 'git_refs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/refs{/sha}', 'trees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/trees{/sha}', 'statuses_url': 'https://api.github.com/repos/dolphin-emu/dolphin/statuses/{sha}', 'languages_url': 'https://api.github.com/repos/dolphin-emu/dolphin/languages', 'stargazers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/stargazers', 'contributors_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contributors', 'subscribers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscribers', 'subscription_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscription', 'commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/commits{/sha}', 'git_commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/commits{/sha}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/comments{/number}', 'issue_comment_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments{/number}', 'contents_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contents/{+path}', 'compare_url': 'https://api.github.com/repos/dolphin-emu/dolphin/compare/{base}...{head}', 'merges_url': 'https://api.github.com/repos/dolphin-emu/dolphin/merges', 'archive_url': 'https://api.github.com/repos/dolphin-emu/dolphin/{archive_format}{/ref}', 'downloads_url': 'https://api.github.com/repos/dolphin-emu/dolphin/downloads', 'issues_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues{/number}', 'pulls_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls{/number}', 'milestones_url': 'https://api.github.com/repos/dolphin-emu/dolphin/milestones{/number}', 'notifications_url': 'https://api.github.com/repos/dolphin-emu/dolphin/notifications{?since,all,participating}', 'labels_url': 'https://api.github.com/repos/dolphin-emu/dolphin/labels{/name}', 'releases_url': 'https://api.github.com/repos/dolphin-emu/dolphin/releases{/id}', 'deployments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/deployments', 'created_at': '2013-07-22T09:07:57Z', 'updated_at': '2026-02-03T16:16:52Z', 'pushed_at': '2026-02-03T04:26:13Z', 'git_url': 'git://github.com/dolphin-emu/dolphin.git', 'ssh_url': 'git@github.com:dolphin-emu/dolphin.git', 'clone_url': 'https://github.com/dolphin-emu/dolphin.git', 'svn_url': 'https://github.com/dolphin-emu/dolphin', 'homepage': 'https://dolphin-emu.org', 'size': 544014, 'stargazers_count': 14581, 'watchers_count': 14581, 'language': 'C++', 'has_issues': False, 'has_projects': False, 'has_downloads': True, 'has_wiki': True, 'has_pages': False, 'has_discussions': False, 'forks_count': 2965, 'mirror_url': None, 'archived': False, 'disabled': False, 'open_issues_count': 360, 'license': {'key': 'other', 'name': 'Other', 'spdx_id': 'NOASSERTION', 'url': None, 'node_id': 'MDc6TGljZW5zZTA='}, 'allow_forking': True, 'is_template': False, 'web_commit_signoff_required': False, 'topics': ['cpp', 'emulation', 'emulator', 'gamecube', 'wii'], 'visibility': 'public', 'forks': 2965, 'open_issues': 360, 'watchers': 14581, 'default_branch': 'master', 'custom_properties': {}}, 'organization': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'url': 'https://api.github.com/orgs/dolphin-emu', 'repos_url': 'https://api.github.com/orgs/dolphin-emu/repos', 'events_url': 'https://api.github.com/orgs/dolphin-emu/events', 'hooks_url': 'https://api.github.com/orgs/dolphin-emu/hooks', 'issues_url': 'https://api.github.com/orgs/dolphin-emu/issues', 'members_url': 'https://api.github.com/orgs/dolphin-emu/members{/member}', 'public_members_url': 'https://api.github.com/orgs/dolphin-emu/public_members{/member}', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'description': ''}, 'sender': {'login': 'pizzzza19', 'id': 183198342, 'node_id': 'U_kgDOCutihg', 'avatar_url': 'https://avatars.githubusercontent.com/u/183198342?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/pizzzza19', 'html_url': 'https://github.com/pizzzza19', 'followers_url': 'https://api.github.com/users/pizzzza19/followers', 'following_url': 'https://api.github.com/users/pizzzza19/following{/other_user}', 'gists_url': 'https://api.github.com/users/pizzzza19/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/pizzzza19/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/pizzzza19/subscriptions', 'organizations_url': 'https://api.github.com/users/pizzzza19/orgs', 'repos_url': 'https://api.github.com/users/pizzzza19/repos', 'events_url': 'https://api.github.com/users/pizzzza19/events{/privacy}', 'received_events_url': 'https://api.github.com/users/pizzzza19/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'installation': {'id': 35437981, 'node_id': 'MDIzOkludGVncmF0aW9uSW5zdGFsbGF0aW9uMzU0Mzc5ODE='}}, 'type': 'raw_gh_hook'}
2026-02-03T16:31:29.616413 {'source': 'webserver', 'gh_type': 'issue_comment', 'raw': {'action': 'created', 'issue': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13844', 'repository_url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'labels_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13844/labels{/name}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13844/comments', 'events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13844/events', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/13844', 'id': 3286480944, 'node_id': 'PR_kwDOALCn2M6h2r1s', 'number': 13844, 'title': 'Core: Triforce support', 'user': {'login': 'crediar', 'id': 145270593, 'node_id': 'U_kgDOCKinQQ', 'avatar_url': 'https://avatars.githubusercontent.com/u/145270593?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/crediar', 'html_url': 'https://github.com/crediar', 'followers_url': 'https://api.github.com/users/crediar/followers', 'following_url': 'https://api.github.com/users/crediar/following{/other_user}', 'gists_url': 'https://api.github.com/users/crediar/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/crediar/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/crediar/subscriptions', 'organizations_url': 'https://api.github.com/users/crediar/orgs', 'repos_url': 'https://api.github.com/users/crediar/repos', 'events_url': 'https://api.github.com/users/crediar/events{/privacy}', 'received_events_url': 'https://api.github.com/users/crediar/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'labels': [], 'state': 'open', 'locked': False, 'assignee': None, 'assignees': [], 'milestone': None, 'comments': 100, 'created_at': '2025-08-02T23:06:09Z', 'updated_at': '2026-02-03T16:31:27Z', 'closed_at': None, 'author_association': 'NONE', 'type': None, 'active_lock_reason': None, 'draft': False, 'pull_request': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13844', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/13844', 'diff_url': 'https://github.com/dolphin-emu/dolphin/pull/13844.diff', 'patch_url': 'https://github.com/dolphin-emu/dolphin/pull/13844.patch', 'merged_at': None}, 'body': 'All the required changes to make Triforce games bootable.', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13844/reactions', 'total_count': 35, '+1': 4, '-1': 0, 'laugh': 0, 'hooray': 24, 'confused': 0, 'heart': 2, 'rocket': 5, 'eyes': 0}, 'timeline_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13844/timeline', 'performed_via_github_app': None, 'state_reason': None}, 'comment': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments/3842350109', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/13844#issuecomment-3842350109', 'issue_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13844', 'id': 3842350109, 'node_id': 'IC_kwDOALCn2M7lBZwd', 'user': {'login': 'sepalani', 'id': 7890055, 'node_id': 'MDQ6VXNlcjc4OTAwNTU=', 'avatar_url': 'https://avatars.githubusercontent.com/u/7890055?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/sepalani', 'html_url': 'https://github.com/sepalani', 'followers_url': 'https://api.github.com/users/sepalani/followers', 'following_url': 'https://api.github.com/users/sepalani/following{/other_user}', 'gists_url': 'https://api.github.com/users/sepalani/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/sepalani/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/sepalani/subscriptions', 'organizations_url': 'https://api.github.com/users/sepalani/orgs', 'repos_url': 'https://api.github.com/users/sepalani/repos', 'events_url': 'https://api.github.com/users/sepalani/events{/privacy}', 'received_events_url': 'https://api.github.com/users/sepalani/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'created_at': '2026-02-03T16:31:27Z', 'updated_at': '2026-02-03T16:31:27Z', 'body': '> Hello I built .exe with your latest code. it is a long journey to add triforce support but there are errors with some ROMs. \r\n[GEYJ6E] VS2002: Game ID duplicated with JPN and EXP region. JPN rom will be overwritten as EXP mode. (removed Japan team and license logos) \r\n[GHNJ6E] VS4 VerA (JPN) : Game frozen at demo screen or entering team select screen. This will only happen with JPN rom. It is well known that specific gekko code can skip boot error, but above cannot pass without fixing the main dolphin code.\r\n\r\n@pizzzza19 \r\nDid these games worked at some point with this PR? Do these games display in Dolphin\'s log window (with the highest verbosity enabled) error messages that can hint us regarding which pieces are missing? Does the issue occur with the build provided by [Dolphin\'s buildbot](https://dolphin.ci/#/builders/2/builds/8918)?\r\n\r\n<a href="https://dolphin.ci/#/builders/2/builds/8918"><img width="1427" height="486" alt="Image" src="https://github.com/user-attachments/assets/480e061d-edd0-4836-bdab-e87b46c5d60a" /></a>\r\n\r\n@JMC47 \r\nI don\'t have any Triforce games so you\'re probably more knowledgeable than me regarding the above games.', 'author_association': 'MEMBER', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments/3842350109/reactions', 'total_count': 0, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 0, 'confused': 0, 'heart': 0, 'rocket': 0, 'eyes': 0}, 'performed_via_github_app': None}, 'repository': {'id': 11577304, 'node_id': 'MDEwOlJlcG9zaXRvcnkxMTU3NzMwNA==', 'name': 'dolphin', 'full_name': 'dolphin-emu/dolphin', 'private': False, 'owner': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/dolphin-emu', 'html_url': 'https://github.com/dolphin-emu', 'followers_url': 'https://api.github.com/users/dolphin-emu/followers', 'following_url': 'https://api.github.com/users/dolphin-emu/following{/other_user}', 'gists_url': 'https://api.github.com/users/dolphin-emu/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/dolphin-emu/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/dolphin-emu/subscriptions', 'organizations_url': 'https://api.github.com/users/dolphin-emu/orgs', 'repos_url': 'https://api.github.com/users/dolphin-emu/repos', 'events_url': 'https://api.github.com/users/dolphin-emu/events{/privacy}', 'received_events_url': 'https://api.github.com/users/dolphin-emu/received_events', 'type': 'Organization', 'user_view_type': 'public', 'site_admin': False}, 'html_url': 'https://github.com/dolphin-emu/dolphin', 'description': 'Dolphin is a GameCube / Wii emulator, allowing you to play games for these two platforms on PC with improvements.', 'fork': False, 'url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'forks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/forks', 'keys_url': 'https://api.github.com/repos/dolphin-emu/dolphin/keys{/key_id}', 'collaborators_url': 'https://api.github.com/repos/dolphin-emu/dolphin/collaborators{/collaborator}', 'teams_url': 'https://api.github.com/repos/dolphin-emu/dolphin/teams', 'hooks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/hooks', 'issue_events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/events{/number}', 'events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/events', 'assignees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/assignees{/user}', 'branches_url': 'https://api.github.com/repos/dolphin-emu/dolphin/branches{/branch}', 'tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/tags', 'blobs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/blobs{/sha}', 'git_tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/tags{/sha}', 'git_refs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/refs{/sha}', 'trees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/trees{/sha}', 'statuses_url': 'https://api.github.com/repos/dolphin-emu/dolphin/statuses/{sha}', 'languages_url': 'https://api.github.com/repos/dolphin-emu/dolphin/languages', 'stargazers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/stargazers', 'contributors_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contributors', 'subscribers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscribers', 'subscription_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscription', 'commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/commits{/sha}', 'git_commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/commits{/sha}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/comments{/number}', 'issue_comment_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments{/number}', 'contents_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contents/{+path}', 'compare_url': 'https://api.github.com/repos/dolphin-emu/dolphin/compare/{base}...{head}', 'merges_url': 'https://api.github.com/repos/dolphin-emu/dolphin/merges', 'archive_url': 'https://api.github.com/repos/dolphin-emu/dolphin/{archive_format}{/ref}', 'downloads_url': 'https://api.github.com/repos/dolphin-emu/dolphin/downloads', 'issues_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues{/number}', 'pulls_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls{/number}', 'milestones_url': 'https://api.github.com/repos/dolphin-emu/dolphin/milestones{/number}', 'notifications_url': 'https://api.github.com/repos/dolphin-emu/dolphin/notifications{?since,all,participating}', 'labels_url': 'https://api.github.com/repos/dolphin-emu/dolphin/labels{/name}', 'releases_url': 'https://api.github.com/repos/dolphin-emu/dolphin/releases{/id}', 'deployments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/deployments', 'created_at': '2013-07-22T09:07:57Z', 'updated_at': '2026-02-03T16:16:52Z', 'pushed_at': '2026-02-03T04:26:13Z', 'git_url': 'git://github.com/dolphin-emu/dolphin.git', 'ssh_url': 'git@github.com:dolphin-emu/dolphin.git', 'clone_url': 'https://github.com/dolphin-emu/dolphin.git', 'svn_url': 'https://github.com/dolphin-emu/dolphin', 'homepage': 'https://dolphin-emu.org', 'size': 544014, 'stargazers_count': 14581, 'watchers_count': 14581, 'language': 'C++', 'has_issues': False, 'has_projects': False, 'has_downloads': True, 'has_wiki': True, 'has_pages': False, 'has_discussions': False, 'forks_count': 2965, 'mirror_url': None, 'archived': False, 'disabled': False, 'open_issues_count': 360, 'license': {'key': 'other', 'name': 'Other', 'spdx_id': 'NOASSERTION', 'url': None, 'node_id': 'MDc6TGljZW5zZTA='}, 'allow_forking': True, 'is_template': False, 'web_commit_signoff_required': False, 'topics': ['cpp', 'emulation', 'emulator', 'gamecube', 'wii'], 'visibility': 'public', 'forks': 2965, 'open_issues': 360, 'watchers': 14581, 'default_branch': 'master', 'custom_properties': {}}, 'organization': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'url': 'https://api.github.com/orgs/dolphin-emu', 'repos_url': 'https://api.github.com/orgs/dolphin-emu/repos', 'events_url': 'https://api.github.com/orgs/dolphin-emu/events', 'hooks_url': 'https://api.github.com/orgs/dolphin-emu/hooks', 'issues_url': 'https://api.github.com/orgs/dolphin-emu/issues', 'members_url': 'https://api.github.com/orgs/dolphin-emu/members{/member}', 'public_members_url': 'https://api.github.com/orgs/dolphin-emu/public_members{/member}', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'description': ''}, 'sender': {'login': 'sepalani', 'id': 7890055, 'node_id': 'MDQ6VXNlcjc4OTAwNTU=', 'avatar_url': 'https://avatars.githubusercontent.com/u/7890055?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/sepalani', 'html_url': 'https://github.com/sepalani', 'followers_url': 'https://api.github.com/users/sepalani/followers', 'following_url': 'https://api.github.com/users/sepalani/following{/other_user}', 'gists_url': 'https://api.github.com/users/sepalani/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/sepalani/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/sepalani/subscriptions', 'organizations_url': 'https://api.github.com/users/sepalani/orgs', 'repos_url': 'https://api.github.com/users/sepalani/repos', 'events_url': 'https://api.github.com/users/sepalani/events{/privacy}', 'received_events_url': 'https://api.github.com/users/sepalani/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'installation': {'id': 35437981, 'node_id': 'MDIzOkludGVncmF0aW9uSW5zdGFsbGF0aW9uMzU0Mzc5ODE='}}, 'type': 'raw_gh_hook'}
2026-02-03T16:08:06.874146 {'source': 'webserver', 'gh_type': 'commit_comment', 'raw': {'action': 'created', 'comment': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/comments/176299891', 'html_url': 'https://github.com/dolphin-emu/dolphin/commit/128a6454b6247c0c2a2cafb2d91d03f71fc112e0#commitcomment-176299891', 'id': 176299891, 'node_id': 'CC_kwDOALCn2M4Kgh9z', 'user': {'login': 'pizzzza19', 'id': 183198342, 'node_id': 'U_kgDOCutihg', 'avatar_url': 'https://avatars.githubusercontent.com/u/183198342?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/pizzzza19', 'html_url': 'https://github.com/pizzzza19', 'followers_url': 'https://api.github.com/users/pizzzza19/followers', 'following_url': 'https://api.github.com/users/pizzzza19/following{/other_user}', 'gists_url': 'https://api.github.com/users/pizzzza19/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/pizzzza19/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/pizzzza19/subscriptions', 'organizations_url': 'https://api.github.com/users/pizzzza19/orgs', 'repos_url': 'https://api.github.com/users/pizzzza19/repos', 'events_url': 'https://api.github.com/users/pizzzza19/events{/privacy}', 'received_events_url': 'https://api.github.com/users/pizzzza19/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'position': None, 'line': None, 'path': None, 'commit_id': '128a6454b6247c0c2a2cafb2d91d03f71fc112e0', 'created_at': '2026-02-03T16:08:04Z', 'updated_at': '2026-02-03T16:08:04Z', 'body': 'Hello I built .exe with your latest code.\r\nit is a long journey to add triforce support but there are errors with some ROMs.\r\n[GEYJ6E] VS2002: Game ID duplicated with JPN and EXP region. JPN rom will be overwritten as EXP mode. (removed Japan team and license logos)\r\n[GHNJ6E] VS4 VerA (JPN) : Game frozen at demo screen or entering team select screen. This will only happen with JPN rom.\r\nIt is well known that specific gekko code can skip boot error, but above cannot pass without fixing the main dolphin code.', 'author_association': 'NONE', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/comments/176299891/reactions', 'total_count': 0, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 0, 'confused': 0, 'heart': 0, 'rocket': 0, 'eyes': 0}}, 'repository': {'id': 11577304, 'node_id': 'MDEwOlJlcG9zaXRvcnkxMTU3NzMwNA==', 'name': 'dolphin', 'full_name': 'dolphin-emu/dolphin', 'private': False, 'owner': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/dolphin-emu', 'html_url': 'https://github.com/dolphin-emu', 'followers_url': 'https://api.github.com/users/dolphin-emu/followers', 'following_url': 'https://api.github.com/users/dolphin-emu/following{/other_user}', 'gists_url': 'https://api.github.com/users/dolphin-emu/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/dolphin-emu/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/dolphin-emu/subscriptions', 'organizations_url': 'https://api.github.com/users/dolphin-emu/orgs', 'repos_url': 'https://api.github.com/users/dolphin-emu/repos', 'events_url': 'https://api.github.com/users/dolphin-emu/events{/privacy}', 'received_events_url': 'https://api.github.com/users/dolphin-emu/received_events', 'type': 'Organization', 'user_view_type': 'public', 'site_admin': False}, 'html_url': 'https://github.com/dolphin-emu/dolphin', 'description': 'Dolphin is a GameCube / Wii emulator, allowing you to play games for these two platforms on PC with improvements.', 'fork': False, 'url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'forks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/forks', 'keys_url': 'https://api.github.com/repos/dolphin-emu/dolphin/keys{/key_id}', 'collaborators_url': 'https://api.github.com/repos/dolphin-emu/dolphin/collaborators{/collaborator}', 'teams_url': 'https://api.github.com/repos/dolphin-emu/dolphin/teams', 'hooks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/hooks', 'issue_events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/events{/number}', 'events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/events', 'assignees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/assignees{/user}', 'branches_url': 'https://api.github.com/repos/dolphin-emu/dolphin/branches{/branch}', 'tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/tags', 'blobs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/blobs{/sha}', 'git_tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/tags{/sha}', 'git_refs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/refs{/sha}', 'trees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/trees{/sha}', 'statuses_url': 'https://api.github.com/repos/dolphin-emu/dolphin/statuses/{sha}', 'languages_url': 'https://api.github.com/repos/dolphin-emu/dolphin/languages', 'stargazers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/stargazers', 'contributors_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contributors', 'subscribers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscribers', 'subscription_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscription', 'commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/commits{/sha}', 'git_commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/commits{/sha}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/comments{/number}', 'issue_comment_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments{/number}', 'contents_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contents/{+path}', 'compare_url': 'https://api.github.com/repos/dolphin-emu/dolphin/compare/{base}...{head}', 'merges_url': 'https://api.github.com/repos/dolphin-emu/dolphin/merges', 'archive_url': 'https://api.github.com/repos/dolphin-emu/dolphin/{archive_format}{/ref}', 'downloads_url': 'https://api.github.com/repos/dolphin-emu/dolphin/downloads', 'issues_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues{/number}', 'pulls_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls{/number}', 'milestones_url': 'https://api.github.com/repos/dolphin-emu/dolphin/milestones{/number}', 'notifications_url': 'https://api.github.com/repos/dolphin-emu/dolphin/notifications{?since,all,participating}', 'labels_url': 'https://api.github.com/repos/dolphin-emu/dolphin/labels{/name}', 'releases_url': 'https://api.github.com/repos/dolphin-emu/dolphin/releases{/id}', 'deployments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/deployments', 'created_at': '2013-07-22T09:07:57Z', 'updated_at': '2026-02-03T15:41:29Z', 'pushed_at': '2026-02-03T04:26:13Z', 'git_url': 'git://github.com/dolphin-emu/dolphin.git', 'ssh_url': 'git@github.com:dolphin-emu/dolphin.git', 'clone_url': 'https://github.com/dolphin-emu/dolphin.git', 'svn_url': 'https://github.com/dolphin-emu/dolphin', 'homepage': 'https://dolphin-emu.org', 'size': 544014, 'stargazers_count': 14580, 'watchers_count': 14580, 'language': 'C++', 'has_issues': False, 'has_projects': False, 'has_downloads': True, 'has_wiki': True, 'has_pages': False, 'has_discussions': False, 'forks_count': 2965, 'mirror_url': None, 'archived': False, 'disabled': False, 'open_issues_count': 360, 'license': {'key': 'other', 'name': 'Other', 'spdx_id': 'NOASSERTION', 'url': None, 'node_id': 'MDc6TGljZW5zZTA='}, 'allow_forking': True, 'is_template': False, 'web_commit_signoff_required': False, 'topics': ['cpp', 'emulation', 'emulator', 'gamecube', 'wii'], 'visibility': 'public', 'forks': 2965, 'open_issues': 360, 'watchers': 14580, 'default_branch': 'master', 'custom_properties': {}}, 'organization': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'url': 'https://api.github.com/orgs/dolphin-emu', 'repos_url': 'https://api.github.com/orgs/dolphin-emu/repos', 'events_url': 'https://api.github.com/orgs/dolphin-emu/events', 'hooks_url': 'https://api.github.com/orgs/dolphin-emu/hooks', 'issues_url': 'https://api.github.com/orgs/dolphin-emu/issues', 'members_url': 'https://api.github.com/orgs/dolphin-emu/members{/member}', 'public_members_url': 'https://api.github.com/orgs/dolphin-emu/public_members{/member}', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'description': ''}, 'sender': {'login': 'pizzzza19', 'id': 183198342, 'node_id': 'U_kgDOCutihg', 'avatar_url': 'https://avatars.githubusercontent.com/u/183198342?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/pizzzza19', 'html_url': 'https://github.com/pizzzza19', 'followers_url': 'https://api.github.com/users/pizzzza19/followers', 'following_url': 'https://api.github.com/users/pizzzza19/following{/other_user}', 'gists_url': 'https://api.github.com/users/pizzzza19/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/pizzzza19/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/pizzzza19/subscriptions', 'organizations_url': 'https://api.github.com/users/pizzzza19/orgs', 'repos_url': 'https://api.github.com/users/pizzzza19/repos', 'events_url': 'https://api.github.com/users/pizzzza19/events{/privacy}', 'received_events_url': 'https://api.github.com/users/pizzzza19/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'installation': {'id': 35437981, 'node_id': 'MDIzOkludGVncmF0aW9uSW5zdGFsbGF0aW9uMzU0Mzc5ODE='}}, 'type': 'raw_gh_hook'}
2026-02-03T13:20:38.893387 {'source': 'webserver', 'gh_type': 'pull_request', 'raw': {'action': 'synchronize', 'number': 13997, 'pull_request': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13997', 'id': 2893849083, 'node_id': 'PR_kwDOALCn2M6sfKH7', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/13997', 'diff_url': 'https://github.com/dolphin-emu/dolphin/pull/13997.diff', 'patch_url': 'https://github.com/dolphin-emu/dolphin/pull/13997.patch', 'issue_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13997', 'number': 13997, 'state': 'open', 'locked': False, 'title': 'Externals: Update minizip-ng to 4.1.0', 'user': {'login': 'bgermann', 'id': 9329107, 'node_id': 'MDQ6VXNlcjkzMjkxMDc=', 'avatar_url': 'https://avatars.githubusercontent.com/u/9329107?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/bgermann', 'html_url': 'https://github.com/bgermann', 'followers_url': 'https://api.github.com/users/bgermann/followers', 'following_url': 'https://api.github.com/users/bgermann/following{/other_user}', 'gists_url': 'https://api.github.com/users/bgermann/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/bgermann/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/bgermann/subscriptions', 'organizations_url': 'https://api.github.com/users/bgermann/orgs', 'repos_url': 'https://api.github.com/users/bgermann/repos', 'events_url': 'https://api.github.com/users/bgermann/events{/privacy}', 'received_events_url': 'https://api.github.com/users/bgermann/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'body': 'This includes removals of non-distributable files.', 'created_at': '2025-10-07T12:39:37Z', 'updated_at': '2026-02-03T13:20:36Z', 'closed_at': None, 'merged_at': None, 'merge_commit_sha': '0f5aa622248621b8a16127a67f76c51c88e8f718', 'assignee': None, 'assignees': [], 'requested_reviewers': [], 'requested_teams': [], 'labels': [], 'milestone': None, 'draft': False, 'commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13997/commits', 'review_comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13997/comments', 'review_comment_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments{/number}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13997/comments', 'statuses_url': 'https://api.github.com/repos/dolphin-emu/dolphin/statuses/df85a2c99d667c537a5a896cd81954fb63fab027', 'head': {'label': 'bgermann:minizip-ng', 'ref': 'minizip-ng', 'sha': 'df85a2c99d667c537a5a896cd81954fb63fab027', 'user': {'login': 'bgermann', 'id': 9329107, 'node_id': 'MDQ6VXNlcjkzMjkxMDc=', 'avatar_url': 'https://avatars.githubusercontent.com/u/9329107?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/bgermann', 'html_url': 'https://github.com/bgermann', 'followers_url': 'https://api.github.com/users/bgermann/followers', 'following_url': 'https://api.github.com/users/bgermann/following{/other_user}', 'gists_url': 'https://api.github.com/users/bgermann/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/bgermann/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/bgermann/subscriptions', 'organizations_url': 'https://api.github.com/users/bgermann/orgs', 'repos_url': 'https://api.github.com/users/bgermann/repos', 'events_url': 'https://api.github.com/users/bgermann/events{/privacy}', 'received_events_url': 'https://api.github.com/users/bgermann/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'repo': {'id': 1071493499, 'node_id': 'R_kgDOP92xew', 'name': 'dolphin', 'full_name': 'bgermann/dolphin', 'private': False, 'owner': {'login': 'bgermann', 'id': 9329107, 'node_id': 'MDQ6VXNlcjkzMjkxMDc=', 'avatar_url': 'https://avatars.githubusercontent.com/u/9329107?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/bgermann', 'html_url': 'https://github.com/bgermann', 'followers_url': 'https://api.github.com/users/bgermann/followers', 'following_url': 'https://api.github.com/users/bgermann/following{/other_user}', 'gists_url': 'https://api.github.com/users/bgermann/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/bgermann/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/bgermann/subscriptions', 'organizations_url': 'https://api.github.com/users/bgermann/orgs', 'repos_url': 'https://api.github.com/users/bgermann/repos', 'events_url': 'https://api.github.com/users/bgermann/events{/privacy}', 'received_events_url': 'https://api.github.com/users/bgermann/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'html_url': 'https://github.com/bgermann/dolphin', 'description': 'Dolphin is a GameCube / Wii emulator, allowing you to play games for these two platforms on PC with improvements.', 'fork': True, 'url': 'https://api.github.com/repos/bgermann/dolphin', 'forks_url': 'https://api.github.com/repos/bgermann/dolphin/forks', 'keys_url': 'https://api.github.com/repos/bgermann/dolphin/keys{/key_id}', 'collaborators_url': 'https://api.github.com/repos/bgermann/dolphin/collaborators{/collaborator}', 'teams_url': 'https://api.github.com/repos/bgermann/dolphin/teams', 'hooks_url': 'https://api.github.com/repos/bgermann/dolphin/hooks', 'issue_events_url': 'https://api.github.com/repos/bgermann/dolphin/issues/events{/number}', 'events_url': 'https://api.github.com/repos/bgermann/dolphin/events', 'assignees_url': 'https://api.github.com/repos/bgermann/dolphin/assignees{/user}', 'branches_url': 'https://api.github.com/repos/bgermann/dolphin/branches{/branch}', 'tags_url': 'https://api.github.com/repos/bgermann/dolphin/tags', 'blobs_url': 'https://api.github.com/repos/bgermann/dolphin/git/blobs{/sha}', 'git_tags_url': 'https://api.github.com/repos/bgermann/dolphin/git/tags{/sha}', 'git_refs_url': 'https://api.github.com/repos/bgermann/dolphin/git/refs{/sha}', 'trees_url': 'https://api.github.com/repos/bgermann/dolphin/git/trees{/sha}', 'statuses_url': 'https://api.github.com/repos/bgermann/dolphin/statuses/{sha}', 'languages_url': 'https://api.github.com/repos/bgermann/dolphin/languages', 'stargazers_url': 'https://api.github.com/repos/bgermann/dolphin/stargazers', 'contributors_url': 'https://api.github.com/repos/bgermann/dolphin/contributors', 'subscribers_url': 'https://api.github.com/repos/bgermann/dolphin/subscribers', 'subscription_url': 'https://api.github.com/repos/bgermann/dolphin/subscription', 'commits_url': 'https://api.github.com/repos/bgermann/dolphin/commits{/sha}', 'git_commits_url': 'https://api.github.com/repos/bgermann/dolphin/git/commits{/sha}', 'comments_url': 'https://api.github.com/repos/bgermann/dolphin/comments{/number}', 'issue_comment_url': 'https://api.github.com/repos/bgermann/dolphin/issues/comments{/number}', 'contents_url': 'https://api.github.com/repos/bgermann/dolphin/contents/{+path}', 'compare_url': 'https://api.github.com/repos/bgermann/dolphin/compare/{base}...{head}', 'merges_url': 'https://api.github.com/repos/bgermann/dolphin/merges', 'archive_url': 'https://api.github.com/repos/bgermann/dolphin/{archive_format}{/ref}', 'downloads_url': 'https://api.github.com/repos/bgermann/dolphin/downloads', 'issues_url': 'https://api.github.com/repos/bgermann/dolphin/issues{/number}', 'pulls_url': 'https://api.github.com/repos/bgermann/dolphin/pulls{/number}', 'milestones_url': 'https://api.github.com/repos/bgermann/dolphin/milestones{/number}', 'notifications_url': 'https://api.github.com/repos/bgermann/dolphin/notifications{?since,all,participating}', 'labels_url': 'https://api.github.com/repos/bgermann/dolphin/labels{/name}', 'releases_url': 'https://api.github.com/repos/bgermann/dolphin/releases{/id}', 'deployments_url': 'https://api.github.com/repos/bgermann/dolphin/deployments', 'created_at': '2025-10-07T12:28:54Z', 'updated_at': '2026-01-29T08:36:46Z', 'pushed_at': '2026-02-03T13:20:35Z', 'git_url': 'git://github.com/bgermann/dolphin.git', 'ssh_url': 'git@github.com:bgermann/dolphin.git', 'clone_url': 'https://github.com/bgermann/dolphin.git', 'svn_url': 'https://github.com/bgermann/dolphin', 'homepage': 'https://dolphin-emu.org', 'size': 506471, 'stargazers_count': 0, 'watchers_count': 0, 'language': 'C++', 'has_issues': False, 'has_projects': True, 'has_downloads': True, 'has_wiki': True, 'has_pages': False, 'has_discussions': False, 'forks_count': 0, 'mirror_url': None, 'archived': False, 'disabled': False, 'open_issues_count': 0, 'license': {'key': 'other', 'name': 'Other', 'spdx_id': 'NOASSERTION', 'url': None, 'node_id': 'MDc6TGljZW5zZTA='}, 'allow_forking': True, 'is_template': False, 'web_commit_signoff_required': False, 'topics': [], 'visibility': 'public', 'forks': 0, 'open_issues': 0, 'watchers': 0, 'default_branch': 'master', 'allow_squash_merge': True, 'allow_merge_commit': True, 'allow_rebase_merge': True, 'allow_auto_merge': False, 'delete_branch_on_merge': False, 'allow_update_branch': False, 'use_squash_pr_title_as_default': False, 'squash_merge_commit_message': 'COMMIT_MESSAGES', 'squash_merge_commit_title': 'COMMIT_OR_PR_TITLE', 'merge_commit_message': 'PR_TITLE', 'merge_commit_title': 'MERGE_MESSAGE'}}, 'base': {'label': 'dolphin-emu:master', 'ref': 'master', 'sha': 'eac7b290423efea867c2c7f3f2b2792d05418bdd', 'user': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/dolphin-emu', 'html_url': 'https://github.com/dolphin-emu', 'followers_url': 'https://api.github.com/users/dolphin-emu/followers', 'following_url': 'https://api.github.com/users/dolphin-emu/following{/other_user}', 'gists_url': 'https://api.github.com/users/dolphin-emu/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/dolphin-emu/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/dolphin-emu/subscriptions', 'organizations_url': 'https://api.github.com/users/dolphin-emu/orgs', 'repos_url': 'https://api.github.com/users/dolphin-emu/repos', 'events_url': 'https://api.github.com/users/dolphin-emu/events{/privacy}', 'received_events_url': 'https://api.github.com/users/dolphin-emu/received_events', 'type': 'Organization', 'user_view_type': 'public', 'site_admin': False}, 'repo': {'id': 11577304, 'node_id': 'MDEwOlJlcG9zaXRvcnkxMTU3NzMwNA==', 'name': 'dolphin', 'full_name': 'dolphin-emu/dolphin', 'private': False, 'owner': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/dolphin-emu', 'html_url': 'https://github.com/dolphin-emu', 'followers_url': 'https://api.github.com/users/dolphin-emu/followers', 'following_url': 'https://api.github.com/users/dolphin-emu/following{/other_user}', 'gists_url': 'https://api.github.com/users/dolphin-emu/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/dolphin-emu/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/dolphin-emu/subscriptions', 'organizations_url': 'https://api.github.com/users/dolphin-emu/orgs', 'repos_url': 'https://api.github.com/users/dolphin-emu/repos', 'events_url': 'https://api.github.com/users/dolphin-emu/events{/privacy}', 'received_events_url': 'https://api.github.com/users/dolphin-emu/received_events', 'type': 'Organization', 'user_view_type': 'public', 'site_admin': False}, 'html_url': 'https://github.com/dolphin-emu/dolphin', 'description': 'Dolphin is a GameCube / Wii emulator, allowing you to play games for these two platforms on PC with improvements.', 'fork': False, 'url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'forks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/forks', 'keys_url': 'https://api.github.com/repos/dolphin-emu/dolphin/keys{/key_id}', 'collaborators_url': 'https://api.github.com/repos/dolphin-emu/dolphin/collaborators{/collaborator}', 'teams_url': 'https://api.github.com/repos/dolphin-emu/dolphin/teams', 'hooks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/hooks', 'issue_events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/events{/number}', 'events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/events', 'assignees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/assignees{/user}', 'branches_url': 'https://api.github.com/repos/dolphin-emu/dolphin/branches{/branch}', 'tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/tags', 'blobs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/blobs{/sha}', 'git_tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/tags{/sha}', 'git_refs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/refs{/sha}', 'trees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/trees{/sha}', 'statuses_url': 'https://api.github.com/repos/dolphin-emu/dolphin/statuses/{sha}', 'languages_url': 'https://api.github.com/repos/dolphin-emu/dolphin/languages', 'stargazers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/stargazers', 'contributors_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contributors', 'subscribers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscribers', 'subscription_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscription', 'commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/commits{/sha}', 'git_commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/commits{/sha}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/comments{/number}', 'issue_comment_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments{/number}', 'contents_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contents/{+path}', 'compare_url': 'https://api.github.com/repos/dolphin-emu/dolphin/compare/{base}...{head}', 'merges_url': 'https://api.github.com/repos/dolphin-emu/dolphin/merges', 'archive_url': 'https://api.github.com/repos/dolphin-emu/dolphin/{archive_format}{/ref}', 'downloads_url': 'https://api.github.com/repos/dolphin-emu/dolphin/downloads', 'issues_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues{/number}', 'pulls_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls{/number}', 'milestones_url': 'https://api.github.com/repos/dolphin-emu/dolphin/milestones{/number}', 'notifications_url': 'https://api.github.com/repos/dolphin-emu/dolphin/notifications{?since,all,participating}', 'labels_url': 'https://api.github.com/repos/dolphin-emu/dolphin/labels{/name}', 'releases_url': 'https://api.github.com/repos/dolphin-emu/dolphin/releases{/id}', 'deployments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/deployments', 'created_at': '2013-07-22T09:07:57Z', 'updated_at': '2026-02-03T12:36:07Z', 'pushed_at': '2026-02-03T04:26:13Z', 'git_url': 'git://github.com/dolphin-emu/dolphin.git', 'ssh_url': 'git@github.com:dolphin-emu/dolphin.git', 'clone_url': 'https://github.com/dolphin-emu/dolphin.git', 'svn_url': 'https://github.com/dolphin-emu/dolphin', 'homepage': 'https://dolphin-emu.org', 'size': 544014, 'stargazers_count': 14579, 'watchers_count': 14579, 'language': 'C++', 'has_issues': False, 'has_projects': False, 'has_downloads': True, 'has_wiki': True, 'has_pages': False, 'has_discussions': False, 'forks_count': 2965, 'mirror_url': None, 'archived': False, 'disabled': False, 'open_issues_count': 360, 'license': {'key': 'other', 'name': 'Other', 'spdx_id': 'NOASSERTION', 'url': None, 'node_id': 'MDc6TGljZW5zZTA='}, 'allow_forking': True, 'is_template': False, 'web_commit_signoff_required': False, 'topics': ['cpp', 'emulation', 'emulator', 'gamecube', 'wii'], 'visibility': 'public', 'forks': 2965, 'open_issues': 360, 'watchers': 14579, 'default_branch': 'master', 'allow_squash_merge': False, 'allow_merge_commit': True, 'allow_rebase_merge': False, 'allow_auto_merge': False, 'delete_branch_on_merge': False, 'allow_update_branch': False, 'use_squash_pr_title_as_default': False, 'squash_merge_commit_message': 'COMMIT_MESSAGES', 'squash_merge_commit_title': 'COMMIT_OR_PR_TITLE', 'merge_commit_message': 'PR_TITLE', 'merge_commit_title': 'MERGE_MESSAGE'}}, '_links': {'self': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13997'}, 'html': {'href': 'https://github.com/dolphin-emu/dolphin/pull/13997'}, 'issue': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13997'}, 'comments': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13997/comments'}, 'review_comments': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13997/comments'}, 'review_comment': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments{/number}'}, 'commits': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13997/commits'}, 'statuses': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/statuses/df85a2c99d667c537a5a896cd81954fb63fab027'}}, 'author_association': 'NONE', 'auto_merge': None, 'active_lock_reason': None, 'merged': False, 'mergeable': None, 'rebaseable': None, 'mergeable_state': 'unknown', 'merged_by': None, 'comments': 3, 'review_comments': 0, 'maintainer_can_modify': True, 'commits': 1, 'additions': 1, 'deletions': 1, 'changed_files': 1}, 'before': '5a875f6cbebf015e5948e7c99f4277982e85e8a5', 'after': 'df85a2c99d667c537a5a896cd81954fb63fab027', 'repository': {'id': 11577304, 'node_id': 'MDEwOlJlcG9zaXRvcnkxMTU3NzMwNA==', 'name': 'dolphin', 'full_name': 'dolphin-emu/dolphin', 'private': False, 'owner': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/dolphin-emu', 'html_url': 'https://github.com/dolphin-emu', 'followers_url': 'https://api.github.com/users/dolphin-emu/followers', 'following_url': 'https://api.github.com/users/dolphin-emu/following{/other_user}', 'gists_url': 'https://api.github.com/users/dolphin-emu/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/dolphin-emu/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/dolphin-emu/subscriptions', 'organizations_url': 'https://api.github.com/users/dolphin-emu/orgs', 'repos_url': 'https://api.github.com/users/dolphin-emu/repos', 'events_url': 'https://api.github.com/users/dolphin-emu/events{/privacy}', 'received_events_url': 'https://api.github.com/users/dolphin-emu/received_events', 'type': 'Organization', 'user_view_type': 'public', 'site_admin': False}, 'html_url': 'https://github.com/dolphin-emu/dolphin', 'description': 'Dolphin is a GameCube / Wii emulator, allowing you to play games for these two platforms on PC with improvements.', 'fork': False, 'url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'forks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/forks', 'keys_url': 'https://api.github.com/repos/dolphin-emu/dolphin/keys{/key_id}', 'collaborators_url': 'https://api.github.com/repos/dolphin-emu/dolphin/collaborators{/collaborator}', 'teams_url': 'https://api.github.com/repos/dolphin-emu/dolphin/teams', 'hooks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/hooks', 'issue_events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/events{/number}', 'events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/events', 'assignees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/assignees{/user}', 'branches_url': 'https://api.github.com/repos/dolphin-emu/dolphin/branches{/branch}', 'tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/tags', 'blobs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/blobs{/sha}', 'git_tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/tags{/sha}', 'git_refs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/refs{/sha}', 'trees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/trees{/sha}', 'statuses_url': 'https://api.github.com/repos/dolphin-emu/dolphin/statuses/{sha}', 'languages_url': 'https://api.github.com/repos/dolphin-emu/dolphin/languages', 'stargazers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/stargazers', 'contributors_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contributors', 'subscribers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscribers', 'subscription_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscription', 'commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/commits{/sha}', 'git_commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/commits{/sha}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/comments{/number}', 'issue_comment_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments{/number}', 'contents_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contents/{+path}', 'compare_url': 'https://api.github.com/repos/dolphin-emu/dolphin/compare/{base}...{head}', 'merges_url': 'https://api.github.com/repos/dolphin-emu/dolphin/merges', 'archive_url': 'https://api.github.com/repos/dolphin-emu/dolphin/{archive_format}{/ref}', 'downloads_url': 'https://api.github.com/repos/dolphin-emu/dolphin/downloads', 'issues_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues{/number}', 'pulls_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls{/number}', 'milestones_url': 'https://api.github.com/repos/dolphin-emu/dolphin/milestones{/number}', 'notifications_url': 'https://api.github.com/repos/dolphin-emu/dolphin/notifications{?since,all,participating}', 'labels_url': 'https://api.github.com/repos/dolphin-emu/dolphin/labels{/name}', 'releases_url': 'https://api.github.com/repos/dolphin-emu/dolphin/releases{/id}', 'deployments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/deployments', 'created_at': '2013-07-22T09:07:57Z', 'updated_at': '2026-02-03T12:36:07Z', 'pushed_at': '2026-02-03T04:26:13Z', 'git_url': 'git://github.com/dolphin-emu/dolphin.git', 'ssh_url': 'git@github.com:dolphin-emu/dolphin.git', 'clone_url': 'https://github.com/dolphin-emu/dolphin.git', 'svn_url': 'https://github.com/dolphin-emu/dolphin', 'homepage': 'https://dolphin-emu.org', 'size': 544014, 'stargazers_count': 14579, 'watchers_count': 14579, 'language': 'C++', 'has_issues': False, 'has_projects': False, 'has_downloads': True, 'has_wiki': True, 'has_pages': False, 'has_discussions': False, 'forks_count': 2965, 'mirror_url': None, 'archived': False, 'disabled': False, 'open_issues_count': 360, 'license': {'key': 'other', 'name': 'Other', 'spdx_id': 'NOASSERTION', 'url': None, 'node_id': 'MDc6TGljZW5zZTA='}, 'allow_forking': True, 'is_template': False, 'web_commit_signoff_required': False, 'topics': ['cpp', 'emulation', 'emulator', 'gamecube', 'wii'], 'visibility': 'public', 'forks': 2965, 'open_issues': 360, 'watchers': 14579, 'default_branch': 'master', 'custom_properties': {}}, 'organization': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'url': 'https://api.github.com/orgs/dolphin-emu', 'repos_url': 'https://api.github.com/orgs/dolphin-emu/repos', 'events_url': 'https://api.github.com/orgs/dolphin-emu/events', 'hooks_url': 'https://api.github.com/orgs/dolphin-emu/hooks', 'issues_url': 'https://api.github.com/orgs/dolphin-emu/issues', 'members_url': 'https://api.github.com/orgs/dolphin-emu/members{/member}', 'public_members_url': 'https://api.github.com/orgs/dolphin-emu/public_members{/member}', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'description': ''}, 'sender': {'login': 'bgermann', 'id': 9329107, 'node_id': 'MDQ6VXNlcjkzMjkxMDc=', 'avatar_url': 'https://avatars.githubusercontent.com/u/9329107?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/bgermann', 'html_url': 'https://github.com/bgermann', 'followers_url': 'https://api.github.com/users/bgermann/followers', 'following_url': 'https://api.github.com/users/bgermann/following{/other_user}', 'gists_url': 'https://api.github.com/users/bgermann/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/bgermann/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/bgermann/subscriptions', 'organizations_url': 'https://api.github.com/users/bgermann/orgs', 'repos_url': 'https://api.github.com/users/bgermann/repos', 'events_url': 'https://api.github.com/users/bgermann/events{/privacy}', 'received_events_url': 'https://api.github.com/users/bgermann/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'installation': {'id': 35437981, 'node_id': 'MDIzOkludGVncmF0aW9uSW5zdGFsbGF0aW9uMzU0Mzc5ODE='}}, 'type': 'raw_gh_hook'}
2026-02-03T04:59:38.731148 {'source': 'webserver', 'gh_type': 'pull_request_review', 'raw': {'action': 'submitted', 'review': {'id': 3742915580, 'node_id': 'PRR_kwDOALCn2M7fGFv8', 'user': {'login': 'jordan-woyak', 'id': 1768214, 'node_id': 'MDQ6VXNlcjE3NjgyMTQ=', 'avatar_url': 'https://avatars.githubusercontent.com/u/1768214?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/jordan-woyak', 'html_url': 'https://github.com/jordan-woyak', 'followers_url': 'https://api.github.com/users/jordan-woyak/followers', 'following_url': 'https://api.github.com/users/jordan-woyak/following{/other_user}', 'gists_url': 'https://api.github.com/users/jordan-woyak/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/jordan-woyak/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/jordan-woyak/subscriptions', 'organizations_url': 'https://api.github.com/users/jordan-woyak/orgs', 'repos_url': 'https://api.github.com/users/jordan-woyak/repos', 'events_url': 'https://api.github.com/users/jordan-woyak/events{/privacy}', 'received_events_url': 'https://api.github.com/users/jordan-woyak/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'body': None, 'commit_id': '41c26ea85f34764a41e9f4e79211cf9fe6bad48c', 'submitted_at': '2026-02-03T04:55:05Z', 'state': 'commented', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/14218#pullrequestreview-3742915580', 'pull_request_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/14218', '_links': {'html': {'href': 'https://github.com/dolphin-emu/dolphin/pull/14218#pullrequestreview-3742915580'}, 'pull_request': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/14218'}}, 'updated_at': '2026-02-03T04:55:05Z', 'author_association': 'MEMBER'}, 'pull_request': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/14218', 'id': 3099870143, 'node_id': 'PR_kwDOALCn2M64xEO_', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/14218', 'diff_url': 'https://github.com/dolphin-emu/dolphin/pull/14218.diff', 'patch_url': 'https://github.com/dolphin-emu/dolphin/pull/14218.patch', 'issue_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14218', 'number': 14218, 'state': 'open', 'locked': False, 'title': 'Jit64: Make ABI_CallFunction generic', 'user': {'login': 'JosJuice', 'id': 6716818, 'node_id': 'MDQ6VXNlcjY3MTY4MTg=', 'avatar_url': 'https://avatars.githubusercontent.com/u/6716818?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/JosJuice', 'html_url': 'https://github.com/JosJuice', 'followers_url': 'https://api.github.com/users/JosJuice/followers', 'following_url': 'https://api.github.com/users/JosJuice/following{/other_user}', 'gists_url': 'https://api.github.com/users/JosJuice/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/JosJuice/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/JosJuice/subscriptions', 'organizations_url': 'https://api.github.com/users/JosJuice/orgs', 'repos_url': 'https://api.github.com/users/JosJuice/repos', 'events_url': 'https://api.github.com/users/JosJuice/events{/privacy}', 'received_events_url': 'https://api.github.com/users/JosJuice/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'body': 'This replaces all the hand-coded variants of ABI_CallFunction with a single generic function. It handles all existing cases, plus many new cases that would have been annoying to hand-code.\r\n\r\nThe implementation is based on our AArch64 ABI_CallFunction, but adapted for x64. The most notable differences are support for memory operands and a new way for callers to specify the sizes of operands.', 'created_at': '2025-12-14T13:50:33Z', 'updated_at': '2026-02-03T04:55:05Z', 'closed_at': None, 'merged_at': None, 'merge_commit_sha': 'f86a133381f5a524545ebeff9efa0a63c20ca31f', 'assignee': None, 'assignees': [], 'requested_reviewers': [], 'requested_teams': [], 'labels': [], 'milestone': None, 'draft': False, 'commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/14218/commits', 'review_comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/14218/comments', 'review_comment_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments{/number}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14218/comments', 'statuses_url': 'https://api.github.com/repos/dolphin-emu/dolphin/statuses/41c26ea85f34764a41e9f4e79211cf9fe6bad48c', 'head': {'label': 'JosJuice:jit64-abi-call-function-parameters', 'ref': 'jit64-abi-call-function-parameters', 'sha': '41c26ea85f34764a41e9f4e79211cf9fe6bad48c', 'user': {'login': 'JosJuice', 'id': 6716818, 'node_id': 'MDQ6VXNlcjY3MTY4MTg=', 'avatar_url': 'https://avatars.githubusercontent.com/u/6716818?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/JosJuice', 'html_url': 'https://github.com/JosJuice', 'followers_url': 'https://api.github.com/users/JosJuice/followers', 'following_url': 'https://api.github.com/users/JosJuice/following{/other_user}', 'gists_url': 'https://api.github.com/users/JosJuice/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/JosJuice/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/JosJuice/subscriptions', 'organizations_url': 'https://api.github.com/users/JosJuice/orgs', 'repos_url': 'https://api.github.com/users/JosJuice/repos', 'events_url': 'https://api.github.com/users/JosJuice/events{/privacy}', 'received_events_url': 'https://api.github.com/users/JosJuice/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'repo': {'id': 26057138, 'node_id': 'MDEwOlJlcG9zaXRvcnkyNjA1NzEzOA==', 'name': 'dolphin', 'full_name': 'JosJuice/dolphin', 'private': False, 'owner': {'login': 'JosJuice', 'id': 6716818, 'node_id': 'MDQ6VXNlcjY3MTY4MTg=', 'avatar_url': 'https://avatars.githubusercontent.com/u/6716818?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/JosJuice', 'html_url': 'https://github.com/JosJuice', 'followers_url': 'https://api.github.com/users/JosJuice/followers', 'following_url': 'https://api.github.com/users/JosJuice/following{/other_user}', 'gists_url': 'https://api.github.com/users/JosJuice/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/JosJuice/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/JosJuice/subscriptions', 'organizations_url': 'https://api.github.com/users/JosJuice/orgs', 'repos_url': 'https://api.github.com/users/JosJuice/repos', 'events_url': 'https://api.github.com/users/JosJuice/events{/privacy}', 'received_events_url': 'https://api.github.com/users/JosJuice/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'html_url': 'https://github.com/JosJuice/dolphin', 'description': 'Dolphin is a GameCube/Wii emulator, allowing you to play games for these two platforms on PC, with improvements.', 'fork': True, 'url': 'https://api.github.com/repos/JosJuice/dolphin', 'forks_url': 'https://api.github.com/repos/JosJuice/dolphin/forks', 'keys_url': 'https://api.github.com/repos/JosJuice/dolphin/keys{/key_id}', 'collaborators_url': 'https://api.github.com/repos/JosJuice/dolphin/collaborators{/collaborator}', 'teams_url': 'https://api.github.com/repos/JosJuice/dolphin/teams', 'hooks_url': 'https://api.github.com/repos/JosJuice/dolphin/hooks', 'issue_events_url': 'https://api.github.com/repos/JosJuice/dolphin/issues/events{/number}', 'events_url': 'https://api.github.com/repos/JosJuice/dolphin/events', 'assignees_url': 'https://api.github.com/repos/JosJuice/dolphin/assignees{/user}', 'branches_url': 'https://api.github.com/repos/JosJuice/dolphin/branches{/branch}', 'tags_url': 'https://api.github.com/repos/JosJuice/dolphin/tags', 'blobs_url': 'https://api.github.com/repos/JosJuice/dolphin/git/blobs{/sha}', 'git_tags_url': 'https://api.github.com/repos/JosJuice/dolphin/git/tags{/sha}', 'git_refs_url': 'https://api.github.com/repos/JosJuice/dolphin/git/refs{/sha}', 'trees_url': 'https://api.github.com/repos/JosJuice/dolphin/git/trees{/sha}', 'statuses_url': 'https://api.github.com/repos/JosJuice/dolphin/statuses/{sha}', 'languages_url': 'https://api.github.com/repos/JosJuice/dolphin/languages', 'stargazers_url': 'https://api.github.com/repos/JosJuice/dolphin/stargazers', 'contributors_url': 'https://api.github.com/repos/JosJuice/dolphin/contributors', 'subscribers_url': 'https://api.github.com/repos/JosJuice/dolphin/subscribers', 'subscription_url': 'https://api.github.com/repos/JosJuice/dolphin/subscription', 'commits_url': 'https://api.github.com/repos/JosJuice/dolphin/commits{/sha}', 'git_commits_url': 'https://api.github.com/repos/JosJuice/dolphin/git/commits{/sha}', 'comments_url': 'https://api.github.com/repos/JosJuice/dolphin/comments{/number}', 'issue_comment_url': 'https://api.github.com/repos/JosJuice/dolphin/issues/comments{/number}', 'contents_url': 'https://api.github.com/repos/JosJuice/dolphin/contents/{+path}', 'compare_url': 'https://api.github.com/repos/JosJuice/dolphin/compare/{base}...{head}', 'merges_url': 'https://api.github.com/repos/JosJuice/dolphin/merges', 'archive_url': 'https://api.github.com/repos/JosJuice/dolphin/{archive_format}{/ref}', 'downloads_url': 'https://api.github.com/repos/JosJuice/dolphin/downloads', 'issues_url': 'https://api.github.com/repos/JosJuice/dolphin/issues{/number}', 'pulls_url': 'https://api.github.com/repos/JosJuice/dolphin/pulls{/number}', 'milestones_url': 'https://api.github.com/repos/JosJuice/dolphin/milestones{/number}', 'notifications_url': 'https://api.github.com/repos/JosJuice/dolphin/notifications{?since,all,participating}', 'labels_url': 'https://api.github.com/repos/JosJuice/dolphin/labels{/name}', 'releases_url': 'https://api.github.com/repos/JosJuice/dolphin/releases{/id}', 'deployments_url': 'https://api.github.com/repos/JosJuice/dolphin/deployments', 'created_at': '2014-11-01T17:12:40Z', 'updated_at': '2026-02-01T11:39:14Z', 'pushed_at': '2026-02-02T21:07:04Z', 'git_url': 'git://github.com/JosJuice/dolphin.git', 'ssh_url': 'git@github.com:JosJuice/dolphin.git', 'clone_url': 'https://github.com/JosJuice/dolphin.git', 'svn_url': 'https://github.com/JosJuice/dolphin', 'homepage': None, 'size': 509732, 'stargazers_count': 6, 'watchers_count': 6, 'language': 'C++', 'has_issues': False, 'has_projects': True, 'has_downloads': True, 'has_wiki': True, 'has_pages': False, 'has_discussions': False, 'forks_count': 1, 'mirror_url': None, 'archived': False, 'disabled': False, 'open_issues_count': 0, 'license': {'key': 'other', 'name': 'Other', 'spdx_id': 'NOASSERTION', 'url': None, 'node_id': 'MDc6TGljZW5zZTA='}, 'allow_forking': True, 'is_template': False, 'web_commit_signoff_required': False, 'topics': [], 'visibility': 'public', 'forks': 1, 'open_issues': 0, 'watchers': 6, 'default_branch': 'master', 'allow_squash_merge': True, 'allow_merge_commit': True, 'allow_rebase_merge': True, 'allow_auto_merge': False, 'delete_branch_on_merge': False, 'allow_update_branch': False, 'use_squash_pr_title_as_default': False, 'squash_merge_commit_message': 'COMMIT_MESSAGES', 'squash_merge_commit_title': 'COMMIT_OR_PR_TITLE', 'merge_commit_message': 'PR_TITLE', 'merge_commit_title': 'MERGE_MESSAGE'}}, 'base': {'label': 'dolphin-emu:master', 'ref': 'master', 'sha': 'a32779340e39392750de791c80d9773ff613c841', 'user': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/dolphin-emu', 'html_url': 'https://github.com/dolphin-emu', 'followers_url': 'https://api.github.com/users/dolphin-emu/followers', 'following_url': 'https://api.github.com/users/dolphin-emu/following{/other_user}', 'gists_url': 'https://api.github.com/users/dolphin-emu/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/dolphin-emu/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/dolphin-emu/subscriptions', 'organizations_url': 'https://api.github.com/users/dolphin-emu/orgs', 'repos_url': 'https://api.github.com/users/dolphin-emu/repos', 'events_url': 'https://api.github.com/users/dolphin-emu/events{/privacy}', 'received_events_url': 'https://api.github.com/users/dolphin-emu/received_events', 'type': 'Organization', 'user_view_type': 'public', 'site_admin': False}, 'repo': {'id': 11577304, 'node_id': 'MDEwOlJlcG9zaXRvcnkxMTU3NzMwNA==', 'name': 'dolphin', 'full_name': 'dolphin-emu/dolphin', 'private': False, 'owner': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/dolphin-emu', 'html_url': 'https://github.com/dolphin-emu', 'followers_url': 'https://api.github.com/users/dolphin-emu/followers', 'following_url': 'https://api.github.com/users/dolphin-emu/following{/other_user}', 'gists_url': 'https://api.github.com/users/dolphin-emu/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/dolphin-emu/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/dolphin-emu/subscriptions', 'organizations_url': 'https://api.github.com/users/dolphin-emu/orgs', 'repos_url': 'https://api.github.com/users/dolphin-emu/repos', 'events_url': 'https://api.github.com/users/dolphin-emu/events{/privacy}', 'received_events_url': 'https://api.github.com/users/dolphin-emu/received_events', 'type': 'Organization', 'user_view_type': 'public', 'site_admin': False}, 'html_url': 'https://github.com/dolphin-emu/dolphin', 'description': 'Dolphin is a GameCube / Wii emulator, allowing you to play games for these two platforms on PC with improvements.', 'fork': False, 'url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'forks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/forks', 'keys_url': 'https://api.github.com/repos/dolphin-emu/dolphin/keys{/key_id}', 'collaborators_url': 'https://api.github.com/repos/dolphin-emu/dolphin/collaborators{/collaborator}', 'teams_url': 'https://api.github.com/repos/dolphin-emu/dolphin/teams', 'hooks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/hooks', 'issue_events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/events{/number}', 'events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/events', 'assignees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/assignees{/user}', 'branches_url': 'https://api.github.com/repos/dolphin-emu/dolphin/branches{/branch}', 'tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/tags', 'blobs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/blobs{/sha}', 'git_tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/tags{/sha}', 'git_refs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/refs{/sha}', 'trees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/trees{/sha}', 'statuses_url': 'https://api.github.com/repos/dolphin-emu/dolphin/statuses/{sha}', 'languages_url': 'https://api.github.com/repos/dolphin-emu/dolphin/languages', 'stargazers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/stargazers', 'contributors_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contributors', 'subscribers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscribers', 'subscription_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscription', 'commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/commits{/sha}', 'git_commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/commits{/sha}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/comments{/number}', 'issue_comment_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments{/number}', 'contents_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contents/{+path}', 'compare_url': 'https://api.github.com/repos/dolphin-emu/dolphin/compare/{base}...{head}', 'merges_url': 'https://api.github.com/repos/dolphin-emu/dolphin/merges', 'archive_url': 'https://api.github.com/repos/dolphin-emu/dolphin/{archive_format}{/ref}', 'downloads_url': 'https://api.github.com/repos/dolphin-emu/dolphin/downloads', 'issues_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues{/number}', 'pulls_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls{/number}', 'milestones_url': 'https://api.github.com/repos/dolphin-emu/dolphin/milestones{/number}', 'notifications_url': 'https://api.github.com/repos/dolphin-emu/dolphin/notifications{?since,all,participating}', 'labels_url': 'https://api.github.com/repos/dolphin-emu/dolphin/labels{/name}', 'releases_url': 'https://api.github.com/repos/dolphin-emu/dolphin/releases{/id}', 'deployments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/deployments', 'created_at': '2013-07-22T09:07:57Z', 'updated_at': '2026-02-03T04:26:20Z', 'pushed_at': '2026-02-03T04:26:13Z', 'git_url': 'git://github.com/dolphin-emu/dolphin.git', 'ssh_url': 'git@github.com:dolphin-emu/dolphin.git', 'clone_url': 'https://github.com/dolphin-emu/dolphin.git', 'svn_url': 'https://github.com/dolphin-emu/dolphin', 'homepage': 'https://dolphin-emu.org', 'size': 544012, 'stargazers_count': 14577, 'watchers_count': 14577, 'language': 'C++', 'has_issues': False, 'has_projects': False, 'has_downloads': True, 'has_wiki': True, 'has_pages': False, 'has_discussions': False, 'forks_count': 2966, 'mirror_url': None, 'archived': False, 'disabled': False, 'open_issues_count': 360, 'license': {'key': 'other', 'name': 'Other', 'spdx_id': 'NOASSERTION', 'url': None, 'node_id': 'MDc6TGljZW5zZTA='}, 'allow_forking': True, 'is_template': False, 'web_commit_signoff_required': False, 'topics': ['cpp', 'emulation', 'emulator', 'gamecube', 'wii'], 'visibility': 'public', 'forks': 2966, 'open_issues': 360, 'watchers': 14577, 'default_branch': 'master', 'allow_squash_merge': False, 'allow_merge_commit': True, 'allow_rebase_merge': False, 'allow_auto_merge': False, 'delete_branch_on_merge': False, 'allow_update_branch': False, 'use_squash_pr_title_as_default': False, 'squash_merge_commit_message': 'COMMIT_MESSAGES', 'squash_merge_commit_title': 'COMMIT_OR_PR_TITLE', 'merge_commit_message': 'PR_TITLE', 'merge_commit_title': 'MERGE_MESSAGE'}}, '_links': {'self': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/14218'}, 'html': {'href': 'https://github.com/dolphin-emu/dolphin/pull/14218'}, 'issue': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14218'}, 'comments': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14218/comments'}, 'review_comments': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/14218/comments'}, 'review_comment': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments{/number}'}, 'commits': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/14218/commits'}, 'statuses': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/statuses/41c26ea85f34764a41e9f4e79211cf9fe6bad48c'}}, 'author_association': 'MEMBER', 'auto_merge': None, 'active_lock_reason': None}, 'repository': {'id': 11577304, 'node_id': 'MDEwOlJlcG9zaXRvcnkxMTU3NzMwNA==', 'name': 'dolphin', 'full_name': 'dolphin-emu/dolphin', 'private': False, 'owner': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/dolphin-emu', 'html_url': 'https://github.com/dolphin-emu', 'followers_url': 'https://api.github.com/users/dolphin-emu/followers', 'following_url': 'https://api.github.com/users/dolphin-emu/following{/other_user}', 'gists_url': 'https://api.github.com/users/dolphin-emu/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/dolphin-emu/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/dolphin-emu/subscriptions', 'organizations_url': 'https://api.github.com/users/dolphin-emu/orgs', 'repos_url': 'https://api.github.com/users/dolphin-emu/repos', 'events_url': 'https://api.github.com/users/dolphin-emu/events{/privacy}', 'received_events_url': 'https://api.github.com/users/dolphin-emu/received_events', 'type': 'Organization', 'user_view_type': 'public', 'site_admin': False}, 'html_url': 'https://github.com/dolphin-emu/dolphin', 'description': 'Dolphin is a GameCube / Wii emulator, allowing you to play games for these two platforms on PC with improvements.', 'fork': False, 'url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'forks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/forks', 'keys_url': 'https://api.github.com/repos/dolphin-emu/dolphin/keys{/key_id}', 'collaborators_url': 'https://api.github.com/repos/dolphin-emu/dolphin/collaborators{/collaborator}', 'teams_url': 'https://api.github.com/repos/dolphin-emu/dolphin/teams', 'hooks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/hooks', 'issue_events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/events{/number}', 'events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/events', 'assignees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/assignees{/user}', 'branches_url': 'https://api.github.com/repos/dolphin-emu/dolphin/branches{/branch}', 'tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/tags', 'blobs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/blobs{/sha}', 'git_tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/tags{/sha}', 'git_refs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/refs{/sha}', 'trees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/trees{/sha}', 'statuses_url': 'https://api.github.com/repos/dolphin-emu/dolphin/statuses/{sha}', 'languages_url': 'https://api.github.com/repos/dolphin-emu/dolphin/languages', 'stargazers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/stargazers', 'contributors_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contributors', 'subscribers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscribers', 'subscription_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscription', 'commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/commits{/sha}', 'git_commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/commits{/sha}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/comments{/number}', 'issue_comment_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments{/number}', 'contents_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contents/{+path}', 'compare_url': 'https://api.github.com/repos/dolphin-emu/dolphin/compare/{base}...{head}', 'merges_url': 'https://api.github.com/repos/dolphin-emu/dolphin/merges', 'archive_url': 'https://api.github.com/repos/dolphin-emu/dolphin/{archive_format}{/ref}', 'downloads_url': 'https://api.github.com/repos/dolphin-emu/dolphin/downloads', 'issues_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues{/number}', 'pulls_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls{/number}', 'milestones_url': 'https://api.github.com/repos/dolphin-emu/dolphin/milestones{/number}', 'notifications_url': 'https://api.github.com/repos/dolphin-emu/dolphin/notifications{?since,all,participating}', 'labels_url': 'https://api.github.com/repos/dolphin-emu/dolphin/labels{/name}', 'releases_url': 'https://api.github.com/repos/dolphin-emu/dolphin/releases{/id}', 'deployments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/deployments', 'created_at': '2013-07-22T09:07:57Z', 'updated_at': '2026-02-03T04:26:20Z', 'pushed_at': '2026-02-03T04:26:13Z', 'git_url': 'git://github.com/dolphin-emu/dolphin.git', 'ssh_url': 'git@github.com:dolphin-emu/dolphin.git', 'clone_url': 'https://github.com/dolphin-emu/dolphin.git', 'svn_url': 'https://github.com/dolphin-emu/dolphin', 'homepage': 'https://dolphin-emu.org', 'size': 544012, 'stargazers_count': 14577, 'watchers_count': 14577, 'language': 'C++', 'has_issues': False, 'has_projects': False, 'has_downloads': True, 'has_wiki': True, 'has_pages': False, 'has_discussions': False, 'forks_count': 2966, 'mirror_url': None, 'archived': False, 'disabled': False, 'open_issues_count': 360, 'license': {'key': 'other', 'name': 'Other', 'spdx_id': 'NOASSERTION', 'url': None, 'node_id': 'MDc6TGljZW5zZTA='}, 'allow_forking': True, 'is_template': False, 'web_commit_signoff_required': False, 'topics': ['cpp', 'emulation', 'emulator', 'gamecube', 'wii'], 'visibility': 'public', 'forks': 2966, 'open_issues': 360, 'watchers': 14577, 'default_branch': 'master', 'custom_properties': {}}, 'organization': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'url': 'https://api.github.com/orgs/dolphin-emu', 'repos_url': 'https://api.github.com/orgs/dolphin-emu/repos', 'events_url': 'https://api.github.com/orgs/dolphin-emu/events', 'hooks_url': 'https://api.github.com/orgs/dolphin-emu/hooks', 'issues_url': 'https://api.github.com/orgs/dolphin-emu/issues', 'members_url': 'https://api.github.com/orgs/dolphin-emu/members{/member}', 'public_members_url': 'https://api.github.com/orgs/dolphin-emu/public_members{/member}', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'description': ''}, 'sender': {'login': 'jordan-woyak', 'id': 1768214, 'node_id': 'MDQ6VXNlcjE3NjgyMTQ=', 'avatar_url': 'https://avatars.githubusercontent.com/u/1768214?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/jordan-woyak', 'html_url': 'https://github.com/jordan-woyak', 'followers_url': 'https://api.github.com/users/jordan-woyak/followers', 'following_url': 'https://api.github.com/users/jordan-woyak/following{/other_user}', 'gists_url': 'https://api.github.com/users/jordan-woyak/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/jordan-woyak/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/jordan-woyak/subscriptions', 'organizations_url': 'https://api.github.com/users/jordan-woyak/orgs', 'repos_url': 'https://api.github.com/users/jordan-woyak/repos', 'events_url': 'https://api.github.com/users/jordan-woyak/events{/privacy}', 'received_events_url': 'https://api.github.com/users/jordan-woyak/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'installation': {'id': 35437981, 'node_id': 'MDIzOkludGVncmF0aW9uSW5zdGFsbGF0aW9uMzU0Mzc5ODE='}}, 'type': 'raw_gh_hook'}
2026-02-03T04:58:23.579068 {'source': 'webserver', 'gh_type': 'pull_request_review_comment', 'raw': {'action': 'created', 'comment': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2757167327', 'pull_request_review_id': 3742915580, 'id': 2757167327, 'node_id': 'PRRC_kwDOALCn2M6kVwjf', 'diff_hunk': '@@ -377,6 +423,11 @@ class XEmitter\n void ABI_CalculateFrameSize(BitSet32 mask, size_t rsp_alignment, size_t needed_frame_size,\n size_t* shadowp, size_t* subtractionp, size_t* xmm_offsetp);\n \n+ // This function solves the "parallel moves" problem that\'s common in compilers.\n+ // The arguments are mutated!\n+ void ParallelMoves(RegisterMove* begin, RegisterMove* end, std::array<u8, 16>* source_reg_uses,\n+ std::array<u8, 16>* destination_reg_uses);', 'path': 'Source/Core/Common/x64Emitter.h', 'commit_id': '41c26ea85f34764a41e9f4e79211cf9fe6bad48c', 'original_commit_id': '41c26ea85f34764a41e9f4e79211cf9fe6bad48c', 'user': {'login': 'jordan-woyak', 'id': 1768214, 'node_id': 'MDQ6VXNlcjE3NjgyMTQ=', 'avatar_url': 'https://avatars.githubusercontent.com/u/1768214?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/jordan-woyak', 'html_url': 'https://github.com/jordan-woyak', 'followers_url': 'https://api.github.com/users/jordan-woyak/followers', 'following_url': 'https://api.github.com/users/jordan-woyak/following{/other_user}', 'gists_url': 'https://api.github.com/users/jordan-woyak/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/jordan-woyak/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/jordan-woyak/subscriptions', 'organizations_url': 'https://api.github.com/users/jordan-woyak/orgs', 'repos_url': 'https://api.github.com/users/jordan-woyak/repos', 'events_url': 'https://api.github.com/users/jordan-woyak/events{/privacy}', 'received_events_url': 'https://api.github.com/users/jordan-woyak/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'body': '```suggestion\n void ParallelMoves(std::span<RegisterMove> pending_moves, std::array<u8, 16>* source_reg_uses,\n std::array<u8, 16>* destination_reg_uses);\n```\nI suppose a `span` would be cleaner than two pointers.\n\nThen the call site becomes just:\n```cpp\nParallelMoves(pending_moves, &source_reg_uses, &destination_reg_uses);\n```', 'created_at': '2026-02-03T04:42:30Z', 'updated_at': '2026-02-03T04:55:05Z', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/14218#discussion_r2757167327', 'pull_request_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/14218', '_links': {'self': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2757167327'}, 'html': {'href': 'https://github.com/dolphin-emu/dolphin/pull/14218#discussion_r2757167327'}, 'pull_request': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/14218'}}, 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2757167327/reactions', 'total_count': 0, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 0, 'confused': 0, 'heart': 0, 'rocket': 0, 'eyes': 0}, 'start_line': 428, 'original_start_line': 428, 'start_side': 'RIGHT', 'line': 429, 'original_line': 429, 'side': 'RIGHT', 'author_association': 'MEMBER', 'original_position': 84, 'position': 84, 'subject_type': 'line'}, 'pull_request': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/14218', 'id': 3099870143, 'node_id': 'PR_kwDOALCn2M64xEO_', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/14218', 'diff_url': 'https://github.com/dolphin-emu/dolphin/pull/14218.diff', 'patch_url': 'https://github.com/dolphin-emu/dolphin/pull/14218.patch', 'issue_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14218', 'number': 14218, 'state': 'open', 'locked': False, 'title': 'Jit64: Make ABI_CallFunction generic', 'user': {'login': 'JosJuice', 'id': 6716818, 'node_id': 'MDQ6VXNlcjY3MTY4MTg=', 'avatar_url': 'https://avatars.githubusercontent.com/u/6716818?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/JosJuice', 'html_url': 'https://github.com/JosJuice', 'followers_url': 'https://api.github.com/users/JosJuice/followers', 'following_url': 'https://api.github.com/users/JosJuice/following{/other_user}', 'gists_url': 'https://api.github.com/users/JosJuice/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/JosJuice/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/JosJuice/subscriptions', 'organizations_url': 'https://api.github.com/users/JosJuice/orgs', 'repos_url': 'https://api.github.com/users/JosJuice/repos', 'events_url': 'https://api.github.com/users/JosJuice/events{/privacy}', 'received_events_url': 'https://api.github.com/users/JosJuice/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'body': 'This replaces all the hand-coded variants of ABI_CallFunction with a single generic function. It handles all existing cases, plus many new cases that would have been annoying to hand-code.\r\n\r\nThe implementation is based on our AArch64 ABI_CallFunction, but adapted for x64. The most notable differences are support for memory operands and a new way for callers to specify the sizes of operands.', 'created_at': '2025-12-14T13:50:33Z', 'updated_at': '2026-02-03T04:55:05Z', 'closed_at': None, 'merged_at': None, 'merge_commit_sha': 'f86a133381f5a524545ebeff9efa0a63c20ca31f', 'assignee': None, 'assignees': [], 'requested_reviewers': [], 'requested_teams': [], 'labels': [], 'milestone': None, 'draft': False, 'commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/14218/commits', 'review_comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/14218/comments', 'review_comment_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments{/number}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14218/comments', 'statuses_url': 'https://api.github.com/repos/dolphin-emu/dolphin/statuses/41c26ea85f34764a41e9f4e79211cf9fe6bad48c', 'head': {'label': 'JosJuice:jit64-abi-call-function-parameters', 'ref': 'jit64-abi-call-function-parameters', 'sha': '41c26ea85f34764a41e9f4e79211cf9fe6bad48c', 'user': {'login': 'JosJuice', 'id': 6716818, 'node_id': 'MDQ6VXNlcjY3MTY4MTg=', 'avatar_url': 'https://avatars.githubusercontent.com/u/6716818?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/JosJuice', 'html_url': 'https://github.com/JosJuice', 'followers_url': 'https://api.github.com/users/JosJuice/followers', 'following_url': 'https://api.github.com/users/JosJuice/following{/other_user}', 'gists_url': 'https://api.github.com/users/JosJuice/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/JosJuice/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/JosJuice/subscriptions', 'organizations_url': 'https://api.github.com/users/JosJuice/orgs', 'repos_url': 'https://api.github.com/users/JosJuice/repos', 'events_url': 'https://api.github.com/users/JosJuice/events{/privacy}', 'received_events_url': 'https://api.github.com/users/JosJuice/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'repo': {'id': 26057138, 'node_id': 'MDEwOlJlcG9zaXRvcnkyNjA1NzEzOA==', 'name': 'dolphin', 'full_name': 'JosJuice/dolphin', 'private': False, 'owner': {'login': 'JosJuice', 'id': 6716818, 'node_id': 'MDQ6VXNlcjY3MTY4MTg=', 'avatar_url': 'https://avatars.githubusercontent.com/u/6716818?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/JosJuice', 'html_url': 'https://github.com/JosJuice', 'followers_url': 'https://api.github.com/users/JosJuice/followers', 'following_url': 'https://api.github.com/users/JosJuice/following{/other_user}', 'gists_url': 'https://api.github.com/users/JosJuice/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/JosJuice/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/JosJuice/subscriptions', 'organizations_url': 'https://api.github.com/users/JosJuice/orgs', 'repos_url': 'https://api.github.com/users/JosJuice/repos', 'events_url': 'https://api.github.com/users/JosJuice/events{/privacy}', 'received_events_url': 'https://api.github.com/users/JosJuice/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'html_url': 'https://github.com/JosJuice/dolphin', 'description': 'Dolphin is a GameCube/Wii emulator, allowing you to play games for these two platforms on PC, with improvements.', 'fork': True, 'url': 'https://api.github.com/repos/JosJuice/dolphin', 'forks_url': 'https://api.github.com/repos/JosJuice/dolphin/forks', 'keys_url': 'https://api.github.com/repos/JosJuice/dolphin/keys{/key_id}', 'collaborators_url': 'https://api.github.com/repos/JosJuice/dolphin/collaborators{/collaborator}', 'teams_url': 'https://api.github.com/repos/JosJuice/dolphin/teams', 'hooks_url': 'https://api.github.com/repos/JosJuice/dolphin/hooks', 'issue_events_url': 'https://api.github.com/repos/JosJuice/dolphin/issues/events{/number}', 'events_url': 'https://api.github.com/repos/JosJuice/dolphin/events', 'assignees_url': 'https://api.github.com/repos/JosJuice/dolphin/assignees{/user}', 'branches_url': 'https://api.github.com/repos/JosJuice/dolphin/branches{/branch}', 'tags_url': 'https://api.github.com/repos/JosJuice/dolphin/tags', 'blobs_url': 'https://api.github.com/repos/JosJuice/dolphin/git/blobs{/sha}', 'git_tags_url': 'https://api.github.com/repos/JosJuice/dolphin/git/tags{/sha}', 'git_refs_url': 'https://api.github.com/repos/JosJuice/dolphin/git/refs{/sha}', 'trees_url': 'https://api.github.com/repos/JosJuice/dolphin/git/trees{/sha}', 'statuses_url': 'https://api.github.com/repos/JosJuice/dolphin/statuses/{sha}', 'languages_url': 'https://api.github.com/repos/JosJuice/dolphin/languages', 'stargazers_url': 'https://api.github.com/repos/JosJuice/dolphin/stargazers', 'contributors_url': 'https://api.github.com/repos/JosJuice/dolphin/contributors', 'subscribers_url': 'https://api.github.com/repos/JosJuice/dolphin/subscribers', 'subscription_url': 'https://api.github.com/repos/JosJuice/dolphin/subscription', 'commits_url': 'https://api.github.com/repos/JosJuice/dolphin/commits{/sha}', 'git_commits_url': 'https://api.github.com/repos/JosJuice/dolphin/git/commits{/sha}', 'comments_url': 'https://api.github.com/repos/JosJuice/dolphin/comments{/number}', 'issue_comment_url': 'https://api.github.com/repos/JosJuice/dolphin/issues/comments{/number}', 'contents_url': 'https://api.github.com/repos/JosJuice/dolphin/contents/{+path}', 'compare_url': 'https://api.github.com/repos/JosJuice/dolphin/compare/{base}...{head}', 'merges_url': 'https://api.github.com/repos/JosJuice/dolphin/merges', 'archive_url': 'https://api.github.com/repos/JosJuice/dolphin/{archive_format}{/ref}', 'downloads_url': 'https://api.github.com/repos/JosJuice/dolphin/downloads', 'issues_url': 'https://api.github.com/repos/JosJuice/dolphin/issues{/number}', 'pulls_url': 'https://api.github.com/repos/JosJuice/dolphin/pulls{/number}', 'milestones_url': 'https://api.github.com/repos/JosJuice/dolphin/milestones{/number}', 'notifications_url': 'https://api.github.com/repos/JosJuice/dolphin/notifications{?since,all,participating}', 'labels_url': 'https://api.github.com/repos/JosJuice/dolphin/labels{/name}', 'releases_url': 'https://api.github.com/repos/JosJuice/dolphin/releases{/id}', 'deployments_url': 'https://api.github.com/repos/JosJuice/dolphin/deployments', 'created_at': '2014-11-01T17:12:40Z', 'updated_at': '2026-02-01T11:39:14Z', 'pushed_at': '2026-02-02T21:07:04Z', 'git_url': 'git://github.com/JosJuice/dolphin.git', 'ssh_url': 'git@github.com:JosJuice/dolphin.git', 'clone_url': 'https://github.com/JosJuice/dolphin.git', 'svn_url': 'https://github.com/JosJuice/dolphin', 'homepage': None, 'size': 509732, 'stargazers_count': 6, 'watchers_count': 6, 'language': 'C++', 'has_issues': False, 'has_projects': True, 'has_downloads': True, 'has_wiki': True, 'has_pages': False, 'has_discussions': False, 'forks_count': 1, 'mirror_url': None, 'archived': False, 'disabled': False, 'open_issues_count': 0, 'license': {'key': 'other', 'name': 'Other', 'spdx_id': 'NOASSERTION', 'url': None, 'node_id': 'MDc6TGljZW5zZTA='}, 'allow_forking': True, 'is_template': False, 'web_commit_signoff_required': False, 'topics': [], 'visibility': 'public', 'forks': 1, 'open_issues': 0, 'watchers': 6, 'default_branch': 'master', 'allow_squash_merge': True, 'allow_merge_commit': True, 'allow_rebase_merge': True, 'allow_auto_merge': False, 'delete_branch_on_merge': False, 'allow_update_branch': False, 'use_squash_pr_title_as_default': False, 'squash_merge_commit_message': 'COMMIT_MESSAGES', 'squash_merge_commit_title': 'COMMIT_OR_PR_TITLE', 'merge_commit_message': 'PR_TITLE', 'merge_commit_title': 'MERGE_MESSAGE'}}, 'base': {'label': 'dolphin-emu:master', 'ref': 'master', 'sha': 'a32779340e39392750de791c80d9773ff613c841', 'user': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/dolphin-emu', 'html_url': 'https://github.com/dolphin-emu', 'followers_url': 'https://api.github.com/users/dolphin-emu/followers', 'following_url': 'https://api.github.com/users/dolphin-emu/following{/other_user}', 'gists_url': 'https://api.github.com/users/dolphin-emu/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/dolphin-emu/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/dolphin-emu/subscriptions', 'organizations_url': 'https://api.github.com/users/dolphin-emu/orgs', 'repos_url': 'https://api.github.com/users/dolphin-emu/repos', 'events_url': 'https://api.github.com/users/dolphin-emu/events{/privacy}', 'received_events_url': 'https://api.github.com/users/dolphin-emu/received_events', 'type': 'Organization', 'user_view_type': 'public', 'site_admin': False}, 'repo': {'id': 11577304, 'node_id': 'MDEwOlJlcG9zaXRvcnkxMTU3NzMwNA==', 'name': 'dolphin', 'full_name': 'dolphin-emu/dolphin', 'private': False, 'owner': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/dolphin-emu', 'html_url': 'https://github.com/dolphin-emu', 'followers_url': 'https://api.github.com/users/dolphin-emu/followers', 'following_url': 'https://api.github.com/users/dolphin-emu/following{/other_user}', 'gists_url': 'https://api.github.com/users/dolphin-emu/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/dolphin-emu/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/dolphin-emu/subscriptions', 'organizations_url': 'https://api.github.com/users/dolphin-emu/orgs', 'repos_url': 'https://api.github.com/users/dolphin-emu/repos', 'events_url': 'https://api.github.com/users/dolphin-emu/events{/privacy}', 'received_events_url': 'https://api.github.com/users/dolphin-emu/received_events', 'type': 'Organization', 'user_view_type': 'public', 'site_admin': False}, 'html_url': 'https://github.com/dolphin-emu/dolphin', 'description': 'Dolphin is a GameCube / Wii emulator, allowing you to play games for these two platforms on PC with improvements.', 'fork': False, 'url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'forks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/forks', 'keys_url': 'https://api.github.com/repos/dolphin-emu/dolphin/keys{/key_id}', 'collaborators_url': 'https://api.github.com/repos/dolphin-emu/dolphin/collaborators{/collaborator}', 'teams_url': 'https://api.github.com/repos/dolphin-emu/dolphin/teams', 'hooks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/hooks', 'issue_events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/events{/number}', 'events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/events', 'assignees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/assignees{/user}', 'branches_url': 'https://api.github.com/repos/dolphin-emu/dolphin/branches{/branch}', 'tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/tags', 'blobs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/blobs{/sha}', 'git_tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/tags{/sha}', 'git_refs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/refs{/sha}', 'trees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/trees{/sha}', 'statuses_url': 'https://api.github.com/repos/dolphin-emu/dolphin/statuses/{sha}', 'languages_url': 'https://api.github.com/repos/dolphin-emu/dolphin/languages', 'stargazers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/stargazers', 'contributors_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contributors', 'subscribers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscribers', 'subscription_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscription', 'commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/commits{/sha}', 'git_commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/commits{/sha}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/comments{/number}', 'issue_comment_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments{/number}', 'contents_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contents/{+path}', 'compare_url': 'https://api.github.com/repos/dolphin-emu/dolphin/compare/{base}...{head}', 'merges_url': 'https://api.github.com/repos/dolphin-emu/dolphin/merges', 'archive_url': 'https://api.github.com/repos/dolphin-emu/dolphin/{archive_format}{/ref}', 'downloads_url': 'https://api.github.com/repos/dolphin-emu/dolphin/downloads', 'issues_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues{/number}', 'pulls_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls{/number}', 'milestones_url': 'https://api.github.com/repos/dolphin-emu/dolphin/milestones{/number}', 'notifications_url': 'https://api.github.com/repos/dolphin-emu/dolphin/notifications{?since,all,participating}', 'labels_url': 'https://api.github.com/repos/dolphin-emu/dolphin/labels{/name}', 'releases_url': 'https://api.github.com/repos/dolphin-emu/dolphin/releases{/id}', 'deployments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/deployments', 'created_at': '2013-07-22T09:07:57Z', 'updated_at': '2026-02-03T04:26:20Z', 'pushed_at': '2026-02-03T04:26:13Z', 'git_url': 'git://github.com/dolphin-emu/dolphin.git', 'ssh_url': 'git@github.com:dolphin-emu/dolphin.git', 'clone_url': 'https://github.com/dolphin-emu/dolphin.git', 'svn_url': 'https://github.com/dolphin-emu/dolphin', 'homepage': 'https://dolphin-emu.org', 'size': 544012, 'stargazers_count': 14577, 'watchers_count': 14577, 'language': 'C++', 'has_issues': False, 'has_projects': False, 'has_downloads': True, 'has_wiki': True, 'has_pages': False, 'has_discussions': False, 'forks_count': 2966, 'mirror_url': None, 'archived': False, 'disabled': False, 'open_issues_count': 360, 'license': {'key': 'other', 'name': 'Other', 'spdx_id': 'NOASSERTION', 'url': None, 'node_id': 'MDc6TGljZW5zZTA='}, 'allow_forking': True, 'is_template': False, 'web_commit_signoff_required': False, 'topics': ['cpp', 'emulation', 'emulator', 'gamecube', 'wii'], 'visibility': 'public', 'forks': 2966, 'open_issues': 360, 'watchers': 14577, 'default_branch': 'master', 'allow_squash_merge': False, 'allow_merge_commit': True, 'allow_rebase_merge': False, 'allow_auto_merge': False, 'delete_branch_on_merge': False, 'allow_update_branch': False, 'use_squash_pr_title_as_default': False, 'squash_merge_commit_message': 'COMMIT_MESSAGES', 'squash_merge_commit_title': 'COMMIT_OR_PR_TITLE', 'merge_commit_message': 'PR_TITLE', 'merge_commit_title': 'MERGE_MESSAGE'}}, '_links': {'self': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/14218'}, 'html': {'href': 'https://github.com/dolphin-emu/dolphin/pull/14218'}, 'issue': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14218'}, 'comments': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14218/comments'}, 'review_comments': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/14218/comments'}, 'review_comment': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments{/number}'}, 'commits': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/14218/commits'}, 'statuses': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/statuses/41c26ea85f34764a41e9f4e79211cf9fe6bad48c'}}, 'author_association': 'MEMBER', 'auto_merge': None, 'active_lock_reason': None}, 'repository': {'id': 11577304, 'node_id': 'MDEwOlJlcG9zaXRvcnkxMTU3NzMwNA==', 'name': 'dolphin', 'full_name': 'dolphin-emu/dolphin', 'private': False, 'owner': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/dolphin-emu', 'html_url': 'https://github.com/dolphin-emu', 'followers_url': 'https://api.github.com/users/dolphin-emu/followers', 'following_url': 'https://api.github.com/users/dolphin-emu/following{/other_user}', 'gists_url': 'https://api.github.com/users/dolphin-emu/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/dolphin-emu/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/dolphin-emu/subscriptions', 'organizations_url': 'https://api.github.com/users/dolphin-emu/orgs', 'repos_url': 'https://api.github.com/users/dolphin-emu/repos', 'events_url': 'https://api.github.com/users/dolphin-emu/events{/privacy}', 'received_events_url': 'https://api.github.com/users/dolphin-emu/received_events', 'type': 'Organization', 'user_view_type': 'public', 'site_admin': False}, 'html_url': 'https://github.com/dolphin-emu/dolphin', 'description': 'Dolphin is a GameCube / Wii emulator, allowing you to play games for these two platforms on PC with improvements.', 'fork': False, 'url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'forks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/forks', 'keys_url': 'https://api.github.com/repos/dolphin-emu/dolphin/keys{/key_id}', 'collaborators_url': 'https://api.github.com/repos/dolphin-emu/dolphin/collaborators{/collaborator}', 'teams_url': 'https://api.github.com/repos/dolphin-emu/dolphin/teams', 'hooks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/hooks', 'issue_events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/events{/number}', 'events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/events', 'assignees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/assignees{/user}', 'branches_url': 'https://api.github.com/repos/dolphin-emu/dolphin/branches{/branch}', 'tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/tags', 'blobs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/blobs{/sha}', 'git_tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/tags{/sha}', 'git_refs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/refs{/sha}', 'trees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/trees{/sha}', 'statuses_url': 'https://api.github.com/repos/dolphin-emu/dolphin/statuses/{sha}', 'languages_url': 'https://api.github.com/repos/dolphin-emu/dolphin/languages', 'stargazers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/stargazers', 'contributors_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contributors', 'subscribers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscribers', 'subscription_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscription', 'commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/commits{/sha}', 'git_commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/commits{/sha}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/comments{/number}', 'issue_comment_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments{/number}', 'contents_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contents/{+path}', 'compare_url': 'https://api.github.com/repos/dolphin-emu/dolphin/compare/{base}...{head}', 'merges_url': 'https://api.github.com/repos/dolphin-emu/dolphin/merges', 'archive_url': 'https://api.github.com/repos/dolphin-emu/dolphin/{archive_format}{/ref}', 'downloads_url': 'https://api.github.com/repos/dolphin-emu/dolphin/downloads', 'issues_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues{/number}', 'pulls_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls{/number}', 'milestones_url': 'https://api.github.com/repos/dolphin-emu/dolphin/milestones{/number}', 'notifications_url': 'https://api.github.com/repos/dolphin-emu/dolphin/notifications{?since,all,participating}', 'labels_url': 'https://api.github.com/repos/dolphin-emu/dolphin/labels{/name}', 'releases_url': 'https://api.github.com/repos/dolphin-emu/dolphin/releases{/id}', 'deployments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/deployments', 'created_at': '2013-07-22T09:07:57Z', 'updated_at': '2026-02-03T04:26:20Z', 'pushed_at': '2026-02-03T04:26:13Z', 'git_url': 'git://github.com/dolphin-emu/dolphin.git', 'ssh_url': 'git@github.com:dolphin-emu/dolphin.git', 'clone_url': 'https://github.com/dolphin-emu/dolphin.git', 'svn_url': 'https://github.com/dolphin-emu/dolphin', 'homepage': 'https://dolphin-emu.org', 'size': 544012, 'stargazers_count': 14577, 'watchers_count': 14577, 'language': 'C++', 'has_issues': False, 'has_projects': False, 'has_downloads': True, 'has_wiki': True, 'has_pages': False, 'has_discussions': False, 'forks_count': 2966, 'mirror_url': None, 'archived': False, 'disabled': False, 'open_issues_count': 360, 'license': {'key': 'other', 'name': 'Other', 'spdx_id': 'NOASSERTION', 'url': None, 'node_id': 'MDc6TGljZW5zZTA='}, 'allow_forking': True, 'is_template': False, 'web_commit_signoff_required': False, 'topics': ['cpp', 'emulation', 'emulator', 'gamecube', 'wii'], 'visibility': 'public', 'forks': 2966, 'open_issues': 360, 'watchers': 14577, 'default_branch': 'master', 'custom_properties': {}}, 'organization': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'url': 'https://api.github.com/orgs/dolphin-emu', 'repos_url': 'https://api.github.com/orgs/dolphin-emu/repos', 'events_url': 'https://api.github.com/orgs/dolphin-emu/events', 'hooks_url': 'https://api.github.com/orgs/dolphin-emu/hooks', 'issues_url': 'https://api.github.com/orgs/dolphin-emu/issues', 'members_url': 'https://api.github.com/orgs/dolphin-emu/members{/member}', 'public_members_url': 'https://api.github.com/orgs/dolphin-emu/public_members{/member}', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'description': ''}, 'sender': {'login': 'jordan-woyak', 'id': 1768214, 'node_id': 'MDQ6VXNlcjE3NjgyMTQ=', 'avatar_url': 'https://avatars.githubusercontent.com/u/1768214?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/jordan-woyak', 'html_url': 'https://github.com/jordan-woyak', 'followers_url': 'https://api.github.com/users/jordan-woyak/followers', 'following_url': 'https://api.github.com/users/jordan-woyak/following{/other_user}', 'gists_url': 'https://api.github.com/users/jordan-woyak/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/jordan-woyak/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/jordan-woyak/subscriptions', 'organizations_url': 'https://api.github.com/users/jordan-woyak/orgs', 'repos_url': 'https://api.github.com/users/jordan-woyak/repos', 'events_url': 'https://api.github.com/users/jordan-woyak/events{/privacy}', 'received_events_url': 'https://api.github.com/users/jordan-woyak/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'installation': {'id': 35437981, 'node_id': 'MDIzOkludGVncmF0aW9uSW5zdGFsbGF0aW9uMzU0Mzc5ODE='}}, 'type': 'raw_gh_hook'}
2026-02-03T04:26:15.538299 {'source': 'webserver', 'gh_type': 'push', 'raw': {'ref': 'refs/heads/master', 'before': 'c7de9162765452fc2787f3ba4f0092394067d603', 'after': 'eac7b290423efea867c2c7f3f2b2792d05418bdd', 'repository': {'id': 11577304, 'node_id': 'MDEwOlJlcG9zaXRvcnkxMTU3NzMwNA==', 'name': 'dolphin', 'full_name': 'dolphin-emu/dolphin', 'private': False, 'owner': {'name': 'dolphin-emu', 'email': None, 'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/dolphin-emu', 'html_url': 'https://github.com/dolphin-emu', 'followers_url': 'https://api.github.com/users/dolphin-emu/followers', 'following_url': 'https://api.github.com/users/dolphin-emu/following{/other_user}', 'gists_url': 'https://api.github.com/users/dolphin-emu/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/dolphin-emu/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/dolphin-emu/subscriptions', 'organizations_url': 'https://api.github.com/users/dolphin-emu/orgs', 'repos_url': 'https://api.github.com/users/dolphin-emu/repos', 'events_url': 'https://api.github.com/users/dolphin-emu/events{/privacy}', 'received_events_url': 'https://api.github.com/users/dolphin-emu/received_events', 'type': 'Organization', 'user_view_type': 'public', 'site_admin': False}, 'html_url': 'https://github.com/dolphin-emu/dolphin', 'description': 'Dolphin is a GameCube / Wii emulator, allowing you to play games for these two platforms on PC with improvements.', 'fork': False, 'url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'forks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/forks', 'keys_url': 'https://api.github.com/repos/dolphin-emu/dolphin/keys{/key_id}', 'collaborators_url': 'https://api.github.com/repos/dolphin-emu/dolphin/collaborators{/collaborator}', 'teams_url': 'https://api.github.com/repos/dolphin-emu/dolphin/teams', 'hooks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/hooks', 'issue_events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/events{/number}', 'events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/events', 'assignees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/assignees{/user}', 'branches_url': 'https://api.github.com/repos/dolphin-emu/dolphin/branches{/branch}', 'tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/tags', 'blobs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/blobs{/sha}', 'git_tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/tags{/sha}', 'git_refs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/refs{/sha}', 'trees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/trees{/sha}', 'statuses_url': 'https://api.github.com/repos/dolphin-emu/dolphin/statuses/{sha}', 'languages_url': 'https://api.github.com/repos/dolphin-emu/dolphin/languages', 'stargazers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/stargazers', 'contributors_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contributors', 'subscribers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscribers', 'subscription_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscription', 'commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/commits{/sha}', 'git_commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/commits{/sha}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/comments{/number}', 'issue_comment_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments{/number}', 'contents_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contents/{+path}', 'compare_url': 'https://api.github.com/repos/dolphin-emu/dolphin/compare/{base}...{head}', 'merges_url': 'https://api.github.com/repos/dolphin-emu/dolphin/merges', 'archive_url': 'https://api.github.com/repos/dolphin-emu/dolphin/{archive_format}{/ref}', 'downloads_url': 'https://api.github.com/repos/dolphin-emu/dolphin/downloads', 'issues_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues{/number}', 'pulls_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls{/number}', 'milestones_url': 'https://api.github.com/repos/dolphin-emu/dolphin/milestones{/number}', 'notifications_url': 'https://api.github.com/repos/dolphin-emu/dolphin/notifications{?since,all,participating}', 'labels_url': 'https://api.github.com/repos/dolphin-emu/dolphin/labels{/name}', 'releases_url': 'https://api.github.com/repos/dolphin-emu/dolphin/releases{/id}', 'deployments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/deployments', 'created_at': 1374484077, 'updated_at': '2026-02-02T23:15:04Z', 'pushed_at': 1770092773, 'git_url': 'git://github.com/dolphin-emu/dolphin.git', 'ssh_url': 'git@github.com:dolphin-emu/dolphin.git', 'clone_url': 'https://github.com/dolphin-emu/dolphin.git', 'svn_url': 'https://github.com/dolphin-emu/dolphin', 'homepage': 'https://dolphin-emu.org', 'size': 544012, 'stargazers_count': 14577, 'watchers_count': 14577, 'language': 'C++', 'has_issues': False, 'has_projects': False, 'has_downloads': True, 'has_wiki': True, 'has_pages': False, 'has_discussions': False, 'forks_count': 2966, 'mirror_url': None, 'archived': False, 'disabled': False, 'open_issues_count': 360, 'license': {'key': 'other', 'name': 'Other', 'spdx_id': 'NOASSERTION', 'url': None, 'node_id': 'MDc6TGljZW5zZTA='}, 'allow_forking': True, 'is_template': False, 'web_commit_signoff_required': False, 'topics': ['cpp', 'emulation', 'emulator', 'gamecube', 'wii'], 'visibility': 'public', 'forks': 2966, 'open_issues': 360, 'watchers': 14577, 'default_branch': 'master', 'stargazers': 14577, 'master_branch': 'master', 'organization': 'dolphin-emu', 'custom_properties': {}}, 'pusher': {'name': 'jordan-woyak', 'email': 'jordan.woyak@gmail.com'}, 'forced': False, 'organization': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'url': 'https://api.github.com/orgs/dolphin-emu', 'repos_url': 'https://api.github.com/orgs/dolphin-emu/repos', 'events_url': 'https://api.github.com/orgs/dolphin-emu/events', 'hooks_url': 'https://api.github.com/orgs/dolphin-emu/hooks', 'issues_url': 'https://api.github.com/orgs/dolphin-emu/issues', 'members_url': 'https://api.github.com/orgs/dolphin-emu/members{/member}', 'public_members_url': 'https://api.github.com/orgs/dolphin-emu/public_members{/member}', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'description': ''}, 'sender': {'login': 'jordan-woyak', 'id': 1768214, 'node_id': 'MDQ6VXNlcjE3NjgyMTQ=', 'avatar_url': 'https://avatars.githubusercontent.com/u/1768214?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/jordan-woyak', 'html_url': 'https://github.com/jordan-woyak', 'followers_url': 'https://api.github.com/users/jordan-woyak/followers', 'following_url': 'https://api.github.com/users/jordan-woyak/following{/other_user}', 'gists_url': 'https://api.github.com/users/jordan-woyak/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/jordan-woyak/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/jordan-woyak/subscriptions', 'organizations_url': 'https://api.github.com/users/jordan-woyak/orgs', 'repos_url': 'https://api.github.com/users/jordan-woyak/repos', 'events_url': 'https://api.github.com/users/jordan-woyak/events{/privacy}', 'received_events_url': 'https://api.github.com/users/jordan-woyak/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'installation': {'id': 35437981, 'node_id': 'MDIzOkludGVncmF0aW9uSW5zdGFsbGF0aW9uMzU0Mzc5ODE='}, 'created': False, 'deleted': False, 'base_ref': None, 'compare': 'https://github.com/dolphin-emu/dolphin/compare/c7de91627654...eac7b290423e', 'commits': [{'id': 'ac19ef5452a1abcd7bcfc9d166fcdbe28c714ba1', 'tree_id': 'f1dea370112a71e24e5c6623e7424e66e70b5661', 'distinct': False, 'message': 'GameINI: Force Safe Texture Cache to Samurai Shodown Anthology', 'timestamp': '2026-02-01T00:42:27-05:00', 'url': 'https://github.com/dolphin-emu/dolphin/commit/ac19ef5452a1abcd7bcfc9d166fcdbe28c714ba1', 'author': {'name': 'JMC47', 'email': 'JMC4789@gmail.com', 'date': '2026-02-01T00:42:27-05:00', 'username': 'JMC47'}, 'committer': {'name': 'JMC47', 'email': 'JMC4789@gmail.com', 'date': '2026-02-01T00:42:27-05:00', 'username': 'JMC47'}, 'added': [], 'removed': [], 'modified': ['Data/Sys/GameSettings/RSS.ini']}, {'id': 'eac7b290423efea867c2c7f3f2b2792d05418bdd', 'tree_id': 'ec7ff444f3da935c70f0745b24158930233504d7', 'distinct': True, 'message': 'Merge pull request #14314 from JMC47/shodown\n\nGameINI: Force Safe Texture Cache to Samurai Shodown Anthology', 'timestamp': '2026-02-02T22:26:13-06:00', 'url': 'https://github.com/dolphin-emu/dolphin/commit/eac7b290423efea867c2c7f3f2b2792d05418bdd', 'author': {'name': 'Jordan Woyak', 'email': 'jordan.woyak@gmail.com', 'date': '2026-02-02T22:26:13-06:00', 'username': 'jordan-woyak'}, 'committer': {'name': 'GitHub', 'email': 'noreply@github.com', 'date': '2026-02-02T22:26:13-06:00', 'username': 'web-flow'}, 'added': [], 'removed': [], 'modified': ['Data/Sys/GameSettings/RSS.ini']}], 'head_commit': {'id': 'eac7b290423efea867c2c7f3f2b2792d05418bdd', 'tree_id': 'ec7ff444f3da935c70f0745b24158930233504d7', 'distinct': True, 'message': 'Merge pull request #14314 from JMC47/shodown\n\nGameINI: Force Safe Texture Cache to Samurai Shodown Anthology', 'timestamp': '2026-02-02T22:26:13-06:00', 'url': 'https://github.com/dolphin-emu/dolphin/commit/eac7b290423efea867c2c7f3f2b2792d05418bdd', 'author': {'name': 'Jordan Woyak', 'email': 'jordan.woyak@gmail.com', 'date': '2026-02-02T22:26:13-06:00', 'username': 'jordan-woyak'}, 'committer': {'name': 'GitHub', 'email': 'noreply@github.com', 'date': '2026-02-02T22:26:13-06:00', 'username': 'web-flow'}, 'added': [], 'removed': [], 'modified': ['Data/Sys/GameSettings/RSS.ini']}}, 'type': 'raw_gh_hook'}
2026-02-03T04:24:35.371322 {'source': 'webserver', 'gh_type': 'pull_request_review_comment', 'raw': {'action': 'created', 'comment': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2757133526', 'pull_request_review_id': 3742877805, 'id': 2757133526, 'node_id': 'PRRC_kwDOALCn2M6kVoTW', 'diff_hunk': '@@ -33,6 +34,11 @@ struct EFB\n u32* scaled_height;\n };\n \n+struct PostEFB\n+{\n+ VideoCommon::MaterialResource** material;', 'path': 'Source/Core/VideoCommon/GraphicsModSystem/Runtime/GraphicsModActionData.h', 'commit_id': 'd58818f317de389fb1ddded9412a1d9b2a29462e', 'original_commit_id': 'd58818f317de389fb1ddded9412a1d9b2a29462e', 'user': {'login': 'jordan-woyak', 'id': 1768214, 'node_id': 'MDQ6VXNlcjE3NjgyMTQ=', 'avatar_url': 'https://avatars.githubusercontent.com/u/1768214?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/jordan-woyak', 'html_url': 'https://github.com/jordan-woyak', 'followers_url': 'https://api.github.com/users/jordan-woyak/followers', 'following_url': 'https://api.github.com/users/jordan-woyak/following{/other_user}', 'gists_url': 'https://api.github.com/users/jordan-woyak/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/jordan-woyak/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/jordan-woyak/subscriptions', 'organizations_url': 'https://api.github.com/users/jordan-woyak/orgs', 'repos_url': 'https://api.github.com/users/jordan-woyak/repos', 'events_url': 'https://api.github.com/users/jordan-woyak/events{/privacy}', 'received_events_url': 'https://api.github.com/users/jordan-woyak/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'body': 'This double pointer seems unnecessary.\nI think it would be less confusing as a `MaterialResource*`.\n\nSince `AfterEFB` takes a `PostEFB*` it can assign the `material` member there, then the caller can inspect `efb->material` afterwards.\n\nUnless you have some future plans where this `MaterialResource**` makes more sense?', 'created_at': '2026-02-03T04:24:30Z', 'updated_at': '2026-02-03T04:24:33Z', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/14140#discussion_r2757133526', 'pull_request_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/14140', '_links': {'self': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2757133526'}, 'html': {'href': 'https://github.com/dolphin-emu/dolphin/pull/14140#discussion_r2757133526'}, 'pull_request': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/14140'}}, 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments/2757133526/reactions', 'total_count': 0, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 0, 'confused': 0, 'heart': 0, 'rocket': 0, 'eyes': 0}, 'start_line': None, 'original_start_line': None, 'start_side': None, 'line': 39, 'original_line': 39, 'side': 'RIGHT', 'author_association': 'MEMBER', 'original_position': 23, 'position': 23, 'subject_type': 'line'}, 'pull_request': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/14140', 'id': 3020230967, 'node_id': 'PR_kwDOALCn2M60BRE3', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/14140', 'diff_url': 'https://github.com/dolphin-emu/dolphin/pull/14140.diff', 'patch_url': 'https://github.com/dolphin-emu/dolphin/pull/14140.patch', 'issue_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14140', 'number': 14140, 'state': 'open', 'locked': False, 'title': 'VideoCommon: add a graphics mod feature to modify EFBs with a custom material, enhance bloom', 'user': {'login': 'iwubcode', 'id': 15224722, 'node_id': 'MDQ6VXNlcjE1MjI0NzIy', 'avatar_url': 'https://avatars.githubusercontent.com/u/15224722?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/iwubcode', 'html_url': 'https://github.com/iwubcode', 'followers_url': 'https://api.github.com/users/iwubcode/followers', 'following_url': 'https://api.github.com/users/iwubcode/following{/other_user}', 'gists_url': 'https://api.github.com/users/iwubcode/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/iwubcode/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/iwubcode/subscriptions', 'organizations_url': 'https://api.github.com/users/iwubcode/orgs', 'repos_url': 'https://api.github.com/users/iwubcode/repos', 'events_url': 'https://api.github.com/users/iwubcode/events{/privacy}', 'received_events_url': 'https://api.github.com/users/iwubcode/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'body': "( this builds on #14043 )\r\n\r\nThis PR adds a feature to modify EFBs created using a custom Dolphin material. This is the same feature I have been building for custom draw shaders and this will eventually form the base for the new post processing system.\r\n\r\nStory time below...\r\n\r\nIf you recall, targeting by EFB is prone to error. One example of this is in Mario Galaxy 2 where targeting bloom actually targets the DOF during level selection. This is because Dolphin can't discern between the two effects, it only has so much information to leverage.\r\n\r\nMy answer to this was Graphics Mods 2.0 (unreleased), by targeting the draw call (not the EFB), I was able to easily pick what effect I actually intended to modify. I originally planned to provide a new enhanced bloom system with that feature. However, what I found was that while I could target with higher accuracy and correctly replace that draw with a custom effect, doing so generically was a much bigger challenge. Games had different ways of using the bloom EFB copy and that meant I needed to support those various scenarios. Discouraged, I let the idea disappear from my mind...\r\n\r\nFellow developer @TryTwo has maintained a custom fork for a long time. The fork supports modifying bloom at the EFB level. While it is arguably less accurate than Graphics Mods 1.0, it provides some really nice features as well. The first is a way to blur the bloom. The second, is a nice UI that allows you to control the spread of the blur and the brightness. Many users have adopted this feature due to Graphics Mods 1.0 having noticeable shimmering when playing some games.**\r\n\r\nA couple months ago, I started working on a separate feature. I won't go into great depth about that today but my hope is it will allow for us to get the accuracy at the draw level (with Graphics Mods 2.0) while still using the EFB for modification. It is still being developed and I can't say for sure it will work but it gave me new hope. Because of that, I opted to add the ability to modify the EFB copies, similar to TryTwo's fork.\r\n\r\nWhat you get, is the ability to target EFBs and provide a custom material to draw over the EFB with. This is how the bloom is performed (done in two passes).\r\n\r\nI started with a modified version of @TryTwo 's implementation of the blur shader but I may investigate other blur shaders as part of this PR or part of the future. Fellow developer phire originally discussed a Gaussian blur and I think that would be nice from a performance standpoint as well.\r\n\r\nThis adds more games to the list of bloom enabled games. These have been lightly tested:\r\n\r\n* [Conduit 2](https://wiki.dolphin-emu.org/index.php?title=Conduit_2)\r\n* [De Blob](https://wiki.dolphin-emu.org/index.php?title=De_Blob)\r\n* [De Blob 2](https://wiki.dolphin-emu.org/index.php?title=De_Blob_2)\r\n* [Epic Mickey](https://wiki.dolphin-emu.org/index.php?title=Epic_Mickey)\r\n* [Epic Mickey 2](https://wiki.dolphin-emu.org/index.php?title=Epic_Mickey_2:_The_Power_of_Two)\r\n* [Go Vacation](https://wiki.dolphin-emu.org/index.php?title=Go_Vacation)\r\n* [Lego Batman](https://wiki.dolphin-emu.org/index.php?title=Lego_Batman:_The_Videogame)\r\n* [Lord of the Rings: Aragorn's Quest](https://wiki.dolphin-emu.org/index.php?title=The_Lord_of_the_Rings:_Aragorn%27s_Quest)\r\n* [LostWinds](https://wiki.dolphin-emu.org/index.php?title=LostWinds)\r\n* [LostWinds: Winter of the Melodias](https://wiki.dolphin-emu.org/index.php?title=LostWinds:_Winter_of_the_Melodias)\r\n* [Metroid: Other M](https://wiki.dolphin-emu.org/index.php?title=Metroid:_Other_M)\r\n* [Metroid Prime: Trilogy](https://wiki.dolphin-emu.org/index.php?title=Metroid_Prime:_Trilogy)\r\n* [Overlord: Dark Legend](https://wiki.dolphin-emu.org/index.php?title=Overlord:_Dark_Legend) ( from the [forum](https://forums.dolphin-emu.org/Thread-wii-overlord-dark-legend?pid=538496#pid538496) )\r\n* [Sonic Unleashed](https://wiki.dolphin-emu.org/index.php?title=Sonic_Unleashed) ( from PR #13402 )\r\n* [Spectrobes: Origins](https://wiki.dolphin-emu.org/index.php?title=Spectrobes:_Origins)\r\n* [Spyborgs](https://wiki.dolphin-emu.org/index.php?title=Spyborgs)\r\n* [Takt of Magic](https://wiki.dolphin-emu.org/index.php?title=Takt_of_Magic)\r\n* [Zangeki no Reginleiv](https://wiki.dolphin-emu.org/index.php?title=Zangeki_no_Reginleiv)\r\n\r\nThere were other games I wanted to add but they ran into accuracy issues when testing.\r\n\r\n\r\n<sub>** Admiral's PR (9860) from 4 years ago explained well why shimmering might occur:</sub>\r\n\r\n> <sub>This has become known as 'shimmering' artifacts in enthusiast circles and happens because the individual columns of source pixels are unevenly sized -- some are 3 pixels wide, some are 2 wide -- so a pattern moving across them stands out poorly.</sub>", 'created_at': '2025-11-18T06:39:36Z', 'updated_at': '2026-02-03T04:24:33Z', 'closed_at': None, 'merged_at': None, 'merge_commit_sha': '73f80d68bec195e6e3511a357d0e064e708262d3', 'assignee': None, 'assignees': [], 'requested_reviewers': [], 'requested_teams': [], 'labels': [], 'milestone': None, 'draft': False, 'commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/14140/commits', 'review_comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/14140/comments', 'review_comment_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments{/number}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14140/comments', 'statuses_url': 'https://api.github.com/repos/dolphin-emu/dolphin/statuses/d58818f317de389fb1ddded9412a1d9b2a29462e', 'head': {'label': 'iwubcode:blurry_bloom', 'ref': 'blurry_bloom', 'sha': 'd58818f317de389fb1ddded9412a1d9b2a29462e', 'user': {'login': 'iwubcode', 'id': 15224722, 'node_id': 'MDQ6VXNlcjE1MjI0NzIy', 'avatar_url': 'https://avatars.githubusercontent.com/u/15224722?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/iwubcode', 'html_url': 'https://github.com/iwubcode', 'followers_url': 'https://api.github.com/users/iwubcode/followers', 'following_url': 'https://api.github.com/users/iwubcode/following{/other_user}', 'gists_url': 'https://api.github.com/users/iwubcode/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/iwubcode/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/iwubcode/subscriptions', 'organizations_url': 'https://api.github.com/users/iwubcode/orgs', 'repos_url': 'https://api.github.com/users/iwubcode/repos', 'events_url': 'https://api.github.com/users/iwubcode/events{/privacy}', 'received_events_url': 'https://api.github.com/users/iwubcode/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'repo': {'id': 77596353, 'node_id': 'MDEwOlJlcG9zaXRvcnk3NzU5NjM1Mw==', 'name': 'dolphin', 'full_name': 'iwubcode/dolphin', 'private': False, 'owner': {'login': 'iwubcode', 'id': 15224722, 'node_id': 'MDQ6VXNlcjE1MjI0NzIy', 'avatar_url': 'https://avatars.githubusercontent.com/u/15224722?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/iwubcode', 'html_url': 'https://github.com/iwubcode', 'followers_url': 'https://api.github.com/users/iwubcode/followers', 'following_url': 'https://api.github.com/users/iwubcode/following{/other_user}', 'gists_url': 'https://api.github.com/users/iwubcode/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/iwubcode/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/iwubcode/subscriptions', 'organizations_url': 'https://api.github.com/users/iwubcode/orgs', 'repos_url': 'https://api.github.com/users/iwubcode/repos', 'events_url': 'https://api.github.com/users/iwubcode/events{/privacy}', 'received_events_url': 'https://api.github.com/users/iwubcode/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'html_url': 'https://github.com/iwubcode/dolphin', 'description': 'Dolphin is a GameCube / Wii emulator, allowing you to play games for these two platforms on PC with improvements.', 'fork': True, 'url': 'https://api.github.com/repos/iwubcode/dolphin', 'forks_url': 'https://api.github.com/repos/iwubcode/dolphin/forks', 'keys_url': 'https://api.github.com/repos/iwubcode/dolphin/keys{/key_id}', 'collaborators_url': 'https://api.github.com/repos/iwubcode/dolphin/collaborators{/collaborator}', 'teams_url': 'https://api.github.com/repos/iwubcode/dolphin/teams', 'hooks_url': 'https://api.github.com/repos/iwubcode/dolphin/hooks', 'issue_events_url': 'https://api.github.com/repos/iwubcode/dolphin/issues/events{/number}', 'events_url': 'https://api.github.com/repos/iwubcode/dolphin/events', 'assignees_url': 'https://api.github.com/repos/iwubcode/dolphin/assignees{/user}', 'branches_url': 'https://api.github.com/repos/iwubcode/dolphin/branches{/branch}', 'tags_url': 'https://api.github.com/repos/iwubcode/dolphin/tags', 'blobs_url': 'https://api.github.com/repos/iwubcode/dolphin/git/blobs{/sha}', 'git_tags_url': 'https://api.github.com/repos/iwubcode/dolphin/git/tags{/sha}', 'git_refs_url': 'https://api.github.com/repos/iwubcode/dolphin/git/refs{/sha}', 'trees_url': 'https://api.github.com/repos/iwubcode/dolphin/git/trees{/sha}', 'statuses_url': 'https://api.github.com/repos/iwubcode/dolphin/statuses/{sha}', 'languages_url': 'https://api.github.com/repos/iwubcode/dolphin/languages', 'stargazers_url': 'https://api.github.com/repos/iwubcode/dolphin/stargazers', 'contributors_url': 'https://api.github.com/repos/iwubcode/dolphin/contributors', 'subscribers_url': 'https://api.github.com/repos/iwubcode/dolphin/subscribers', 'subscription_url': 'https://api.github.com/repos/iwubcode/dolphin/subscription', 'commits_url': 'https://api.github.com/repos/iwubcode/dolphin/commits{/sha}', 'git_commits_url': 'https://api.github.com/repos/iwubcode/dolphin/git/commits{/sha}', 'comments_url': 'https://api.github.com/repos/iwubcode/dolphin/comments{/number}', 'issue_comment_url': 'https://api.github.com/repos/iwubcode/dolphin/issues/comments{/number}', 'contents_url': 'https://api.github.com/repos/iwubcode/dolphin/contents/{+path}', 'compare_url': 'https://api.github.com/repos/iwubcode/dolphin/compare/{base}...{head}', 'merges_url': 'https://api.github.com/repos/iwubcode/dolphin/merges', 'archive_url': 'https://api.github.com/repos/iwubcode/dolphin/{archive_format}{/ref}', 'downloads_url': 'https://api.github.com/repos/iwubcode/dolphin/downloads', 'issues_url': 'https://api.github.com/repos/iwubcode/dolphin/issues{/number}', 'pulls_url': 'https://api.github.com/repos/iwubcode/dolphin/pulls{/number}', 'milestones_url': 'https://api.github.com/repos/iwubcode/dolphin/milestones{/number}', 'notifications_url': 'https://api.github.com/repos/iwubcode/dolphin/notifications{?since,all,participating}', 'labels_url': 'https://api.github.com/repos/iwubcode/dolphin/labels{/name}', 'releases_url': 'https://api.github.com/repos/iwubcode/dolphin/releases{/id}', 'deployments_url': 'https://api.github.com/repos/iwubcode/dolphin/deployments', 'created_at': '2016-12-29T08:26:39Z', 'updated_at': '2025-12-24T07:50:59Z', 'pushed_at': '2026-01-31T08:01:51Z', 'git_url': 'git://github.com/iwubcode/dolphin.git', 'ssh_url': 'git@github.com:iwubcode/dolphin.git', 'clone_url': 'https://github.com/iwubcode/dolphin.git', 'svn_url': 'https://github.com/iwubcode/dolphin', 'homepage': 'https://dolphin-emu.org/', 'size': 507925, 'stargazers_count': 1, 'watchers_count': 1, 'language': 'C++', 'has_issues': False, 'has_projects': True, 'has_downloads': True, 'has_wiki': True, 'has_pages': False, 'has_discussions': False, 'forks_count': 3, 'mirror_url': None, 'archived': False, 'disabled': False, 'open_issues_count': 1, 'license': {'key': 'other', 'name': 'Other', 'spdx_id': 'NOASSERTION', 'url': None, 'node_id': 'MDc6TGljZW5zZTA='}, 'allow_forking': True, 'is_template': False, 'web_commit_signoff_required': False, 'topics': [], 'visibility': 'public', 'forks': 3, 'open_issues': 1, 'watchers': 1, 'default_branch': 'master', 'allow_squash_merge': True, 'allow_merge_commit': True, 'allow_rebase_merge': True, 'allow_auto_merge': False, 'delete_branch_on_merge': False, 'allow_update_branch': False, 'use_squash_pr_title_as_default': False, 'squash_merge_commit_message': 'COMMIT_MESSAGES', 'squash_merge_commit_title': 'COMMIT_OR_PR_TITLE', 'merge_commit_message': 'PR_TITLE', 'merge_commit_title': 'MERGE_MESSAGE'}}, 'base': {'label': 'dolphin-emu:master', 'ref': 'master', 'sha': 'b1020186519d690a9b64a215f00da7453ab2803b', 'user': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/dolphin-emu', 'html_url': 'https://github.com/dolphin-emu', 'followers_url': 'https://api.github.com/users/dolphin-emu/followers', 'following_url': 'https://api.github.com/users/dolphin-emu/following{/other_user}', 'gists_url': 'https://api.github.com/users/dolphin-emu/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/dolphin-emu/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/dolphin-emu/subscriptions', 'organizations_url': 'https://api.github.com/users/dolphin-emu/orgs', 'repos_url': 'https://api.github.com/users/dolphin-emu/repos', 'events_url': 'https://api.github.com/users/dolphin-emu/events{/privacy}', 'received_events_url': 'https://api.github.com/users/dolphin-emu/received_events', 'type': 'Organization', 'user_view_type': 'public', 'site_admin': False}, 'repo': {'id': 11577304, 'node_id': 'MDEwOlJlcG9zaXRvcnkxMTU3NzMwNA==', 'name': 'dolphin', 'full_name': 'dolphin-emu/dolphin', 'private': False, 'owner': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/dolphin-emu', 'html_url': 'https://github.com/dolphin-emu', 'followers_url': 'https://api.github.com/users/dolphin-emu/followers', 'following_url': 'https://api.github.com/users/dolphin-emu/following{/other_user}', 'gists_url': 'https://api.github.com/users/dolphin-emu/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/dolphin-emu/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/dolphin-emu/subscriptions', 'organizations_url': 'https://api.github.com/users/dolphin-emu/orgs', 'repos_url': 'https://api.github.com/users/dolphin-emu/repos', 'events_url': 'https://api.github.com/users/dolphin-emu/events{/privacy}', 'received_events_url': 'https://api.github.com/users/dolphin-emu/received_events', 'type': 'Organization', 'user_view_type': 'public', 'site_admin': False}, 'html_url': 'https://github.com/dolphin-emu/dolphin', 'description': 'Dolphin is a GameCube / Wii emulator, allowing you to play games for these two platforms on PC with improvements.', 'fork': False, 'url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'forks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/forks', 'keys_url': 'https://api.github.com/repos/dolphin-emu/dolphin/keys{/key_id}', 'collaborators_url': 'https://api.github.com/repos/dolphin-emu/dolphin/collaborators{/collaborator}', 'teams_url': 'https://api.github.com/repos/dolphin-emu/dolphin/teams', 'hooks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/hooks', 'issue_events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/events{/number}', 'events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/events', 'assignees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/assignees{/user}', 'branches_url': 'https://api.github.com/repos/dolphin-emu/dolphin/branches{/branch}', 'tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/tags', 'blobs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/blobs{/sha}', 'git_tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/tags{/sha}', 'git_refs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/refs{/sha}', 'trees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/trees{/sha}', 'statuses_url': 'https://api.github.com/repos/dolphin-emu/dolphin/statuses/{sha}', 'languages_url': 'https://api.github.com/repos/dolphin-emu/dolphin/languages', 'stargazers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/stargazers', 'contributors_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contributors', 'subscribers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscribers', 'subscription_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscription', 'commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/commits{/sha}', 'git_commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/commits{/sha}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/comments{/number}', 'issue_comment_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments{/number}', 'contents_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contents/{+path}', 'compare_url': 'https://api.github.com/repos/dolphin-emu/dolphin/compare/{base}...{head}', 'merges_url': 'https://api.github.com/repos/dolphin-emu/dolphin/merges', 'archive_url': 'https://api.github.com/repos/dolphin-emu/dolphin/{archive_format}{/ref}', 'downloads_url': 'https://api.github.com/repos/dolphin-emu/dolphin/downloads', 'issues_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues{/number}', 'pulls_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls{/number}', 'milestones_url': 'https://api.github.com/repos/dolphin-emu/dolphin/milestones{/number}', 'notifications_url': 'https://api.github.com/repos/dolphin-emu/dolphin/notifications{?since,all,participating}', 'labels_url': 'https://api.github.com/repos/dolphin-emu/dolphin/labels{/name}', 'releases_url': 'https://api.github.com/repos/dolphin-emu/dolphin/releases{/id}', 'deployments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/deployments', 'created_at': '2013-07-22T09:07:57Z', 'updated_at': '2026-02-02T23:15:04Z', 'pushed_at': '2026-02-02T20:05:51Z', 'git_url': 'git://github.com/dolphin-emu/dolphin.git', 'ssh_url': 'git@github.com:dolphin-emu/dolphin.git', 'clone_url': 'https://github.com/dolphin-emu/dolphin.git', 'svn_url': 'https://github.com/dolphin-emu/dolphin', 'homepage': 'https://dolphin-emu.org', 'size': 544012, 'stargazers_count': 14577, 'watchers_count': 14577, 'language': 'C++', 'has_issues': False, 'has_projects': False, 'has_downloads': True, 'has_wiki': True, 'has_pages': False, 'has_discussions': False, 'forks_count': 2966, 'mirror_url': None, 'archived': False, 'disabled': False, 'open_issues_count': 361, 'license': {'key': 'other', 'name': 'Other', 'spdx_id': 'NOASSERTION', 'url': None, 'node_id': 'MDc6TGljZW5zZTA='}, 'allow_forking': True, 'is_template': False, 'web_commit_signoff_required': False, 'topics': ['cpp', 'emulation', 'emulator', 'gamecube', 'wii'], 'visibility': 'public', 'forks': 2966, 'open_issues': 361, 'watchers': 14577, 'default_branch': 'master', 'allow_squash_merge': False, 'allow_merge_commit': True, 'allow_rebase_merge': False, 'allow_auto_merge': False, 'delete_branch_on_merge': False, 'allow_update_branch': False, 'use_squash_pr_title_as_default': False, 'squash_merge_commit_message': 'COMMIT_MESSAGES', 'squash_merge_commit_title': 'COMMIT_OR_PR_TITLE', 'merge_commit_message': 'PR_TITLE', 'merge_commit_title': 'MERGE_MESSAGE'}}, '_links': {'self': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/14140'}, 'html': {'href': 'https://github.com/dolphin-emu/dolphin/pull/14140'}, 'issue': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14140'}, 'comments': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14140/comments'}, 'review_comments': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/14140/comments'}, 'review_comment': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments{/number}'}, 'commits': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/14140/commits'}, 'statuses': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/statuses/d58818f317de389fb1ddded9412a1d9b2a29462e'}}, 'author_association': 'CONTRIBUTOR', 'auto_merge': None, 'active_lock_reason': None}, 'repository': {'id': 11577304, 'node_id': 'MDEwOlJlcG9zaXRvcnkxMTU3NzMwNA==', 'name': 'dolphin', 'full_name': 'dolphin-emu/dolphin', 'private': False, 'owner': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/dolphin-emu', 'html_url': 'https://github.com/dolphin-emu', 'followers_url': 'https://api.github.com/users/dolphin-emu/followers', 'following_url': 'https://api.github.com/users/dolphin-emu/following{/other_user}', 'gists_url': 'https://api.github.com/users/dolphin-emu/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/dolphin-emu/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/dolphin-emu/subscriptions', 'organizations_url': 'https://api.github.com/users/dolphin-emu/orgs', 'repos_url': 'https://api.github.com/users/dolphin-emu/repos', 'events_url': 'https://api.github.com/users/dolphin-emu/events{/privacy}', 'received_events_url': 'https://api.github.com/users/dolphin-emu/received_events', 'type': 'Organization', 'user_view_type': 'public', 'site_admin': False}, 'html_url': 'https://github.com/dolphin-emu/dolphin', 'description': 'Dolphin is a GameCube / Wii emulator, allowing you to play games for these two platforms on PC with improvements.', 'fork': False, 'url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'forks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/forks', 'keys_url': 'https://api.github.com/repos/dolphin-emu/dolphin/keys{/key_id}', 'collaborators_url': 'https://api.github.com/repos/dolphin-emu/dolphin/collaborators{/collaborator}', 'teams_url': 'https://api.github.com/repos/dolphin-emu/dolphin/teams', 'hooks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/hooks', 'issue_events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/events{/number}', 'events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/events', 'assignees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/assignees{/user}', 'branches_url': 'https://api.github.com/repos/dolphin-emu/dolphin/branches{/branch}', 'tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/tags', 'blobs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/blobs{/sha}', 'git_tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/tags{/sha}', 'git_refs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/refs{/sha}', 'trees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/trees{/sha}', 'statuses_url': 'https://api.github.com/repos/dolphin-emu/dolphin/statuses/{sha}', 'languages_url': 'https://api.github.com/repos/dolphin-emu/dolphin/languages', 'stargazers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/stargazers', 'contributors_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contributors', 'subscribers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscribers', 'subscription_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscription', 'commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/commits{/sha}', 'git_commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/commits{/sha}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/comments{/number}', 'issue_comment_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments{/number}', 'contents_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contents/{+path}', 'compare_url': 'https://api.github.com/repos/dolphin-emu/dolphin/compare/{base}...{head}', 'merges_url': 'https://api.github.com/repos/dolphin-emu/dolphin/merges', 'archive_url': 'https://api.github.com/repos/dolphin-emu/dolphin/{archive_format}{/ref}', 'downloads_url': 'https://api.github.com/repos/dolphin-emu/dolphin/downloads', 'issues_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues{/number}', 'pulls_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls{/number}', 'milestones_url': 'https://api.github.com/repos/dolphin-emu/dolphin/milestones{/number}', 'notifications_url': 'https://api.github.com/repos/dolphin-emu/dolphin/notifications{?since,all,participating}', 'labels_url': 'https://api.github.com/repos/dolphin-emu/dolphin/labels{/name}', 'releases_url': 'https://api.github.com/repos/dolphin-emu/dolphin/releases{/id}', 'deployments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/deployments', 'created_at': '2013-07-22T09:07:57Z', 'updated_at': '2026-02-02T23:15:04Z', 'pushed_at': '2026-02-02T20:05:51Z', 'git_url': 'git://github.com/dolphin-emu/dolphin.git', 'ssh_url': 'git@github.com:dolphin-emu/dolphin.git', 'clone_url': 'https://github.com/dolphin-emu/dolphin.git', 'svn_url': 'https://github.com/dolphin-emu/dolphin', 'homepage': 'https://dolphin-emu.org', 'size': 544012, 'stargazers_count': 14577, 'watchers_count': 14577, 'language': 'C++', 'has_issues': False, 'has_projects': False, 'has_downloads': True, 'has_wiki': True, 'has_pages': False, 'has_discussions': False, 'forks_count': 2966, 'mirror_url': None, 'archived': False, 'disabled': False, 'open_issues_count': 361, 'license': {'key': 'other', 'name': 'Other', 'spdx_id': 'NOASSERTION', 'url': None, 'node_id': 'MDc6TGljZW5zZTA='}, 'allow_forking': True, 'is_template': False, 'web_commit_signoff_required': False, 'topics': ['cpp', 'emulation', 'emulator', 'gamecube', 'wii'], 'visibility': 'public', 'forks': 2966, 'open_issues': 361, 'watchers': 14577, 'default_branch': 'master', 'custom_properties': {}}, 'organization': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'url': 'https://api.github.com/orgs/dolphin-emu', 'repos_url': 'https://api.github.com/orgs/dolphin-emu/repos', 'events_url': 'https://api.github.com/orgs/dolphin-emu/events', 'hooks_url': 'https://api.github.com/orgs/dolphin-emu/hooks', 'issues_url': 'https://api.github.com/orgs/dolphin-emu/issues', 'members_url': 'https://api.github.com/orgs/dolphin-emu/members{/member}', 'public_members_url': 'https://api.github.com/orgs/dolphin-emu/public_members{/member}', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'description': ''}, 'sender': {'login': 'jordan-woyak', 'id': 1768214, 'node_id': 'MDQ6VXNlcjE3NjgyMTQ=', 'avatar_url': 'https://avatars.githubusercontent.com/u/1768214?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/jordan-woyak', 'html_url': 'https://github.com/jordan-woyak', 'followers_url': 'https://api.github.com/users/jordan-woyak/followers', 'following_url': 'https://api.github.com/users/jordan-woyak/following{/other_user}', 'gists_url': 'https://api.github.com/users/jordan-woyak/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/jordan-woyak/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/jordan-woyak/subscriptions', 'organizations_url': 'https://api.github.com/users/jordan-woyak/orgs', 'repos_url': 'https://api.github.com/users/jordan-woyak/repos', 'events_url': 'https://api.github.com/users/jordan-woyak/events{/privacy}', 'received_events_url': 'https://api.github.com/users/jordan-woyak/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'installation': {'id': 35437981, 'node_id': 'MDIzOkludGVncmF0aW9uSW5zdGFsbGF0aW9uMzU0Mzc5ODE='}}, 'type': 'raw_gh_hook'}
2026-02-03T04:07:47.475567 {'source': 'webserver', 'gh_type': 'pull_request_review', 'raw': {'action': 'submitted', 'review': {'id': 3742805702, 'node_id': 'PRR_kwDOALCn2M7fFq7G', 'user': {'login': 'jordan-woyak', 'id': 1768214, 'node_id': 'MDQ6VXNlcjE3NjgyMTQ=', 'avatar_url': 'https://avatars.githubusercontent.com/u/1768214?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/jordan-woyak', 'html_url': 'https://github.com/jordan-woyak', 'followers_url': 'https://api.github.com/users/jordan-woyak/followers', 'following_url': 'https://api.github.com/users/jordan-woyak/following{/other_user}', 'gists_url': 'https://api.github.com/users/jordan-woyak/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/jordan-woyak/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/jordan-woyak/subscriptions', 'organizations_url': 'https://api.github.com/users/jordan-woyak/orgs', 'repos_url': 'https://api.github.com/users/jordan-woyak/repos', 'events_url': 'https://api.github.com/users/jordan-woyak/events{/privacy}', 'received_events_url': 'https://api.github.com/users/jordan-woyak/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'body': None, 'commit_id': 'e88ee42d345e0b2a85cc6bd44c7258eb8be3c6b8', 'submitted_at': '2026-02-03T04:07:45Z', 'state': 'commented', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/13768#pullrequestreview-3742805702', 'pull_request_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13768', '_links': {'html': {'href': 'https://github.com/dolphin-emu/dolphin/pull/13768#pullrequestreview-3742805702'}, 'pull_request': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13768'}}, 'updated_at': '2026-02-03T04:07:45Z', 'author_association': 'MEMBER'}, 'pull_request': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13768', 'id': 2608905795, 'node_id': 'PR_kwDOALCn2M6bgL5D', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/13768', 'diff_url': 'https://github.com/dolphin-emu/dolphin/pull/13768.diff', 'patch_url': 'https://github.com/dolphin-emu/dolphin/pull/13768.patch', 'issue_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13768', 'number': 13768, 'state': 'open', 'locked': False, 'title': 'Core: Create fastmem mappings for page address translation', 'user': {'login': 'JosJuice', 'id': 6716818, 'node_id': 'MDQ6VXNlcjY3MTY4MTg=', 'avatar_url': 'https://avatars.githubusercontent.com/u/6716818?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/JosJuice', 'html_url': 'https://github.com/JosJuice', 'followers_url': 'https://api.github.com/users/JosJuice/followers', 'following_url': 'https://api.github.com/users/JosJuice/following{/other_user}', 'gists_url': 'https://api.github.com/users/JosJuice/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/JosJuice/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/JosJuice/subscriptions', 'organizations_url': 'https://api.github.com/users/JosJuice/orgs', 'repos_url': 'https://api.github.com/users/JosJuice/repos', 'events_url': 'https://api.github.com/users/JosJuice/events{/privacy}', 'received_events_url': 'https://api.github.com/users/JosJuice/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'body': "Previously we've only been setting up fastmem mappings for block address translation, but now we also do it for page address translation. This increases performance when games access memory using page tables, but decreases performance when games set up page tables.\r\n\r\nThe tlbie instruction is used as an indication that the mappings need to be updated.\r\n\r\nThere is the accuracy downside that the TLB is now effectively infinitely large. No games are known to be affected by this, and you still get the old, more accurate behavior if Enable Write-Back Cache is on.\r\n\r\nLeft to do:\r\n\r\n- [x] Support for host page sizes larger than 4K\r\n- [x] Support for host page sizes larger than 4K is untested\r\n- [x] macOS is untested\r\n- [x] Rogue Squadron 3 is super slow due to the pessimistic setting of R and C bits (I guess the game is heavily swapping? DoJit is running very often)\r\n- [x] Unit tests\r\n- [x] Unit tests fail on the Ubuntu and Debian runners", 'created_at': '2025-06-21T09:20:23Z', 'updated_at': '2026-02-03T04:07:45Z', 'closed_at': None, 'merged_at': None, 'merge_commit_sha': 'b8643856cdcf8d4227ad32709bada837d257e972', 'assignee': None, 'assignees': [], 'requested_reviewers': [], 'requested_teams': [], 'labels': [], 'milestone': None, 'draft': False, 'commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13768/commits', 'review_comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13768/comments', 'review_comment_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments{/number}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13768/comments', 'statuses_url': 'https://api.github.com/repos/dolphin-emu/dolphin/statuses/e88ee42d345e0b2a85cc6bd44c7258eb8be3c6b8', 'head': {'label': 'JosJuice:page-table-fastmem-2', 'ref': 'page-table-fastmem-2', 'sha': 'e88ee42d345e0b2a85cc6bd44c7258eb8be3c6b8', 'user': {'login': 'JosJuice', 'id': 6716818, 'node_id': 'MDQ6VXNlcjY3MTY4MTg=', 'avatar_url': 'https://avatars.githubusercontent.com/u/6716818?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/JosJuice', 'html_url': 'https://github.com/JosJuice', 'followers_url': 'https://api.github.com/users/JosJuice/followers', 'following_url': 'https://api.github.com/users/JosJuice/following{/other_user}', 'gists_url': 'https://api.github.com/users/JosJuice/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/JosJuice/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/JosJuice/subscriptions', 'organizations_url': 'https://api.github.com/users/JosJuice/orgs', 'repos_url': 'https://api.github.com/users/JosJuice/repos', 'events_url': 'https://api.github.com/users/JosJuice/events{/privacy}', 'received_events_url': 'https://api.github.com/users/JosJuice/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'repo': {'id': 26057138, 'node_id': 'MDEwOlJlcG9zaXRvcnkyNjA1NzEzOA==', 'name': 'dolphin', 'full_name': 'JosJuice/dolphin', 'private': False, 'owner': {'login': 'JosJuice', 'id': 6716818, 'node_id': 'MDQ6VXNlcjY3MTY4MTg=', 'avatar_url': 'https://avatars.githubusercontent.com/u/6716818?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/JosJuice', 'html_url': 'https://github.com/JosJuice', 'followers_url': 'https://api.github.com/users/JosJuice/followers', 'following_url': 'https://api.github.com/users/JosJuice/following{/other_user}', 'gists_url': 'https://api.github.com/users/JosJuice/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/JosJuice/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/JosJuice/subscriptions', 'organizations_url': 'https://api.github.com/users/JosJuice/orgs', 'repos_url': 'https://api.github.com/users/JosJuice/repos', 'events_url': 'https://api.github.com/users/JosJuice/events{/privacy}', 'received_events_url': 'https://api.github.com/users/JosJuice/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'html_url': 'https://github.com/JosJuice/dolphin', 'description': 'Dolphin is a GameCube/Wii emulator, allowing you to play games for these two platforms on PC, with improvements.', 'fork': True, 'url': 'https://api.github.com/repos/JosJuice/dolphin', 'forks_url': 'https://api.github.com/repos/JosJuice/dolphin/forks', 'keys_url': 'https://api.github.com/repos/JosJuice/dolphin/keys{/key_id}', 'collaborators_url': 'https://api.github.com/repos/JosJuice/dolphin/collaborators{/collaborator}', 'teams_url': 'https://api.github.com/repos/JosJuice/dolphin/teams', 'hooks_url': 'https://api.github.com/repos/JosJuice/dolphin/hooks', 'issue_events_url': 'https://api.github.com/repos/JosJuice/dolphin/issues/events{/number}', 'events_url': 'https://api.github.com/repos/JosJuice/dolphin/events', 'assignees_url': 'https://api.github.com/repos/JosJuice/dolphin/assignees{/user}', 'branches_url': 'https://api.github.com/repos/JosJuice/dolphin/branches{/branch}', 'tags_url': 'https://api.github.com/repos/JosJuice/dolphin/tags', 'blobs_url': 'https://api.github.com/repos/JosJuice/dolphin/git/blobs{/sha}', 'git_tags_url': 'https://api.github.com/repos/JosJuice/dolphin/git/tags{/sha}', 'git_refs_url': 'https://api.github.com/repos/JosJuice/dolphin/git/refs{/sha}', 'trees_url': 'https://api.github.com/repos/JosJuice/dolphin/git/trees{/sha}', 'statuses_url': 'https://api.github.com/repos/JosJuice/dolphin/statuses/{sha}', 'languages_url': 'https://api.github.com/repos/JosJuice/dolphin/languages', 'stargazers_url': 'https://api.github.com/repos/JosJuice/dolphin/stargazers', 'contributors_url': 'https://api.github.com/repos/JosJuice/dolphin/contributors', 'subscribers_url': 'https://api.github.com/repos/JosJuice/dolphin/subscribers', 'subscription_url': 'https://api.github.com/repos/JosJuice/dolphin/subscription', 'commits_url': 'https://api.github.com/repos/JosJuice/dolphin/commits{/sha}', 'git_commits_url': 'https://api.github.com/repos/JosJuice/dolphin/git/commits{/sha}', 'comments_url': 'https://api.github.com/repos/JosJuice/dolphin/comments{/number}', 'issue_comment_url': 'https://api.github.com/repos/JosJuice/dolphin/issues/comments{/number}', 'contents_url': 'https://api.github.com/repos/JosJuice/dolphin/contents/{+path}', 'compare_url': 'https://api.github.com/repos/JosJuice/dolphin/compare/{base}...{head}', 'merges_url': 'https://api.github.com/repos/JosJuice/dolphin/merges', 'archive_url': 'https://api.github.com/repos/JosJuice/dolphin/{archive_format}{/ref}', 'downloads_url': 'https://api.github.com/repos/JosJuice/dolphin/downloads', 'issues_url': 'https://api.github.com/repos/JosJuice/dolphin/issues{/number}', 'pulls_url': 'https://api.github.com/repos/JosJuice/dolphin/pulls{/number}', 'milestones_url': 'https://api.github.com/repos/JosJuice/dolphin/milestones{/number}', 'notifications_url': 'https://api.github.com/repos/JosJuice/dolphin/notifications{?since,all,participating}', 'labels_url': 'https://api.github.com/repos/JosJuice/dolphin/labels{/name}', 'releases_url': 'https://api.github.com/repos/JosJuice/dolphin/releases{/id}', 'deployments_url': 'https://api.github.com/repos/JosJuice/dolphin/deployments', 'created_at': '2014-11-01T17:12:40Z', 'updated_at': '2026-02-01T11:39:14Z', 'pushed_at': '2026-02-02T21:07:04Z', 'git_url': 'git://github.com/JosJuice/dolphin.git', 'ssh_url': 'git@github.com:JosJuice/dolphin.git', 'clone_url': 'https://github.com/JosJuice/dolphin.git', 'svn_url': 'https://github.com/JosJuice/dolphin', 'homepage': None, 'size': 509732, 'stargazers_count': 6, 'watchers_count': 6, 'language': 'C++', 'has_issues': False, 'has_projects': True, 'has_downloads': True, 'has_wiki': True, 'has_pages': False, 'has_discussions': False, 'forks_count': 1, 'mirror_url': None, 'archived': False, 'disabled': False, 'open_issues_count': 0, 'license': {'key': 'other', 'name': 'Other', 'spdx_id': 'NOASSERTION', 'url': None, 'node_id': 'MDc6TGljZW5zZTA='}, 'allow_forking': True, 'is_template': False, 'web_commit_signoff_required': False, 'topics': [], 'visibility': 'public', 'forks': 1, 'open_issues': 0, 'watchers': 6, 'default_branch': 'master', 'allow_squash_merge': True, 'allow_merge_commit': True, 'allow_rebase_merge': True, 'allow_auto_merge': False, 'delete_branch_on_merge': False, 'allow_update_branch': False, 'use_squash_pr_title_as_default': False, 'squash_merge_commit_message': 'COMMIT_MESSAGES', 'squash_merge_commit_title': 'COMMIT_OR_PR_TITLE', 'merge_commit_message': 'PR_TITLE', 'merge_commit_title': 'MERGE_MESSAGE'}}, 'base': {'label': 'dolphin-emu:master', 'ref': 'master', 'sha': '6711d77b9901ed0a4418203db49ed4d8576e6fe0', 'user': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/dolphin-emu', 'html_url': 'https://github.com/dolphin-emu', 'followers_url': 'https://api.github.com/users/dolphin-emu/followers', 'following_url': 'https://api.github.com/users/dolphin-emu/following{/other_user}', 'gists_url': 'https://api.github.com/users/dolphin-emu/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/dolphin-emu/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/dolphin-emu/subscriptions', 'organizations_url': 'https://api.github.com/users/dolphin-emu/orgs', 'repos_url': 'https://api.github.com/users/dolphin-emu/repos', 'events_url': 'https://api.github.com/users/dolphin-emu/events{/privacy}', 'received_events_url': 'https://api.github.com/users/dolphin-emu/received_events', 'type': 'Organization', 'user_view_type': 'public', 'site_admin': False}, 'repo': {'id': 11577304, 'node_id': 'MDEwOlJlcG9zaXRvcnkxMTU3NzMwNA==', 'name': 'dolphin', 'full_name': 'dolphin-emu/dolphin', 'private': False, 'owner': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/dolphin-emu', 'html_url': 'https://github.com/dolphin-emu', 'followers_url': 'https://api.github.com/users/dolphin-emu/followers', 'following_url': 'https://api.github.com/users/dolphin-emu/following{/other_user}', 'gists_url': 'https://api.github.com/users/dolphin-emu/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/dolphin-emu/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/dolphin-emu/subscriptions', 'organizations_url': 'https://api.github.com/users/dolphin-emu/orgs', 'repos_url': 'https://api.github.com/users/dolphin-emu/repos', 'events_url': 'https://api.github.com/users/dolphin-emu/events{/privacy}', 'received_events_url': 'https://api.github.com/users/dolphin-emu/received_events', 'type': 'Organization', 'user_view_type': 'public', 'site_admin': False}, 'html_url': 'https://github.com/dolphin-emu/dolphin', 'description': 'Dolphin is a GameCube / Wii emulator, allowing you to play games for these two platforms on PC with improvements.', 'fork': False, 'url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'forks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/forks', 'keys_url': 'https://api.github.com/repos/dolphin-emu/dolphin/keys{/key_id}', 'collaborators_url': 'https://api.github.com/repos/dolphin-emu/dolphin/collaborators{/collaborator}', 'teams_url': 'https://api.github.com/repos/dolphin-emu/dolphin/teams', 'hooks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/hooks', 'issue_events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/events{/number}', 'events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/events', 'assignees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/assignees{/user}', 'branches_url': 'https://api.github.com/repos/dolphin-emu/dolphin/branches{/branch}', 'tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/tags', 'blobs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/blobs{/sha}', 'git_tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/tags{/sha}', 'git_refs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/refs{/sha}', 'trees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/trees{/sha}', 'statuses_url': 'https://api.github.com/repos/dolphin-emu/dolphin/statuses/{sha}', 'languages_url': 'https://api.github.com/repos/dolphin-emu/dolphin/languages', 'stargazers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/stargazers', 'contributors_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contributors', 'subscribers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscribers', 'subscription_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscription', 'commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/commits{/sha}', 'git_commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/commits{/sha}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/comments{/number}', 'issue_comment_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments{/number}', 'contents_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contents/{+path}', 'compare_url': 'https://api.github.com/repos/dolphin-emu/dolphin/compare/{base}...{head}', 'merges_url': 'https://api.github.com/repos/dolphin-emu/dolphin/merges', 'archive_url': 'https://api.github.com/repos/dolphin-emu/dolphin/{archive_format}{/ref}', 'downloads_url': 'https://api.github.com/repos/dolphin-emu/dolphin/downloads', 'issues_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues{/number}', 'pulls_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls{/number}', 'milestones_url': 'https://api.github.com/repos/dolphin-emu/dolphin/milestones{/number}', 'notifications_url': 'https://api.github.com/repos/dolphin-emu/dolphin/notifications{?since,all,participating}', 'labels_url': 'https://api.github.com/repos/dolphin-emu/dolphin/labels{/name}', 'releases_url': 'https://api.github.com/repos/dolphin-emu/dolphin/releases{/id}', 'deployments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/deployments', 'created_at': '2013-07-22T09:07:57Z', 'updated_at': '2026-02-02T23:15:04Z', 'pushed_at': '2026-02-02T20:05:51Z', 'git_url': 'git://github.com/dolphin-emu/dolphin.git', 'ssh_url': 'git@github.com:dolphin-emu/dolphin.git', 'clone_url': 'https://github.com/dolphin-emu/dolphin.git', 'svn_url': 'https://github.com/dolphin-emu/dolphin', 'homepage': 'https://dolphin-emu.org', 'size': 544012, 'stargazers_count': 14577, 'watchers_count': 14577, 'language': 'C++', 'has_issues': False, 'has_projects': False, 'has_downloads': True, 'has_wiki': True, 'has_pages': False, 'has_discussions': False, 'forks_count': 2966, 'mirror_url': None, 'archived': False, 'disabled': False, 'open_issues_count': 361, 'license': {'key': 'other', 'name': 'Other', 'spdx_id': 'NOASSERTION', 'url': None, 'node_id': 'MDc6TGljZW5zZTA='}, 'allow_forking': True, 'is_template': False, 'web_commit_signoff_required': False, 'topics': ['cpp', 'emulation', 'emulator', 'gamecube', 'wii'], 'visibility': 'public', 'forks': 2966, 'open_issues': 361, 'watchers': 14577, 'default_branch': 'master', 'allow_squash_merge': False, 'allow_merge_commit': True, 'allow_rebase_merge': False, 'allow_auto_merge': False, 'delete_branch_on_merge': False, 'allow_update_branch': False, 'use_squash_pr_title_as_default': False, 'squash_merge_commit_message': 'COMMIT_MESSAGES', 'squash_merge_commit_title': 'COMMIT_OR_PR_TITLE', 'merge_commit_message': 'PR_TITLE', 'merge_commit_title': 'MERGE_MESSAGE'}}, '_links': {'self': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13768'}, 'html': {'href': 'https://github.com/dolphin-emu/dolphin/pull/13768'}, 'issue': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13768'}, 'comments': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13768/comments'}, 'review_comments': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13768/comments'}, 'review_comment': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments{/number}'}, 'commits': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13768/commits'}, 'statuses': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/statuses/e88ee42d345e0b2a85cc6bd44c7258eb8be3c6b8'}}, 'author_association': 'MEMBER', 'auto_merge': None, 'active_lock_reason': None}, 'repository': {'id': 11577304, 'node_id': 'MDEwOlJlcG9zaXRvcnkxMTU3NzMwNA==', 'name': 'dolphin', 'full_name': 'dolphin-emu/dolphin', 'private': False, 'owner': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/dolphin-emu', 'html_url': 'https://github.com/dolphin-emu', 'followers_url': 'https://api.github.com/users/dolphin-emu/followers', 'following_url': 'https://api.github.com/users/dolphin-emu/following{/other_user}', 'gists_url': 'https://api.github.com/users/dolphin-emu/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/dolphin-emu/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/dolphin-emu/subscriptions', 'organizations_url': 'https://api.github.com/users/dolphin-emu/orgs', 'repos_url': 'https://api.github.com/users/dolphin-emu/repos', 'events_url': 'https://api.github.com/users/dolphin-emu/events{/privacy}', 'received_events_url': 'https://api.github.com/users/dolphin-emu/received_events', 'type': 'Organization', 'user_view_type': 'public', 'site_admin': False}, 'html_url': 'https://github.com/dolphin-emu/dolphin', 'description': 'Dolphin is a GameCube / Wii emulator, allowing you to play games for these two platforms on PC with improvements.', 'fork': False, 'url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'forks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/forks', 'keys_url': 'https://api.github.com/repos/dolphin-emu/dolphin/keys{/key_id}', 'collaborators_url': 'https://api.github.com/repos/dolphin-emu/dolphin/collaborators{/collaborator}', 'teams_url': 'https://api.github.com/repos/dolphin-emu/dolphin/teams', 'hooks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/hooks', 'issue_events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/events{/number}', 'events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/events', 'assignees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/assignees{/user}', 'branches_url': 'https://api.github.com/repos/dolphin-emu/dolphin/branches{/branch}', 'tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/tags', 'blobs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/blobs{/sha}', 'git_tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/tags{/sha}', 'git_refs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/refs{/sha}', 'trees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/trees{/sha}', 'statuses_url': 'https://api.github.com/repos/dolphin-emu/dolphin/statuses/{sha}', 'languages_url': 'https://api.github.com/repos/dolphin-emu/dolphin/languages', 'stargazers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/stargazers', 'contributors_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contributors', 'subscribers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscribers', 'subscription_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscription', 'commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/commits{/sha}', 'git_commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/commits{/sha}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/comments{/number}', 'issue_comment_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments{/number}', 'contents_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contents/{+path}', 'compare_url': 'https://api.github.com/repos/dolphin-emu/dolphin/compare/{base}...{head}', 'merges_url': 'https://api.github.com/repos/dolphin-emu/dolphin/merges', 'archive_url': 'https://api.github.com/repos/dolphin-emu/dolphin/{archive_format}{/ref}', 'downloads_url': 'https://api.github.com/repos/dolphin-emu/dolphin/downloads', 'issues_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues{/number}', 'pulls_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls{/number}', 'milestones_url': 'https://api.github.com/repos/dolphin-emu/dolphin/milestones{/number}', 'notifications_url': 'https://api.github.com/repos/dolphin-emu/dolphin/notifications{?since,all,participating}', 'labels_url': 'https://api.github.com/repos/dolphin-emu/dolphin/labels{/name}', 'releases_url': 'https://api.github.com/repos/dolphin-emu/dolphin/releases{/id}', 'deployments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/deployments', 'created_at': '2013-07-22T09:07:57Z', 'updated_at': '2026-02-02T23:15:04Z', 'pushed_at': '2026-02-02T20:05:51Z', 'git_url': 'git://github.com/dolphin-emu/dolphin.git', 'ssh_url': 'git@github.com:dolphin-emu/dolphin.git', 'clone_url': 'https://github.com/dolphin-emu/dolphin.git', 'svn_url': 'https://github.com/dolphin-emu/dolphin', 'homepage': 'https://dolphin-emu.org', 'size': 544012, 'stargazers_count': 14577, 'watchers_count': 14577, 'language': 'C++', 'has_issues': False, 'has_projects': False, 'has_downloads': True, 'has_wiki': True, 'has_pages': False, 'has_discussions': False, 'forks_count': 2966, 'mirror_url': None, 'archived': False, 'disabled': False, 'open_issues_count': 361, 'license': {'key': 'other', 'name': 'Other', 'spdx_id': 'NOASSERTION', 'url': None, 'node_id': 'MDc6TGljZW5zZTA='}, 'allow_forking': True, 'is_template': False, 'web_commit_signoff_required': False, 'topics': ['cpp', 'emulation', 'emulator', 'gamecube', 'wii'], 'visibility': 'public', 'forks': 2966, 'open_issues': 361, 'watchers': 14577, 'default_branch': 'master', 'custom_properties': {}}, 'organization': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'url': 'https://api.github.com/orgs/dolphin-emu', 'repos_url': 'https://api.github.com/orgs/dolphin-emu/repos', 'events_url': 'https://api.github.com/orgs/dolphin-emu/events', 'hooks_url': 'https://api.github.com/orgs/dolphin-emu/hooks', 'issues_url': 'https://api.github.com/orgs/dolphin-emu/issues', 'members_url': 'https://api.github.com/orgs/dolphin-emu/members{/member}', 'public_members_url': 'https://api.github.com/orgs/dolphin-emu/public_members{/member}', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'description': ''}, 'sender': {'login': 'jordan-woyak', 'id': 1768214, 'node_id': 'MDQ6VXNlcjE3NjgyMTQ=', 'avatar_url': 'https://avatars.githubusercontent.com/u/1768214?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/jordan-woyak', 'html_url': 'https://github.com/jordan-woyak', 'followers_url': 'https://api.github.com/users/jordan-woyak/followers', 'following_url': 'https://api.github.com/users/jordan-woyak/following{/other_user}', 'gists_url': 'https://api.github.com/users/jordan-woyak/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/jordan-woyak/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/jordan-woyak/subscriptions', 'organizations_url': 'https://api.github.com/users/jordan-woyak/orgs', 'repos_url': 'https://api.github.com/users/jordan-woyak/repos', 'events_url': 'https://api.github.com/users/jordan-woyak/events{/privacy}', 'received_events_url': 'https://api.github.com/users/jordan-woyak/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'installation': {'id': 35437981, 'node_id': 'MDIzOkludGVncmF0aW9uSW5zdGFsbGF0aW9uMzU0Mzc5ODE='}}, 'type': 'raw_gh_hook'}
2026-02-02T21:17:44.679800 {'source': 'webserver', 'gh_type': 'issue_comment', 'raw': {'action': 'created', 'issue': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13991', 'repository_url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'labels_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13991/labels{/name}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13991/comments', 'events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13991/events', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/13991', 'id': 3485022552, 'node_id': 'PR_kwDOALCn2M6sJ1qq', 'number': 13991, 'title': "Jit: Emit Branch Watch code only if it's enabled", 'user': {'login': 'SuperSamus', 'id': 40663462, 'node_id': 'MDQ6VXNlcjQwNjYzNDYy', 'avatar_url': 'https://avatars.githubusercontent.com/u/40663462?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/SuperSamus', 'html_url': 'https://github.com/SuperSamus', 'followers_url': 'https://api.github.com/users/SuperSamus/followers', 'following_url': 'https://api.github.com/users/SuperSamus/following{/other_user}', 'gists_url': 'https://api.github.com/users/SuperSamus/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/SuperSamus/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/SuperSamus/subscriptions', 'organizations_url': 'https://api.github.com/users/SuperSamus/orgs', 'repos_url': 'https://api.github.com/users/SuperSamus/repos', 'events_url': 'https://api.github.com/users/SuperSamus/events{/privacy}', 'received_events_url': 'https://api.github.com/users/SuperSamus/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'labels': [], 'state': 'open', 'locked': False, 'assignee': None, 'assignees': [], 'milestone': None, 'comments': 5, 'created_at': '2025-10-05T17:58:27Z', 'updated_at': '2026-02-02T21:17:42Z', 'closed_at': None, 'author_association': 'CONTRIBUTOR', 'type': None, 'active_lock_reason': None, 'draft': False, 'pull_request': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/13991', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/13991', 'diff_url': 'https://github.com/dolphin-emu/dolphin/pull/13991.diff', 'patch_url': 'https://github.com/dolphin-emu/dolphin/pull/13991.patch', 'merged_at': None}, 'body': 'JIT code related to Branch Watch was emitted if the debugging UI was active: the emitted code would dynamically check whether Branch Watch is active.\r\nHowever, this causes two problems:\r\n1. It decreases performance by just having the debugging UI enabled*\r\n2. It clutters the host assembly in the JIT tab, making it harder to read (unaware readers will wonder what these instructions are for)\r\n\r\nWith this PR, code related to Branch Watch is emitted only if Branch Watch itself is active, fixing the issues above.\r\nThe JIT cache will now be wiped whenever the feature is toggled, causing a slight stutter. However, this isn\'t the kind of feature that is toggled over and over, so IMO it is an acceptable trade-off.\r\n\r\nARM64 is untested.\r\n\r\n#### A note about the debugging UI\r\n\r\nSo, you might wonder what the asterisk is about. It\'s not that the PR doesn\'t improve performance with the debug UI on, because it does.\r\nThe problem is that the performance is still worse than with the debugging UI disabled. I\'m not sure why.[^1]\r\nEven though this performance decrease is intended, *this downside isn\'t communicated anywhere in Dolphin*, and it\'s very possible for someone to unawarely leave the option enabled. (Hi.)\r\nSo... that should be communicated, but where? Only the tooltip of the option, or even in the option title itself `(disables optimizations)`? (Plus the help text in the command line for the `-d` argument.)\r\n\r\nI\'ll leave the discussion for this and on whether to merge the PR (IMO, still worthy due to point 2) to the jury.\r\n\r\n[^1]: I already tested that "reverting" [the disabled BLR optimization](https://github.com/dolphin-emu/dolphin/blob/70bd0943a7a271606367ac129eb39cbdcd659e42/Source/Core/Core/PowerPC/JitCommon/JitBase.cpp#L153-L154) or [the extra checks in the main loop meant for the memory breakpoints](https://github.com/dolphin-emu/dolphin/blob/70bd0943a7a271606367ac129eb39cbdcd659e42/Source/Core/Core/PowerPC/Jit64/JitAsm.cpp#L99-L104) changes basically nothing.', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13991/reactions', 'total_count': 0, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 0, 'confused': 0, 'heart': 0, 'rocket': 0, 'eyes': 0}, 'timeline_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13991/timeline', 'performed_via_github_app': None, 'state_reason': None}, 'comment': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments/3837468646', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/13991#issuecomment-3837468646', 'issue_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/13991', 'id': 3837468646, 'node_id': 'IC_kwDOALCn2M7kux_m', 'user': {'login': 'Hibyehello', 'id': 36666883, 'node_id': 'MDQ6VXNlcjM2NjY2ODgz', 'avatar_url': 'https://avatars.githubusercontent.com/u/36666883?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/Hibyehello', 'html_url': 'https://github.com/Hibyehello', 'followers_url': 'https://api.github.com/users/Hibyehello/followers', 'following_url': 'https://api.github.com/users/Hibyehello/following{/other_user}', 'gists_url': 'https://api.github.com/users/Hibyehello/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/Hibyehello/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/Hibyehello/subscriptions', 'organizations_url': 'https://api.github.com/users/Hibyehello/orgs', 'repos_url': 'https://api.github.com/users/Hibyehello/repos', 'events_url': 'https://api.github.com/users/Hibyehello/events{/privacy}', 'received_events_url': 'https://api.github.com/users/Hibyehello/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'created_at': '2026-02-02T21:17:41Z', 'updated_at': '2026-02-02T21:17:41Z', 'body': 'Tested on an M4 MacBook with no problems', 'author_association': 'CONTRIBUTOR', 'reactions': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments/3837468646/reactions', 'total_count': 0, '+1': 0, '-1': 0, 'laugh': 0, 'hooray': 0, 'confused': 0, 'heart': 0, 'rocket': 0, 'eyes': 0}, 'performed_via_github_app': None}, 'repository': {'id': 11577304, 'node_id': 'MDEwOlJlcG9zaXRvcnkxMTU3NzMwNA==', 'name': 'dolphin', 'full_name': 'dolphin-emu/dolphin', 'private': False, 'owner': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/dolphin-emu', 'html_url': 'https://github.com/dolphin-emu', 'followers_url': 'https://api.github.com/users/dolphin-emu/followers', 'following_url': 'https://api.github.com/users/dolphin-emu/following{/other_user}', 'gists_url': 'https://api.github.com/users/dolphin-emu/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/dolphin-emu/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/dolphin-emu/subscriptions', 'organizations_url': 'https://api.github.com/users/dolphin-emu/orgs', 'repos_url': 'https://api.github.com/users/dolphin-emu/repos', 'events_url': 'https://api.github.com/users/dolphin-emu/events{/privacy}', 'received_events_url': 'https://api.github.com/users/dolphin-emu/received_events', 'type': 'Organization', 'user_view_type': 'public', 'site_admin': False}, 'html_url': 'https://github.com/dolphin-emu/dolphin', 'description': 'Dolphin is a GameCube / Wii emulator, allowing you to play games for these two platforms on PC with improvements.', 'fork': False, 'url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'forks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/forks', 'keys_url': 'https://api.github.com/repos/dolphin-emu/dolphin/keys{/key_id}', 'collaborators_url': 'https://api.github.com/repos/dolphin-emu/dolphin/collaborators{/collaborator}', 'teams_url': 'https://api.github.com/repos/dolphin-emu/dolphin/teams', 'hooks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/hooks', 'issue_events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/events{/number}', 'events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/events', 'assignees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/assignees{/user}', 'branches_url': 'https://api.github.com/repos/dolphin-emu/dolphin/branches{/branch}', 'tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/tags', 'blobs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/blobs{/sha}', 'git_tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/tags{/sha}', 'git_refs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/refs{/sha}', 'trees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/trees{/sha}', 'statuses_url': 'https://api.github.com/repos/dolphin-emu/dolphin/statuses/{sha}', 'languages_url': 'https://api.github.com/repos/dolphin-emu/dolphin/languages', 'stargazers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/stargazers', 'contributors_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contributors', 'subscribers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscribers', 'subscription_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscription', 'commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/commits{/sha}', 'git_commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/commits{/sha}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/comments{/number}', 'issue_comment_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments{/number}', 'contents_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contents/{+path}', 'compare_url': 'https://api.github.com/repos/dolphin-emu/dolphin/compare/{base}...{head}', 'merges_url': 'https://api.github.com/repos/dolphin-emu/dolphin/merges', 'archive_url': 'https://api.github.com/repos/dolphin-emu/dolphin/{archive_format}{/ref}', 'downloads_url': 'https://api.github.com/repos/dolphin-emu/dolphin/downloads', 'issues_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues{/number}', 'pulls_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls{/number}', 'milestones_url': 'https://api.github.com/repos/dolphin-emu/dolphin/milestones{/number}', 'notifications_url': 'https://api.github.com/repos/dolphin-emu/dolphin/notifications{?since,all,participating}', 'labels_url': 'https://api.github.com/repos/dolphin-emu/dolphin/labels{/name}', 'releases_url': 'https://api.github.com/repos/dolphin-emu/dolphin/releases{/id}', 'deployments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/deployments', 'created_at': '2013-07-22T09:07:57Z', 'updated_at': '2026-02-02T20:05:56Z', 'pushed_at': '2026-02-02T20:05:51Z', 'git_url': 'git://github.com/dolphin-emu/dolphin.git', 'ssh_url': 'git@github.com:dolphin-emu/dolphin.git', 'clone_url': 'https://github.com/dolphin-emu/dolphin.git', 'svn_url': 'https://github.com/dolphin-emu/dolphin', 'homepage': 'https://dolphin-emu.org', 'size': 544012, 'stargazers_count': 14576, 'watchers_count': 14576, 'language': 'C++', 'has_issues': False, 'has_projects': False, 'has_downloads': True, 'has_wiki': True, 'has_pages': False, 'has_discussions': False, 'forks_count': 2966, 'mirror_url': None, 'archived': False, 'disabled': False, 'open_issues_count': 361, 'license': {'key': 'other', 'name': 'Other', 'spdx_id': 'NOASSERTION', 'url': None, 'node_id': 'MDc6TGljZW5zZTA='}, 'allow_forking': True, 'is_template': False, 'web_commit_signoff_required': False, 'topics': ['cpp', 'emulation', 'emulator', 'gamecube', 'wii'], 'visibility': 'public', 'forks': 2966, 'open_issues': 361, 'watchers': 14576, 'default_branch': 'master', 'custom_properties': {}}, 'organization': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'url': 'https://api.github.com/orgs/dolphin-emu', 'repos_url': 'https://api.github.com/orgs/dolphin-emu/repos', 'events_url': 'https://api.github.com/orgs/dolphin-emu/events', 'hooks_url': 'https://api.github.com/orgs/dolphin-emu/hooks', 'issues_url': 'https://api.github.com/orgs/dolphin-emu/issues', 'members_url': 'https://api.github.com/orgs/dolphin-emu/members{/member}', 'public_members_url': 'https://api.github.com/orgs/dolphin-emu/public_members{/member}', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'description': ''}, 'sender': {'login': 'Hibyehello', 'id': 36666883, 'node_id': 'MDQ6VXNlcjM2NjY2ODgz', 'avatar_url': 'https://avatars.githubusercontent.com/u/36666883?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/Hibyehello', 'html_url': 'https://github.com/Hibyehello', 'followers_url': 'https://api.github.com/users/Hibyehello/followers', 'following_url': 'https://api.github.com/users/Hibyehello/following{/other_user}', 'gists_url': 'https://api.github.com/users/Hibyehello/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/Hibyehello/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/Hibyehello/subscriptions', 'organizations_url': 'https://api.github.com/users/Hibyehello/orgs', 'repos_url': 'https://api.github.com/users/Hibyehello/repos', 'events_url': 'https://api.github.com/users/Hibyehello/events{/privacy}', 'received_events_url': 'https://api.github.com/users/Hibyehello/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'installation': {'id': 35437981, 'node_id': 'MDIzOkludGVncmF0aW9uSW5zdGFsbGF0aW9uMzU0Mzc5ODE='}}, 'type': 'raw_gh_hook'}
2026-02-02T21:07:21.898039 {'source': 'webserver', 'gh_type': 'pull_request', 'raw': {'action': 'opened', 'number': 14317, 'pull_request': {'url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/14317', 'id': 3238193063, 'node_id': 'PR_kwDOALCn2M7BAuen', 'html_url': 'https://github.com/dolphin-emu/dolphin/pull/14317', 'diff_url': 'https://github.com/dolphin-emu/dolphin/pull/14317.diff', 'patch_url': 'https://github.com/dolphin-emu/dolphin/pull/14317.patch', 'issue_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14317', 'number': 14317, 'state': 'open', 'locked': False, 'title': 'Optimize JitBaseBlockCache::ErasePhysicalRange', 'user': {'login': 'JosJuice', 'id': 6716818, 'node_id': 'MDQ6VXNlcjY3MTY4MTg=', 'avatar_url': 'https://avatars.githubusercontent.com/u/6716818?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/JosJuice', 'html_url': 'https://github.com/JosJuice', 'followers_url': 'https://api.github.com/users/JosJuice/followers', 'following_url': 'https://api.github.com/users/JosJuice/following{/other_user}', 'gists_url': 'https://api.github.com/users/JosJuice/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/JosJuice/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/JosJuice/subscriptions', 'organizations_url': 'https://api.github.com/users/JosJuice/orgs', 'repos_url': 'https://api.github.com/users/JosJuice/repos', 'events_url': 'https://api.github.com/users/JosJuice/events{/privacy}', 'received_events_url': 'https://api.github.com/users/JosJuice/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'body': 'This speeds up switching to and from cockpit view in Rogue Squadron 2 by maybe 5%. This scenario spends most time jitting, with jitting taking up 10x more time than block invalidation, but the time spent in block invalidation is non-trivial and I was able to find this low-hanging fruit there.', 'created_at': '2026-02-02T21:07:19Z', 'updated_at': '2026-02-02T21:07:19Z', 'closed_at': None, 'merged_at': None, 'merge_commit_sha': None, 'assignee': None, 'assignees': [], 'requested_reviewers': [], 'requested_teams': [], 'labels': [], 'milestone': None, 'draft': False, 'commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/14317/commits', 'review_comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/14317/comments', 'review_comment_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments{/number}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14317/comments', 'statuses_url': 'https://api.github.com/repos/dolphin-emu/dolphin/statuses/c8180071a552720e5436e908afc4eda7e87be08c', 'head': {'label': 'JosJuice:jit-cache-macro-loop', 'ref': 'jit-cache-macro-loop', 'sha': 'c8180071a552720e5436e908afc4eda7e87be08c', 'user': {'login': 'JosJuice', 'id': 6716818, 'node_id': 'MDQ6VXNlcjY3MTY4MTg=', 'avatar_url': 'https://avatars.githubusercontent.com/u/6716818?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/JosJuice', 'html_url': 'https://github.com/JosJuice', 'followers_url': 'https://api.github.com/users/JosJuice/followers', 'following_url': 'https://api.github.com/users/JosJuice/following{/other_user}', 'gists_url': 'https://api.github.com/users/JosJuice/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/JosJuice/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/JosJuice/subscriptions', 'organizations_url': 'https://api.github.com/users/JosJuice/orgs', 'repos_url': 'https://api.github.com/users/JosJuice/repos', 'events_url': 'https://api.github.com/users/JosJuice/events{/privacy}', 'received_events_url': 'https://api.github.com/users/JosJuice/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'repo': {'id': 26057138, 'node_id': 'MDEwOlJlcG9zaXRvcnkyNjA1NzEzOA==', 'name': 'dolphin', 'full_name': 'JosJuice/dolphin', 'private': False, 'owner': {'login': 'JosJuice', 'id': 6716818, 'node_id': 'MDQ6VXNlcjY3MTY4MTg=', 'avatar_url': 'https://avatars.githubusercontent.com/u/6716818?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/JosJuice', 'html_url': 'https://github.com/JosJuice', 'followers_url': 'https://api.github.com/users/JosJuice/followers', 'following_url': 'https://api.github.com/users/JosJuice/following{/other_user}', 'gists_url': 'https://api.github.com/users/JosJuice/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/JosJuice/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/JosJuice/subscriptions', 'organizations_url': 'https://api.github.com/users/JosJuice/orgs', 'repos_url': 'https://api.github.com/users/JosJuice/repos', 'events_url': 'https://api.github.com/users/JosJuice/events{/privacy}', 'received_events_url': 'https://api.github.com/users/JosJuice/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'html_url': 'https://github.com/JosJuice/dolphin', 'description': 'Dolphin is a GameCube/Wii emulator, allowing you to play games for these two platforms on PC, with improvements.', 'fork': True, 'url': 'https://api.github.com/repos/JosJuice/dolphin', 'forks_url': 'https://api.github.com/repos/JosJuice/dolphin/forks', 'keys_url': 'https://api.github.com/repos/JosJuice/dolphin/keys{/key_id}', 'collaborators_url': 'https://api.github.com/repos/JosJuice/dolphin/collaborators{/collaborator}', 'teams_url': 'https://api.github.com/repos/JosJuice/dolphin/teams', 'hooks_url': 'https://api.github.com/repos/JosJuice/dolphin/hooks', 'issue_events_url': 'https://api.github.com/repos/JosJuice/dolphin/issues/events{/number}', 'events_url': 'https://api.github.com/repos/JosJuice/dolphin/events', 'assignees_url': 'https://api.github.com/repos/JosJuice/dolphin/assignees{/user}', 'branches_url': 'https://api.github.com/repos/JosJuice/dolphin/branches{/branch}', 'tags_url': 'https://api.github.com/repos/JosJuice/dolphin/tags', 'blobs_url': 'https://api.github.com/repos/JosJuice/dolphin/git/blobs{/sha}', 'git_tags_url': 'https://api.github.com/repos/JosJuice/dolphin/git/tags{/sha}', 'git_refs_url': 'https://api.github.com/repos/JosJuice/dolphin/git/refs{/sha}', 'trees_url': 'https://api.github.com/repos/JosJuice/dolphin/git/trees{/sha}', 'statuses_url': 'https://api.github.com/repos/JosJuice/dolphin/statuses/{sha}', 'languages_url': 'https://api.github.com/repos/JosJuice/dolphin/languages', 'stargazers_url': 'https://api.github.com/repos/JosJuice/dolphin/stargazers', 'contributors_url': 'https://api.github.com/repos/JosJuice/dolphin/contributors', 'subscribers_url': 'https://api.github.com/repos/JosJuice/dolphin/subscribers', 'subscription_url': 'https://api.github.com/repos/JosJuice/dolphin/subscription', 'commits_url': 'https://api.github.com/repos/JosJuice/dolphin/commits{/sha}', 'git_commits_url': 'https://api.github.com/repos/JosJuice/dolphin/git/commits{/sha}', 'comments_url': 'https://api.github.com/repos/JosJuice/dolphin/comments{/number}', 'issue_comment_url': 'https://api.github.com/repos/JosJuice/dolphin/issues/comments{/number}', 'contents_url': 'https://api.github.com/repos/JosJuice/dolphin/contents/{+path}', 'compare_url': 'https://api.github.com/repos/JosJuice/dolphin/compare/{base}...{head}', 'merges_url': 'https://api.github.com/repos/JosJuice/dolphin/merges', 'archive_url': 'https://api.github.com/repos/JosJuice/dolphin/{archive_format}{/ref}', 'downloads_url': 'https://api.github.com/repos/JosJuice/dolphin/downloads', 'issues_url': 'https://api.github.com/repos/JosJuice/dolphin/issues{/number}', 'pulls_url': 'https://api.github.com/repos/JosJuice/dolphin/pulls{/number}', 'milestones_url': 'https://api.github.com/repos/JosJuice/dolphin/milestones{/number}', 'notifications_url': 'https://api.github.com/repos/JosJuice/dolphin/notifications{?since,all,participating}', 'labels_url': 'https://api.github.com/repos/JosJuice/dolphin/labels{/name}', 'releases_url': 'https://api.github.com/repos/JosJuice/dolphin/releases{/id}', 'deployments_url': 'https://api.github.com/repos/JosJuice/dolphin/deployments', 'created_at': '2014-11-01T17:12:40Z', 'updated_at': '2026-02-01T11:39:14Z', 'pushed_at': '2026-02-02T21:07:04Z', 'git_url': 'git://github.com/JosJuice/dolphin.git', 'ssh_url': 'git@github.com:JosJuice/dolphin.git', 'clone_url': 'https://github.com/JosJuice/dolphin.git', 'svn_url': 'https://github.com/JosJuice/dolphin', 'homepage': None, 'size': 509721, 'stargazers_count': 6, 'watchers_count': 6, 'language': 'C++', 'has_issues': False, 'has_projects': True, 'has_downloads': True, 'has_wiki': True, 'has_pages': False, 'has_discussions': False, 'forks_count': 1, 'mirror_url': None, 'archived': False, 'disabled': False, 'open_issues_count': 0, 'license': {'key': 'other', 'name': 'Other', 'spdx_id': 'NOASSERTION', 'url': None, 'node_id': 'MDc6TGljZW5zZTA='}, 'allow_forking': True, 'is_template': False, 'web_commit_signoff_required': False, 'topics': [], 'visibility': 'public', 'forks': 1, 'open_issues': 0, 'watchers': 6, 'default_branch': 'master', 'allow_squash_merge': True, 'allow_merge_commit': True, 'allow_rebase_merge': True, 'allow_auto_merge': False, 'delete_branch_on_merge': False, 'allow_update_branch': False, 'use_squash_pr_title_as_default': False, 'squash_merge_commit_message': 'COMMIT_MESSAGES', 'squash_merge_commit_title': 'COMMIT_OR_PR_TITLE', 'merge_commit_message': 'PR_TITLE', 'merge_commit_title': 'MERGE_MESSAGE'}}, 'base': {'label': 'dolphin-emu:master', 'ref': 'master', 'sha': 'c7de9162765452fc2787f3ba4f0092394067d603', 'user': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/dolphin-emu', 'html_url': 'https://github.com/dolphin-emu', 'followers_url': 'https://api.github.com/users/dolphin-emu/followers', 'following_url': 'https://api.github.com/users/dolphin-emu/following{/other_user}', 'gists_url': 'https://api.github.com/users/dolphin-emu/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/dolphin-emu/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/dolphin-emu/subscriptions', 'organizations_url': 'https://api.github.com/users/dolphin-emu/orgs', 'repos_url': 'https://api.github.com/users/dolphin-emu/repos', 'events_url': 'https://api.github.com/users/dolphin-emu/events{/privacy}', 'received_events_url': 'https://api.github.com/users/dolphin-emu/received_events', 'type': 'Organization', 'user_view_type': 'public', 'site_admin': False}, 'repo': {'id': 11577304, 'node_id': 'MDEwOlJlcG9zaXRvcnkxMTU3NzMwNA==', 'name': 'dolphin', 'full_name': 'dolphin-emu/dolphin', 'private': False, 'owner': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/dolphin-emu', 'html_url': 'https://github.com/dolphin-emu', 'followers_url': 'https://api.github.com/users/dolphin-emu/followers', 'following_url': 'https://api.github.com/users/dolphin-emu/following{/other_user}', 'gists_url': 'https://api.github.com/users/dolphin-emu/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/dolphin-emu/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/dolphin-emu/subscriptions', 'organizations_url': 'https://api.github.com/users/dolphin-emu/orgs', 'repos_url': 'https://api.github.com/users/dolphin-emu/repos', 'events_url': 'https://api.github.com/users/dolphin-emu/events{/privacy}', 'received_events_url': 'https://api.github.com/users/dolphin-emu/received_events', 'type': 'Organization', 'user_view_type': 'public', 'site_admin': False}, 'html_url': 'https://github.com/dolphin-emu/dolphin', 'description': 'Dolphin is a GameCube / Wii emulator, allowing you to play games for these two platforms on PC with improvements.', 'fork': False, 'url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'forks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/forks', 'keys_url': 'https://api.github.com/repos/dolphin-emu/dolphin/keys{/key_id}', 'collaborators_url': 'https://api.github.com/repos/dolphin-emu/dolphin/collaborators{/collaborator}', 'teams_url': 'https://api.github.com/repos/dolphin-emu/dolphin/teams', 'hooks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/hooks', 'issue_events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/events{/number}', 'events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/events', 'assignees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/assignees{/user}', 'branches_url': 'https://api.github.com/repos/dolphin-emu/dolphin/branches{/branch}', 'tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/tags', 'blobs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/blobs{/sha}', 'git_tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/tags{/sha}', 'git_refs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/refs{/sha}', 'trees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/trees{/sha}', 'statuses_url': 'https://api.github.com/repos/dolphin-emu/dolphin/statuses/{sha}', 'languages_url': 'https://api.github.com/repos/dolphin-emu/dolphin/languages', 'stargazers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/stargazers', 'contributors_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contributors', 'subscribers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscribers', 'subscription_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscription', 'commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/commits{/sha}', 'git_commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/commits{/sha}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/comments{/number}', 'issue_comment_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments{/number}', 'contents_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contents/{+path}', 'compare_url': 'https://api.github.com/repos/dolphin-emu/dolphin/compare/{base}...{head}', 'merges_url': 'https://api.github.com/repos/dolphin-emu/dolphin/merges', 'archive_url': 'https://api.github.com/repos/dolphin-emu/dolphin/{archive_format}{/ref}', 'downloads_url': 'https://api.github.com/repos/dolphin-emu/dolphin/downloads', 'issues_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues{/number}', 'pulls_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls{/number}', 'milestones_url': 'https://api.github.com/repos/dolphin-emu/dolphin/milestones{/number}', 'notifications_url': 'https://api.github.com/repos/dolphin-emu/dolphin/notifications{?since,all,participating}', 'labels_url': 'https://api.github.com/repos/dolphin-emu/dolphin/labels{/name}', 'releases_url': 'https://api.github.com/repos/dolphin-emu/dolphin/releases{/id}', 'deployments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/deployments', 'created_at': '2013-07-22T09:07:57Z', 'updated_at': '2026-02-02T20:05:56Z', 'pushed_at': '2026-02-02T20:05:51Z', 'git_url': 'git://github.com/dolphin-emu/dolphin.git', 'ssh_url': 'git@github.com:dolphin-emu/dolphin.git', 'clone_url': 'https://github.com/dolphin-emu/dolphin.git', 'svn_url': 'https://github.com/dolphin-emu/dolphin', 'homepage': 'https://dolphin-emu.org', 'size': 544012, 'stargazers_count': 14576, 'watchers_count': 14576, 'language': 'C++', 'has_issues': False, 'has_projects': False, 'has_downloads': True, 'has_wiki': True, 'has_pages': False, 'has_discussions': False, 'forks_count': 2966, 'mirror_url': None, 'archived': False, 'disabled': False, 'open_issues_count': 361, 'license': {'key': 'other', 'name': 'Other', 'spdx_id': 'NOASSERTION', 'url': None, 'node_id': 'MDc6TGljZW5zZTA='}, 'allow_forking': True, 'is_template': False, 'web_commit_signoff_required': False, 'topics': ['cpp', 'emulation', 'emulator', 'gamecube', 'wii'], 'visibility': 'public', 'forks': 2966, 'open_issues': 361, 'watchers': 14576, 'default_branch': 'master', 'allow_squash_merge': False, 'allow_merge_commit': True, 'allow_rebase_merge': False, 'allow_auto_merge': False, 'delete_branch_on_merge': False, 'allow_update_branch': False, 'use_squash_pr_title_as_default': False, 'squash_merge_commit_message': 'COMMIT_MESSAGES', 'squash_merge_commit_title': 'COMMIT_OR_PR_TITLE', 'merge_commit_message': 'PR_TITLE', 'merge_commit_title': 'MERGE_MESSAGE'}}, '_links': {'self': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/14317'}, 'html': {'href': 'https://github.com/dolphin-emu/dolphin/pull/14317'}, 'issue': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14317'}, 'comments': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/14317/comments'}, 'review_comments': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/14317/comments'}, 'review_comment': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/comments{/number}'}, 'commits': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls/14317/commits'}, 'statuses': {'href': 'https://api.github.com/repos/dolphin-emu/dolphin/statuses/c8180071a552720e5436e908afc4eda7e87be08c'}}, 'author_association': 'MEMBER', 'auto_merge': None, 'active_lock_reason': None, 'merged': False, 'mergeable': None, 'rebaseable': None, 'mergeable_state': 'unknown', 'merged_by': None, 'comments': 0, 'review_comments': 0, 'maintainer_can_modify': True, 'commits': 1, 'additions': 10, 'deletions': 2, 'changed_files': 1}, 'repository': {'id': 11577304, 'node_id': 'MDEwOlJlcG9zaXRvcnkxMTU3NzMwNA==', 'name': 'dolphin', 'full_name': 'dolphin-emu/dolphin', 'private': False, 'owner': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/dolphin-emu', 'html_url': 'https://github.com/dolphin-emu', 'followers_url': 'https://api.github.com/users/dolphin-emu/followers', 'following_url': 'https://api.github.com/users/dolphin-emu/following{/other_user}', 'gists_url': 'https://api.github.com/users/dolphin-emu/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/dolphin-emu/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/dolphin-emu/subscriptions', 'organizations_url': 'https://api.github.com/users/dolphin-emu/orgs', 'repos_url': 'https://api.github.com/users/dolphin-emu/repos', 'events_url': 'https://api.github.com/users/dolphin-emu/events{/privacy}', 'received_events_url': 'https://api.github.com/users/dolphin-emu/received_events', 'type': 'Organization', 'user_view_type': 'public', 'site_admin': False}, 'html_url': 'https://github.com/dolphin-emu/dolphin', 'description': 'Dolphin is a GameCube / Wii emulator, allowing you to play games for these two platforms on PC with improvements.', 'fork': False, 'url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'forks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/forks', 'keys_url': 'https://api.github.com/repos/dolphin-emu/dolphin/keys{/key_id}', 'collaborators_url': 'https://api.github.com/repos/dolphin-emu/dolphin/collaborators{/collaborator}', 'teams_url': 'https://api.github.com/repos/dolphin-emu/dolphin/teams', 'hooks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/hooks', 'issue_events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/events{/number}', 'events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/events', 'assignees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/assignees{/user}', 'branches_url': 'https://api.github.com/repos/dolphin-emu/dolphin/branches{/branch}', 'tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/tags', 'blobs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/blobs{/sha}', 'git_tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/tags{/sha}', 'git_refs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/refs{/sha}', 'trees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/trees{/sha}', 'statuses_url': 'https://api.github.com/repos/dolphin-emu/dolphin/statuses/{sha}', 'languages_url': 'https://api.github.com/repos/dolphin-emu/dolphin/languages', 'stargazers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/stargazers', 'contributors_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contributors', 'subscribers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscribers', 'subscription_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscription', 'commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/commits{/sha}', 'git_commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/commits{/sha}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/comments{/number}', 'issue_comment_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments{/number}', 'contents_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contents/{+path}', 'compare_url': 'https://api.github.com/repos/dolphin-emu/dolphin/compare/{base}...{head}', 'merges_url': 'https://api.github.com/repos/dolphin-emu/dolphin/merges', 'archive_url': 'https://api.github.com/repos/dolphin-emu/dolphin/{archive_format}{/ref}', 'downloads_url': 'https://api.github.com/repos/dolphin-emu/dolphin/downloads', 'issues_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues{/number}', 'pulls_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls{/number}', 'milestones_url': 'https://api.github.com/repos/dolphin-emu/dolphin/milestones{/number}', 'notifications_url': 'https://api.github.com/repos/dolphin-emu/dolphin/notifications{?since,all,participating}', 'labels_url': 'https://api.github.com/repos/dolphin-emu/dolphin/labels{/name}', 'releases_url': 'https://api.github.com/repos/dolphin-emu/dolphin/releases{/id}', 'deployments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/deployments', 'created_at': '2013-07-22T09:07:57Z', 'updated_at': '2026-02-02T20:05:56Z', 'pushed_at': '2026-02-02T20:05:51Z', 'git_url': 'git://github.com/dolphin-emu/dolphin.git', 'ssh_url': 'git@github.com:dolphin-emu/dolphin.git', 'clone_url': 'https://github.com/dolphin-emu/dolphin.git', 'svn_url': 'https://github.com/dolphin-emu/dolphin', 'homepage': 'https://dolphin-emu.org', 'size': 544012, 'stargazers_count': 14576, 'watchers_count': 14576, 'language': 'C++', 'has_issues': False, 'has_projects': False, 'has_downloads': True, 'has_wiki': True, 'has_pages': False, 'has_discussions': False, 'forks_count': 2966, 'mirror_url': None, 'archived': False, 'disabled': False, 'open_issues_count': 361, 'license': {'key': 'other', 'name': 'Other', 'spdx_id': 'NOASSERTION', 'url': None, 'node_id': 'MDc6TGljZW5zZTA='}, 'allow_forking': True, 'is_template': False, 'web_commit_signoff_required': False, 'topics': ['cpp', 'emulation', 'emulator', 'gamecube', 'wii'], 'visibility': 'public', 'forks': 2966, 'open_issues': 361, 'watchers': 14576, 'default_branch': 'master', 'custom_properties': {}}, 'organization': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'url': 'https://api.github.com/orgs/dolphin-emu', 'repos_url': 'https://api.github.com/orgs/dolphin-emu/repos', 'events_url': 'https://api.github.com/orgs/dolphin-emu/events', 'hooks_url': 'https://api.github.com/orgs/dolphin-emu/hooks', 'issues_url': 'https://api.github.com/orgs/dolphin-emu/issues', 'members_url': 'https://api.github.com/orgs/dolphin-emu/members{/member}', 'public_members_url': 'https://api.github.com/orgs/dolphin-emu/public_members{/member}', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'description': ''}, 'sender': {'login': 'JosJuice', 'id': 6716818, 'node_id': 'MDQ6VXNlcjY3MTY4MTg=', 'avatar_url': 'https://avatars.githubusercontent.com/u/6716818?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/JosJuice', 'html_url': 'https://github.com/JosJuice', 'followers_url': 'https://api.github.com/users/JosJuice/followers', 'following_url': 'https://api.github.com/users/JosJuice/following{/other_user}', 'gists_url': 'https://api.github.com/users/JosJuice/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/JosJuice/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/JosJuice/subscriptions', 'organizations_url': 'https://api.github.com/users/JosJuice/orgs', 'repos_url': 'https://api.github.com/users/JosJuice/repos', 'events_url': 'https://api.github.com/users/JosJuice/events{/privacy}', 'received_events_url': 'https://api.github.com/users/JosJuice/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'installation': {'id': 35437981, 'node_id': 'MDIzOkludGVncmF0aW9uSW5zdGFsbGF0aW9uMzU0Mzc5ODE='}}, 'type': 'raw_gh_hook'}
2026-02-02T20:05:54.683640 {'source': 'webserver', 'gh_type': 'check_suite', 'raw': {'action': 'requested', 'check_suite': {'id': 56183190815, 'node_id': 'CS_kwDOALCn2M8AAAANFMd1Hw', 'head_branch': 'master', 'head_sha': 'c7de9162765452fc2787f3ba4f0092394067d603', 'status': 'queued', 'conclusion': None, 'url': 'https://api.github.com/repos/dolphin-emu/dolphin/check-suites/56183190815', 'before': '6711d77b9901ed0a4418203db49ed4d8576e6fe0', 'after': 'c7de9162765452fc2787f3ba4f0092394067d603', 'pull_requests': [{'url': 'https://api.github.com/repos/MarioPartyNetplay/Dolphin-MPN/pulls/191', 'id': 3229846782, 'number': 191, 'head': {'ref': 'master', 'sha': 'c7de9162765452fc2787f3ba4f0092394067d603', 'repo': {'id': 11577304, 'url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'name': 'dolphin'}}, 'base': {'ref': 'master', 'sha': 'bb679f420d6ea68263904009bebf3e8905f5c8e1', 'repo': {'id': 623850328, 'url': 'https://api.github.com/repos/MarioPartyNetplay/Dolphin-MPN', 'name': 'Dolphin-MPN'}}}, {'url': 'https://api.github.com/repos/Faster-Brawl/dolphin/pulls/5', 'id': 1222541325, 'number': 5, 'head': {'ref': 'master', 'sha': 'c7de9162765452fc2787f3ba4f0092394067d603', 'repo': {'id': 11577304, 'url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'name': 'dolphin'}}, 'base': {'ref': 'master', 'sha': 'eb9e232680dfdc04ec2adab05dbc530b1e707fc9', 'repo': {'id': 577809188, 'url': 'https://api.github.com/repos/Faster-Brawl/dolphin', 'name': 'dolphin'}}}, {'url': 'https://api.github.com/repos/dirextric-auto/dolphin/pulls/1', 'id': 571486662, 'number': 1, 'head': {'ref': 'master', 'sha': 'c7de9162765452fc2787f3ba4f0092394067d603', 'repo': {'id': 11577304, 'url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'name': 'dolphin'}}, 'base': {'ref': 'master', 'sha': '1e71904cb9930eccc036e2ce2360beaa746befa7', 'repo': {'id': 337848960, 'url': 'https://api.github.com/repos/dirextric-auto/dolphin', 'name': 'dolphin'}}}, {'url': 'https://api.github.com/repos/zurgeg/dolphin-vr-no-ovr/pulls/1', 'id': 518053317, 'number': 1, 'head': {'ref': 'master', 'sha': 'c7de9162765452fc2787f3ba4f0092394067d603', 'repo': {'id': 11577304, 'url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'name': 'dolphin'}}, 'base': {'ref': 'VR-Hydra', 'sha': 'ea9f95955e5335cc238b2f9c5a0a6dfdc8698aec', 'repo': {'id': 311469400, 'url': 'https://api.github.com/repos/zurgeg/dolphin-vr-no-ovr', 'name': 'dolphin-vr-no-ovr'}}}, {'url': 'https://api.github.com/repos/weihuoya/dolphin/pulls/1', 'id': 400884538, 'number': 1, 'head': {'ref': 'master', 'sha': '6711d77b9901ed0a4418203db49ed4d8576e6fe0', 'repo': {'id': 11577304, 'url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'name': 'dolphin'}}, 'base': {'ref': 'master', 'sha': '0f4c971326ae9389b3ad55b0fefacb708d148f4d', 'repo': {'id': 143011855, 'url': 'https://api.github.com/repos/weihuoya/dolphin', 'name': 'dolphin'}}}], 'app': {'id': 49947, 'client_id': 'Iv1.1fcaea7644d8b727', 'slug': 'dolphin-ci', 'node_id': 'MDM6QXBwNDk5NDc=', 'owner': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/dolphin-emu', 'html_url': 'https://github.com/dolphin-emu', 'followers_url': 'https://api.github.com/users/dolphin-emu/followers', 'following_url': 'https://api.github.com/users/dolphin-emu/following{/other_user}', 'gists_url': 'https://api.github.com/users/dolphin-emu/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/dolphin-emu/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/dolphin-emu/subscriptions', 'organizations_url': 'https://api.github.com/users/dolphin-emu/orgs', 'repos_url': 'https://api.github.com/users/dolphin-emu/repos', 'events_url': 'https://api.github.com/users/dolphin-emu/events{/privacy}', 'received_events_url': 'https://api.github.com/users/dolphin-emu/received_events', 'type': 'Organization', 'user_view_type': 'public', 'site_admin': False}, 'name': 'Dolphin CI', 'description': 'Continuous Integration setup for [dolphin-emu.org](https://dolphin-emu.org/).', 'external_url': 'https://github.com/dolphin-emu', 'html_url': 'https://github.com/apps/dolphin-ci', 'created_at': '2019-12-26T22:26:07Z', 'updated_at': '2019-12-26T22:33:19Z', 'permissions': {'checks': 'write', 'contents': 'read', 'issues': 'write', 'members': 'read', 'metadata': 'read', 'pull_requests': 'write', 'statuses': 'write'}, 'events': ['check_run', 'commit_comment', 'issue_comment', 'pull_request', 'pull_request_review', 'pull_request_review_comment', 'push']}, 'created_at': '2026-02-02T20:05:53Z', 'updated_at': '2026-02-02T20:05:53Z', 'rerequestable': True, 'runs_rerequestable': True, 'latest_check_runs_count': 0, 'check_runs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/check-suites/56183190815/check-runs', 'head_commit': {'id': 'c7de9162765452fc2787f3ba4f0092394067d603', 'tree_id': 'df1616469bb5667243b07405978fffcc84476e14', 'message': 'Merge pull request #14316 from JoshuaVandaele/resourcefix\n\nResourcePack: Fix loading resource packs', 'timestamp': '2026-02-02T20:05:51Z', 'author': {'name': 'JMC47', 'email': 'JMC4789@gmail.com'}, 'committer': {'name': 'GitHub', 'email': 'noreply@github.com'}}}, 'repository': {'id': 11577304, 'node_id': 'MDEwOlJlcG9zaXRvcnkxMTU3NzMwNA==', 'name': 'dolphin', 'full_name': 'dolphin-emu/dolphin', 'private': False, 'owner': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/dolphin-emu', 'html_url': 'https://github.com/dolphin-emu', 'followers_url': 'https://api.github.com/users/dolphin-emu/followers', 'following_url': 'https://api.github.com/users/dolphin-emu/following{/other_user}', 'gists_url': 'https://api.github.com/users/dolphin-emu/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/dolphin-emu/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/dolphin-emu/subscriptions', 'organizations_url': 'https://api.github.com/users/dolphin-emu/orgs', 'repos_url': 'https://api.github.com/users/dolphin-emu/repos', 'events_url': 'https://api.github.com/users/dolphin-emu/events{/privacy}', 'received_events_url': 'https://api.github.com/users/dolphin-emu/received_events', 'type': 'Organization', 'user_view_type': 'public', 'site_admin': False}, 'html_url': 'https://github.com/dolphin-emu/dolphin', 'description': 'Dolphin is a GameCube / Wii emulator, allowing you to play games for these two platforms on PC with improvements.', 'fork': False, 'url': 'https://api.github.com/repos/dolphin-emu/dolphin', 'forks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/forks', 'keys_url': 'https://api.github.com/repos/dolphin-emu/dolphin/keys{/key_id}', 'collaborators_url': 'https://api.github.com/repos/dolphin-emu/dolphin/collaborators{/collaborator}', 'teams_url': 'https://api.github.com/repos/dolphin-emu/dolphin/teams', 'hooks_url': 'https://api.github.com/repos/dolphin-emu/dolphin/hooks', 'issue_events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/events{/number}', 'events_url': 'https://api.github.com/repos/dolphin-emu/dolphin/events', 'assignees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/assignees{/user}', 'branches_url': 'https://api.github.com/repos/dolphin-emu/dolphin/branches{/branch}', 'tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/tags', 'blobs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/blobs{/sha}', 'git_tags_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/tags{/sha}', 'git_refs_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/refs{/sha}', 'trees_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/trees{/sha}', 'statuses_url': 'https://api.github.com/repos/dolphin-emu/dolphin/statuses/{sha}', 'languages_url': 'https://api.github.com/repos/dolphin-emu/dolphin/languages', 'stargazers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/stargazers', 'contributors_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contributors', 'subscribers_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscribers', 'subscription_url': 'https://api.github.com/repos/dolphin-emu/dolphin/subscription', 'commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/commits{/sha}', 'git_commits_url': 'https://api.github.com/repos/dolphin-emu/dolphin/git/commits{/sha}', 'comments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/comments{/number}', 'issue_comment_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues/comments{/number}', 'contents_url': 'https://api.github.com/repos/dolphin-emu/dolphin/contents/{+path}', 'compare_url': 'https://api.github.com/repos/dolphin-emu/dolphin/compare/{base}...{head}', 'merges_url': 'https://api.github.com/repos/dolphin-emu/dolphin/merges', 'archive_url': 'https://api.github.com/repos/dolphin-emu/dolphin/{archive_format}{/ref}', 'downloads_url': 'https://api.github.com/repos/dolphin-emu/dolphin/downloads', 'issues_url': 'https://api.github.com/repos/dolphin-emu/dolphin/issues{/number}', 'pulls_url': 'https://api.github.com/repos/dolphin-emu/dolphin/pulls{/number}', 'milestones_url': 'https://api.github.com/repos/dolphin-emu/dolphin/milestones{/number}', 'notifications_url': 'https://api.github.com/repos/dolphin-emu/dolphin/notifications{?since,all,participating}', 'labels_url': 'https://api.github.com/repos/dolphin-emu/dolphin/labels{/name}', 'releases_url': 'https://api.github.com/repos/dolphin-emu/dolphin/releases{/id}', 'deployments_url': 'https://api.github.com/repos/dolphin-emu/dolphin/deployments', 'created_at': '2013-07-22T09:07:57Z', 'updated_at': '2026-02-02T18:49:39Z', 'pushed_at': '2026-02-02T20:05:51Z', 'git_url': 'git://github.com/dolphin-emu/dolphin.git', 'ssh_url': 'git@github.com:dolphin-emu/dolphin.git', 'clone_url': 'https://github.com/dolphin-emu/dolphin.git', 'svn_url': 'https://github.com/dolphin-emu/dolphin', 'homepage': 'https://dolphin-emu.org', 'size': 544012, 'stargazers_count': 14576, 'watchers_count': 14576, 'language': 'C++', 'has_issues': False, 'has_projects': False, 'has_downloads': True, 'has_wiki': True, 'has_pages': False, 'has_discussions': False, 'forks_count': 2966, 'mirror_url': None, 'archived': False, 'disabled': False, 'open_issues_count': 360, 'license': {'key': 'other', 'name': 'Other', 'spdx_id': 'NOASSERTION', 'url': None, 'node_id': 'MDc6TGljZW5zZTA='}, 'allow_forking': True, 'is_template': False, 'web_commit_signoff_required': False, 'topics': ['cpp', 'emulation', 'emulator', 'gamecube', 'wii'], 'visibility': 'public', 'forks': 2966, 'open_issues': 360, 'watchers': 14576, 'default_branch': 'master', 'custom_properties': {}}, 'organization': {'login': 'dolphin-emu', 'id': 5050316, 'node_id': 'MDEyOk9yZ2FuaXphdGlvbjUwNTAzMTY=', 'url': 'https://api.github.com/orgs/dolphin-emu', 'repos_url': 'https://api.github.com/orgs/dolphin-emu/repos', 'events_url': 'https://api.github.com/orgs/dolphin-emu/events', 'hooks_url': 'https://api.github.com/orgs/dolphin-emu/hooks', 'issues_url': 'https://api.github.com/orgs/dolphin-emu/issues', 'members_url': 'https://api.github.com/orgs/dolphin-emu/members{/member}', 'public_members_url': 'https://api.github.com/orgs/dolphin-emu/public_members{/member}', 'avatar_url': 'https://avatars.githubusercontent.com/u/5050316?v=4', 'description': ''}, 'sender': {'login': 'JMC47', 'id': 6598209, 'node_id': 'MDQ6VXNlcjY1OTgyMDk=', 'avatar_url': 'https://avatars.githubusercontent.com/u/6598209?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/JMC47', 'html_url': 'https://github.com/JMC47', 'followers_url': 'https://api.github.com/users/JMC47/followers', 'following_url': 'https://api.github.com/users/JMC47/following{/other_user}', 'gists_url': 'https://api.github.com/users/JMC47/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/JMC47/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/JMC47/subscriptions', 'organizations_url': 'https://api.github.com/users/JMC47/orgs', 'repos_url': 'https://api.github.com/users/JMC47/repos', 'events_url': 'https://api.github.com/users/JMC47/events{/privacy}', 'received_events_url': 'https://api.github.com/users/JMC47/received_events', 'type': 'User', 'user_view_type': 'public', 'site_admin': False}, 'installation': {'id': 35437981, 'node_id': 'MDIzOkludGVncmF0aW9uSW5zdGFsbGF0aW9uMzU0Mzc5ODE='}}, 'type': 'raw_gh_hook'}