Adding functional video emulation, still not scaled to the canvas size.
This commit is contained in:
parent
5b67d106c0
commit
508b781b5a
@ -6,105 +6,77 @@ use strict;
|
|||||||
use warnings;
|
use warnings;
|
||||||
|
|
||||||
use Mojo::Base 'Mojolicious::Controller', -signatures;
|
use Mojo::Base 'Mojolicious::Controller', -signatures;
|
||||||
|
use Mojo::IOLoop;
|
||||||
|
|
||||||
sub proxy {
|
sub proxy {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
my $conn = MSGBA::Web::Controller::WS::Connection->new(ws => $self, config => $self->config);
|
$self->tx->max_websocket_size(100000000);
|
||||||
return $conn->handle();
|
|
||||||
|
handle($self);
|
||||||
}
|
}
|
||||||
|
|
||||||
package MSGBA::Web::Controller::WS::Connection {
|
sub _handle_on_connect {
|
||||||
use v5.34.1;
|
my $stream = shift;
|
||||||
|
my $ws = shift;
|
||||||
use strict;
|
|
||||||
use warnings;
|
|
||||||
|
|
||||||
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 {
|
$ws->on('binary', sub {
|
||||||
my ($ws, $bytes) = @_;
|
my ($ws, $bytes) = @_;
|
||||||
if (!$bytes) {
|
if (!$bytes) {
|
||||||
warn "Received empty message";
|
warn "Received empty message";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
say "Received message";
|
$stream->write($bytes);
|
||||||
say unpack 'H*', $bytes;
|
});
|
||||||
open my $fh, '<', \$bytes;
|
}
|
||||||
|
|
||||||
|
sub handle {
|
||||||
|
my $ws = shift;
|
||||||
|
_build_msgba_connection($ws);
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
$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 $id, 8;
|
||||||
read $fh, my $size, 8;
|
read $fh, my $size, 8;
|
||||||
$size = unpack 'Q>', $size;
|
|
||||||
close $fh;
|
|
||||||
$self->msgba_connection->print($bytes);
|
|
||||||
});
|
|
||||||
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;
|
my $size_num = unpack 'Q>', $size;
|
||||||
if ((read $fh, $raw_data, $size_num) != $size_num) {
|
if (length $tx_buffer->{$ws->tx} < 16 + $size_num) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
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,
|
id => $id,
|
||||||
size => $size,
|
size => $size,
|
||||||
raw_data => $raw_data,
|
raw_data => $raw_data,
|
||||||
};
|
});
|
||||||
|
|
||||||
|
close $fh;
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
1;
|
||||||
|
@ -48,7 +48,6 @@
|
|||||||
const PACKET_ID_HELLO = 0n;
|
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.
|
// 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;
|
const PACKET_ID_SEND_FRAME = 1n;
|
||||||
let websocket = null;
|
|
||||||
let is_little_endian = true;
|
let is_little_endian = true;
|
||||||
(()=>{
|
(()=>{
|
||||||
let buf = new ArrayBuffer(4);
|
let buf = new ArrayBuffer(4);
|
||||||
@ -79,7 +78,8 @@
|
|||||||
return final_array;
|
return final_array;
|
||||||
}
|
}
|
||||||
|
|
||||||
start_emulation_button.addEventListener('click', () => {
|
start_emulation_button.addEventListener('click', (e) => {
|
||||||
|
e.preventDefault();
|
||||||
if (rom_file_selector.files.length == 0) {
|
if (rom_file_selector.files.length == 0) {
|
||||||
alert('There is no rom still');
|
alert('There is no rom still');
|
||||||
return;
|
return;
|
||||||
@ -94,15 +94,69 @@
|
|||||||
savestate_file.arrayBuffer().then((savestate_buffer) => {
|
savestate_file.arrayBuffer().then((savestate_buffer) => {
|
||||||
const rom_array = new Uint8Array(rom_buffer);
|
const rom_array = new Uint8Array(rom_buffer);
|
||||||
const savestate_array = new Uint8Array(savestate_buffer);
|
const savestate_array = new Uint8Array(savestate_buffer);
|
||||||
websocket = new WebSocket(`ws://${location.host}/ws`);
|
const websocket = new WebSocket(`ws://localhost:3000/ws`);
|
||||||
websocket.addEventListener('open', () => {
|
websocket.binaryType = 'arraybuffer';
|
||||||
|
websocket.onclose = (message) => console.log('CLOSE', message);
|
||||||
|
websocket.onopen = () => {
|
||||||
console.log('Opened websocket.');
|
console.log('Opened websocket.');
|
||||||
sendHello(websocket, rom_array, savestate_array);
|
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<data.length; i++) {
|
||||||
|
if (i % 4 == 3) {
|
||||||
|
img_data_u8[i] = 255;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
img_data_u8[i] = data[i];
|
||||||
|
}
|
||||||
|
ctx.putImageData(img_data, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
function byteArrayToU32BigEndian(input_array) {
|
||||||
|
if (is_little_endian) {
|
||||||
|
input_array = input_array.reverse();
|
||||||
|
}
|
||||||
|
const buffer = input_array.buffer;
|
||||||
|
const output_u32_array = new Uint32Array(buffer);
|
||||||
|
return output_u32_array[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
function byteArrayToU64BigEndian(input_array) {
|
||||||
|
if (is_little_endian) {
|
||||||
|
input_array = input_array.reverse();
|
||||||
|
}
|
||||||
|
const buffer = input_array.buffer;
|
||||||
|
const output_u64_array = new BigUint64Array(buffer);
|
||||||
|
return output_u64_array[0];
|
||||||
|
}
|
||||||
|
|
||||||
function sendHello(websocket, rom_array, savestate_array) {
|
function sendHello(websocket, rom_array, savestate_array) {
|
||||||
console.log('Sending hello.');
|
console.log('Sending hello.');
|
||||||
const length_rom = BigInt(rom_array.length);
|
const length_rom = BigInt(rom_array.length);
|
||||||
@ -120,12 +174,18 @@
|
|||||||
|
|
||||||
function sendPacket(websocket, id, raw_data) {
|
function sendPacket(websocket, id, raw_data) {
|
||||||
const packet_u8 = concatU8Array(
|
const packet_u8 = concatU8Array(
|
||||||
concatU8Array(u64ToByteArrayBigEndian(BigInt(id)), u64ToByteArrayBigEndian(BigInt(raw_data.length))),
|
concatU8Array(u64ToByteArrayBigEndian(id), u64ToByteArrayBigEndian(BigInt(raw_data.length))),
|
||||||
raw_data
|
raw_data
|
||||||
);
|
);
|
||||||
const packet_blob = packet_u8.buffer;
|
const packet_buffer = packet_u8.buffer;
|
||||||
console.log('Sending packet');
|
console.log('Sending packet');
|
||||||
websocket.send(packet_blob);
|
websocket.send(packet_buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
function buf2hex(buffer) { // buffer is an ArrayBuffer
|
||||||
|
return [...new Uint8Array(buffer)]
|
||||||
|
.map(x => x.toString(16).padStart(2, '0'))
|
||||||
|
.join('');
|
||||||
}
|
}
|
||||||
|
|
||||||
function resizeEmulator(canvas) {
|
function resizeEmulator(canvas) {
|
||||||
|
Loading…
Reference in New Issue
Block a user