From e637b3e72bc266454130b15a28645580e9f6b5de Mon Sep 17 00:00:00 2001 From: Jake Teton-Landis Date: Sun, 21 Jun 2015 00:56:02 -0700 Subject: [PATCH 1/3] remove all rendered things other than display turns out we coundn't stream because blitting some PNGs around takes so long that pygame misses multiple stream service packets. this results in never having enough data to really get stream rendering going. many things have been hastily fucktored as we tested parts across the stack to track down this stupid, stupid issue. TODO: clean up everything. --- H264Decoder.py | 12 +++++++----- app.py | 8 +++++--- controls/base.py | 11 ++++++----- controls/keyboard.py | 8 ++++---- controls/mouse.py | 21 ++++++++++++++------- controls/wireless.py | 5 +++-- drc-sim.py | 24 +++++++++++++----------- services.py | 35 ++++++++++++++++++++++++++--------- simulator.py | 27 +++++++++++++++++---------- 9 files changed, 95 insertions(+), 56 deletions(-) diff --git a/H264Decoder.py b/H264Decoder.py index c9f391a..7188cdf 100644 --- a/H264Decoder.py +++ b/H264Decoder.py @@ -81,7 +81,7 @@ def __init_avcodec(s): s.got_frame = s.ffi.new('int *') s.out_frame = s.ns.avcodec_alloc_frame() - def __init__(s, (in_x, in_y), (out_x, out_y), output_surface): + def __init__(s, (in_x, in_y), (out_x, out_y), output_surface=None): s.sws_context = None s.__init_ffi() s.__init_avcodec() @@ -142,7 +142,9 @@ def display_frame(s, encoded_nalu): (s.out_x, s.out_y), 'RGB') # I guess this is an extra copy...? - s.output_surface.blit(surface, (0, 0)) - # prev implementation just wrote out to the main display and did the flip: - # pygame.display.get_surface().blit(surface, (0, 0)) - # pygame.display.flip() + if s.output_surface: + s.output_surface.blit(surface, (0, 0)) + else: + # prev implementation just wrote out to the main display and did the flip: + pygame.display.get_surface().blit(surface, (0, 0)) + pygame.display.flip() diff --git a/app.py b/app.py index 0c44046..d4536c5 100644 --- a/app.py +++ b/app.py @@ -9,6 +9,8 @@ class ExitMain(StandardError): """ pass +SURFACE_OPTS = 0 # pygame.FULLSCREEN | pygame.HWSURFACE +SIZE = (854, 480) class App(object): """ @@ -18,7 +20,7 @@ class App(object): def __init__(self, title): pygame.init() pygame.display.set_caption(title) - pygame.display.set_mode((20, 20)) + pygame.display.set_mode((20, 20), SURFACE_OPTS) load_all_assets() asset_names = ASSET_DICT.keys() @@ -36,8 +38,8 @@ def __init__(self, title): if w > max_w: max_w = w screen = pygame.display.set_mode( - (BORDER_W + max_w + BORDER_W, - BORDER_H + max_h + BORDER_H) + SIZE, + SURFACE_OPTS ) log('calculated window size ({width}, {height}) from {count} assets'.format( diff --git a/controls/base.py b/controls/base.py index bd97b50..267e262 100644 --- a/controls/base.py +++ b/controls/base.py @@ -24,12 +24,11 @@ 'A': 0x8000 } -EXTRA_BOTTOMS = { - 'L3': 0x08, - 'R3': 0x04, +EXTRA_BUTTONS = { + 'R3': 0x40, + 'L3': 0x80, } - def button_mask(controls): """ Given controls, a `Controls` instance, compute the bitmask of all pressed @@ -44,9 +43,11 @@ def button_mask(controls): def extra_button_mask(controls): mask = 0 - for button_name, button_mask in EXTRA_BOTTOMS.iteritems(): + for button_name, button_mask in EXTRA_BUTTONS.iteritems(): if controls.invoke(button_name.lower()): mask |= button_mask + #log('wow special button {b} clicked and invoked! new mask {m}'.format( + #b=button_name, m=mask), 'FUGGIN BUTTANS') return mask diff --git a/controls/keyboard.py b/controls/keyboard.py index 3a8e615..4cb9f05 100644 --- a/controls/keyboard.py +++ b/controls/keyboard.py @@ -43,7 +43,7 @@ def a(self): return self.key(K_h) or self.key(K_RETURN) def b(self): - return self.key(K_j) + return self.key(K_j) or self.key(K_BACKSPACE) def x(self): return self.key(K_k) or self.key(K_SPACE) @@ -83,14 +83,14 @@ def home(self): return False def minus(self): - return False + return self.key(K_MINUS) def plus(self): - return False + return self.key(K_EQUALS) # joystick presses def r3(self): - return False + return self.key(K_e) def l3(self): return False diff --git a/controls/mouse.py b/controls/mouse.py index 3b4e8e3..b9bd42c 100644 --- a/controls/mouse.py +++ b/controls/mouse.py @@ -5,6 +5,7 @@ from util import log # TODO switch to absolute paths +JOYSTICK_MIN = 0.2 TOGGLE_LOCK_KEY = K_BACKQUOTE # lag high-magnitude movements another frame ENABLE_LAG = True @@ -55,9 +56,9 @@ def get(self): gets the joystick pairs """ dx, dy = pygame.mouse.get_rel() - if abs(dx) > 0 or abs(dy) > 0: - log("pygame mouse movement: ({dx}, {dy})".format(dx=dx, dy=dy), - 'MOUSE') + #if abs(dx) > 0 or abs(dy) > 0: + #log("pygame mouse movement: ({dx}, {dy})".format(dx=dx, dy=dy), + #'MOUSE') return (self.convert_x_axis(dx), self.convert_y_axis(dy)) @@ -92,7 +93,7 @@ def convert_axis(self, d): float(abs(d)), # value 0.0, # old min float(self.get_max()), # old max - 0.06, # new min -- escape deadzone + max(JOYSTICK_MIN, 0.0), # new min -- escape deadzone 1.0 # new max ) @@ -128,6 +129,9 @@ def handle_event(self, event): self.sensitivity += self.sensitivity_incr log('[+] set sensitivity to {s}, delta_max is {max} (lower = more sensitive)'.format(s=self.sensitivity, max=self.get_max()), 'MOUSE') + def is_locked(self): + return pygame.event.get_grab() + def lock(self): """ locks the mouse to the window, enabling accurate readings @@ -158,10 +162,13 @@ def handle_event(self, event): self.mouse.handle_event(event) def r(self): - return pygame.mouse.get_pressed()[2] + if self.mouse.is_locked(): + return pygame.mouse.get_pressed()[2] def zr(self): - return pygame.mouse.get_pressed()[0] + if self.mouse.is_locked(): + return pygame.mouse.get_pressed()[0] def r3(self): - return pygame.mouse.get_pressed()[1] + if self.mouse.is_locked(): + return pygame.mouse.get_pressed()[1] diff --git a/controls/wireless.py b/controls/wireless.py index 9fa5315..4ca8ec1 100644 --- a/controls/wireless.py +++ b/controls/wireless.py @@ -70,10 +70,11 @@ def a(self): # extra buttons - joystick presses def r3(self): - return False + return self.joy.get_button(10) def l3(self): - return False + return self.joy.get_button(9) + # the 'TV' button. Unsure of utility of implementing this. def tv(self): diff --git a/drc-sim.py b/drc-sim.py index f6350f5..727b5dd 100644 --- a/drc-sim.py +++ b/drc-sim.py @@ -6,6 +6,8 @@ import pygame import time from H264Decoder import H264Decoder +from util import log +# from IPython.Shell import IPShellEmbed pygame.init() pygame.display.set_mode([854, 480], pygame.RESIZABLE) @@ -125,14 +127,14 @@ def update(s, packet): if h.packet_type == 0: seq_ok = s.update_seq_id(h.seq_id) if not seq_ok: - print 'astrm bad seq_id' + log('astrm bad seq_id') if h.fmt != 1 or h.channel != 0: raise Exception('astrm currently only handles 48kHz PCM stereo') if len(packet) != 8 + h.payload_size: raise Exception('astrm bad payload_size') if h.vibrate: - print '*vibrate*' + log('*vibrate*') s.pa_ring[s.pa_rpos] = array.array('H', packet[8:]) s.pa_rpos += 1 @@ -306,18 +308,18 @@ def cmd0_5_6(s, h, packet): s.send_response_cmd0(h, r) def cmd0(s, h, packet): - print 'CMD0:%i:%i' % (h.id_primary, h.id_secondary) + log('CMD0:%i:%i' % (h.id_primary, h.id_secondary)) if h.id_primary not in s.cmd0_handlers or h.id_secondary not in s.cmd0_handlers[h.id_primary]: - print 'unhandled', packet.encode('hex') + log('unhandled', packet.encode('hex')) return s.cmd0_handlers[h.id_primary][h.id_secondary](h, packet) def cmd1(s, h, packet): - print 'CMD1', packet[8:].encode('hex') + log('CMD1', packet[8:].encode('hex')) s.send_response(h, '\x00\x16\x00\x19\x9e\x00\x00\x00\x40\x00\x40\x00\x00\x00\x01\xff') def cmd2(s, h, packet): - print 'TIME base {:04x} seconds {:08x}'.format(h.JDN_base, h.seconds) + log('TIME base {:04x} seconds {:08x}'.format(h.JDN_base, h.seconds)) s.send_response(h) def ack(s, h): @@ -356,13 +358,13 @@ def update(s, packet): h = s.header.parse(packet) # don't track acks from the console for now if h.packet_type in (s.PT_REQ, s.PT_RESP): - #print 'CMD', packet.encode('hex') + #log('CMD', packet.encode('hex')) s.ack(h) s.cmd_handlers[h.cmd_id](h, packet) class ServiceMSG(ServiceBase): def update(s, packet): - print 'MSG', packet.encode('hex') + log('MSG', packet.encode('hex')) class ServiceNOP(ServiceBase): def update(s, packet): @@ -454,7 +456,7 @@ def scale_stick(OldValue, OldMin, OldMax, NewMin, NewMax): scaled = scale_stick(orig, -1, 1, 900, 3200) elif i in (1, 4): scaled = scale_stick(orig, 1, -1, 900, 3200) - #print '%04i %04i %f' % (i, scaled, orig) + #log('%04i %04i %f' % (i, scaled, orig)) stick_mapping = { 0 : 0, 1 : 1, 3 : 2, 4 : 3 } report[3 + stick_mapping[i]] = scaled report[1] = (button_bits >> 8) | ((button_bits & 0xff) << 8) @@ -473,7 +475,6 @@ def scale_stick(OldValue, OldMin, OldMax, NewMin, NewMax): x = scale_stick(point[0], 0, screen_x, 200, 3800) y = scale_stick(point[1], 0, screen_y, 200, 3800) z1 = 2000 - for i in xrange(10): report[18 + i * 2 + 0] = 0x80 | x report[18 + i * 2 + 1] = 0x80 | y @@ -501,12 +502,13 @@ def scale_stick(OldValue, OldMin, OldMax, NewMin, NewMax): # i didn't want to move this up because I'm # worried its location here is important. TODO # think about it. + # special extra buttons for i in xrange(9,11): if joystick.get_button(i): report[40] |= button_mapping[i] report[0x3f] = 0xe000 - #print report.tostring().encode('hex') + #log(report.tostring().encode('hex')) HID_S.sendto(report, ('192.168.1.10', PORT_HID)) hid_seq_id = hid_seq_id + 1 % 65534 # prevent overflow diff --git a/services.py b/services.py index 04af1c7..2a6e482 100644 --- a/services.py +++ b/services.py @@ -3,8 +3,16 @@ import socket import pyaudio import pygame -from util import log, GAMEPAD_DIM +from util import ( + log, + GAMEPAD_DIM, + ) +from H264Decoder import H264Decoder + +#def log(foo, name='bar'): + #pass +FALLBACK_IP = '127.0.0.1' def service_addend(ip): if int(ip.split('.')[3]) == 10: @@ -22,10 +30,10 @@ def udp_service(ip, port): log("couldn't bind {ip}:{port}".format(ip=ip, port=actual_port), "NETWORK") log("trying {ip}:{port} instead".format( - ip='0.0.0.0', + ip=FALLBACK_IP, port=actual_port), "NETWORK") - sock.bind(('0.0.0.0', actual_port)) + sock.bind((FALLBACK_IP, actual_port)) return sock PORT_MSG = 50010 @@ -53,8 +61,16 @@ def update_seq_id(s, seq_id): ret = True if s.seq_id_expect is None: s.seq_id_expect = seq_id - elif s.seq_id_expect != seq_id: + + if s.seq_id_expect != seq_id: + #log("[BAD] Got seq_id of " + str(seq_id) + ", expected " + str(s.seq_id_expect), + #s.__class__.__name__) ret = False + else: + pass + #log("[good :)] Got seq_id of " + str(seq_id) + ", expected " + str(s.seq_id_expect), + #s.__class__.__name__) + s.seq_id_expect = (seq_id + 1) & 0x3ff return ret @@ -140,7 +156,7 @@ def update(s, packet): if h.vibrate: # TODO: play a tone! :) - log('******* vibrate ********, ''ASTRM') + log('******* vibrate ********', 'ASTRM') s.pa_ring[s.pa_rpos] = array.array('H', packet[8:]) s.pa_rpos += 1 @@ -163,8 +179,9 @@ def update(s, packet): class ServiceVSTRM(ServiceBase): - def __init__(s, decoder): + def __init__(s, decoder, msg_socket=None): super(ServiceVSTRM, s).__init__() + s.MSG_S = msg_socket or MSG_S s.decoder = decoder s.header = construct.BitStruct('VSTRMHeader', construct.Nibble('magic'), @@ -249,7 +266,7 @@ def update(s, packet): s.is_streaming = True else: # request a new IDR frame - MSG_S.sendto('\1\0\0\0', ('192.168.1.10', PORT_MSG)) + s.MSG_S.sendto('\1\0\0\0', ('192.168.1.10', PORT_MSG)) return s.frame.fromstring(packet[16:]) @@ -323,12 +340,12 @@ def cmd0_5_6(s, h, packet): def cmd0(s, h, packet): log('CMD0:%i:%i' % (h.id_primary, h.id_secondary), 'CMD') if h.id_primary not in s.cmd0_handlers or h.id_secondary not in s.cmd0_handlers[h.id_primary]: - log('unhandled', packet.encode('hex'), 'CMD') + log('unhandled {packet}'.format(packet=packet.encode('hex')), 'CMD') return s.cmd0_handlers[h.id_primary][h.id_secondary](h, packet) def cmd1(s, h, packet): - log('CMD1', packet[8:].encode('hex'), 'CMD') + log('CMD1: {packet}'.format(packet=packet[8:].encode('hex')), 'CMD') s.send_response(h, '\x00\x16\x00\x19\x9e\x00\x00\x00\x40\x00\x40\x00\x00\x00\x01\xff') def cmd2(s, h, packet): diff --git a/simulator.py b/simulator.py index 03dce39..b465012 100644 --- a/simulator.py +++ b/simulator.py @@ -38,6 +38,7 @@ class Simulator(App): def __init__(self): super(Simulator, self).__init__("DRC Simulator") self.vid_offset = (409, 247) + self.vid_offset = (0, 0) self.vid_frame = pygame.Surface(GAMEPAD_DIM) self.vid_rect = pygame.Rect(self.vid_offset, GAMEPAD_DIM) self.bg = ASSET_DICT['gamepad'] @@ -59,14 +60,14 @@ def __init__(self): self.decoder = H264Decoder( GAMEPAD_DIM, - GAMEPAD_DIM, + GAMEPAD_DIM) # pygame.display.get_surface().get_size(), - self.vid_frame) + #self.vid_frame) self.service_handlers = { services.MSG_S: services.ServiceMSG(), services.VID_S: services.ServiceVSTRM(self.decoder), - services.AUD_S: services.ServiceASTRM(), + # services.AUD_S: services.ServiceASTRM(), services.CMD_S: services.ServiceCMD() } @@ -120,11 +121,11 @@ def hid_snd(self): if (pygame.mouse.get_pressed()[0] and not pygame.event.get_grab() and # mouse was over the "screen" when clicked - self.vid_rect.collidepoint(self.mouse.get_pos())): + self.vid_rect.collidepoint(pygame.mouse.get_pos())): point = pygame.mouse.get_pos() in_x = point[0] - self.vid_offset[0] in_y = point[1] - self.vid_offset[1] - log('touchscreen click at {x, y}'.format(x, y), 'HID') + log('touchscreen click at ({x}, {y})'.format(x=in_x, y=in_y), 'HID') x = int(scale(point[0], self.vid_rect.left, self.vid_rect.right, 200, 3800)) y = int(scale(point[1], self.vid_rect.top, self.vid_rect.bottom, 200, 3800)) z1 = 2000 @@ -157,6 +158,7 @@ def hid_snd(self): # worried its location here is important. TODO # think about it. report[40] |= extra_button_mask(self.ctlr) + # log('report 40 = {n}'.format(n=report[40])) report[0x3f] = 0xe000 #print report.tostring().encode('hex') @@ -169,16 +171,19 @@ def handle_event(self, event): if event.type == pygame.KEYDOWN: if event.key == pygame.K_BACKSLASH: # TODO: What message is this? + # it has to do with video service and expected seq id failures + log('SENDING \1\0\0\0 ON YOUR COMMAND!') services.MSG_S.sendto('\1\0\0\0', ('192.168.1.10', services.PORT_MSG)) elif event.type == EVT_SEND_HID: self.hid_snd() def render(self): - self.screen.fill(15) - self.screen.blit(self.bg, self.offset) - self.vis.render(self.l_stick_prev, self.r_stick_prev) - self.screen.blit(self.vid_frame, self.vid_offset) + #self.screen.fill(15) + #self.screen.blit(self.bg, self.offset) + #self.vis.render(self.l_stick_prev, self.r_stick_prev) + #self.screen.blit(self.vid_frame, self.vid_offset) + #pygame.display.flip() # this stuff came from the default event loop in drc-sim.py # TODO see if I'm mis-managing it somehow such that video never works @@ -197,4 +202,6 @@ def clean_up(self): if __name__ == '__main__': app = Simulator() - app.main() + while True: + app.handle_all_events() + app.render() From be03a58396c104624bef8e9195abe537842fa118 Mon Sep 17 00:00:00 2001 From: Jake Teton-Landis Date: Wed, 24 Jun 2015 23:36:09 -0700 Subject: [PATCH 2/3] a few mouse tweaks --- controls/mouse.py | 11 ++++++++--- simulator.py | 19 +++++++++++++++++++ 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/controls/mouse.py b/controls/mouse.py index b9bd42c..5655881 100644 --- a/controls/mouse.py +++ b/controls/mouse.py @@ -5,12 +5,14 @@ from util import log # TODO switch to absolute paths -JOYSTICK_MIN = 0.2 +JOYSTICK_MIN = 0.17 TOGGLE_LOCK_KEY = K_BACKQUOTE # lag high-magnitude movements another frame ENABLE_LAG = True -ENABLE_DOUBLE_LAG = True +ENABLE_DOUBLE_LAG = False MAX_MOVEMENT = 50.0 +LAG_WHEN_LESS_THAN = 0.1 +LAG_WHEN_GREATER_THAN = 0.8 def lag(final, prev): @@ -22,7 +24,10 @@ def lag(final, prev): # TO DO: switch joystick magnitude measuring to polar-coordiate vectors # and mreasure magnitude of both axes at once - if ENABLE_LAG and abs(prev) > 0.8 > abs(final): + if ENABLE_LAG and ( + abs(prev) > LAG_WHEN_GREATER_THAN + and abs(final) < LAG_WHEN_LESS_THAN): + # swap em. we'll play this frame again to smooth the falloff # and we get a bonus frame of high-magnitude movement log('lagged {prev} again instead of {final}'.format(prev=prev, final=final), 'MOUSE') diff --git a/simulator.py b/simulator.py index b465012..383158d 100644 --- a/simulator.py +++ b/simulator.py @@ -108,6 +108,25 @@ def hid_snd(self): report[3 + 3] = wiiu_axis(r_vert) report[1] = (button_bits >> 8) | ((button_bits & 0xff) << 8) + +# 0 u16 seq_id; +# 1 u16 buttons; // see ButtonsMask +# 2 u8 power_status; // see PowerStatusMask +# u8 battery_charge; +# 3 u16 left_stick_x; +# 4 u16 left_stick_y; +# 5 u16 right_stick_x; +# 6 u16 right_stick_y; +# 7 u8 audio_volume; +# 7.5 AccelerometerData accelerometer; // FUCK +# GyroscopeData gyro; +# MagnetData magnet; +# TouchscreenData touchscreen; +# char unk0[4]; +# u8 extra_buttons; // see ExtraButtonsMask +# char unk1[46]; +# u8 fw_version_neg; // ~fw_version + # touchpanel crap @ 36 - 76 byte_18 = 0 From 53d670317898e44fe65d78351931e46496026b57 Mon Sep 17 00:00:00 2001 From: Jake Teton-Landis Date: Thu, 25 Jun 2015 01:34:44 -0700 Subject: [PATCH 3/3] start working on construct-based HID service --- InputData.py | 57 +++++++++++---- controls/base.py | 53 +++++++++++++- data_tester.py | 184 +++++++++++++++++++++++++++++++++++++++++++++++ simulator.py | 12 +--- 4 files changed, 278 insertions(+), 28 deletions(-) create mode 100644 data_tester.py diff --git a/InputData.py b/InputData.py index 5792092..eceb138 100644 --- a/InputData.py +++ b/InputData.py @@ -6,21 +6,30 @@ TODO tests """ from construct import ( - ULint8, - ULint16, - Array, - Struct, - BitStruct, - BitField, - Byte, - SLInt16, - Padding, - Rename - ) + ULInt8, + ULInt16, + SLInt16, + UBInt8, + UBInt16, + SBInt16, + Array, + Struct, + BitStruct, + BitField, + Byte, + Padding, + Rename, + Flag +) -u16 = ULint16 -s16 = SLInt16 -u8 = ULint8 +## turn on to make little-endian values +# u16 = ULInt16 +# s16 = SLInt16 +# u8 = ULInt8 + +u16 = UBInt16 +s16 = SBInt16 +u8 = UBInt8 AccelerometerData = Struct("AccelerometerData", s16("x_accel"), @@ -52,6 +61,24 @@ TouchscreenData = Struct("TouchscreenData", Array(10, Point)) +Buttons = BitStruct("Buttons", + Flag("a"), + Flag("b"), + Flag("x"), + Flag("y"), + Flag("left"), + Flag("right"), + Flag("up"), + Flag("down"), + Flag("zl"), + Flag("zr"), + Flag("l"), + Flag("r"), + Flag("plus"), + Flag("minus"), + Flag("home"), + Flag("sync"),) + # the real shebang InputData = Struct("InputData", u16("seq_id"), @@ -69,4 +96,4 @@ Array(4, Byte("unkown0")), u8("extra_buttons"), Array(46, Byte("unknown1")), - u8("fw_version_neg")) # ~fw_version + u8("fw_version_neg")) # u~fw_version diff --git a/controls/base.py b/controls/base.py index 267e262..f723434 100644 --- a/controls/base.py +++ b/controls/base.py @@ -29,6 +29,31 @@ 'L3': 0x80, } + +def inspect_mask(integer, bits=8, byte=8): + """ + print a value as a bits. printing in byte-sized blocks + + >>> bits(255) + '11111111' + + >>> bits(256) + '00000001 00000000' + """ + as_bin = bin(integer)[2:] + if len(as_bin) < bits: + missing_bits = bits - len(as_bin) + as_bin = '0' * missing_bits + as_bin + + return ' '.join(chunks(as_bin, byte)) + + +def chunks(l, n): + """Yield successive n-sized chunks from l.""" + for i in xrange(0, len(l), n): + yield l[i:i+n] + + def button_mask(controls): """ Given controls, a `Controls` instance, compute the bitmask of all pressed @@ -155,11 +180,21 @@ def accelerometer(self): def gyroscope(self): return (0, 0, 0) + """ + should return a tuple (x, y, force) of a touch location. + force should be a float between 0 and 1. + + return None if there is no touch. + """ + def touch(self): + return None + def handle_event(self, event): """ override if you wish to handle events with your controller - """ - pass + """ pass + + class MethodMissing(object): @@ -212,3 +247,17 @@ def scale(OldValue, OldMin, OldMax, NewMin, NewMax): def add(point_a, point_b): return (point_a[0] + point_b[0], point_a[1] + point_b[1]) + + +def wiiu_axis(orig): + """ + given a joystick axis motion, scale into Wii U space + """ + if abs(orig) < 0.0001: + # unsure why this starts as this value 0x800 + return 0x800 + return int(scale(orig, -1, 1, 900, 3200)) + +### +# TODO: move control value constructors to new file +### diff --git a/data_tester.py b/data_tester.py new file mode 100644 index 0000000..61affc8 --- /dev/null +++ b/data_tester.py @@ -0,0 +1,184 @@ +import array +from app import App +import InputData +from controls.mouse import KeyboardMouse +from controls.base import ( + Controls, button_mask, inspect_mask, BUTTONS, scale, + extra_button_mask, + wiiu_axis +) +from construct import ( + Container +) + + +def button_container(controls): + c = Container() + for button in BUTTONS.iterkeys(): + setattr(c, button.lower(), controls.invoke(button.lower())) + return c + + +def build_controls(controls): + return InputData.Buttons.build(button_container(controls)) + + +class DummyControls(Controls): + def a(self): + return True + + def zl(self): + return True + + def l(self): + return True + + +def int_of_bytes(bytestring): + return int(bytestring.encode('hex'), 16) + + +controller = DummyControls() + +reg = inspect_mask(button_mask(controller)) +newer = inspect_mask(int_of_bytes(build_controls(controller))) +print "{0} regular".format(reg) +print "{0} construct".format(newer) + + +def create_response(ctlr, seq_id=0): + report = array.array('H', '\0\0' * 0x40) + + # 16bit LE @ 0 seq_id + # seems to be ignored + report[0] = seq_id() + # 16bit @ 2 + button_bits = button_mask(ctlr) + + # save sticks around for rendering joystick fiddling + left_stick = ctlr.left_stick() + right_stick = ctlr.right_stick() + + # bada bing bada boom engineer sticks + l_horiz, l_vert = left_stick + r_horiz, r_vert = right_stick + report[3 + 0] = wiiu_axis(l_horiz) + report[3 + 1] = wiiu_axis(l_vert) + report[3 + 2] = wiiu_axis(r_horiz) + report[3 + 3] = wiiu_axis(r_vert) + + report[1] = (button_bits >> 8) | ((button_bits & 0xff) << 8) + + # 0 u16 seq_id; + # 1 u16 buttons; // see ButtonsMask + # 2 u8 power_status; // see PowerStatusMask + # u8 battery_charge; + # 3 u16 left_stick_x; + # 4 u16 left_stick_y; + # 5 u16 right_stick_x; + # 6 u16 right_stick_y; + # 7 u8 audio_volume; + # 7.5 AccelerometerData accelerometer; // FUCK (this is why looking into structs) + # GyroscopeData gyro; + # MagnetData magnet; + # TouchscreenData touchscreen; + # char unk0[4]; + # u8 extra_buttons; // see ExtraButtonsMask + # char unk1[46]; + # u8 fw_version_neg; // ~fw_version + + + # touchpanel crap @ 36 - 76 + byte_18 = 0 + byte_17 = 3 + byte_9b8 = 0 + byte_9fd = 6 + umi_fw_rev = 0x40 + byte_9fb = 0 + byte_19 = 2 + # TODO use controller for thisj + touch = ctlr.touch() + if touch is not None: + in_x, in_y = touch + x = int(scale(in_x, 0, 854, 200, 3800)) + y = int(scale(in_y, 0, 480, 200, 3800)) + z1 = 2000 + + for i in xrange(10): + report[18 + i * 2 + 0] = 0x80 | x + report[18 + i * 2 + 1] = 0x80 | y + + report[18 + 0 * 2 + 0] |= ((z1 >> 0) & 7) << 12 + report[18 + 0 * 2 + 1] |= ((z1 >> 3) & 7) << 12 + report[18 + 1 * 2 + 0] |= ((z1 >> 6) & 7) << 12 + report[18 + 1 * 2 + 1] |= ((z1 >> 9) & 7) << 12 + + report[18 + 3 * 2 + 1] |= ((byte_17 >> 0) & 7) << 12 + report[18 + 4 * 2 + 0] |= ((byte_17 >> 3) & 7) << 12 + report[18 + 4 * 2 + 1] |= ((byte_17 >> 6) & 3) << 12 + + report[18 + 5 * 2 + 0] |= ((byte_9fd >> 0) & 7) << 12 + report[18 + 5 * 2 + 1] |= ((byte_9fd >> 3) & 7) << 12 + report[18 + 6 * 2 + 0] |= ((byte_9fd >> 6) & 3) << 12 + + report[18 + 7 * 2 + 0] |= ((umi_fw_rev >> 4) & 7) << 12 + + # not my comment. spooky. + # TODO checkout what's up with | 4 + report[18 + 9 * 2 + 1] |= ((byte_19 & 2) | 4) << 12 + + # 8bit @ 80 + # i didn't want to move this up because I'm + # worried its location here is important. TODO + # think about it. + report[40] |= extra_button_mask(ctlr) + # log('report 40 = {n}'.format(n=report[40])) + + report[0x3f] = 0xe000 + return report + + +def accel_container(ctlr): + return Container( + x_accel=0, + y_accel=0, + z_accel=0) + + +def gyro_container(ctlr): + return Container( + roll=0, + yaw=0, + pitch=0, + ) + + +def touch_container(ctlr): + in_x, in_y = ctlr.touch() + x = int(scale(in_x, 0, 854, 200, 3800)) + y = int(scale(in_y, 0, 480, 200, 3800)) + raise NotImplementedError # got bored + + + +def controller_container(ctlr, seq_id): + l_x, l_y = ctlr.left_stick() + r_x, r_y, = ctlr.right_stick() + return Container( + seq_id=seq_id, + buttons=button_container(ctlr), + power_status=0, + left_stick_x=wiiu_axis(l_x), + left_stick_y=wiiu_axis(l_y), + right_stick_x=wiiu_axis(r_x), + right_stick_y=wiiu_axis(r_y), + audio_volume=0, + accelerometer=accel_container(ctlr), + gyro=gyro_container(ctlr), + magnet=None, + touchscreen=touch_container(ctlr), + unknown0=0, + extra_buttons=extra_buttons_container(ctlr), + unknown1=0, + fw_version_neg=0xe000 + ) diff --git a/simulator.py b/simulator.py index 383158d..cda89b6 100644 --- a/simulator.py +++ b/simulator.py @@ -8,7 +8,7 @@ from H264Decoder import H264Decoder from app import App from controls.base import ( - button_mask, extra_button_mask, UnionController, scale + button_mask, extra_button_mask, UnionController, scale, wiiu_axis ) from controls.wireless import ProMap360 from controls.mouse import KeyboardMouse @@ -21,16 +21,6 @@ EVT_SEND_HID = pygame.USEREVENT -def wiiu_axis(orig): - """ - given a joystick axis motion, scale into Wii U space - """ - if abs(orig) < 0.0001: - # unsure why this starts as this value 0x800 - return 0x800 - return int(scale(orig, -1, 1, 900, 3200)) - - class Simulator(App): """ customized DRC simulator using drc-sim-keyboard clases and following pep8