From 508b781b5a41586774cee958676569add3c600b8 Mon Sep 17 00:00:00 2001 From: Sergiotarxz Date: Tue, 14 Mar 2023 23:09:05 +0100 Subject: [PATCH] Adding functional video emulation, still not scaled to the canvas size. --- lib/MSGBA/Web/Controller/WS.pm | 152 ++++++++++++++------------------- templates/static/index.html.ep | 74 ++++++++++++++-- 2 files changed, 129 insertions(+), 97 deletions(-) diff --git a/lib/MSGBA/Web/Controller/WS.pm b/lib/MSGBA/Web/Controller/WS.pm index 3b1cf82..eda782f 100644 --- a/lib/MSGBA/Web/Controller/WS.pm +++ b/lib/MSGBA/Web/Controller/WS.pm @@ -6,105 +6,77 @@ use strict; use warnings; use Mojo::Base 'Mojolicious::Controller', -signatures; +use Mojo::IOLoop; sub proxy { my $self = shift; - my $conn = MSGBA::Web::Controller::WS::Connection->new(ws => $self, config => $self->config); - return $conn->handle(); + $self->tx->max_websocket_size(100000000); + + handle($self); } -package MSGBA::Web::Controller::WS::Connection { - use v5.34.1; +sub _handle_on_connect { + my $stream = shift; + my $ws = shift; + $ws->on('binary', sub { + my ($ws, $bytes) = @_; + if (!$bytes) { + warn "Received empty message"; + return; + } + $stream->write($bytes); + }); +} - use strict; - use warnings; +sub handle { + my $ws = shift; + _build_msgba_connection($ws); +} - use Moo; - use IO::Socket::UNIX; - - - has config => ( - is => 'ro', - required => 1, - ); - - has ws => ( - is => 'ro', - required => 1, - ); - - has msgba_connection => ( - is => 'rw', - required => 0, - ); - - sub handle { - my $self = shift; - my $ws = $self->ws; - $self->_build_msgba_connection; - $ws->on('binary', sub { - my ($ws, $bytes) = @_; - if (!$bytes) { - warn "Received empty message"; +my $tx_buffer = {}; +sub _build_msgba_connection { + my $ws = shift; + my $config = $ws->config; + Mojo::IOLoop->client(path => $config->{domain_socket}, sub ($loop, $err, $stream) { + _handle_on_connect($stream, $ws); + $tx_buffer->{$ws->tx} .= ''; + $stream->on(read => sub ($stream, $bytes) { + if (!defined $ws->tx) { + $stream->close(); return; } - say "Received message"; - say unpack 'H*', $bytes; - open my $fh, '<', \$bytes; - read $fh, my $id, 8; - read $fh, my $size, 8; - $size = unpack 'Q>', $size; - close $fh; - $self->msgba_connection->print($bytes); + $tx_buffer->{$ws->tx} //= ''; + $tx_buffer->{$ws->tx} .= $bytes; + while (1) { + open my $fh, '<', \$tx_buffer->{$ws->tx}; + if (length $tx_buffer->{$ws->tx} < 16) { + return; + } + read $fh, my $id, 8; + read $fh, my $size, 8; + my $size_num = unpack 'Q>', $size; + if (length $tx_buffer->{$ws->tx} < 16 + $size_num) { + return; + } + read $fh, my $raw_data, $size_num; + $tx_buffer->{$ws->tx} = substr $tx_buffer->{$ws->tx}, 16 + $size_num, length $tx_buffer->{$ws->tx}; + handle_packet($ws, { + id => $id, + size => $size, + raw_data => $raw_data, + }); + + close $fh; + } + }); - return; - #while (my $packet = $self->read_packet) { - # $self->handle_packet($packet); - #} - #close $self->msgba_connection; - #$ws->closed; - } - - sub handle_packet { - my $self = shift; - my $packet = shift; - my $ws = $self->ws; - my ($id, $size, $raw_data) = $packet->@{qw/id size raw_data/}; - $ws->send({binary => "${id}${size}${raw_data}"}); - } - - sub read_packet { - my $self = shift; - my $fh = $self->msgba_connection; - my $ws = $self->ws; - my ($id, $size, $raw_data); - if ((read $fh, $id, 8) != 8) { - return; - } - if ((read $fh, $size, 8) != 8) { - return; - } - my $size_num = unpack 'Q>', $size; - if ((read $fh, $raw_data, $size_num) != $size_num) { - return; - } - return { - id => $id, - size => $size, - raw_data => $raw_data, - }; - } - - sub _build_msgba_connection { - my $self = shift; - my $config = $self->config; - my $msgba_connection = IO::Socket::UNIX->new( - Type => SOCK_STREAM(), - Peer => $config->{domain_socket}, - ) or die "@{[$config->{domain_socket}]}: $!"; - $self->msgba_connection($msgba_connection); - } - -}; + }); +} +sub handle_packet { + my $ws = shift; + my $packet = shift; + my ($id, $size, $raw_data) = $packet->@{qw/id size raw_data/}; + $ws->send({binary => "${id}${size}${raw_data}"}); +} 1; diff --git a/templates/static/index.html.ep b/templates/static/index.html.ep index 0039fb4..6a409db 100644 --- a/templates/static/index.html.ep +++ b/templates/static/index.html.ep @@ -48,7 +48,6 @@ const PACKET_ID_HELLO = 0n; // Send is sent from server, so it is get here but whatever, it is the bad naming of the protocol. const PACKET_ID_SEND_FRAME = 1n; - let websocket = null; let is_little_endian = true; (()=>{ let buf = new ArrayBuffer(4); @@ -79,7 +78,8 @@ return final_array; } - start_emulation_button.addEventListener('click', () => { + start_emulation_button.addEventListener('click', (e) => { + e.preventDefault(); if (rom_file_selector.files.length == 0) { alert('There is no rom still'); return; @@ -94,15 +94,69 @@ savestate_file.arrayBuffer().then((savestate_buffer) => { const rom_array = new Uint8Array(rom_buffer); const savestate_array = new Uint8Array(savestate_buffer); - websocket = new WebSocket(`ws://${location.host}/ws`); - websocket.addEventListener('open', () => { + const websocket = new WebSocket(`ws://localhost:3000/ws`); + websocket.binaryType = 'arraybuffer'; + websocket.onclose = (message) => console.log('CLOSE', message); + websocket.onopen = () => { console.log('Opened websocket.'); sendHello(websocket, rom_array, savestate_array); + }; + websocket.addEventListener('message', (e) => { + const buffer = e.data; + let packet_u8 = new Uint8Array(buffer); + const id = byteArrayToU64BigEndian(packet_u8.slice(0, 8)); + packet_u8 = packet_u8.slice(8, packet_u8.length); + const size = byteArrayToU64BigEndian(packet_u8.slice(0, 8)); + const raw_data = packet_u8.slice(8, packet_u8.length); + switch (id) { + case PACKET_ID_SEND_FRAME: + handleSendFrame(raw_data); + break; + default: + console.log(`Received unknown packet ${id}`); + } }); }); }); }); + function handleSendFrame(raw_data) { + let data = raw_data; + const stride = byteArrayToU32BigEndian(data.slice(0, 4)); + data = data.slice(4, data.length); + const output_buffer_size = byteArrayToU32BigEndian(data.slice(0, 8)); + data = data.slice(8, data.length); + console.log(data.length / 4 / MIN_WIDTH); + const img_data = ctx.createImageData(MIN_WIDTH, MIN_HEIGHT); + const img_data_u8 = new Uint8Array(img_data.data.buffer); + for (let i = 0; i x.toString(16).padStart(2, '0')) + .join(''); } function resizeEmulator(canvas) {