mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-26 10:19:47 -04:00
154 lines
7.5 KiB
Python
154 lines
7.5 KiB
Python
"""Exercise the actual debug route handlers/getters without SDL or a game.
|
|
|
|
Extract the unchanged function bodies from debug_server.c; stub only JSON and
|
|
responses. Live game qualification must additionally check native pad input.
|
|
"""
|
|
import argparse
|
|
from pathlib import Path
|
|
import re
|
|
import subprocess
|
|
import tempfile
|
|
|
|
|
|
def function(source, name):
|
|
found = re.search(r"^(?:static )?(?:int|void) " + name + r"\([^\n]*\)\s*\{.*?^\}",
|
|
source, re.M | re.S)
|
|
if not found:
|
|
raise ValueError("Missing source function: " + name)
|
|
return found[0]
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
parser.add_argument('--cc', required=True)
|
|
args = parser.parse_args()
|
|
source = (Path(__file__).resolve().parents[1] / 'src/debug_server.c').read_text()
|
|
preamble = r'''
|
|
#include <assert.h>
|
|
#include "input_route.h"
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdarg.h>
|
|
#define PSX_BSS
|
|
static uint64_t s_frame_count;
|
|
typedef struct { unsigned unused; } PsxFrameFingerprint;
|
|
#define PSX_FRAME_FINGERPRINT_INIT {0}
|
|
static int s_fp_reset_on_load;
|
|
static PsxFrameFingerprint s_fp;
|
|
static unsigned s_fp_head,s_fp_total;
|
|
static int s_input_override=-1,s_input_override_p2=-1;
|
|
static int s_input_frames,s_input_frames_p2,s_axis_override,s_pad_type_override=-1;
|
|
static int s_pad_connected_override[2]={-1,-1};
|
|
static int s_extra_active[8],s_extra_frames[8];
|
|
static InputRoute s_route;
|
|
static unsigned s_input_route_port=1;
|
|
static int s_input_route_wait_load;
|
|
static unsigned char s_axis_st[4],s_host_axes[4],s_host_lt,s_host_rt;
|
|
static int s_host_layer;
|
|
static uint16_t s_host_buttons;
|
|
static char response[512];
|
|
static void send_ok(int id){(void)id;strcpy(response,"ok");}
|
|
static void send_err(int id,const char *text){(void)id;snprintf(response,sizeof response,"ERROR:%s",text);}
|
|
static void send_fmt(const char *format,...){va_list v;va_start(v,format);vsnprintf(response,sizeof response,format,v);va_end(v);}
|
|
static int json_get_int(const char *json,const char *key,int fallback){
|
|
char name[80];snprintf(name,sizeof name,"\"%s\"",key);
|
|
const char *p=strstr(json,name);if(!p)return fallback;
|
|
p=strchr(p,':');assert(p);return atoi(p+1);
|
|
}
|
|
'''
|
|
body = '\n'.join(function(source, name) for name in (
|
|
'handle_clear_input', 'handle_input_route_clear', 'handle_input_route_append',
|
|
'handle_input_route_start', 'handle_input_route_stop', 'handle_input_route_status',
|
|
'input_route_loaded', 'debug_server_note_savestate_loaded',
|
|
'input_route_poll_port', 'debug_server_get_input_override',
|
|
'debug_server_get_input_override_port2'))
|
|
checks = r'''
|
|
static void queue(void){
|
|
handle_input_route_clear(1,"{}");
|
|
handle_input_route_append(1,"{\"frames\":2,\"buttons\":32767}");
|
|
handle_input_route_append(1,"{\"frames\":1,\"buttons\":65535}");
|
|
}
|
|
int main(void){
|
|
queue();s_input_override=123;s_input_override_p2=456;
|
|
handle_input_route_start(1,"{\"port\":2,\"at_frame\":10}");
|
|
assert(strstr(response,"\"port\":2"));
|
|
assert(s_input_override==123&&s_input_override_p2==-1);
|
|
s_frame_count=9;
|
|
assert(debug_server_get_input_override_port2()==-1);
|
|
assert(s_route.remaining==2);
|
|
s_frame_count=10;
|
|
assert(debug_server_get_input_override()==123);
|
|
assert(s_route.remaining==2);
|
|
assert(debug_server_get_input_override_port2()==32767);
|
|
assert(s_route.remaining==1);
|
|
++s_frame_count;
|
|
assert(debug_server_get_input_override()==123);
|
|
assert(debug_server_get_input_override_port2()==32767);
|
|
assert(s_route.index==1&&s_route.remaining==1);
|
|
++s_frame_count;
|
|
assert(debug_server_get_input_override_port2()==65535);
|
|
assert(!s_route.active);
|
|
assert(debug_server_get_input_override_port2()==-1);
|
|
queue();handle_input_route_start(1,"{\"port\":2}");
|
|
handle_clear_input(1,"{\"port\":1}");assert(s_route.active);
|
|
handle_clear_input(1,"{\"port\":2}");assert(!s_route.active);
|
|
queue();handle_input_route_start(1,"{}");
|
|
assert(s_input_route_port==1);s_input_override_p2=456;
|
|
assert(debug_server_get_input_override_port2()==456&&s_route.remaining==2);
|
|
assert(debug_server_get_input_override()==32767);
|
|
handle_clear_input(1,"{\"port\":2}");assert(s_route.active);
|
|
handle_input_route_status(1,"{}");assert(strstr(response,"\"port\":1"));
|
|
handle_input_route_append(1,"{\"frames\":1,\"buttons\":0}");
|
|
assert(strstr(response,"ERROR"));
|
|
handle_input_route_stop(1,"{}");assert(!s_route.active);
|
|
handle_input_route_start(1,"{\"port\":3}");
|
|
assert(strstr(response,"ERROR")&&!s_route.active);
|
|
handle_input_route_start(1,"{\"port\":2}");
|
|
handle_clear_input(1,"{}");assert(!s_route.active);
|
|
handle_input_route_clear(1,"{}");handle_input_route_start(1,"{}");
|
|
assert(strstr(response,"empty"));
|
|
queue();handle_input_route_start(1,"{\"port\":2,\"after_load\":1}");
|
|
assert(s_route.active&&s_input_route_wait_load);
|
|
assert(strstr(response,"\"waiting_for_load\":true"));
|
|
for(unsigned n=0;n<20;++n){++s_frame_count;assert(debug_server_get_input_override_port2()==-1);}
|
|
assert(s_route.remaining==2);
|
|
handle_input_route_status(1,"{}");assert(strstr(response,"\"waiting_for_load\":true"));
|
|
/* Route admission is independent of optional fingerprint reset. */
|
|
debug_server_note_savestate_loaded();assert(!s_input_route_wait_load);
|
|
assert(debug_server_get_input_override_port2()==32767&&s_route.remaining==1);
|
|
debug_server_note_savestate_loaded(); /* An ordinary later load does not restart it. */
|
|
assert(s_route.remaining==1);
|
|
assert(debug_server_get_input_override_port2()==32767);
|
|
assert(debug_server_get_input_override_port2()==65535&&!s_route.active);
|
|
for(unsigned port=1;port<=2;++port){
|
|
queue();handle_input_route_start(1,port==1?"{\"after_load\":1}":"{\"port\":2,\"after_load\":1}");
|
|
handle_clear_input(1,port==1?"{\"port\":2}":"{\"port\":1}");assert(s_input_route_wait_load);
|
|
handle_clear_input(1,port==1?"{\"port\":1}":"{\"port\":2}");assert(!s_input_route_wait_load&&!s_route.active);
|
|
}
|
|
queue();handle_input_route_start(1,"{\"after_load\":1}");
|
|
handle_input_route_stop(1,"{}");debug_server_note_savestate_loaded();assert(!s_route.active);
|
|
queue();handle_input_route_start(1,"{\"after_load\":1}");
|
|
handle_input_route_clear(1,"{}");debug_server_note_savestate_loaded();assert(!s_route.active);
|
|
queue();handle_input_route_start(1,"{\"after_load\":2}");assert(strstr(response,"ERROR")&&!s_route.active);
|
|
handle_input_route_start(1,"{\"after_load\":1,\"at_frame\":10}");assert(strstr(response,"ERROR")&&!s_route.active);
|
|
handle_input_route_start(1,"{\"after_load\":1}");s_fp_reset_on_load=1;s_fp_head=4;s_fp_total=5;
|
|
debug_server_note_savestate_loaded();assert(!s_input_route_wait_load&&!s_fp_reset_on_load&&!s_fp_head&&!s_fp_total);
|
|
puts("debug input route: P2 timing, isolation, deferred start, clearing and legacy P1 pass");
|
|
return 0;
|
|
}
|
|
'''
|
|
with tempfile.TemporaryDirectory(prefix='psx-input-route-') as directory:
|
|
path = Path(directory) / 'route.c'
|
|
path.write_text(preamble + body + checks,encoding='utf-8',newline='\n')
|
|
executable = Path(directory) / 'route.exe'
|
|
subprocess.run([args.cc, '-std=c11', '-Wall', '-Wextra', '-Werror', str(path),
|
|
'-I',str(Path(__file__).resolve().parents[1]/'include'),
|
|
str(Path(__file__).resolve().parents[1]/'src/input_route.c'),'-o', str(executable)], check=True)
|
|
subprocess.run([str(executable)], check=True)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|