Update #44 - WebAssembly GC support, fix more WebRTC bugs

This commit is contained in:
lax1dude
2024-12-03 23:38:28 -08:00
parent 919014b4df
commit 70b52bbf7a
216 changed files with 34358 additions and 91 deletions

View File

@ -0,0 +1,293 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/* JOrbis
* Copyright (C) 2000 ymnk, JCraft,Inc.
*
* Written by: 2000 ymnk<ymnk@jcraft.com>
*
* Many thanks to
* Monty <monty@xiph.org> and
* The XIPHOPHORUS Company http://www.xiph.org/ .
* JOrbis has been based on their awesome works, Vorbis codec.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package com.jcraft.jogg;
public class Buffer {
private static final int BUFFER_INCREMENT = 256;
private static final int[] mask = { 0x00000000, 0x00000001, 0x00000003, 0x00000007, 0x0000000f, 0x0000001f,
0x0000003f, 0x0000007f, 0x000000ff, 0x000001ff, 0x000003ff, 0x000007ff, 0x00000fff, 0x00001fff, 0x00003fff,
0x00007fff, 0x0000ffff, 0x0001ffff, 0x0003ffff, 0x0007ffff, 0x000fffff, 0x001fffff, 0x003fffff, 0x007fffff,
0x00ffffff, 0x01ffffff, 0x03ffffff, 0x07ffffff, 0x0fffffff, 0x1fffffff, 0x3fffffff, 0x7fffffff,
0xffffffff };
int ptr = 0;
byte[] buffer = null;
int endbit = 0;
int endbyte = 0;
int storage = 0;
public void writeinit() {
buffer = new byte[BUFFER_INCREMENT];
ptr = 0;
buffer[0] = (byte) '\0';
storage = BUFFER_INCREMENT;
}
public void write(byte[] s) {
for (int i = 0; i < s.length; i++) {
if (s[i] == 0)
break;
write(s[i], 8);
}
}
public void read(byte[] s, int bytes) {
int i = 0;
while (bytes-- != 0) {
s[i++] = (byte) (read(8));
}
}
void reset() {
ptr = 0;
buffer[0] = (byte) '\0';
endbit = endbyte = 0;
}
public void writeclear() {
buffer = null;
}
public void readinit(byte[] buf, int bytes) {
readinit(buf, 0, bytes);
}
public void readinit(byte[] buf, int start, int bytes) {
ptr = start;
buffer = buf;
endbit = endbyte = 0;
storage = bytes;
}
public void write(int value, int bits) {
if (endbyte + 4 >= storage) {
byte[] foo = new byte[storage + BUFFER_INCREMENT];
System.arraycopy(buffer, 0, foo, 0, storage);
buffer = foo;
storage += BUFFER_INCREMENT;
}
value &= mask[bits];
bits += endbit;
buffer[ptr] |= (byte) (value << endbit);
if (bits >= 8) {
buffer[ptr + 1] = (byte) (value >>> (8 - endbit));
if (bits >= 16) {
buffer[ptr + 2] = (byte) (value >>> (16 - endbit));
if (bits >= 24) {
buffer[ptr + 3] = (byte) (value >>> (24 - endbit));
if (bits >= 32) {
if (endbit > 0)
buffer[ptr + 4] = (byte) (value >>> (32 - endbit));
else
buffer[ptr + 4] = 0;
}
}
}
}
endbyte += bits / 8;
ptr += bits / 8;
endbit = bits & 7;
}
public int look(int bits) {
int ret;
int m = mask[bits];
bits += endbit;
if (endbyte + 4 >= storage) {
if (endbyte + (bits - 1) / 8 >= storage)
return (-1);
}
ret = ((buffer[ptr]) & 0xff) >>> endbit;
if (bits > 8) {
ret |= ((buffer[ptr + 1]) & 0xff) << (8 - endbit);
if (bits > 16) {
ret |= ((buffer[ptr + 2]) & 0xff) << (16 - endbit);
if (bits > 24) {
ret |= ((buffer[ptr + 3]) & 0xff) << (24 - endbit);
if (bits > 32 && endbit != 0) {
ret |= ((buffer[ptr + 4]) & 0xff) << (32 - endbit);
}
}
}
}
return (m & ret);
}
public int look1() {
if (endbyte >= storage)
return (-1);
return ((buffer[ptr] >> endbit) & 1);
}
public void adv(int bits) {
bits += endbit;
ptr += bits / 8;
endbyte += bits / 8;
endbit = bits & 7;
}
public void adv1() {
++endbit;
if (endbit > 7) {
endbit = 0;
ptr++;
endbyte++;
}
}
public int read(int bits) {
int ret;
int m = mask[bits];
bits += endbit;
if (endbyte + 4 >= storage) {
ret = -1;
if (endbyte + (bits - 1) / 8 >= storage) {
ptr += bits / 8;
endbyte += bits / 8;
endbit = bits & 7;
return (ret);
}
}
ret = ((buffer[ptr]) & 0xff) >>> endbit;
if (bits > 8) {
ret |= ((buffer[ptr + 1]) & 0xff) << (8 - endbit);
if (bits > 16) {
ret |= ((buffer[ptr + 2]) & 0xff) << (16 - endbit);
if (bits > 24) {
ret |= ((buffer[ptr + 3]) & 0xff) << (24 - endbit);
if (bits > 32 && endbit != 0) {
ret |= ((buffer[ptr + 4]) & 0xff) << (32 - endbit);
}
}
}
}
ret &= m;
ptr += bits / 8;
endbyte += bits / 8;
endbit = bits & 7;
return (ret);
}
public int readB(int bits) {
int ret;
int m = 32 - bits;
bits += endbit;
if (endbyte + 4 >= storage) {
/* not the main path */
ret = -1;
if (endbyte * 8 + bits > storage * 8) {
ptr += bits / 8;
endbyte += bits / 8;
endbit = bits & 7;
return (ret);
}
}
ret = (buffer[ptr] & 0xff) << (24 + endbit);
if (bits > 8) {
ret |= (buffer[ptr + 1] & 0xff) << (16 + endbit);
if (bits > 16) {
ret |= (buffer[ptr + 2] & 0xff) << (8 + endbit);
if (bits > 24) {
ret |= (buffer[ptr + 3] & 0xff) << (endbit);
if (bits > 32 && (endbit != 0))
ret |= (buffer[ptr + 4] & 0xff) >> (8 - endbit);
}
}
}
ret = (ret >>> (m >> 1)) >>> ((m + 1) >> 1);
ptr += bits / 8;
endbyte += bits / 8;
endbit = bits & 7;
return (ret);
}
public int read1() {
int ret;
if (endbyte >= storage) {
ret = -1;
endbit++;
if (endbit > 7) {
endbit = 0;
ptr++;
endbyte++;
}
return (ret);
}
ret = (buffer[ptr] >> endbit) & 1;
endbit++;
if (endbit > 7) {
endbit = 0;
ptr++;
endbyte++;
}
return (ret);
}
public int bytes() {
return (endbyte + (endbit + 7) / 8);
}
public int bits() {
return (endbyte * 8 + endbit);
}
public byte[] buffer() {
return (buffer);
}
public static int ilog(int v) {
int ret = 0;
while (v > 0) {
ret++;
v >>>= 1;
}
return (ret);
}
public static void report(String in) {
System.err.println(in);
System.exit(1);
}
}

View File

@ -0,0 +1,45 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/* JOrbis
* Copyright (C) 2000 ymnk, JCraft,Inc.
*
* Written by: 2000 ymnk<ymnk@jcraft.com>
*
* Many thanks to
* Monty <monty@xiph.org> and
* The XIPHOPHORUS Company http://www.xiph.org/ .
* JOrbis has been based on their awesome works, Vorbis codec.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package com.jcraft.jogg;
public class Packet {
public byte[] packet_base;
public int packet;
public int bytes;
public int b_o_s;
public int e_o_s;
public long granulepos;
/**
* sequence number for decode; the framing knows where there's a hole in the
* data, but we need coupling so that the codec (which is in a seperate
* abstraction layer) also knows about the gap
*/
public long packetno;
}

View File

@ -0,0 +1,130 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/* JOrbis
* Copyright (C) 2000 ymnk, JCraft,Inc.
*
* Written by: 2000 ymnk<ymnk@jcraft.com>
*
* Many thanks to
* Monty <monty@xiph.org> and
* The XIPHOPHORUS Company http://www.xiph.org/ .
* JOrbis has been based on their awesome works, Vorbis codec.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package com.jcraft.jogg;
public class Page {
private static int[] crc_lookup = new int[256];
static {
for (int i = 0; i < crc_lookup.length; i++) {
crc_lookup[i] = crc_entry(i);
}
}
private static int crc_entry(int index) {
int r = index << 24;
for (int i = 0; i < 8; i++) {
if ((r & 0x80000000) != 0) {
r = (r << 1) ^ 0x04c11db7; /*
* The same as the ethernet generator polynomial, although we use an
* unreflected alg and an init/final of 0, not 0xffffffff
*/
} else {
r <<= 1;
}
}
return (r & 0xffffffff);
}
public byte[] header_base;
public int header;
public int header_len;
public byte[] body_base;
public int body;
public int body_len;
int version() {
return header_base[header + 4] & 0xff;
}
int continued() {
return (header_base[header + 5] & 0x01);
}
public int bos() {
return (header_base[header + 5] & 0x02);
}
public int eos() {
return (header_base[header + 5] & 0x04);
}
public long granulepos() {
long foo = header_base[header + 13] & 0xff;
foo = (foo << 8) | (header_base[header + 12] & 0xff);
foo = (foo << 8) | (header_base[header + 11] & 0xff);
foo = (foo << 8) | (header_base[header + 10] & 0xff);
foo = (foo << 8) | (header_base[header + 9] & 0xff);
foo = (foo << 8) | (header_base[header + 8] & 0xff);
foo = (foo << 8) | (header_base[header + 7] & 0xff);
foo = (foo << 8) | (header_base[header + 6] & 0xff);
return (foo);
}
public int serialno() {
return (header_base[header + 14] & 0xff) | ((header_base[header + 15] & 0xff) << 8)
| ((header_base[header + 16] & 0xff) << 16) | ((header_base[header + 17] & 0xff) << 24);
}
int pageno() {
return (header_base[header + 18] & 0xff) | ((header_base[header + 19] & 0xff) << 8)
| ((header_base[header + 20] & 0xff) << 16) | ((header_base[header + 21] & 0xff) << 24);
}
void checksum() {
int crc_reg = 0;
for (int i = 0; i < header_len; i++) {
crc_reg = (crc_reg << 8) ^ crc_lookup[((crc_reg >>> 24) & 0xff) ^ (header_base[header + i] & 0xff)];
}
for (int i = 0; i < body_len; i++) {
crc_reg = (crc_reg << 8) ^ crc_lookup[((crc_reg >>> 24) & 0xff) ^ (body_base[body + i] & 0xff)];
}
header_base[header + 22] = (byte) crc_reg;
header_base[header + 23] = (byte) (crc_reg >>> 8);
header_base[header + 24] = (byte) (crc_reg >>> 16);
header_base[header + 25] = (byte) (crc_reg >>> 24);
}
public Page copy() {
return copy(new Page());
}
public Page copy(Page p) {
byte[] tmp = new byte[header_len];
System.arraycopy(header_base, header, tmp, 0, header_len);
p.header_len = header_len;
p.header_base = tmp;
p.header = 0;
tmp = new byte[body_len];
System.arraycopy(body_base, body, tmp, 0, body_len);
p.body_len = body_len;
p.body_base = tmp;
p.body = 0;
return p;
}
}

View File

@ -0,0 +1,538 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/* JOrbis
* Copyright (C) 2000 ymnk, JCraft,Inc.
*
* Written by: 2000 ymnk<ymnk@jcraft.com>
*
* Many thanks to
* Monty <monty@xiph.org> and
* The XIPHOPHORUS Company http://www.xiph.org/ .
* JOrbis has been based on their awesome works, Vorbis codec.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package com.jcraft.jogg;
public class StreamState {
byte[] body_data; /* bytes from packet bodies */
int body_storage; /* storage elements allocated */
int body_fill; /* elements stored; fill mark */
private int body_returned; /* elements of fill returned */
int[] lacing_vals; /* The values that will go to the segment table */
long[] granule_vals; /*
* pcm_pos values for headers. Not compact this way, but it is simple coupled to
* the lacing fifo
*/
int lacing_storage;
int lacing_fill;
int lacing_packet;
int lacing_returned;
byte[] header = new byte[282]; /* working space for header encode */
int header_fill;
public int e_o_s; /*
* set when we have buffered the last packet in the logical bitstream
*/
int b_o_s; /*
* set after we've written the initial page of a logical bitstream
*/
int serialno;
int pageno;
long packetno; /*
* sequence number for decode; the framing knows where there's a hole in the
* data, but we need coupling so that the codec (which is in a seperate
* abstraction layer) also knows about the gap
*/
long granulepos;
public StreamState() {
init();
}
StreamState(int serialno) {
this();
init(serialno);
}
void init() {
body_storage = 16 * 1024;
body_data = new byte[body_storage];
lacing_storage = 1024;
lacing_vals = new int[lacing_storage];
granule_vals = new long[lacing_storage];
}
public void init(int serialno) {
if (body_data == null) {
init();
} else {
for (int i = 0; i < body_data.length; i++)
body_data[i] = 0;
for (int i = 0; i < lacing_vals.length; i++)
lacing_vals[i] = 0;
for (int i = 0; i < granule_vals.length; i++)
granule_vals[i] = 0;
}
this.serialno = serialno;
}
public void clear() {
body_data = null;
lacing_vals = null;
granule_vals = null;
}
void destroy() {
clear();
}
void body_expand(int needed) {
if (body_storage <= body_fill + needed) {
body_storage += (needed + 1024);
byte[] foo = new byte[body_storage];
System.arraycopy(body_data, 0, foo, 0, body_data.length);
body_data = foo;
}
}
void lacing_expand(int needed) {
if (lacing_storage <= lacing_fill + needed) {
lacing_storage += (needed + 32);
int[] foo = new int[lacing_storage];
System.arraycopy(lacing_vals, 0, foo, 0, lacing_vals.length);
lacing_vals = foo;
long[] bar = new long[lacing_storage];
System.arraycopy(granule_vals, 0, bar, 0, granule_vals.length);
granule_vals = bar;
}
}
/* submit data to the internal buffer of the framing engine */
public int packetin(Packet op) {
int lacing_val = op.bytes / 255 + 1;
if (body_returned != 0) {
/*
* advance packet data according to the body_returned pointer. We had to keep it
* around to return a pointer into the buffer last call
*/
body_fill -= body_returned;
if (body_fill != 0) {
System.arraycopy(body_data, body_returned, body_data, 0, body_fill);
}
body_returned = 0;
}
/* make sure we have the buffer storage */
body_expand(op.bytes);
lacing_expand(lacing_val);
/*
* Copy in the submitted packet. Yes, the copy is a waste; this is the liability
* of overly clean abstraction for the time being. It will actually be fairly
* easy to eliminate the extra copy in the future
*/
System.arraycopy(op.packet_base, op.packet, body_data, body_fill, op.bytes);
body_fill += op.bytes;
/* Store lacing vals for this packet */
int j;
for (j = 0; j < lacing_val - 1; j++) {
lacing_vals[lacing_fill + j] = 255;
granule_vals[lacing_fill + j] = granulepos;
}
lacing_vals[lacing_fill + j] = (op.bytes) % 255;
granulepos = granule_vals[lacing_fill + j] = op.granulepos;
/* flag the first segment as the beginning of the packet */
lacing_vals[lacing_fill] |= 0x100;
lacing_fill += lacing_val;
/* for the sake of completeness */
packetno++;
if (op.e_o_s != 0)
e_o_s = 1;
return (0);
}
public int packetout(Packet op) {
/*
* The last part of decode. We have the stream broken into packet segments. Now
* we need to group them into packets (or return the out of sync markers)
*/
int ptr = lacing_returned;
if (lacing_packet <= ptr) {
return (0);
}
if ((lacing_vals[ptr] & 0x400) != 0) {
/* We lost sync here; let the app know */
lacing_returned++;
/*
* we need to tell the codec there's a gap; it might need to handle previous
* packet dependencies.
*/
packetno++;
return (-1);
}
/* Gather the whole packet. We'll have no holes or a partial packet */
{
int size = lacing_vals[ptr] & 0xff;
int bytes = 0;
op.packet_base = body_data;
op.packet = body_returned;
op.e_o_s = lacing_vals[ptr] & 0x200; /* last packet of the stream? */
op.b_o_s = lacing_vals[ptr] & 0x100; /* first packet of the stream? */
bytes += size;
while (size == 255) {
int val = lacing_vals[++ptr];
size = val & 0xff;
if ((val & 0x200) != 0)
op.e_o_s = 0x200;
bytes += size;
}
op.packetno = packetno;
op.granulepos = granule_vals[ptr];
op.bytes = bytes;
body_returned += bytes;
lacing_returned = ptr + 1;
}
packetno++;
return (1);
}
// add the incoming page to the stream state; we decompose the page
// into packet segments here as well.
public int pagein(Page og) {
byte[] header_base = og.header_base;
int header = og.header;
byte[] body_base = og.body_base;
int body = og.body;
int bodysize = og.body_len;
int segptr = 0;
int version = og.version();
int continued = og.continued();
int bos = og.bos();
int eos = og.eos();
long granulepos = og.granulepos();
int _serialno = og.serialno();
int _pageno = og.pageno();
int segments = header_base[header + 26] & 0xff;
// clean up 'returned data'
{
int lr = lacing_returned;
int br = body_returned;
// body data
if (br != 0) {
body_fill -= br;
if (body_fill != 0) {
System.arraycopy(body_data, br, body_data, 0, body_fill);
}
body_returned = 0;
}
if (lr != 0) {
// segment table
if ((lacing_fill - lr) != 0) {
System.arraycopy(lacing_vals, lr, lacing_vals, 0, lacing_fill - lr);
System.arraycopy(granule_vals, lr, granule_vals, 0, lacing_fill - lr);
}
lacing_fill -= lr;
lacing_packet -= lr;
lacing_returned = 0;
}
}
// check the serial number
if (_serialno != serialno)
return (-1);
if (version > 0)
return (-1);
lacing_expand(segments + 1);
// are we in sequence?
if (_pageno != pageno) {
int i;
// unroll previous partial packet (if any)
for (i = lacing_packet; i < lacing_fill; i++) {
body_fill -= lacing_vals[i] & 0xff;
// System.out.println("??");
}
lacing_fill = lacing_packet;
// make a note of dropped data in segment table
if (pageno != -1) {
lacing_vals[lacing_fill++] = 0x400;
lacing_packet++;
}
// are we a 'continued packet' page? If so, we'll need to skip
// some segments
if (continued != 0) {
bos = 0;
for (; segptr < segments; segptr++) {
int val = (header_base[header + 27 + segptr] & 0xff);
body += val;
bodysize -= val;
if (val < 255) {
segptr++;
break;
}
}
}
}
if (bodysize != 0) {
body_expand(bodysize);
System.arraycopy(body_base, body, body_data, body_fill, bodysize);
body_fill += bodysize;
}
{
int saved = -1;
while (segptr < segments) {
int val = (header_base[header + 27 + segptr] & 0xff);
lacing_vals[lacing_fill] = val;
granule_vals[lacing_fill] = -1;
if (bos != 0) {
lacing_vals[lacing_fill] |= 0x100;
bos = 0;
}
if (val < 255)
saved = lacing_fill;
lacing_fill++;
segptr++;
if (val < 255)
lacing_packet = lacing_fill;
}
/* set the granulepos on the last pcmval of the last full packet */
if (saved != -1) {
granule_vals[saved] = granulepos;
}
}
if (eos != 0) {
e_o_s = 1;
if (lacing_fill > 0)
lacing_vals[lacing_fill - 1] |= 0x200;
}
pageno = _pageno + 1;
return (0);
}
/*
* This will flush remaining packets into a page (returning nonzero), even if
* there is not enough data to trigger a flush normally (undersized page). If
* there are no packets or partial packets to flush, ogg_stream_flush returns 0.
* Note that ogg_stream_flush will try to flush a normal sized page like
* ogg_stream_pageout; a call to ogg_stream_flush does not gurantee that all
* packets have flushed. Only a return value of 0 from ogg_stream_flush
* indicates all packet data is flushed into pages.
*
* ogg_stream_page will flush the last page in a stream even if it's undersized;
* you almost certainly want to use ogg_stream_pageout (and *not*
* ogg_stream_flush) unless you need to flush an undersized page in the middle
* of a stream for some reason.
*/
public int flush(Page og) {
int i;
int vals = 0;
int maxvals = (lacing_fill > 255 ? 255 : lacing_fill);
int bytes = 0;
int acc = 0;
long granule_pos = granule_vals[0];
if (maxvals == 0)
return (0);
/* construct a page */
/* decide how many segments to include */
/*
* If this is the initial header case, the first page must only include the
* initial header packet
*/
if (b_o_s == 0) { /* 'initial header page' case */
granule_pos = 0;
for (vals = 0; vals < maxvals; vals++) {
if ((lacing_vals[vals] & 0x0ff) < 255) {
vals++;
break;
}
}
} else {
for (vals = 0; vals < maxvals; vals++) {
if (acc > 4096)
break;
acc += (lacing_vals[vals] & 0x0ff);
granule_pos = granule_vals[vals];
}
}
/* construct the header in temp storage */
System.arraycopy("OggS".getBytes(), 0, header, 0, 4);
/* stream structure version */
header[4] = 0x00;
/* continued packet flag? */
header[5] = 0x00;
if ((lacing_vals[0] & 0x100) == 0)
header[5] |= 0x01;
/* first page flag? */
if (b_o_s == 0)
header[5] |= 0x02;
/* last page flag? */
if (e_o_s != 0 && lacing_fill == vals)
header[5] |= 0x04;
b_o_s = 1;
/* 64 bits of PCM position */
for (i = 6; i < 14; i++) {
header[i] = (byte) granule_pos;
granule_pos >>>= 8;
}
/* 32 bits of stream serial number */
{
int _serialno = serialno;
for (i = 14; i < 18; i++) {
header[i] = (byte) _serialno;
_serialno >>>= 8;
}
}
/*
* 32 bits of page counter (we have both counter and page header because this
* val can roll over)
*/
if (pageno == -1)
pageno = 0; /*
* because someone called stream_reset; this would be a strange thing to do in
* an encode stream, but it has plausible uses
*/
{
int _pageno = pageno++;
for (i = 18; i < 22; i++) {
header[i] = (byte) _pageno;
_pageno >>>= 8;
}
}
/* zero for computation; filled in later */
header[22] = 0;
header[23] = 0;
header[24] = 0;
header[25] = 0;
/* segment table */
header[26] = (byte) vals;
for (i = 0; i < vals; i++) {
header[i + 27] = (byte) lacing_vals[i];
bytes += (header[i + 27] & 0xff);
}
/* set pointers in the ogg_page struct */
og.header_base = header;
og.header = 0;
og.header_len = header_fill = vals + 27;
og.body_base = body_data;
og.body = body_returned;
og.body_len = bytes;
/* advance the lacing data and set the body_returned pointer */
lacing_fill -= vals;
System.arraycopy(lacing_vals, vals, lacing_vals, 0, lacing_fill * 4);
System.arraycopy(granule_vals, vals, granule_vals, 0, lacing_fill * 8);
body_returned += bytes;
/* calculate the checksum */
og.checksum();
/* done */
return (1);
}
/*
* This constructs pages from buffered packet segments. The pointers returned
* are to static buffers; do not free. The returned buffers are good only until
* the next call (using the same ogg_stream_state)
*/
public int pageout(Page og) {
if ((e_o_s != 0 && lacing_fill != 0) || /* 'were done, now flush' case */
body_fill - body_returned > 4096 || /* 'page nominal size' case */
lacing_fill >= 255 || /* 'segment table full' case */
(lacing_fill != 0 && b_o_s == 0)) { /* 'initial header page' case */
return flush(og);
}
return 0;
}
public int eof() {
return e_o_s;
}
public int reset() {
body_fill = 0;
body_returned = 0;
lacing_fill = 0;
lacing_packet = 0;
lacing_returned = 0;
header_fill = 0;
e_o_s = 0;
b_o_s = 0;
pageno = -1;
packetno = 0;
granulepos = 0;
return (0);
}
}

View File

@ -0,0 +1,273 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/* JOrbis
* Copyright (C) 2000 ymnk, JCraft,Inc.
*
* Written by: 2000 ymnk<ymnk@jcraft.com>
*
* Many thanks to
* Monty <monty@xiph.org> and
* The XIPHOPHORUS Company http://www.xiph.org/ .
* JOrbis has been based on their awesome works, Vorbis codec.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package com.jcraft.jogg;
// DECODING PRIMITIVES: packet streaming layer
// This has two layers to place more of the multi-serialno and paging
// control in the application's hands. First, we expose a data buffer
// using ogg_decode_buffer(). The app either copies into the
// buffer, or passes it directly to read(), etc. We then call
// ogg_decode_wrote() to tell how many bytes we just added.
//
// Pages are returned (pointers into the buffer in ogg_sync_state)
// by ogg_decode_stream(). The page is then submitted to
// ogg_decode_page() along with the appropriate
// ogg_stream_state* (ie, matching serialno). We then get raw
// packets out calling ogg_stream_packet() with a
// ogg_stream_state. See the 'frame-prog.txt' docs for details and
// example code.
public class SyncState {
public byte[] data;
int storage;
int fill;
int returned;
int unsynced;
int headerbytes;
int bodybytes;
public int clear() {
data = null;
return (0);
}
public int buffer(int size) {
// first, clear out any space that has been previously returned
if (returned != 0) {
fill -= returned;
if (fill > 0) {
System.arraycopy(data, returned, data, 0, fill);
}
returned = 0;
}
if (size > storage - fill) {
// We need to extend the internal buffer
int newsize = size + fill + 4096; // an extra page to be nice
if (data != null) {
byte[] foo = new byte[newsize];
System.arraycopy(data, 0, foo, 0, data.length);
data = foo;
} else {
data = new byte[newsize];
}
storage = newsize;
}
return (fill);
}
public int wrote(int bytes) {
if (fill + bytes > storage)
return (-1);
fill += bytes;
return (0);
}
// sync the stream. This is meant to be useful for finding page
// boundaries.
//
// return values for this:
// -n) skipped n bytes
// 0) page not ready; more data (no bytes skipped)
// n) page synced at current location; page length n bytes
private Page pageseek = new Page();
private byte[] chksum = new byte[4];
public int pageseek(Page og) {
int page = returned;
int next;
int bytes = fill - returned;
if (headerbytes == 0) {
int _headerbytes, i;
if (bytes < 27)
return (0); // not enough for a header
/* verify capture pattern */
if (data[page] != 'O' || data[page + 1] != 'g' || data[page + 2] != 'g' || data[page + 3] != 'S') {
headerbytes = 0;
bodybytes = 0;
// search for possible capture
next = 0;
for (int ii = 0; ii < bytes - 1; ii++) {
if (data[page + 1 + ii] == 'O') {
next = page + 1 + ii;
break;
}
}
// next=memchr(page+1,'O',bytes-1);
if (next == 0)
next = fill;
returned = next;
return (-(next - page));
}
_headerbytes = (data[page + 26] & 0xff) + 27;
if (bytes < _headerbytes)
return (0); // not enough for header + seg table
// count up body length in the segment table
for (i = 0; i < (data[page + 26] & 0xff); i++) {
bodybytes += (data[page + 27 + i] & 0xff);
}
headerbytes = _headerbytes;
}
if (bodybytes + headerbytes > bytes)
return (0);
// The whole test page is buffered. Verify the checksum
synchronized (chksum) {
// Grab the checksum bytes, set the header field to zero
System.arraycopy(data, page + 22, chksum, 0, 4);
data[page + 22] = 0;
data[page + 23] = 0;
data[page + 24] = 0;
data[page + 25] = 0;
// set up a temp page struct and recompute the checksum
Page log = pageseek;
log.header_base = data;
log.header = page;
log.header_len = headerbytes;
log.body_base = data;
log.body = page + headerbytes;
log.body_len = bodybytes;
log.checksum();
// Compare
if (chksum[0] != data[page + 22] || chksum[1] != data[page + 23] || chksum[2] != data[page + 24]
|| chksum[3] != data[page + 25]) {
// D'oh. Mismatch! Corrupt page (or miscapture and not a page at all)
// replace the computed checksum with the one actually read in
System.arraycopy(chksum, 0, data, page + 22, 4);
// Bad checksum. Lose sync */
headerbytes = 0;
bodybytes = 0;
// search for possible capture
next = 0;
for (int ii = 0; ii < bytes - 1; ii++) {
if (data[page + 1 + ii] == 'O') {
next = page + 1 + ii;
break;
}
}
// next=memchr(page+1,'O',bytes-1);
if (next == 0)
next = fill;
returned = next;
return (-(next - page));
}
}
// yes, have a whole page all ready to go
{
page = returned;
if (og != null) {
og.header_base = data;
og.header = page;
og.header_len = headerbytes;
og.body_base = data;
og.body = page + headerbytes;
og.body_len = bodybytes;
}
unsynced = 0;
returned += (bytes = headerbytes + bodybytes);
headerbytes = 0;
bodybytes = 0;
return (bytes);
}
}
// sync the stream and get a page. Keep trying until we find a page.
// Supress 'sync errors' after reporting the first.
//
// return values:
// -1) recapture (hole in data)
// 0) need more data
// 1) page returned
//
// Returns pointers into buffered data; invalidated by next call to
// _stream, _clear, _init, or _buffer
public int pageout(Page og) {
// all we need to do is verify a page at the head of the stream
// buffer. If it doesn't verify, we look for the next potential
// frame
while (true) {
int ret = pageseek(og);
if (ret > 0) {
// have a page
return (1);
}
if (ret == 0) {
// need more data
return (0);
}
// head did not start a synced page... skipped some bytes
if (unsynced == 0) {
unsynced = 1;
return (-1);
}
// loop. keep looking
}
}
// clear things to an initial state. Good to call, eg, before seeking
public int reset() {
fill = 0;
returned = 0;
unsynced = 0;
headerbytes = 0;
bodybytes = 0;
return (0);
}
public void init() {
}
public int getDataOffset() {
return returned;
}
public int getBufferOffset() {
return fill;
}
}

View File

@ -0,0 +1,126 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/* JOrbis
* Copyright (C) 2000 ymnk, JCraft,Inc.
*
* Written by: 2000 ymnk<ymnk@jcraft.com>
*
* Many thanks to
* Monty <monty@xiph.org> and
* The XIPHOPHORUS Company http://www.xiph.org/ .
* JOrbis has been based on their awesome works, Vorbis codec.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package com.jcraft.jorbis;
import com.jcraft.jogg.*;
public class Block {
/// necessary stream state for linking to the framing abstraction
float[][] pcm = new float[0][]; // this is a pointer into local storage
Buffer opb = new Buffer();
int lW;
int W;
int nW;
int pcmend;
int mode;
int eofflag;
long granulepos;
long sequence;
DspState vd; // For read-only access of configuration
// bitmetrics for the frame
int glue_bits;
int time_bits;
int floor_bits;
int res_bits;
public Block(DspState vd) {
this.vd = vd;
if (vd.analysisp != 0) {
opb.writeinit();
}
}
public void init(DspState vd) {
this.vd = vd;
}
public int clear() {
if (vd != null) {
if (vd.analysisp != 0) {
opb.writeclear();
}
}
return (0);
}
public int synthesis(Packet op) {
Info vi = vd.vi;
// first things first. Make sure decode is ready
opb.readinit(op.packet_base, op.packet, op.bytes);
// Check the packet type
if (opb.read(1) != 0) {
// Oops. This is not an audio data packet
return (-1);
}
// read our mode and pre/post windowsize
int _mode = opb.read(vd.modebits);
if (_mode == -1)
return (-1);
mode = _mode;
W = vi.mode_param[mode].blockflag;
if (W != 0) {
lW = opb.read(1);
nW = opb.read(1);
if (nW == -1)
return (-1);
} else {
lW = 0;
nW = 0;
}
// more setup
granulepos = op.granulepos;
sequence = op.packetno - 3; // first block is third packet
eofflag = op.e_o_s;
// alloc pcm passback storage
pcmend = vi.blocksizes[W];
if (pcm.length < vi.channels) {
pcm = new float[vi.channels][];
}
for (int i = 0; i < vi.channels; i++) {
if (pcm[i] == null || pcm[i].length < pcmend) {
pcm[i] = new float[pcmend];
} else {
for (int j = 0; j < pcmend; j++) {
pcm[i][j] = 0;
}
}
}
// unpack_header enforces range checking
int type = vi.map_type[vi.mode_param[mode].mapping];
return (FuncMapping.mapping_P[type].inverse(this, vd.mode[mode]));
}
}

View File

@ -0,0 +1,471 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/* JOrbis
* Copyright (C) 2000 ymnk, JCraft,Inc.
*
* Written by: 2000 ymnk<ymnk@jcraft.com>
*
* Many thanks to
* Monty <monty@xiph.org> and
* The XIPHOPHORUS Company http://www.xiph.org/ .
* JOrbis has been based on their awesome works, Vorbis codec.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package com.jcraft.jorbis;
import com.jcraft.jogg.*;
class CodeBook {
int dim; // codebook dimensions (elements per vector)
int entries; // codebook entries
StaticCodeBook c = new StaticCodeBook();
float[] valuelist; // list of dim*entries actual entry values
int[] codelist; // list of bitstream codewords for each entry
DecodeAux decode_tree;
// returns the number of bits
int encode(int a, Buffer b) {
b.write(codelist[a], c.lengthlist[a]);
return (c.lengthlist[a]);
}
// One the encode side, our vector writers are each designed for a
// specific purpose, and the encoder is not flexible without modification:
//
// The LSP vector coder uses a single stage nearest-match with no
// interleave, so no step and no error return. This is specced by floor0
// and doesn't change.
//
// Residue0 encoding interleaves, uses multiple stages, and each stage
// peels of a specific amount of resolution from a lattice (thus we want
// to match by threshhold, not nearest match). Residue doesn't *have* to
// be encoded that way, but to change it, one will need to add more
// infrastructure on the encode side (decode side is specced and simpler)
// floor0 LSP (single stage, non interleaved, nearest match)
// returns entry number and *modifies a* to the quantization value
int errorv(float[] a) {
int best = best(a, 1);
for (int k = 0; k < dim; k++) {
a[k] = valuelist[best * dim + k];
}
return (best);
}
// returns the number of bits and *modifies a* to the quantization value
int encodev(int best, float[] a, Buffer b) {
for (int k = 0; k < dim; k++) {
a[k] = valuelist[best * dim + k];
}
return (encode(best, b));
}
// res0 (multistage, interleave, lattice)
// returns the number of bits and *modifies a* to the remainder value
int encodevs(float[] a, Buffer b, int step, int addmul) {
int best = besterror(a, step, addmul);
return (encode(best, b));
}
private int[] t = new int[15]; // decodevs_add is synchronized for re-using t.
synchronized int decodevs_add(float[] a, int offset, Buffer b, int n) {
int step = n / dim;
int entry;
int i, j, o;
if (t.length < step) {
t = new int[step];
}
for (i = 0; i < step; i++) {
entry = decode(b);
if (entry == -1)
return (-1);
t[i] = entry * dim;
}
for (i = 0, o = 0; i < dim; i++, o += step) {
for (j = 0; j < step; j++) {
a[offset + o + j] += valuelist[t[j] + i];
}
}
return (0);
}
int decodev_add(float[] a, int offset, Buffer b, int n) {
int i, j, entry;
int t;
if (dim > 8) {
for (i = 0; i < n;) {
entry = decode(b);
if (entry == -1)
return (-1);
t = entry * dim;
for (j = 0; j < dim;) {
a[offset + (i++)] += valuelist[t + (j++)];
}
}
} else {
for (i = 0; i < n;) {
entry = decode(b);
if (entry == -1)
return (-1);
t = entry * dim;
j = 0;
switch (dim) {
case 8:
a[offset + (i++)] += valuelist[t + (j++)];
case 7:
a[offset + (i++)] += valuelist[t + (j++)];
case 6:
a[offset + (i++)] += valuelist[t + (j++)];
case 5:
a[offset + (i++)] += valuelist[t + (j++)];
case 4:
a[offset + (i++)] += valuelist[t + (j++)];
case 3:
a[offset + (i++)] += valuelist[t + (j++)];
case 2:
a[offset + (i++)] += valuelist[t + (j++)];
case 1:
a[offset + (i++)] += valuelist[t + (j++)];
case 0:
break;
}
}
}
return (0);
}
int decodev_set(float[] a, int offset, Buffer b, int n) {
int i, j, entry;
int t;
for (i = 0; i < n;) {
entry = decode(b);
if (entry == -1)
return (-1);
t = entry * dim;
for (j = 0; j < dim;) {
a[offset + i++] = valuelist[t + (j++)];
}
}
return (0);
}
int decodevv_add(float[][] a, int offset, int ch, Buffer b, int n) {
int i, j, entry;
int chptr = 0;
for (i = offset / ch; i < (offset + n) / ch;) {
entry = decode(b);
if (entry == -1)
return (-1);
int t = entry * dim;
for (j = 0; j < dim; j++) {
a[chptr++][i] += valuelist[t + j];
if (chptr == ch) {
chptr = 0;
i++;
}
}
}
return (0);
}
// Decode side is specced and easier, because we don't need to find
// matches using different criteria; we simply read and map. There are
// two things we need to do 'depending':
//
// We may need to support interleave. We don't really, but it's
// convenient to do it here rather than rebuild the vector later.
//
// Cascades may be additive or multiplicitive; this is not inherent in
// the codebook, but set in the code using the codebook. Like
// interleaving, it's easiest to do it here.
// stage==0 -> declarative (set the value)
// stage==1 -> additive
// stage==2 -> multiplicitive
// returns the entry number or -1 on eof
int decode(Buffer b) {
int ptr = 0;
DecodeAux t = decode_tree;
int lok = b.look(t.tabn);
if (lok >= 0) {
ptr = t.tab[lok];
b.adv(t.tabl[lok]);
if (ptr <= 0) {
return -ptr;
}
}
do {
switch (b.read1()) {
case 0:
ptr = t.ptr0[ptr];
break;
case 1:
ptr = t.ptr1[ptr];
break;
case -1:
default:
return (-1);
}
} while (ptr > 0);
return (-ptr);
}
// returns the entry number or -1 on eof
int decodevs(float[] a, int index, Buffer b, int step, int addmul) {
int entry = decode(b);
if (entry == -1)
return (-1);
switch (addmul) {
case -1:
for (int i = 0, o = 0; i < dim; i++, o += step)
a[index + o] = valuelist[entry * dim + i];
break;
case 0:
for (int i = 0, o = 0; i < dim; i++, o += step)
a[index + o] += valuelist[entry * dim + i];
break;
case 1:
for (int i = 0, o = 0; i < dim; i++, o += step)
a[index + o] *= valuelist[entry * dim + i];
break;
default:
// System.err.println("CodeBook.decodeves: addmul="+addmul);
}
return (entry);
}
int best(float[] a, int step) {
// brute force it!
{
int besti = -1;
float best = 0.f;
int e = 0;
for (int i = 0; i < entries; i++) {
if (c.lengthlist[i] > 0) {
float _this = dist(dim, valuelist, e, a, step);
if (besti == -1 || _this < best) {
best = _this;
besti = i;
}
}
e += dim;
}
return (besti);
}
}
// returns the entry number and *modifies a* to the remainder value
int besterror(float[] a, int step, int addmul) {
int best = best(a, step);
switch (addmul) {
case 0:
for (int i = 0, o = 0; i < dim; i++, o += step)
a[o] -= valuelist[best * dim + i];
break;
case 1:
for (int i = 0, o = 0; i < dim; i++, o += step) {
float val = valuelist[best * dim + i];
if (val == 0) {
a[o] = 0;
} else {
a[o] /= val;
}
}
break;
}
return (best);
}
void clear() {
}
private static float dist(int el, float[] ref, int index, float[] b, int step) {
float acc = (float) 0.;
for (int i = 0; i < el; i++) {
float val = (ref[index + i] - b[i * step]);
acc += val * val;
}
return (acc);
}
int init_decode(StaticCodeBook s) {
c = s;
entries = s.entries;
dim = s.dim;
valuelist = s.unquantize();
decode_tree = make_decode_tree();
if (decode_tree == null) {
clear();
return (-1);
}
return (0);
}
// given a list of word lengths, generate a list of codewords. Works
// for length ordered or unordered, always assigns the lowest valued
// codewords first. Extended to handle unused entries (length 0)
static int[] make_words(int[] l, int n) {
int[] marker = new int[33];
int[] r = new int[n];
for (int i = 0; i < n; i++) {
int length = l[i];
if (length > 0) {
int entry = marker[length];
// when we claim a node for an entry, we also claim the nodes
// below it (pruning off the imagined tree that may have dangled
// from it) as well as blocking the use of any nodes directly
// above for leaves
// update ourself
if (length < 32 && (entry >>> length) != 0) {
// error condition; the lengths must specify an overpopulated tree
// free(r);
return (null);
}
r[i] = entry;
// Look to see if the next shorter marker points to the node
// above. if so, update it and repeat.
{
for (int j = length; j > 0; j--) {
if ((marker[j] & 1) != 0) {
// have to jump branches
if (j == 1)
marker[1]++;
else
marker[j] = marker[j - 1] << 1;
break; // invariant says next upper marker would already
// have been moved if it was on the same path
}
marker[j]++;
}
}
// prune the tree; the implicit invariant says all the longer
// markers were dangling from our just-taken node. Dangle them
// from our *new* node.
for (int j = length + 1; j < 33; j++) {
if ((marker[j] >>> 1) == entry) {
entry = marker[j];
marker[j] = marker[j - 1] << 1;
} else {
break;
}
}
}
}
// bitreverse the words because our bitwise packer/unpacker is LSb
// endian
for (int i = 0; i < n; i++) {
int temp = 0;
for (int j = 0; j < l[i]; j++) {
temp <<= 1;
temp |= (r[i] >>> j) & 1;
}
r[i] = temp;
}
return (r);
}
// build the decode helper tree from the codewords
DecodeAux make_decode_tree() {
int top = 0;
DecodeAux t = new DecodeAux();
int[] ptr0 = t.ptr0 = new int[entries * 2];
int[] ptr1 = t.ptr1 = new int[entries * 2];
int[] codelist = make_words(c.lengthlist, c.entries);
if (codelist == null)
return (null);
t.aux = entries * 2;
for (int i = 0; i < entries; i++) {
if (c.lengthlist[i] > 0) {
int ptr = 0;
int j;
for (j = 0; j < c.lengthlist[i] - 1; j++) {
int bit = (codelist[i] >>> j) & 1;
if (bit == 0) {
if (ptr0[ptr] == 0) {
ptr0[ptr] = ++top;
}
ptr = ptr0[ptr];
} else {
if (ptr1[ptr] == 0) {
ptr1[ptr] = ++top;
}
ptr = ptr1[ptr];
}
}
if (((codelist[i] >>> j) & 1) == 0) {
ptr0[ptr] = -i;
} else {
ptr1[ptr] = -i;
}
}
}
t.tabn = Util.ilog(entries) - 4;
if (t.tabn < 5)
t.tabn = 5;
int n = 1 << t.tabn;
t.tab = new int[n];
t.tabl = new int[n];
for (int i = 0; i < n; i++) {
int p = 0;
int j = 0;
for (j = 0; j < t.tabn && (p > 0 || j == 0); j++) {
if ((i & (1 << j)) != 0) {
p = ptr1[p];
} else {
p = ptr0[p];
}
}
t.tab[i] = p; // -code
t.tabl[i] = j; // length
}
return (t);
}
class DecodeAux {
int[] tab;
int[] tabl;
int tabn;
int[] ptr0;
int[] ptr1;
int aux; // number of tree entries
}
}

View File

@ -0,0 +1,240 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/* JOrbis
* Copyright (C) 2000 ymnk, JCraft,Inc.
*
* Written by: 2000 ymnk<ymnk@jcraft.com>
*
* Many thanks to
* Monty <monty@xiph.org> and
* The XIPHOPHORUS Company http://www.xiph.org/ .
* JOrbis has been based on their awesome works, Vorbis codec.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package com.jcraft.jorbis;
import com.jcraft.jogg.*;
// the comments are not part of vorbis_info so that vorbis_info can be
// static storage
public class Comment {
private static byte[] _vorbis = "vorbis".getBytes();
private static byte[] _vendor = "Xiphophorus libVorbis I 20000508".getBytes();
private static final int OV_EIMPL = -130;
// unlimited user comment fields.
public byte[][] user_comments;
public int[] comment_lengths;
public int comments;
public byte[] vendor;
public void init() {
user_comments = null;
comments = 0;
vendor = null;
}
public void add(String comment) {
add(comment.getBytes());
}
private void add(byte[] comment) {
byte[][] foo = new byte[comments + 2][];
if (user_comments != null) {
System.arraycopy(user_comments, 0, foo, 0, comments);
}
user_comments = foo;
int[] goo = new int[comments + 2];
if (comment_lengths != null) {
System.arraycopy(comment_lengths, 0, goo, 0, comments);
}
comment_lengths = goo;
byte[] bar = new byte[comment.length + 1];
System.arraycopy(comment, 0, bar, 0, comment.length);
user_comments[comments] = bar;
comment_lengths[comments] = comment.length;
comments++;
user_comments[comments] = null;
}
public void add_tag(String tag, String contents) {
if (contents == null)
contents = "";
add(tag + "=" + contents);
}
static boolean tagcompare(byte[] s1, byte[] s2, int n) {
int c = 0;
byte u1, u2;
while (c < n) {
u1 = s1[c];
u2 = s2[c];
if ('Z' >= u1 && u1 >= 'A')
u1 = (byte) (u1 - 'A' + 'a');
if ('Z' >= u2 && u2 >= 'A')
u2 = (byte) (u2 - 'A' + 'a');
if (u1 != u2) {
return false;
}
c++;
}
return true;
}
public String query(String tag) {
return query(tag, 0);
}
public String query(String tag, int count) {
int foo = query(tag.getBytes(), count);
if (foo == -1)
return null;
byte[] comment = user_comments[foo];
for (int i = 0; i < comment_lengths[foo]; i++) {
if (comment[i] == '=') {
return new String(comment, i + 1, comment_lengths[foo] - (i + 1));
}
}
return null;
}
private int query(byte[] tag, int count) {
int i = 0;
int found = 0;
int fulltaglen = tag.length + 1;
byte[] fulltag = new byte[fulltaglen];
System.arraycopy(tag, 0, fulltag, 0, tag.length);
fulltag[tag.length] = (byte) '=';
for (i = 0; i < comments; i++) {
if (tagcompare(user_comments[i], fulltag, fulltaglen)) {
if (count == found) {
// We return a pointer to the data, not a copy
// return user_comments[i] + taglen + 1;
return i;
} else {
found++;
}
}
}
return -1;
}
int unpack(Buffer opb) {
int vendorlen = opb.read(32);
if (vendorlen < 0) {
clear();
return (-1);
}
vendor = new byte[vendorlen + 1];
opb.read(vendor, vendorlen);
comments = opb.read(32);
if (comments < 0) {
clear();
return (-1);
}
user_comments = new byte[comments + 1][];
comment_lengths = new int[comments + 1];
for (int i = 0; i < comments; i++) {
int len = opb.read(32);
if (len < 0) {
clear();
return (-1);
}
comment_lengths[i] = len;
user_comments[i] = new byte[len + 1];
opb.read(user_comments[i], len);
}
if (opb.read(1) != 1) {
clear();
return (-1);
}
return (0);
}
int pack(Buffer opb) {
// preamble
opb.write(0x03, 8);
opb.write(_vorbis);
// vendor
opb.write(_vendor.length, 32);
opb.write(_vendor);
// comments
opb.write(comments, 32);
if (comments != 0) {
for (int i = 0; i < comments; i++) {
if (user_comments[i] != null) {
opb.write(comment_lengths[i], 32);
opb.write(user_comments[i]);
} else {
opb.write(0, 32);
}
}
}
opb.write(1, 1);
return (0);
}
public int header_out(Packet op) {
Buffer opb = new Buffer();
opb.writeinit();
if (pack(opb) != 0)
return OV_EIMPL;
op.packet_base = new byte[opb.bytes()];
op.packet = 0;
op.bytes = opb.bytes();
System.arraycopy(opb.buffer(), 0, op.packet_base, 0, op.bytes);
op.b_o_s = 0;
op.e_o_s = 0;
op.granulepos = 0;
return 0;
}
void clear() {
for (int i = 0; i < comments; i++)
user_comments[i] = null;
user_comments = null;
vendor = null;
}
public String getVendor() {
return new String(vendor, 0, vendor.length - 1);
}
public String getComment(int i) {
if (comments <= i)
return null;
return new String(user_comments[i], 0, user_comments[i].length - 1);
}
public String toString() {
String foo = "Vendor: " + new String(vendor, 0, vendor.length - 1);
for (int i = 0; i < comments; i++) {
foo = foo + "\nComment: " + new String(user_comments[i], 0, user_comments[i].length - 1);
}
foo = foo + "\n";
return foo;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,369 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/* JOrbis
* Copyright (C) 2000 ymnk, JCraft,Inc.
*
* Written by: 2000 ymnk<ymnk@jcraft.com>
*
* Many thanks to
* Monty <monty@xiph.org> and
* The XIPHOPHORUS Company http://www.xiph.org/ .
* JOrbis has been based on their awesome works, Vorbis codec.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package com.jcraft.jorbis;
public class DspState {
static final float M_PI = 3.1415926539f;
static final int VI_TRANSFORMB = 1;
static final int VI_WINDOWB = 1;
int analysisp;
Info vi;
int modebits;
float[][] pcm;
int pcm_storage;
int pcm_current;
int pcm_returned;
float[] multipliers;
int envelope_storage;
int envelope_current;
int eofflag;
int lW;
int W;
int nW;
int centerW;
long granulepos;
long sequence;
long glue_bits;
long time_bits;
long floor_bits;
long res_bits;
// local lookup storage
float[][][][][] window; // block, leadin, leadout, type
Object[][] transform;
CodeBook[] fullbooks;
// backend lookups are tied to the mode, not the backend or naked mapping
Object[] mode;
// local storage, only used on the encoding side. This way the
// application does not need to worry about freeing some packets'
// memory and not others'; packet storage is always tracked.
// Cleared next call to a _dsp_ function
byte[] header;
byte[] header1;
byte[] header2;
public DspState() {
transform = new Object[2][];
window = new float[2][][][][];
window[0] = new float[2][][][];
window[0][0] = new float[2][][];
window[0][1] = new float[2][][];
window[0][0][0] = new float[2][];
window[0][0][1] = new float[2][];
window[0][1][0] = new float[2][];
window[0][1][1] = new float[2][];
window[1] = new float[2][][][];
window[1][0] = new float[2][][];
window[1][1] = new float[2][][];
window[1][0][0] = new float[2][];
window[1][0][1] = new float[2][];
window[1][1][0] = new float[2][];
window[1][1][1] = new float[2][];
}
static float[] window(int type, int window, int left, int right) {
float[] ret = new float[window];
switch (type) {
case 0:
// The 'vorbis window' (window 0) is sin(sin(x)*sin(x)*2pi)
{
int leftbegin = window / 4 - left / 2;
int rightbegin = window - window / 4 - right / 2;
for (int i = 0; i < left; i++) {
float x = (float) ((i + .5) / left * M_PI / 2.);
x = (float) Math.sin(x);
x *= x;
x *= M_PI / 2.;
x = (float) Math.sin(x);
ret[i + leftbegin] = x;
}
for (int i = leftbegin + left; i < rightbegin; i++) {
ret[i] = 1.f;
}
for (int i = 0; i < right; i++) {
float x = (float) ((right - i - .5) / right * M_PI / 2.);
x = (float) Math.sin(x);
x *= x;
x *= M_PI / 2.;
x = (float) Math.sin(x);
ret[i + rightbegin] = x;
}
}
break;
default:
// free(ret);
return (null);
}
return (ret);
}
// Analysis side code, but directly related to blocking. Thus it's
// here and not in analysis.c (which is for analysis transforms only).
// The init is here because some of it is shared
int init(Info vi, boolean encp) {
this.vi = vi;
modebits = Util.ilog2(vi.modes);
transform[0] = new Object[VI_TRANSFORMB];
transform[1] = new Object[VI_TRANSFORMB];
// MDCT is tranform 0
transform[0][0] = new Mdct();
transform[1][0] = new Mdct();
((Mdct) transform[0][0]).init(vi.blocksizes[0]);
((Mdct) transform[1][0]).init(vi.blocksizes[1]);
window[0][0][0] = new float[VI_WINDOWB][];
window[0][0][1] = window[0][0][0];
window[0][1][0] = window[0][0][0];
window[0][1][1] = window[0][0][0];
window[1][0][0] = new float[VI_WINDOWB][];
window[1][0][1] = new float[VI_WINDOWB][];
window[1][1][0] = new float[VI_WINDOWB][];
window[1][1][1] = new float[VI_WINDOWB][];
for (int i = 0; i < VI_WINDOWB; i++) {
window[0][0][0][i] = window(i, vi.blocksizes[0], vi.blocksizes[0] / 2, vi.blocksizes[0] / 2);
window[1][0][0][i] = window(i, vi.blocksizes[1], vi.blocksizes[0] / 2, vi.blocksizes[0] / 2);
window[1][0][1][i] = window(i, vi.blocksizes[1], vi.blocksizes[0] / 2, vi.blocksizes[1] / 2);
window[1][1][0][i] = window(i, vi.blocksizes[1], vi.blocksizes[1] / 2, vi.blocksizes[0] / 2);
window[1][1][1][i] = window(i, vi.blocksizes[1], vi.blocksizes[1] / 2, vi.blocksizes[1] / 2);
}
fullbooks = new CodeBook[vi.books];
for (int i = 0; i < vi.books; i++) {
fullbooks[i] = new CodeBook();
fullbooks[i].init_decode(vi.book_param[i]);
}
// initialize the storage vectors to a decent size greater than the
// minimum
pcm_storage = 8192; // we'll assume later that we have
// a minimum of twice the blocksize of
// accumulated samples in analysis
pcm = new float[vi.channels][];
{
for (int i = 0; i < vi.channels; i++) {
pcm[i] = new float[pcm_storage];
}
}
// all 1 (large block) or 0 (small block)
// explicitly set for the sake of clarity
lW = 0; // previous window size
W = 0; // current window size
// all vector indexes; multiples of samples_per_envelope_step
centerW = vi.blocksizes[1] / 2;
pcm_current = centerW;
// initialize all the mapping/backend lookups
mode = new Object[vi.modes];
for (int i = 0; i < vi.modes; i++) {
int mapnum = vi.mode_param[i].mapping;
int maptype = vi.map_type[mapnum];
mode[i] = FuncMapping.mapping_P[maptype].look(this, vi.mode_param[i], vi.map_param[mapnum]);
}
return (0);
}
public int synthesis_init(Info vi) {
init(vi, false);
// Adjust centerW to allow an easier mechanism for determining output
pcm_returned = centerW;
centerW -= vi.blocksizes[W] / 4 + vi.blocksizes[lW] / 4;
granulepos = -1;
sequence = -1;
return (0);
}
DspState(Info vi) {
this();
init(vi, false);
// Adjust centerW to allow an easier mechanism for determining output
pcm_returned = centerW;
centerW -= vi.blocksizes[W] / 4 + vi.blocksizes[lW] / 4;
granulepos = -1;
sequence = -1;
}
// Unike in analysis, the window is only partially applied for each
// block. The time domain envelope is not yet handled at the point of
// calling (as it relies on the previous block).
public int synthesis_blockin(Block vb) {
// Shift out any PCM/multipliers that we returned previously
// centerW is currently the center of the last block added
if (centerW > vi.blocksizes[1] / 2 && pcm_returned > 8192) {
// don't shift too much; we need to have a minimum PCM buffer of
// 1/2 long block
int shiftPCM = centerW - vi.blocksizes[1] / 2;
shiftPCM = (pcm_returned < shiftPCM ? pcm_returned : shiftPCM);
pcm_current -= shiftPCM;
centerW -= shiftPCM;
pcm_returned -= shiftPCM;
if (shiftPCM != 0) {
for (int i = 0; i < vi.channels; i++) {
System.arraycopy(pcm[i], shiftPCM, pcm[i], 0, pcm_current);
}
}
}
lW = W;
W = vb.W;
nW = -1;
glue_bits += vb.glue_bits;
time_bits += vb.time_bits;
floor_bits += vb.floor_bits;
res_bits += vb.res_bits;
if (sequence + 1 != vb.sequence)
granulepos = -1; // out of sequence; lose count
sequence = vb.sequence;
{
int sizeW = vi.blocksizes[W];
int _centerW = centerW + vi.blocksizes[lW] / 4 + sizeW / 4;
int beginW = _centerW - sizeW / 2;
int endW = beginW + sizeW;
int beginSl = 0;
int endSl = 0;
// Do we have enough PCM/mult storage for the block?
if (endW > pcm_storage) {
// expand the storage
pcm_storage = endW + vi.blocksizes[1];
for (int i = 0; i < vi.channels; i++) {
float[] foo = new float[pcm_storage];
System.arraycopy(pcm[i], 0, foo, 0, pcm[i].length);
pcm[i] = foo;
}
}
// overlap/add PCM
switch (W) {
case 0:
beginSl = 0;
endSl = vi.blocksizes[0] / 2;
break;
case 1:
beginSl = vi.blocksizes[1] / 4 - vi.blocksizes[lW] / 4;
endSl = beginSl + vi.blocksizes[lW] / 2;
break;
}
for (int j = 0; j < vi.channels; j++) {
int _pcm = beginW;
// the overlap/add section
int i = 0;
for (i = beginSl; i < endSl; i++) {
pcm[j][_pcm + i] += vb.pcm[j][i];
}
// the remaining section
for (; i < sizeW; i++) {
pcm[j][_pcm + i] = vb.pcm[j][i];
}
}
// track the frame number... This is for convenience, but also
// making sure our last packet doesn't end with added padding. If
// the last packet is partial, the number of samples we'll have to
// return will be past the vb->granulepos.
//
// This is not foolproof! It will be confused if we begin
// decoding at the last page after a seek or hole. In that case,
// we don't have a starting point to judge where the last frame
// is. For this reason, vorbisfile will always try to make sure
// it reads the last two marked pages in proper sequence
if (granulepos == -1) {
granulepos = vb.granulepos;
} else {
granulepos += (_centerW - centerW);
if (vb.granulepos != -1 && granulepos != vb.granulepos) {
if (granulepos > vb.granulepos && vb.eofflag != 0) {
// partial last frame. Strip the padding off
_centerW -= (granulepos - vb.granulepos);
} // else{ Shouldn't happen *unless* the bitstream is out of
// spec. Either way, believe the bitstream }
granulepos = vb.granulepos;
}
}
// Update, cleanup
centerW = _centerW;
pcm_current = endW;
if (vb.eofflag != 0)
eofflag = 1;
}
return (0);
}
// pcm==NULL indicates we just want the pending samples, no more
public int synthesis_pcmout(float[][][] _pcm, int[] index) {
if (pcm_returned < centerW) {
if (_pcm != null) {
for (int i = 0; i < vi.channels; i++) {
index[i] = pcm_returned;
}
_pcm[0] = pcm;
}
return (centerW - pcm_returned);
}
return (0);
}
public int synthesis_read(int bytes) {
if (bytes != 0 && pcm_returned + bytes > centerW)
return (-1);
pcm_returned += bytes;
return (0);
}
public void clear() {
}
}

View File

@ -0,0 +1,332 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/* JOrbis
* Copyright (C) 2000 ymnk, JCraft,Inc.
*
* Written by: 2000 ymnk<ymnk@jcraft.com>
*
* Many thanks to
* Monty <monty@xiph.org> and
* The XIPHOPHORUS Company http://www.xiph.org/ .
* JOrbis has been based on their awesome works, Vorbis codec.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package com.jcraft.jorbis;
import com.jcraft.jogg.*;
class Floor0 extends FuncFloor {
void pack(Object i, Buffer opb) {
InfoFloor0 info = (InfoFloor0) i;
opb.write(info.order, 8);
opb.write(info.rate, 16);
opb.write(info.barkmap, 16);
opb.write(info.ampbits, 6);
opb.write(info.ampdB, 8);
opb.write(info.numbooks - 1, 4);
for (int j = 0; j < info.numbooks; j++)
opb.write(info.books[j], 8);
}
Object unpack(Info vi, Buffer opb) {
InfoFloor0 info = new InfoFloor0();
info.order = opb.read(8);
info.rate = opb.read(16);
info.barkmap = opb.read(16);
info.ampbits = opb.read(6);
info.ampdB = opb.read(8);
info.numbooks = opb.read(4) + 1;
if ((info.order < 1) || (info.rate < 1) || (info.barkmap < 1) || (info.numbooks < 1)) {
return (null);
}
for (int j = 0; j < info.numbooks; j++) {
info.books[j] = opb.read(8);
if (info.books[j] < 0 || info.books[j] >= vi.books) {
return (null);
}
}
return (info);
}
Object look(DspState vd, InfoMode mi, Object i) {
float scale;
Info vi = vd.vi;
InfoFloor0 info = (InfoFloor0) i;
LookFloor0 look = new LookFloor0();
look.m = info.order;
look.n = vi.blocksizes[mi.blockflag] / 2;
look.ln = info.barkmap;
look.vi = info;
look.lpclook.init(look.ln, look.m);
// we choose a scaling constant so that:
scale = look.ln / toBARK((float) (info.rate / 2.));
// the mapping from a linear scale to a smaller bark scale is
// straightforward. We do *not* make sure that the linear mapping
// does not skip bark-scale bins; the decoder simply skips them and
// the encoder may do what it wishes in filling them. They're
// necessary in some mapping combinations to keep the scale spacing
// accurate
look.linearmap = new int[look.n];
for (int j = 0; j < look.n; j++) {
int val = (int) Math.floor(toBARK((float) ((info.rate / 2.) / look.n * j)) * scale); // bark numbers
// represent band
// edges
if (val >= look.ln)
val = look.ln; // guard against the approximation
look.linearmap[j] = val;
}
return look;
}
static float toBARK(float f) {
return (float) (13.1 * Math.atan(.00074 * (f)) + 2.24 * Math.atan((f) * (f) * 1.85e-8) + 1e-4 * (f));
}
Object state(Object i) {
EchstateFloor0 state = new EchstateFloor0();
InfoFloor0 info = (InfoFloor0) i;
// a safe size if usually too big (dim==1)
state.codewords = new int[info.order];
state.curve = new float[info.barkmap];
state.frameno = -1;
return (state);
}
void free_info(Object i) {
}
void free_look(Object i) {
}
void free_state(Object vs) {
}
int forward(Block vb, Object i, float[] in, float[] out, Object vs) {
return 0;
}
float[] lsp = null;
int inverse(Block vb, Object i, float[] out) {
// System.err.println("Floor0.inverse "+i.getClass()+"]");
LookFloor0 look = (LookFloor0) i;
InfoFloor0 info = look.vi;
int ampraw = vb.opb.read(info.ampbits);
if (ampraw > 0) { // also handles the -1 out of data case
int maxval = (1 << info.ampbits) - 1;
float amp = (float) ampraw / maxval * info.ampdB;
int booknum = vb.opb.read(Util.ilog(info.numbooks));
if (booknum != -1 && booknum < info.numbooks) {
synchronized (this) {
if (lsp == null || lsp.length < look.m) {
lsp = new float[look.m];
} else {
for (int j = 0; j < look.m; j++)
lsp[j] = 0.f;
}
CodeBook b = vb.vd.fullbooks[info.books[booknum]];
float last = 0.f;
for (int j = 0; j < look.m; j++)
out[j] = 0.0f;
for (int j = 0; j < look.m; j += b.dim) {
if (b.decodevs(lsp, j, vb.opb, 1, -1) == -1) {
for (int k = 0; k < look.n; k++)
out[k] = 0.0f;
return (0);
}
}
for (int j = 0; j < look.m;) {
for (int k = 0; k < b.dim; k++, j++)
lsp[j] += last;
last = lsp[j - 1];
}
// take the coefficients back to a spectral envelope curve
Lsp.lsp_to_curve(out, look.linearmap, look.n, look.ln, lsp, look.m, amp, info.ampdB);
return (1);
}
}
}
return (0);
}
Object inverse1(Block vb, Object i, Object memo) {
LookFloor0 look = (LookFloor0) i;
InfoFloor0 info = look.vi;
float[] lsp = null;
if (memo instanceof float[]) {
lsp = (float[]) memo;
}
int ampraw = vb.opb.read(info.ampbits);
if (ampraw > 0) { // also handles the -1 out of data case
int maxval = (1 << info.ampbits) - 1;
float amp = (float) ampraw / maxval * info.ampdB;
int booknum = vb.opb.read(Util.ilog(info.numbooks));
if (booknum != -1 && booknum < info.numbooks) {
CodeBook b = vb.vd.fullbooks[info.books[booknum]];
float last = 0.f;
if (lsp == null || lsp.length < look.m + 1) {
lsp = new float[look.m + 1];
} else {
for (int j = 0; j < lsp.length; j++)
lsp[j] = 0.f;
}
for (int j = 0; j < look.m; j += b.dim) {
if (b.decodev_set(lsp, j, vb.opb, b.dim) == -1) {
return (null);
}
}
for (int j = 0; j < look.m;) {
for (int k = 0; k < b.dim; k++, j++)
lsp[j] += last;
last = lsp[j - 1];
}
lsp[look.m] = amp;
return (lsp);
}
}
return (null);
}
int inverse2(Block vb, Object i, Object memo, float[] out) {
LookFloor0 look = (LookFloor0) i;
InfoFloor0 info = look.vi;
if (memo != null) {
float[] lsp = (float[]) memo;
float amp = lsp[look.m];
Lsp.lsp_to_curve(out, look.linearmap, look.n, look.ln, lsp, look.m, amp, info.ampdB);
return (1);
}
for (int j = 0; j < look.n; j++) {
out[j] = 0.f;
}
return (0);
}
static float fromdB(float x) {
return (float) (Math.exp((x) * .11512925));
}
static void lsp_to_lpc(float[] lsp, float[] lpc, int m) {
int i, j, m2 = m / 2;
float[] O = new float[m2];
float[] E = new float[m2];
float A;
float[] Ae = new float[m2 + 1];
float[] Ao = new float[m2 + 1];
float B;
float[] Be = new float[m2];
float[] Bo = new float[m2];
float temp;
// even/odd roots setup
for (i = 0; i < m2; i++) {
O[i] = (float) (-2. * Math.cos(lsp[i * 2]));
E[i] = (float) (-2. * Math.cos(lsp[i * 2 + 1]));
}
// set up impulse response
for (j = 0; j < m2; j++) {
Ae[j] = 0.f;
Ao[j] = 1.f;
Be[j] = 0.f;
Bo[j] = 1.f;
}
Ao[j] = 1.f;
Ae[j] = 1.f;
// run impulse response
for (i = 1; i < m + 1; i++) {
A = B = 0.f;
for (j = 0; j < m2; j++) {
temp = O[j] * Ao[j] + Ae[j];
Ae[j] = Ao[j];
Ao[j] = A;
A += temp;
temp = E[j] * Bo[j] + Be[j];
Be[j] = Bo[j];
Bo[j] = B;
B += temp;
}
lpc[i - 1] = (A + Ao[j] + B - Ae[j]) / 2;
Ao[j] = A;
Ae[j] = B;
}
}
static void lpc_to_curve(float[] curve, float[] lpc, float amp, LookFloor0 l, String name, int frameno) {
// l->m+1 must be less than l->ln, but guard in case we get a bad stream
float[] lcurve = new float[Math.max(l.ln * 2, l.m * 2 + 2)];
if (amp == 0) {
for (int j = 0; j < l.n; j++)
curve[j] = 0.0f;
return;
}
l.lpclook.lpc_to_curve(lcurve, lpc, amp);
for (int i = 0; i < l.n; i++)
curve[i] = lcurve[l.linearmap[i]];
}
class InfoFloor0 {
int order;
int rate;
int barkmap;
int ampbits;
int ampdB;
int numbooks; // <= 16
int[] books = new int[16];
}
class LookFloor0 {
int n;
int ln;
int m;
int[] linearmap;
InfoFloor0 vi;
Lpc lpclook = new Lpc();
}
class EchstateFloor0 {
int[] codewords;
float[] curve;
long frameno;
long codes;
}
}

View File

@ -0,0 +1,584 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/* JOrbis
* Copyright (C) 2000 ymnk, JCraft,Inc.
*
* Written by: 2000 ymnk<ymnk@jcraft.com>
*
* Many thanks to
* Monty <monty@xiph.org> and
* The XIPHOPHORUS Company http://www.xiph.org/ .
* JOrbis has been based on their awesome works, Vorbis codec.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package com.jcraft.jorbis;
import com.jcraft.jogg.*;
class Floor1 extends FuncFloor {
static final int floor1_rangedb = 140;
static final int VIF_POSIT = 63;
void pack(Object i, Buffer opb) {
InfoFloor1 info = (InfoFloor1) i;
int count = 0;
int rangebits;
int maxposit = info.postlist[1];
int maxclass = -1;
/* save out partitions */
opb.write(info.partitions, 5); /* only 0 to 31 legal */
for (int j = 0; j < info.partitions; j++) {
opb.write(info.partitionclass[j], 4); /* only 0 to 15 legal */
if (maxclass < info.partitionclass[j])
maxclass = info.partitionclass[j];
}
/* save out partition classes */
for (int j = 0; j < maxclass + 1; j++) {
opb.write(info.class_dim[j] - 1, 3); /* 1 to 8 */
opb.write(info.class_subs[j], 2); /* 0 to 3 */
if (info.class_subs[j] != 0) {
opb.write(info.class_book[j], 8);
}
for (int k = 0; k < (1 << info.class_subs[j]); k++) {
opb.write(info.class_subbook[j][k] + 1, 8);
}
}
/* save out the post list */
opb.write(info.mult - 1, 2); /* only 1,2,3,4 legal now */
opb.write(Util.ilog2(maxposit), 4);
rangebits = Util.ilog2(maxposit);
for (int j = 0, k = 0; j < info.partitions; j++) {
count += info.class_dim[info.partitionclass[j]];
for (; k < count; k++) {
opb.write(info.postlist[k + 2], rangebits);
}
}
}
Object unpack(Info vi, Buffer opb) {
int count = 0, maxclass = -1, rangebits;
InfoFloor1 info = new InfoFloor1();
/* read partitions */
info.partitions = opb.read(5); /* only 0 to 31 legal */
for (int j = 0; j < info.partitions; j++) {
info.partitionclass[j] = opb.read(4); /* only 0 to 15 legal */
if (maxclass < info.partitionclass[j])
maxclass = info.partitionclass[j];
}
/* read partition classes */
for (int j = 0; j < maxclass + 1; j++) {
info.class_dim[j] = opb.read(3) + 1; /* 1 to 8 */
info.class_subs[j] = opb.read(2); /* 0,1,2,3 bits */
if (info.class_subs[j] < 0) {
info.free();
return (null);
}
if (info.class_subs[j] != 0) {
info.class_book[j] = opb.read(8);
}
if (info.class_book[j] < 0 || info.class_book[j] >= vi.books) {
info.free();
return (null);
}
for (int k = 0; k < (1 << info.class_subs[j]); k++) {
info.class_subbook[j][k] = opb.read(8) - 1;
if (info.class_subbook[j][k] < -1 || info.class_subbook[j][k] >= vi.books) {
info.free();
return (null);
}
}
}
/* read the post list */
info.mult = opb.read(2) + 1; /* only 1,2,3,4 legal now */
rangebits = opb.read(4);
for (int j = 0, k = 0; j < info.partitions; j++) {
count += info.class_dim[info.partitionclass[j]];
for (; k < count; k++) {
int t = info.postlist[k + 2] = opb.read(rangebits);
if (t < 0 || t >= (1 << rangebits)) {
info.free();
return (null);
}
}
}
info.postlist[0] = 0;
info.postlist[1] = 1 << rangebits;
return (info);
}
Object look(DspState vd, InfoMode mi, Object i) {
int _n = 0;
int[] sortpointer = new int[VIF_POSIT + 2];
// Info vi=vd.vi;
InfoFloor1 info = (InfoFloor1) i;
LookFloor1 look = new LookFloor1();
look.vi = info;
look.n = info.postlist[1];
/*
* we drop each position value in-between already decoded values, and use linear
* interpolation to predict each new value past the edges. The positions are
* read in the order of the position list... we precompute the bounding
* positions in the lookup. Of course, the neighbors can change (if a position
* is declined), but this is an initial mapping
*/
for (int j = 0; j < info.partitions; j++) {
_n += info.class_dim[info.partitionclass[j]];
}
_n += 2;
look.posts = _n;
/* also store a sorted position index */
for (int j = 0; j < _n; j++) {
sortpointer[j] = j;
}
// qsort(sortpointer,n,sizeof(int),icomp); // !!
int foo;
for (int j = 0; j < _n - 1; j++) {
for (int k = j; k < _n; k++) {
if (info.postlist[sortpointer[j]] > info.postlist[sortpointer[k]]) {
foo = sortpointer[k];
sortpointer[k] = sortpointer[j];
sortpointer[j] = foo;
}
}
}
/* points from sort order back to range number */
for (int j = 0; j < _n; j++) {
look.forward_index[j] = sortpointer[j];
}
/* points from range order to sorted position */
for (int j = 0; j < _n; j++) {
look.reverse_index[look.forward_index[j]] = j;
}
/* we actually need the post values too */
for (int j = 0; j < _n; j++) {
look.sorted_index[j] = info.postlist[look.forward_index[j]];
}
/* quantize values to multiplier spec */
switch (info.mult) {
case 1: /* 1024 -> 256 */
look.quant_q = 256;
break;
case 2: /* 1024 -> 128 */
look.quant_q = 128;
break;
case 3: /* 1024 -> 86 */
look.quant_q = 86;
break;
case 4: /* 1024 -> 64 */
look.quant_q = 64;
break;
default:
look.quant_q = -1;
}
/*
* discover our neighbors for decode where we don't use fit flags (that would
* push the neighbors outward)
*/
for (int j = 0; j < _n - 2; j++) {
int lo = 0;
int hi = 1;
int lx = 0;
int hx = look.n;
int currentx = info.postlist[j + 2];
for (int k = 0; k < j + 2; k++) {
int x = info.postlist[k];
if (x > lx && x < currentx) {
lo = k;
lx = x;
}
if (x < hx && x > currentx) {
hi = k;
hx = x;
}
}
look.loneighbor[j] = lo;
look.hineighbor[j] = hi;
}
return look;
}
void free_info(Object i) {
}
void free_look(Object i) {
}
void free_state(Object vs) {
}
int forward(Block vb, Object i, float[] in, float[] out, Object vs) {
return 0;
}
Object inverse1(Block vb, Object ii, Object memo) {
LookFloor1 look = (LookFloor1) ii;
InfoFloor1 info = look.vi;
CodeBook[] books = vb.vd.fullbooks;
/* unpack wrapped/predicted values from stream */
if (vb.opb.read(1) == 1) {
int[] fit_value = null;
if (memo instanceof int[]) {
fit_value = (int[]) memo;
}
if (fit_value == null || fit_value.length < look.posts) {
fit_value = new int[look.posts];
} else {
for (int i = 0; i < fit_value.length; i++)
fit_value[i] = 0;
}
fit_value[0] = vb.opb.read(Util.ilog(look.quant_q - 1));
fit_value[1] = vb.opb.read(Util.ilog(look.quant_q - 1));
/* partition by partition */
for (int i = 0, j = 2; i < info.partitions; i++) {
int clss = info.partitionclass[i];
int cdim = info.class_dim[clss];
int csubbits = info.class_subs[clss];
int csub = 1 << csubbits;
int cval = 0;
/* decode the partition's first stage cascade value */
if (csubbits != 0) {
cval = books[info.class_book[clss]].decode(vb.opb);
if (cval == -1) {
return (null);
}
}
for (int k = 0; k < cdim; k++) {
int book = info.class_subbook[clss][cval & (csub - 1)];
cval >>>= csubbits;
if (book >= 0) {
if ((fit_value[j + k] = books[book].decode(vb.opb)) == -1) {
return (null);
}
} else {
fit_value[j + k] = 0;
}
}
j += cdim;
}
/* unwrap positive values and reconsitute via linear interpolation */
for (int i = 2; i < look.posts; i++) {
int predicted = render_point(info.postlist[look.loneighbor[i - 2]],
info.postlist[look.hineighbor[i - 2]], fit_value[look.loneighbor[i - 2]],
fit_value[look.hineighbor[i - 2]], info.postlist[i]);
int hiroom = look.quant_q - predicted;
int loroom = predicted;
int room = (hiroom < loroom ? hiroom : loroom) << 1;
int val = fit_value[i];
if (val != 0) {
if (val >= room) {
if (hiroom > loroom) {
val = val - loroom;
} else {
val = -1 - (val - hiroom);
}
} else {
if ((val & 1) != 0) {
val = -((val + 1) >>> 1);
} else {
val >>= 1;
}
}
fit_value[i] = val + predicted;
fit_value[look.loneighbor[i - 2]] &= 0x7fff;
fit_value[look.hineighbor[i - 2]] &= 0x7fff;
} else {
fit_value[i] = predicted | 0x8000;
}
}
return (fit_value);
}
return (null);
}
private static int render_point(int x0, int x1, int y0, int y1, int x) {
y0 &= 0x7fff; /* mask off flag */
y1 &= 0x7fff;
{
int dy = y1 - y0;
int adx = x1 - x0;
int ady = Math.abs(dy);
int err = ady * (x - x0);
int off = (int) (err / adx);
if (dy < 0)
return (y0 - off);
return (y0 + off);
}
}
int inverse2(Block vb, Object i, Object memo, float[] out) {
LookFloor1 look = (LookFloor1) i;
InfoFloor1 info = look.vi;
int n = vb.vd.vi.blocksizes[vb.mode] / 2;
if (memo != null) {
/* render the lines */
int[] fit_value = (int[]) memo;
int hx = 0;
int lx = 0;
int ly = fit_value[0] * info.mult;
for (int j = 1; j < look.posts; j++) {
int current = look.forward_index[j];
int hy = fit_value[current] & 0x7fff;
if (hy == fit_value[current]) {
hy *= info.mult;
hx = info.postlist[current];
render_line(lx, hx, ly, hy, out);
lx = hx;
ly = hy;
}
}
for (int j = hx; j < n; j++) {
out[j] *= out[j - 1]; /* be certain */
}
return (1);
}
for (int j = 0; j < n; j++) {
out[j] = 0.f;
}
return (0);
}
private static float[] FLOOR_fromdB_LOOKUP = { 1.0649863e-07F, 1.1341951e-07F, 1.2079015e-07F, 1.2863978e-07F,
1.3699951e-07F, 1.4590251e-07F, 1.5538408e-07F, 1.6548181e-07F, 1.7623575e-07F, 1.8768855e-07F,
1.9988561e-07F, 2.128753e-07F, 2.2670913e-07F, 2.4144197e-07F, 2.5713223e-07F, 2.7384213e-07F,
2.9163793e-07F, 3.1059021e-07F, 3.3077411e-07F, 3.5226968e-07F, 3.7516214e-07F, 3.9954229e-07F,
4.2550680e-07F, 4.5315863e-07F, 4.8260743e-07F, 5.1396998e-07F, 5.4737065e-07F, 5.8294187e-07F,
6.2082472e-07F, 6.6116941e-07F, 7.0413592e-07F, 7.4989464e-07F, 7.9862701e-07F, 8.5052630e-07F,
9.0579828e-07F, 9.6466216e-07F, 1.0273513e-06F, 1.0941144e-06F, 1.1652161e-06F, 1.2409384e-06F,
1.3215816e-06F, 1.4074654e-06F, 1.4989305e-06F, 1.5963394e-06F, 1.7000785e-06F, 1.8105592e-06F,
1.9282195e-06F, 2.0535261e-06F, 2.1869758e-06F, 2.3290978e-06F, 2.4804557e-06F, 2.6416497e-06F,
2.8133190e-06F, 2.9961443e-06F, 3.1908506e-06F, 3.3982101e-06F, 3.6190449e-06F, 3.8542308e-06F,
4.1047004e-06F, 4.3714470e-06F, 4.6555282e-06F, 4.9580707e-06F, 5.2802740e-06F, 5.6234160e-06F,
5.9888572e-06F, 6.3780469e-06F, 6.7925283e-06F, 7.2339451e-06F, 7.7040476e-06F, 8.2047000e-06F,
8.7378876e-06F, 9.3057248e-06F, 9.9104632e-06F, 1.0554501e-05F, 1.1240392e-05F, 1.1970856e-05F,
1.2748789e-05F, 1.3577278e-05F, 1.4459606e-05F, 1.5399272e-05F, 1.6400004e-05F, 1.7465768e-05F,
1.8600792e-05F, 1.9809576e-05F, 2.1096914e-05F, 2.2467911e-05F, 2.3928002e-05F, 2.5482978e-05F,
2.7139006e-05F, 2.8902651e-05F, 3.0780908e-05F, 3.2781225e-05F, 3.4911534e-05F, 3.7180282e-05F,
3.9596466e-05F, 4.2169667e-05F, 4.4910090e-05F, 4.7828601e-05F, 5.0936773e-05F, 5.4246931e-05F,
5.7772202e-05F, 6.1526565e-05F, 6.5524908e-05F, 6.9783085e-05F, 7.4317983e-05F, 7.9147585e-05F,
8.4291040e-05F, 8.9768747e-05F, 9.5602426e-05F, 0.00010181521F, 0.00010843174F, 0.00011547824F,
0.00012298267F, 0.00013097477F, 0.00013948625F, 0.00014855085F, 0.00015820453F, 0.00016848555F,
0.00017943469F, 0.00019109536F, 0.00020351382F, 0.00021673929F, 0.00023082423F, 0.00024582449F,
0.00026179955F, 0.00027881276F, 0.00029693158F, 0.00031622787F, 0.00033677814F, 0.00035866388F,
0.00038197188F, 0.00040679456F, 0.00043323036F, 0.00046138411F, 0.00049136745F, 0.00052329927F,
0.00055730621F, 0.00059352311F, 0.00063209358F, 0.00067317058F, 0.00071691700F, 0.00076350630F,
0.00081312324F, 0.00086596457F, 0.00092223983F, 0.00098217216F, 0.0010459992F, 0.0011139742F, 0.0011863665F,
0.0012634633F, 0.0013455702F, 0.0014330129F, 0.0015261382F, 0.0016253153F, 0.0017309374F, 0.0018434235F,
0.0019632195F, 0.0020908006F, 0.0022266726F, 0.0023713743F, 0.0025254795F, 0.0026895994F, 0.0028643847F,
0.0030505286F, 0.0032487691F, 0.0034598925F, 0.0036847358F, 0.0039241906F, 0.0041792066F, 0.0044507950F,
0.0047400328F, 0.0050480668F, 0.0053761186F, 0.0057254891F, 0.0060975636F, 0.0064938176F, 0.0069158225F,
0.0073652516F, 0.0078438871F, 0.0083536271F, 0.0088964928F, 0.009474637F, 0.010090352F, 0.010746080F,
0.011444421F, 0.012188144F, 0.012980198F, 0.013823725F, 0.014722068F, 0.015678791F, 0.016697687F,
0.017782797F, 0.018938423F, 0.020169149F, 0.021479854F, 0.022875735F, 0.024362330F, 0.025945531F,
0.027631618F, 0.029427276F, 0.031339626F, 0.033376252F, 0.035545228F, 0.037855157F, 0.040315199F,
0.042935108F, 0.045725273F, 0.048696758F, 0.051861348F, 0.055231591F, 0.058820850F, 0.062643361F,
0.066714279F, 0.071049749F, 0.075666962F, 0.080584227F, 0.085821044F, 0.091398179F, 0.097337747F,
0.10366330F, 0.11039993F, 0.11757434F, 0.12521498F, 0.13335215F, 0.14201813F, 0.15124727F, 0.16107617F,
0.17154380F, 0.18269168F, 0.19456402F, 0.20720788F, 0.22067342F, 0.23501402F, 0.25028656F, 0.26655159F,
0.28387361F, 0.30232132F, 0.32196786F, 0.34289114F, 0.36517414F, 0.38890521F, 0.41417847F, 0.44109412F,
0.46975890F, 0.50028648F, 0.53279791F, 0.56742212F, 0.60429640F, 0.64356699F, 0.68538959F, 0.72993007F,
0.77736504F, 0.82788260F, 0.88168307F, 0.9389798F, 1.F };
private static void render_line(int x0, int x1, int y0, int y1, float[] d) {
int dy = y1 - y0;
int adx = x1 - x0;
int ady = Math.abs(dy);
int base = dy / adx;
int sy = (dy < 0 ? base - 1 : base + 1);
int x = x0;
int y = y0;
int err = 0;
ady -= Math.abs(base * adx);
d[x] *= FLOOR_fromdB_LOOKUP[y];
while (++x < x1) {
err = err + ady;
if (err >= adx) {
err -= adx;
y += sy;
} else {
y += base;
}
d[x] *= FLOOR_fromdB_LOOKUP[y];
}
}
class InfoFloor1 {
static final int VIF_POSIT = 63;
static final int VIF_CLASS = 16;
static final int VIF_PARTS = 31;
int partitions; /* 0 to 31 */
int[] partitionclass = new int[VIF_PARTS]; /* 0 to 15 */
int[] class_dim = new int[VIF_CLASS]; /* 1 to 8 */
int[] class_subs = new int[VIF_CLASS]; /* 0,1,2,3 (bits: 1<<n poss) */
int[] class_book = new int[VIF_CLASS]; /* subs ^ dim entries */
int[][] class_subbook = new int[VIF_CLASS][]; /* [VIF_CLASS][subs] */
int mult; /* 1 2 3 or 4 */
int[] postlist = new int[VIF_POSIT + 2]; /* first two implicit */
/* encode side analysis parameters */
float maxover;
float maxunder;
float maxerr;
int twofitminsize;
int twofitminused;
int twofitweight;
float twofitatten;
int unusedminsize;
int unusedmin_n;
int n;
InfoFloor1() {
for (int i = 0; i < class_subbook.length; i++) {
class_subbook[i] = new int[8];
}
}
void free() {
partitionclass = null;
class_dim = null;
class_subs = null;
class_book = null;
class_subbook = null;
postlist = null;
}
Object copy_info() {
InfoFloor1 info = this;
InfoFloor1 ret = new InfoFloor1();
ret.partitions = info.partitions;
System.arraycopy(info.partitionclass, 0, ret.partitionclass, 0, VIF_PARTS);
System.arraycopy(info.class_dim, 0, ret.class_dim, 0, VIF_CLASS);
System.arraycopy(info.class_subs, 0, ret.class_subs, 0, VIF_CLASS);
System.arraycopy(info.class_book, 0, ret.class_book, 0, VIF_CLASS);
for (int j = 0; j < VIF_CLASS; j++) {
System.arraycopy(info.class_subbook[j], 0, ret.class_subbook[j], 0, 8);
}
ret.mult = info.mult;
System.arraycopy(info.postlist, 0, ret.postlist, 0, VIF_POSIT + 2);
ret.maxover = info.maxover;
ret.maxunder = info.maxunder;
ret.maxerr = info.maxerr;
ret.twofitminsize = info.twofitminsize;
ret.twofitminused = info.twofitminused;
ret.twofitweight = info.twofitweight;
ret.twofitatten = info.twofitatten;
ret.unusedminsize = info.unusedminsize;
ret.unusedmin_n = info.unusedmin_n;
ret.n = info.n;
return (ret);
}
}
class LookFloor1 {
static final int VIF_POSIT = 63;
int[] sorted_index = new int[VIF_POSIT + 2];
int[] forward_index = new int[VIF_POSIT + 2];
int[] reverse_index = new int[VIF_POSIT + 2];
int[] hineighbor = new int[VIF_POSIT];
int[] loneighbor = new int[VIF_POSIT];
int posts;
int n;
int quant_q;
InfoFloor1 vi;
int phrasebits;
int postbits;
int frames;
void free() {
sorted_index = null;
forward_index = null;
reverse_index = null;
hineighbor = null;
loneighbor = null;
}
}
class Lsfit_acc {
long x0;
long x1;
long xa;
long ya;
long x2a;
long y2a;
long xya;
long n;
long an;
long un;
long edgey0;
long edgey1;
}
class EchstateFloor1 {
int[] codewords;
float[] curve;
long frameno;
long codes;
}
}

View File

@ -0,0 +1,52 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/* JOrbis
* Copyright (C) 2000 ymnk, JCraft,Inc.
*
* Written by: 2000 ymnk<ymnk@jcraft.com>
*
* Many thanks to
* Monty <monty@xiph.org> and
* The XIPHOPHORUS Company http://www.xiph.org/ .
* JOrbis has been based on their awesome works, Vorbis codec.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package com.jcraft.jorbis;
import com.jcraft.jogg.*;
abstract class FuncFloor {
public static FuncFloor[] floor_P = { new Floor0(), new Floor1() };
abstract void pack(Object i, Buffer opb);
abstract Object unpack(Info vi, Buffer opb);
abstract Object look(DspState vd, InfoMode mi, Object i);
abstract void free_info(Object i);
abstract void free_look(Object i);
abstract void free_state(Object vs);
abstract int forward(Block vb, Object i, float[] in, float[] out, Object vs);
abstract Object inverse1(Block vb, Object i, Object memo);
abstract int inverse2(Block vb, Object i, Object memo, float[] out);
}

View File

@ -0,0 +1,45 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/* JOrbis
* Copyright (C) 2000 ymnk, JCraft,Inc.
*
* Written by: 2000 ymnk<ymnk@jcraft.com>
*
* Many thanks to
* Monty <monty@xiph.org> and
* The XIPHOPHORUS Company http://www.xiph.org/ .
* JOrbis has been based on their awesome works, Vorbis codec.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package com.jcraft.jorbis;
import com.jcraft.jogg.*;
abstract class FuncMapping {
public static FuncMapping[] mapping_P = { new Mapping0() };
abstract void pack(Info info, Object imap, Buffer buffer);
abstract Object unpack(Info info, Buffer buffer);
abstract Object look(DspState vd, InfoMode vm, Object m);
abstract void free_info(Object imap);
abstract void free_look(Object imap);
abstract int inverse(Block vd, Object lm);
}

View File

@ -0,0 +1,45 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/* JOrbis
* Copyright (C) 2000 ymnk, JCraft,Inc.
*
* Written by: 2000 ymnk<ymnk@jcraft.com>
*
* Many thanks to
* Monty <monty@xiph.org> and
* The XIPHOPHORUS Company http://www.xiph.org/ .
* JOrbis has been based on their awesome works, Vorbis codec.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package com.jcraft.jorbis;
import com.jcraft.jogg.*;
abstract class FuncResidue {
public static FuncResidue[] residue_P = { new Residue0(), new Residue1(), new Residue2() };
abstract void pack(Object vr, Buffer opb);
abstract Object unpack(Info vi, Buffer opb);
abstract Object look(DspState vd, InfoMode vm, Object vr);
abstract void free_info(Object i);
abstract void free_look(Object i);
abstract int inverse(Block vb, Object vl, float[][] in, int[] nonzero, int ch);
}

View File

@ -0,0 +1,45 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/* JOrbis
* Copyright (C) 2000 ymnk, JCraft,Inc.
*
* Written by: 2000 ymnk<ymnk@jcraft.com>
*
* Many thanks to
* Monty <monty@xiph.org> and
* The XIPHOPHORUS Company http://www.xiph.org/ .
* JOrbis has been based on their awesome works, Vorbis codec.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package com.jcraft.jorbis;
import com.jcraft.jogg.*;
abstract class FuncTime {
public static FuncTime[] time_P = { new Time0() };
abstract void pack(Object i, Buffer opb);
abstract Object unpack(Info vi, Buffer opb);
abstract Object look(DspState vd, InfoMode vm, Object i);
abstract void free_info(Object i);
abstract void free_look(Object i);
abstract int inverse(Block vb, Object i, float[] in, float[] out);
}

View File

@ -0,0 +1,468 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/* JOrbis
* Copyright (C) 2000 ymnk, JCraft,Inc.
*
* Written by: 2000 ymnk<ymnk@jcraft.com>
*
* Many thanks to
* Monty <monty@xiph.org> and
* The XIPHOPHORUS Company http://www.xiph.org/ .
* JOrbis has been based on their awesome works, Vorbis codec.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package com.jcraft.jorbis;
import com.jcraft.jogg.*;
public class Info {
private static final int OV_EBADPACKET = -136;
private static final int OV_ENOTAUDIO = -135;
private static byte[] _vorbis = "vorbis".getBytes();
private static final int VI_TIMEB = 1;
// private static final int VI_FLOORB=1;
private static final int VI_FLOORB = 2;
// private static final int VI_RESB=1;
private static final int VI_RESB = 3;
private static final int VI_MAPB = 1;
private static final int VI_WINDOWB = 1;
public int version;
public int channels;
public int rate;
// The below bitrate declarations are *hints*.
// Combinations of the three values carry the following implications:
//
// all three set to the same value:
// implies a fixed rate bitstream
// only nominal set:
// implies a VBR stream that averages the nominal bitrate. No hard
// upper/lower limit
// upper and or lower set:
// implies a VBR bitstream that obeys the bitrate limits. nominal
// may also be set to give a nominal rate.
// none set:
// the coder does not care to speculate.
int bitrate_upper;
int bitrate_nominal;
int bitrate_lower;
// Vorbis supports only short and long blocks, but allows the
// encoder to choose the sizes
int[] blocksizes = new int[2];
// modes are the primary means of supporting on-the-fly different
// blocksizes, different channel mappings (LR or mid-side),
// different residue backends, etc. Each mode consists of a
// blocksize flag and a mapping (along with the mapping setup
int modes;
int maps;
int times;
int floors;
int residues;
int books;
int psys; // encode only
InfoMode[] mode_param = null;
int[] map_type = null;
Object[] map_param = null;
int[] time_type = null;
Object[] time_param = null;
int[] floor_type = null;
Object[] floor_param = null;
int[] residue_type = null;
Object[] residue_param = null;
StaticCodeBook[] book_param = null;
PsyInfo[] psy_param = new PsyInfo[64]; // encode only
// for block long/sort tuning; encode only
int envelopesa;
float preecho_thresh;
float preecho_clamp;
// used by synthesis, which has a full, alloced vi
public void init() {
rate = 0;
}
public void clear() {
for (int i = 0; i < modes; i++) {
mode_param[i] = null;
}
mode_param = null;
for (int i = 0; i < maps; i++) { // unpack does the range checking
FuncMapping.mapping_P[map_type[i]].free_info(map_param[i]);
}
map_param = null;
for (int i = 0; i < times; i++) { // unpack does the range checking
FuncTime.time_P[time_type[i]].free_info(time_param[i]);
}
time_param = null;
for (int i = 0; i < floors; i++) { // unpack does the range checking
FuncFloor.floor_P[floor_type[i]].free_info(floor_param[i]);
}
floor_param = null;
for (int i = 0; i < residues; i++) { // unpack does the range checking
FuncResidue.residue_P[residue_type[i]].free_info(residue_param[i]);
}
residue_param = null;
// the static codebooks *are* freed if you call info_clear, because
// decode side does alloc a 'static' codebook. Calling clear on the
// full codebook does not clear the static codebook (that's our
// responsibility)
for (int i = 0; i < books; i++) {
// just in case the decoder pre-cleared to save space
if (book_param[i] != null) {
book_param[i].clear();
book_param[i] = null;
}
}
// if(vi->book_param)free(vi->book_param);
book_param = null;
for (int i = 0; i < psys; i++) {
psy_param[i].free();
}
}
// Header packing/unpacking
int unpack_info(Buffer opb) {
version = opb.read(32);
if (version != 0)
return (-1);
channels = opb.read(8);
rate = opb.read(32);
bitrate_upper = opb.read(32);
bitrate_nominal = opb.read(32);
bitrate_lower = opb.read(32);
blocksizes[0] = 1 << opb.read(4);
blocksizes[1] = 1 << opb.read(4);
if ((rate < 1) || (channels < 1) || (blocksizes[0] < 8) || (blocksizes[1] < blocksizes[0])
|| (opb.read(1) != 1)) {
clear();
return (-1);
}
return (0);
}
// all of the real encoding details are here. The modes, books,
// everything
int unpack_books(Buffer opb) {
books = opb.read(8) + 1;
if (book_param == null || book_param.length != books)
book_param = new StaticCodeBook[books];
for (int i = 0; i < books; i++) {
book_param[i] = new StaticCodeBook();
if (book_param[i].unpack(opb) != 0) {
clear();
return (-1);
}
}
// time backend settings
times = opb.read(6) + 1;
if (time_type == null || time_type.length != times)
time_type = new int[times];
if (time_param == null || time_param.length != times)
time_param = new Object[times];
for (int i = 0; i < times; i++) {
time_type[i] = opb.read(16);
if (time_type[i] < 0 || time_type[i] >= VI_TIMEB) {
clear();
return (-1);
}
time_param[i] = FuncTime.time_P[time_type[i]].unpack(this, opb);
if (time_param[i] == null) {
clear();
return (-1);
}
}
// floor backend settings
floors = opb.read(6) + 1;
if (floor_type == null || floor_type.length != floors)
floor_type = new int[floors];
if (floor_param == null || floor_param.length != floors)
floor_param = new Object[floors];
for (int i = 0; i < floors; i++) {
floor_type[i] = opb.read(16);
if (floor_type[i] < 0 || floor_type[i] >= VI_FLOORB) {
clear();
return (-1);
}
floor_param[i] = FuncFloor.floor_P[floor_type[i]].unpack(this, opb);
if (floor_param[i] == null) {
clear();
return (-1);
}
}
// residue backend settings
residues = opb.read(6) + 1;
if (residue_type == null || residue_type.length != residues)
residue_type = new int[residues];
if (residue_param == null || residue_param.length != residues)
residue_param = new Object[residues];
for (int i = 0; i < residues; i++) {
residue_type[i] = opb.read(16);
if (residue_type[i] < 0 || residue_type[i] >= VI_RESB) {
clear();
return (-1);
}
residue_param[i] = FuncResidue.residue_P[residue_type[i]].unpack(this, opb);
if (residue_param[i] == null) {
clear();
return (-1);
}
}
// map backend settings
maps = opb.read(6) + 1;
if (map_type == null || map_type.length != maps)
map_type = new int[maps];
if (map_param == null || map_param.length != maps)
map_param = new Object[maps];
for (int i = 0; i < maps; i++) {
map_type[i] = opb.read(16);
if (map_type[i] < 0 || map_type[i] >= VI_MAPB) {
clear();
return (-1);
}
map_param[i] = FuncMapping.mapping_P[map_type[i]].unpack(this, opb);
if (map_param[i] == null) {
clear();
return (-1);
}
}
// mode settings
modes = opb.read(6) + 1;
if (mode_param == null || mode_param.length != modes)
mode_param = new InfoMode[modes];
for (int i = 0; i < modes; i++) {
mode_param[i] = new InfoMode();
mode_param[i].blockflag = opb.read(1);
mode_param[i].windowtype = opb.read(16);
mode_param[i].transformtype = opb.read(16);
mode_param[i].mapping = opb.read(8);
if ((mode_param[i].windowtype >= VI_WINDOWB) || (mode_param[i].transformtype >= VI_WINDOWB)
|| (mode_param[i].mapping >= maps)) {
clear();
return (-1);
}
}
if (opb.read(1) != 1) {
clear();
return (-1);
}
return (0);
}
// The Vorbis header is in three packets; the initial small packet in
// the first page that identifies basic parameters, a second packet
// with bitstream comments and a third packet that holds the
// codebook.
public int synthesis_headerin(Comment vc, Packet op) {
Buffer opb = new Buffer();
if (op != null) {
opb.readinit(op.packet_base, op.packet, op.bytes);
// Which of the three types of header is this?
// Also verify header-ness, vorbis
{
byte[] buffer = new byte[6];
int packtype = opb.read(8);
opb.read(buffer, 6);
if (buffer[0] != 'v' || buffer[1] != 'o' || buffer[2] != 'r' || buffer[3] != 'b' || buffer[4] != 'i'
|| buffer[5] != 's') {
// not a vorbis header
return (-1);
}
switch (packtype) {
case 0x01: // least significant *bit* is read first
if (op.b_o_s == 0) {
// Not the initial packet
return (-1);
}
if (rate != 0) {
// previously initialized info header
return (-1);
}
return (unpack_info(opb));
case 0x03: // least significant *bit* is read first
if (rate == 0) {
// um... we didn't get the initial header
return (-1);
}
return (vc.unpack(opb));
case 0x05: // least significant *bit* is read first
if (rate == 0 || vc.vendor == null) {
// um... we didn;t get the initial header or comments yet
return (-1);
}
return (unpack_books(opb));
default:
// Not a valid vorbis header type
// return(-1);
break;
}
}
}
return (-1);
}
// pack side
int pack_info(Buffer opb) {
// preamble
opb.write(0x01, 8);
opb.write(_vorbis);
// basic information about the stream
opb.write(0x00, 32);
opb.write(channels, 8);
opb.write(rate, 32);
opb.write(bitrate_upper, 32);
opb.write(bitrate_nominal, 32);
opb.write(bitrate_lower, 32);
opb.write(Util.ilog2(blocksizes[0]), 4);
opb.write(Util.ilog2(blocksizes[1]), 4);
opb.write(1, 1);
return (0);
}
int pack_books(Buffer opb) {
opb.write(0x05, 8);
opb.write(_vorbis);
// books
opb.write(books - 1, 8);
for (int i = 0; i < books; i++) {
if (book_param[i].pack(opb) != 0) {
// goto err_out;
return (-1);
}
}
// times
opb.write(times - 1, 6);
for (int i = 0; i < times; i++) {
opb.write(time_type[i], 16);
FuncTime.time_P[time_type[i]].pack(this.time_param[i], opb);
}
// floors
opb.write(floors - 1, 6);
for (int i = 0; i < floors; i++) {
opb.write(floor_type[i], 16);
FuncFloor.floor_P[floor_type[i]].pack(floor_param[i], opb);
}
// residues
opb.write(residues - 1, 6);
for (int i = 0; i < residues; i++) {
opb.write(residue_type[i], 16);
FuncResidue.residue_P[residue_type[i]].pack(residue_param[i], opb);
}
// maps
opb.write(maps - 1, 6);
for (int i = 0; i < maps; i++) {
opb.write(map_type[i], 16);
FuncMapping.mapping_P[map_type[i]].pack(this, map_param[i], opb);
}
// modes
opb.write(modes - 1, 6);
for (int i = 0; i < modes; i++) {
opb.write(mode_param[i].blockflag, 1);
opb.write(mode_param[i].windowtype, 16);
opb.write(mode_param[i].transformtype, 16);
opb.write(mode_param[i].mapping, 8);
}
opb.write(1, 1);
return (0);
}
public int blocksize(Packet op) {
// codec_setup_info
Buffer opb = new Buffer();
int mode;
opb.readinit(op.packet_base, op.packet, op.bytes);
/* Check the packet type */
if (opb.read(1) != 0) {
/* Oops. This is not an audio data packet */
return (OV_ENOTAUDIO);
}
{
int modebits = 0;
int v = modes;
while (v > 1) {
modebits++;
v >>>= 1;
}
/* read our mode and pre/post windowsize */
mode = opb.read(modebits);
}
if (mode == -1)
return (OV_EBADPACKET);
return (blocksizes[mode_param[mode].blockflag]);
}
public String toString() {
return "version:" + version + ", channels:" + channels + ", rate:" + rate
+ ", bitrate:" + bitrate_upper + "," + bitrate_nominal + ","
+ bitrate_lower;
}
}

View File

@ -0,0 +1,34 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/* JOrbis
* Copyright (C) 2000 ymnk, JCraft,Inc.
*
* Written by: 2000 ymnk<ymnk@jcraft.com>
*
* Many thanks to
* Monty <monty@xiph.org> and
* The XIPHOPHORUS Company http://www.xiph.org/ .
* JOrbis has been based on their awesome works, Vorbis codec.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package com.jcraft.jorbis;
class InfoMode {
int blockflag;
int windowtype;
int transformtype;
int mapping;
}

View File

@ -0,0 +1,40 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/* JOrbis
* Copyright (C) 2000 ymnk, JCraft,Inc.
*
* Written by: 2000 ymnk<ymnk@jcraft.com>
*
* Many thanks to
* Monty <monty@xiph.org> and
* The XIPHOPHORUS Company http://www.xiph.org/ .
* JOrbis has been based on their awesome works, Vorbis codec.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package com.jcraft.jorbis;
public class JOrbisException extends Exception {
private static final long serialVersionUID = 1L;
public JOrbisException() {
super();
}
public JOrbisException(String s) {
super("JOrbis: " + s);
}
}

View File

@ -0,0 +1,122 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/* JOrbis
* Copyright (C) 2000 ymnk, JCraft,Inc.
*
* Written by: 2000 ymnk<ymnk@jcraft.com>
*
* Many thanks to
* Monty <monty@xiph.org> and
* The XIPHOPHORUS Company http://www.xiph.org/ .
* JOrbis has been based on their awesome works, Vorbis codec.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package com.jcraft.jorbis;
class Lookup {
static final int COS_LOOKUP_SZ = 128;
static final float[] COS_LOOKUP = { +1.0000000000000f, +0.9996988186962f, +0.9987954562052f, +0.9972904566787f,
+0.9951847266722f, +0.9924795345987f, +0.9891765099648f, +0.9852776423889f, +0.9807852804032f,
+0.9757021300385f, +0.9700312531945f, +0.9637760657954f, +0.9569403357322f, +0.9495281805930f,
+0.9415440651830f, +0.9329927988347f, +0.9238795325113f, +0.9142097557035f, +0.9039892931234f,
+0.8932243011955f, +0.8819212643484f, +0.8700869911087f, +0.8577286100003f, +0.8448535652497f,
+0.8314696123025f, +0.8175848131516f, +0.8032075314806f, +0.7883464276266f, +0.7730104533627f,
+0.7572088465065f, +0.7409511253550f, +0.7242470829515f, +0.7071067811865f, +0.6895405447371f,
+0.6715589548470f, +0.6531728429538f, +0.6343932841636f, +0.6152315905806f, +0.5956993044924f,
+0.5758081914178f, +0.5555702330196f, +0.5349976198871f, +0.5141027441932f, +0.4928981922298f,
+0.4713967368260f, +0.4496113296546f, +0.4275550934303f, +0.4052413140050f, +0.3826834323651f,
+0.3598950365350f, +0.3368898533922f, +0.3136817403989f, +0.2902846772545f, +0.2667127574749f,
+0.2429801799033f, +0.2191012401569f, +0.1950903220161f, +0.1709618887603f, +0.1467304744554f,
+0.1224106751992f, +0.0980171403296f, +0.0735645635997f, +0.0490676743274f, +0.0245412285229f,
+0.0000000000000f, -0.0245412285229f, -0.0490676743274f, -0.0735645635997f, -0.0980171403296f,
-0.1224106751992f, -0.1467304744554f, -0.1709618887603f, -0.1950903220161f, -0.2191012401569f,
-0.2429801799033f, -0.2667127574749f, -0.2902846772545f, -0.3136817403989f, -0.3368898533922f,
-0.3598950365350f, -0.3826834323651f, -0.4052413140050f, -0.4275550934303f, -0.4496113296546f,
-0.4713967368260f, -0.4928981922298f, -0.5141027441932f, -0.5349976198871f, -0.5555702330196f,
-0.5758081914178f, -0.5956993044924f, -0.6152315905806f, -0.6343932841636f, -0.6531728429538f,
-0.6715589548470f, -0.6895405447371f, -0.7071067811865f, -0.7242470829515f, -0.7409511253550f,
-0.7572088465065f, -0.7730104533627f, -0.7883464276266f, -0.8032075314806f, -0.8175848131516f,
-0.8314696123025f, -0.8448535652497f, -0.8577286100003f, -0.8700869911087f, -0.8819212643484f,
-0.8932243011955f, -0.9039892931234f, -0.9142097557035f, -0.9238795325113f, -0.9329927988347f,
-0.9415440651830f, -0.9495281805930f, -0.9569403357322f, -0.9637760657954f, -0.9700312531945f,
-0.9757021300385f, -0.9807852804032f, -0.9852776423889f, -0.9891765099648f, -0.9924795345987f,
-0.9951847266722f, -0.9972904566787f, -0.9987954562052f, -0.9996988186962f, -1.0000000000000f, };
/* interpolated lookup based cos function, domain 0 to PI only */
static float coslook(float a) {
double d = a * (.31830989 * (float) COS_LOOKUP_SZ);
int i = (int) d;
return COS_LOOKUP[i] + ((float) (d - i)) * (COS_LOOKUP[i + 1] - COS_LOOKUP[i]);
}
static final int INVSQ_LOOKUP_SZ = 32;
static final float[] INVSQ_LOOKUP = { 1.414213562373f, 1.392621247646f, 1.371988681140f, 1.352246807566f,
1.333333333333f, 1.315191898443f, 1.297771369046f, 1.281025230441f, 1.264911064067f, 1.249390095109f,
1.234426799697f, 1.219988562661f, 1.206045378311f, 1.192569588000f, 1.179535649239f, 1.166919931983f,
1.154700538379f, 1.142857142857f, 1.131370849898f, 1.120224067222f, 1.109400392450f, 1.098884511590f,
1.088662107904f, 1.078719779941f, 1.069044967650f, 1.059625885652f, 1.050451462878f, 1.041511287847f,
1.032795558989f, 1.024295039463f, 1.016001016002f, 1.007905261358f, 1.000000000000f, };
/* interpolated 1./sqrt(p) where .5 <= p < 1. */
static float invsqlook(float a) {
double d = a * (2.f * (float) INVSQ_LOOKUP_SZ) - (float) INVSQ_LOOKUP_SZ;
int i = (int) d;
return INVSQ_LOOKUP[i] + ((float) (d - i)) * (INVSQ_LOOKUP[i + 1] - INVSQ_LOOKUP[i]);
}
static final int INVSQ2EXP_LOOKUP_MIN = -32;
static final int INVSQ2EXP_LOOKUP_MAX = 32;
static final float[] INVSQ2EXP_LOOKUP = { 65536.f, 46340.95001f, 32768.f, 23170.47501f, 16384.f, 11585.2375f,
8192.f, 5792.618751f, 4096.f, 2896.309376f, 2048.f, 1448.154688f, 1024.f, 724.0773439f, 512.f, 362.038672f,
256.f, 181.019336f, 128.f, 90.50966799f, 64.f, 45.254834f, 32.f, 22.627417f, 16.f, 11.3137085f, 8.f,
5.656854249f, 4.f, 2.828427125f, 2.f, 1.414213562f, 1.f, 0.7071067812f, 0.5f, 0.3535533906f, 0.25f,
0.1767766953f, 0.125f, 0.08838834765f, 0.0625f, 0.04419417382f, 0.03125f, 0.02209708691f, 0.015625f,
0.01104854346f, 0.0078125f, 0.005524271728f, 0.00390625f, 0.002762135864f, 0.001953125f, 0.001381067932f,
0.0009765625f, 0.000690533966f, 0.00048828125f, 0.000345266983f, 0.000244140625f, 0.0001726334915f,
0.0001220703125f, 8.631674575e-05f, 6.103515625e-05f, 4.315837288e-05f, 3.051757812e-05f, 2.157918644e-05f,
1.525878906e-05f, };
/* interpolated 1./sqrt(p) where .5 <= p < 1. */
static float invsq2explook(int a) {
return INVSQ2EXP_LOOKUP[a - INVSQ2EXP_LOOKUP_MIN];
}
static final int FROMdB_LOOKUP_SZ = 35;
static final int FROMdB2_LOOKUP_SZ = 32;
static final int FROMdB_SHIFT = 5;
static final int FROMdB2_SHIFT = 3;
static final int FROMdB2_MASK = 31;
static final float[] FROMdB_LOOKUP = { 1.f, 0.6309573445f, 0.3981071706f, 0.2511886432f, 0.1584893192f, 0.1f,
0.06309573445f, 0.03981071706f, 0.02511886432f, 0.01584893192f, 0.01f, 0.006309573445f, 0.003981071706f,
0.002511886432f, 0.001584893192f, 0.001f, 0.0006309573445f, 0.0003981071706f, 0.0002511886432f,
0.0001584893192f, 0.0001f, 6.309573445e-05f, 3.981071706e-05f, 2.511886432e-05f, 1.584893192e-05f, 1e-05f,
6.309573445e-06f, 3.981071706e-06f, 2.511886432e-06f, 1.584893192e-06f, 1e-06f, 6.309573445e-07f,
3.981071706e-07f, 2.511886432e-07f, 1.584893192e-07f, };
static final float[] FROMdB2_LOOKUP = { 0.9928302478f, 0.9786445908f, 0.9646616199f, 0.9508784391f, 0.9372921937f,
0.92390007f, 0.9106992942f, 0.8976871324f, 0.8848608897f, 0.8722179097f, 0.8597555737f, 0.8474713009f,
0.835362547f, 0.8234268041f, 0.8116616003f, 0.8000644989f, 0.7886330981f, 0.7773650302f, 0.7662579617f,
0.755309592f, 0.7445176537f, 0.7338799116f, 0.7233941627f, 0.7130582353f, 0.7028699885f, 0.6928273125f,
0.6829281272f, 0.6731703824f, 0.6635520573f, 0.6540711597f, 0.6447257262f, 0.6355138211f, };
/* interpolated lookup based fromdB function, domain -140dB to 0dB only */
static float fromdBlook(float a) {
int i = (int) (a * ((float) (-(1 << FROMdB2_SHIFT))));
return (i < 0) ? 1.f
: ((i >= (FROMdB_LOOKUP_SZ << FROMdB_SHIFT)) ? 0.f
: FROMdB_LOOKUP[i >>> FROMdB_SHIFT] * FROMdB2_LOOKUP[i & FROMdB2_MASK]);
}
}

View File

@ -0,0 +1,185 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/* JOrbis
* Copyright (C) 2000 ymnk, JCraft,Inc.
*
* Written by: 2000 ymnk<ymnk@jcraft.com>
*
* Many thanks to
* Monty <monty@xiph.org> and
* The XIPHOPHORUS Company http://www.xiph.org/ .
* JOrbis has been based on their awesome works, Vorbis codec.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package com.jcraft.jorbis;
class Lpc {
// en/decode lookups
Drft fft = new Drft();;
int ln;
int m;
// Autocorrelation LPC coeff generation algorithm invented by
// N. Levinson in 1947, modified by J. Durbin in 1959.
// Input : n elements of time doamin data
// Output: m lpc coefficients, excitation energy
static float lpc_from_data(float[] data, float[] lpc, int n, int m) {
float[] aut = new float[m + 1];
float error;
int i, j;
// autocorrelation, p+1 lag coefficients
j = m + 1;
while (j-- != 0) {
float d = 0;
for (i = j; i < n; i++)
d += data[i] * data[i - j];
aut[j] = d;
}
// Generate lpc coefficients from autocorr values
error = aut[0];
/*
* if(error==0){ for(int k=0; k<m; k++) lpc[k]=0.0f; return 0; }
*/
for (i = 0; i < m; i++) {
float r = -aut[i + 1];
if (error == 0) {
for (int k = 0; k < m; k++)
lpc[k] = 0.0f;
return 0;
}
// Sum up this iteration's reflection coefficient; note that in
// Vorbis we don't save it. If anyone wants to recycle this code
// and needs reflection coefficients, save the results of 'r' from
// each iteration.
for (j = 0; j < i; j++)
r -= lpc[j] * aut[i - j];
r /= error;
// Update LPC coefficients and total error
lpc[i] = r;
for (j = 0; j < i / 2; j++) {
float tmp = lpc[j];
lpc[j] += r * lpc[i - 1 - j];
lpc[i - 1 - j] += r * tmp;
}
if (i % 2 != 0)
lpc[j] += lpc[j] * r;
error *= 1.0 - r * r;
}
// we need the error value to know how big an impulse to hit the
// filter with later
return error;
}
// Input : n element envelope spectral curve
// Output: m lpc coefficients, excitation energy
float lpc_from_curve(float[] curve, float[] lpc) {
int n = ln;
float[] work = new float[n + n];
float fscale = (float) (.5 / n);
int i, j;
// input is a real curve. make it complex-real
// This mixes phase, but the LPC generation doesn't care.
for (i = 0; i < n; i++) {
work[i * 2] = curve[i] * fscale;
work[i * 2 + 1] = 0;
}
work[n * 2 - 1] = curve[n - 1] * fscale;
n *= 2;
fft.backward(work);
// The autocorrelation will not be circular. Shift, else we lose
// most of the power in the edges.
for (i = 0, j = n / 2; i < n / 2;) {
float temp = work[i];
work[i++] = work[j];
work[j++] = temp;
}
return (lpc_from_data(work, lpc, n, m));
}
void init(int mapped, int m) {
ln = mapped;
this.m = m;
// we cheat decoding the LPC spectrum via FFTs
fft.init(mapped * 2);
}
void clear() {
fft.clear();
}
static float FAST_HYPOT(float a, float b) {
return (float) Math.sqrt((a) * (a) + (b) * (b));
}
// One can do this the long way by generating the transfer function in
// the time domain and taking the forward FFT of the result. The
// results from direct calculation are cleaner and faster.
//
// This version does a linear curve generation and then later
// interpolates the log curve from the linear curve.
void lpc_to_curve(float[] curve, float[] lpc, float amp) {
for (int i = 0; i < ln * 2; i++)
curve[i] = 0.0f;
if (amp == 0)
return;
for (int i = 0; i < m; i++) {
curve[i * 2 + 1] = lpc[i] / (4 * amp);
curve[i * 2 + 2] = -lpc[i] / (4 * amp);
}
fft.backward(curve);
{
int l2 = ln * 2;
float unit = (float) (1. / amp);
curve[0] = (float) (1. / (curve[0] * 2 + unit));
for (int i = 1; i < ln; i++) {
float real = (curve[i] + curve[l2 - i]);
float imag = (curve[i] - curve[l2 - i]);
float a = real + unit;
curve[i] = (float) (1.0 / FAST_HYPOT(a, imag));
}
}
}
}

View File

@ -0,0 +1,102 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/* JOrbis
* Copyright (C) 2000 ymnk, JCraft,Inc.
*
* Written by: 2000 ymnk<ymnk@jcraft.com>
*
* Many thanks to
* Monty <monty@xiph.org> and
* The XIPHOPHORUS Company http://www.xiph.org/ .
* JOrbis has been based on their awesome works, Vorbis codec.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package com.jcraft.jorbis;
/*
function: LSP (also called LSF) conversion routines
The LSP generation code is taken (with minimal modification) from
"On the Computation of the LSP Frequencies" by Joseph Rothweiler
<rothwlr@altavista.net>, available at:
http://www2.xtdl.com/~rothwlr/lsfpaper/lsfpage.html
********************************************************************/
class Lsp {
static final float M_PI = (float) (3.1415926539);
static void lsp_to_curve(float[] curve, int[] map, int n, int ln, float[] lsp, int m, float amp, float ampoffset) {
int i;
float wdel = M_PI / ln;
for (i = 0; i < m; i++)
lsp[i] = Lookup.coslook(lsp[i]);
int m2 = (m / 2) * 2;
i = 0;
while (i < n) {
int k = map[i];
float p = .7071067812f;
float q = .7071067812f;
float w = Lookup.coslook(wdel * k);
for (int j = 0; j < m2; j += 2) {
q *= lsp[j] - w;
p *= lsp[j + 1] - w;
}
if ((m & 1) != 0) {
/* odd order filter; slightly assymetric */
/* the last coefficient */
q *= lsp[m - 1] - w;
q *= q;
p *= p * (1.f - w * w);
} else {
/* even order filter; still symmetric */
q *= q * (1.f + w);
p *= p * (1.f - w);
}
// q=frexp(p+q,&qexp);
q = p + q;
int hx = Float.floatToIntBits(q);
int ix = 0x7fffffff & hx;
int qexp = 0;
if (ix >= 0x7f800000 || (ix == 0)) {
// 0,inf,nan
} else {
if (ix < 0x00800000) { // subnormal
q *= 3.3554432000e+07; // 0x4c000000
hx = Float.floatToIntBits(q);
ix = 0x7fffffff & hx;
qexp = -25;
}
qexp += ((ix >>> 23) - 126);
hx = (hx & 0x807fffff) | 0x3f000000;
q = Float.intBitsToFloat(hx);
}
q = Lookup.fromdBlook(amp * Lookup.invsqlook(q) * Lookup.invsq2explook(qexp + m) - ampoffset);
do {
curve[i++] *= q;
} while (i < n && map[i] == k);
}
}
}

View File

@ -0,0 +1,361 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/* JOrbis
* Copyright (C) 2000 ymnk, JCraft,Inc.
*
* Written by: 2000 ymnk<ymnk@jcraft.com>
*
* Many thanks to
* Monty <monty@xiph.org> and
* The XIPHOPHORUS Company http://www.xiph.org/ .
* JOrbis has been based on their awesome works, Vorbis codec.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package com.jcraft.jorbis;
import com.jcraft.jogg.*;
class Mapping0 extends FuncMapping {
static int seq = 0;
void free_info(Object imap) {
};
void free_look(Object imap) {
}
Object look(DspState vd, InfoMode vm, Object m) {
// System.err.println("Mapping0.look");
Info vi = vd.vi;
LookMapping0 look = new LookMapping0();
InfoMapping0 info = look.map = (InfoMapping0) m;
look.mode = vm;
look.time_look = new Object[info.submaps];
look.floor_look = new Object[info.submaps];
look.residue_look = new Object[info.submaps];
look.time_func = new FuncTime[info.submaps];
look.floor_func = new FuncFloor[info.submaps];
look.residue_func = new FuncResidue[info.submaps];
for (int i = 0; i < info.submaps; i++) {
int timenum = info.timesubmap[i];
int floornum = info.floorsubmap[i];
int resnum = info.residuesubmap[i];
look.time_func[i] = FuncTime.time_P[vi.time_type[timenum]];
look.time_look[i] = look.time_func[i].look(vd, vm, vi.time_param[timenum]);
look.floor_func[i] = FuncFloor.floor_P[vi.floor_type[floornum]];
look.floor_look[i] = look.floor_func[i].look(vd, vm, vi.floor_param[floornum]);
look.residue_func[i] = FuncResidue.residue_P[vi.residue_type[resnum]];
look.residue_look[i] = look.residue_func[i].look(vd, vm, vi.residue_param[resnum]);
}
if (vi.psys != 0 && vd.analysisp != 0) {
// ??
}
look.ch = vi.channels;
return (look);
}
void pack(Info vi, Object imap, Buffer opb) {
InfoMapping0 info = (InfoMapping0) imap;
/*
* another 'we meant to do it this way' hack... up to beta 4, we packed 4 binary
* zeros here to signify one submapping in use. We now redefine that to mean
* four bitflags that indicate use of deeper features; bit0:submappings,
* bit1:coupling, bit2,3:reserved. This is backward compatable with all actual
* uses of the beta code.
*/
if (info.submaps > 1) {
opb.write(1, 1);
opb.write(info.submaps - 1, 4);
} else {
opb.write(0, 1);
}
if (info.coupling_steps > 0) {
opb.write(1, 1);
opb.write(info.coupling_steps - 1, 8);
for (int i = 0; i < info.coupling_steps; i++) {
opb.write(info.coupling_mag[i], Util.ilog2(vi.channels));
opb.write(info.coupling_ang[i], Util.ilog2(vi.channels));
}
} else {
opb.write(0, 1);
}
opb.write(0, 2); /* 2,3:reserved */
/* we don't write the channel submappings if we only have one... */
if (info.submaps > 1) {
for (int i = 0; i < vi.channels; i++)
opb.write(info.chmuxlist[i], 4);
}
for (int i = 0; i < info.submaps; i++) {
opb.write(info.timesubmap[i], 8);
opb.write(info.floorsubmap[i], 8);
opb.write(info.residuesubmap[i], 8);
}
}
// also responsible for range checking
Object unpack(Info vi, Buffer opb) {
InfoMapping0 info = new InfoMapping0();
if (opb.read(1) != 0) {
info.submaps = opb.read(4) + 1;
} else {
info.submaps = 1;
}
if (opb.read(1) != 0) {
info.coupling_steps = opb.read(8) + 1;
for (int i = 0; i < info.coupling_steps; i++) {
int testM = info.coupling_mag[i] = opb.read(Util.ilog2(vi.channels));
int testA = info.coupling_ang[i] = opb.read(Util.ilog2(vi.channels));
if (testM < 0 || testA < 0 || testM == testA || testM >= vi.channels || testA >= vi.channels) {
// goto err_out;
info.free();
return (null);
}
}
}
if (opb.read(2) > 0) { /* 2,3:reserved */
info.free();
return (null);
}
if (info.submaps > 1) {
for (int i = 0; i < vi.channels; i++) {
info.chmuxlist[i] = opb.read(4);
if (info.chmuxlist[i] >= info.submaps) {
info.free();
return (null);
}
}
}
for (int i = 0; i < info.submaps; i++) {
info.timesubmap[i] = opb.read(8);
if (info.timesubmap[i] >= vi.times) {
info.free();
return (null);
}
info.floorsubmap[i] = opb.read(8);
if (info.floorsubmap[i] >= vi.floors) {
info.free();
return (null);
}
info.residuesubmap[i] = opb.read(8);
if (info.residuesubmap[i] >= vi.residues) {
info.free();
return (null);
}
}
return info;
}
float[][] pcmbundle = null;
int[] zerobundle = null;
int[] nonzero = null;
Object[] floormemo = null;
synchronized int inverse(Block vb, Object l) {
DspState vd = vb.vd;
Info vi = vd.vi;
LookMapping0 look = (LookMapping0) l;
InfoMapping0 info = look.map;
InfoMode mode = look.mode;
int n = vb.pcmend = vi.blocksizes[vb.W];
float[] window = vd.window[vb.W][vb.lW][vb.nW][mode.windowtype];
if (pcmbundle == null || pcmbundle.length < vi.channels) {
pcmbundle = new float[vi.channels][];
nonzero = new int[vi.channels];
zerobundle = new int[vi.channels];
floormemo = new Object[vi.channels];
}
// time domain information decode (note that applying the
// information would have to happen later; we'll probably add a
// function entry to the harness for that later
// NOT IMPLEMENTED
// recover the spectral envelope; store it in the PCM vector for now
for (int i = 0; i < vi.channels; i++) {
float[] pcm = vb.pcm[i];
int submap = info.chmuxlist[i];
floormemo[i] = look.floor_func[submap].inverse1(vb, look.floor_look[submap], floormemo[i]);
if (floormemo[i] != null) {
nonzero[i] = 1;
} else {
nonzero[i] = 0;
}
for (int j = 0; j < n / 2; j++) {
pcm[j] = 0;
}
}
for (int i = 0; i < info.coupling_steps; i++) {
if (nonzero[info.coupling_mag[i]] != 0 || nonzero[info.coupling_ang[i]] != 0) {
nonzero[info.coupling_mag[i]] = 1;
nonzero[info.coupling_ang[i]] = 1;
}
}
// recover the residue, apply directly to the spectral envelope
for (int i = 0; i < info.submaps; i++) {
int ch_in_bundle = 0;
for (int j = 0; j < vi.channels; j++) {
if (info.chmuxlist[j] == i) {
if (nonzero[j] != 0) {
zerobundle[ch_in_bundle] = 1;
} else {
zerobundle[ch_in_bundle] = 0;
}
pcmbundle[ch_in_bundle++] = vb.pcm[j];
}
}
look.residue_func[i].inverse(vb, look.residue_look[i], pcmbundle, zerobundle, ch_in_bundle);
}
for (int i = info.coupling_steps - 1; i >= 0; i--) {
float[] pcmM = vb.pcm[info.coupling_mag[i]];
float[] pcmA = vb.pcm[info.coupling_ang[i]];
for (int j = 0; j < n / 2; j++) {
float mag = pcmM[j];
float ang = pcmA[j];
if (mag > 0) {
if (ang > 0) {
pcmM[j] = mag;
pcmA[j] = mag - ang;
} else {
pcmA[j] = mag;
pcmM[j] = mag + ang;
}
} else {
if (ang > 0) {
pcmM[j] = mag;
pcmA[j] = mag + ang;
} else {
pcmA[j] = mag;
pcmM[j] = mag - ang;
}
}
}
}
// /* compute and apply spectral envelope */
for (int i = 0; i < vi.channels; i++) {
float[] pcm = vb.pcm[i];
int submap = info.chmuxlist[i];
look.floor_func[submap].inverse2(vb, look.floor_look[submap], floormemo[i], pcm);
}
// transform the PCM data; takes PCM vector, vb; modifies PCM vector
// only MDCT right now....
for (int i = 0; i < vi.channels; i++) {
float[] pcm = vb.pcm[i];
// _analysis_output("out",seq+i,pcm,n/2,0,0);
((Mdct) vd.transform[vb.W][0]).backward(pcm, pcm);
}
// now apply the decoded pre-window time information
// NOT IMPLEMENTED
// window the data
for (int i = 0; i < vi.channels; i++) {
float[] pcm = vb.pcm[i];
if (nonzero[i] != 0) {
for (int j = 0; j < n; j++) {
pcm[j] *= window[j];
}
} else {
for (int j = 0; j < n; j++) {
pcm[j] = 0.f;
}
}
}
// now apply the decoded post-window time information
// NOT IMPLEMENTED
// all done!
return (0);
}
class InfoMapping0 {
int submaps; // <= 16
int[] chmuxlist = new int[256]; // up to 256 channels in a Vorbis stream
int[] timesubmap = new int[16]; // [mux]
int[] floorsubmap = new int[16]; // [mux] submap to floors
int[] residuesubmap = new int[16];// [mux] submap to residue
int[] psysubmap = new int[16]; // [mux]; encode only
int coupling_steps;
int[] coupling_mag = new int[256];
int[] coupling_ang = new int[256];
void free() {
chmuxlist = null;
timesubmap = null;
floorsubmap = null;
residuesubmap = null;
psysubmap = null;
coupling_mag = null;
coupling_ang = null;
}
}
class LookMapping0 {
InfoMode mode;
InfoMapping0 map;
Object[] time_look;
Object[] floor_look;
Object[] floor_state;
Object[] residue_look;
PsyLook[] psy_look;
FuncTime[] time_func;
FuncFloor[] floor_func;
FuncResidue[] residue_func;
int ch;
float[][] decay;
int lastframe; // if a different mode is called, we need to
// invalidate decay and floor state
}
}

View File

@ -0,0 +1,249 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/* JOrbis
* Copyright (C) 2000 ymnk, JCraft,Inc.
*
* Written by: 2000 ymnk<ymnk@jcraft.com>
*
* Many thanks to
* Monty <monty@xiph.org> and
* The XIPHOPHORUS Company http://www.xiph.org/ .
* JOrbis has been based on their awesome works, Vorbis codec.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package com.jcraft.jorbis;
class Mdct {
int n;
int log2n;
float[] trig;
int[] bitrev;
float scale;
void init(int n) {
bitrev = new int[n / 4];
trig = new float[n + n / 4];
log2n = (int) Math.rint(Math.log(n) / Math.log(2));
this.n = n;
int AE = 0;
int AO = 1;
int BE = AE + n / 2;
int BO = BE + 1;
int CE = BE + n / 2;
int CO = CE + 1;
// trig lookups...
for (int i = 0; i < n / 4; i++) {
trig[AE + i * 2] = (float) Math.cos((Math.PI / n) * (4 * i));
trig[AO + i * 2] = (float) -Math.sin((Math.PI / n) * (4 * i));
trig[BE + i * 2] = (float) Math.cos((Math.PI / (2 * n)) * (2 * i + 1));
trig[BO + i * 2] = (float) Math.sin((Math.PI / (2 * n)) * (2 * i + 1));
}
for (int i = 0; i < n / 8; i++) {
trig[CE + i * 2] = (float) Math.cos((Math.PI / n) * (4 * i + 2));
trig[CO + i * 2] = (float) -Math.sin((Math.PI / n) * (4 * i + 2));
}
{
int mask = (1 << (log2n - 1)) - 1;
int msb = 1 << (log2n - 2);
for (int i = 0; i < n / 8; i++) {
int acc = 0;
for (int j = 0; msb >>> j != 0; j++)
if (((msb >>> j) & i) != 0)
acc |= 1 << j;
bitrev[i * 2] = ((~acc) & mask);
// bitrev[i*2]=((~acc)&mask)-1;
bitrev[i * 2 + 1] = acc;
}
}
scale = 4.f / n;
}
void clear() {
}
void forward(float[] in, float[] out) {
}
float[] _x = new float[1024];
float[] _w = new float[1024];
synchronized void backward(float[] in, float[] out) {
if (_x.length < n / 2) {
_x = new float[n / 2];
}
if (_w.length < n / 2) {
_w = new float[n / 2];
}
float[] x = _x;
float[] w = _w;
int n2 = n >>> 1;
int n4 = n >>> 2;
int n8 = n >>> 3;
// rotate + step 1
{
int inO = 1;
int xO = 0;
int A = n2;
int i;
for (i = 0; i < n8; i++) {
A -= 2;
x[xO++] = -in[inO + 2] * trig[A + 1] - in[inO] * trig[A];
x[xO++] = in[inO] * trig[A + 1] - in[inO + 2] * trig[A];
inO += 4;
}
inO = n2 - 4;
for (i = 0; i < n8; i++) {
A -= 2;
x[xO++] = in[inO] * trig[A + 1] + in[inO + 2] * trig[A];
x[xO++] = in[inO] * trig[A] - in[inO + 2] * trig[A + 1];
inO -= 4;
}
}
float[] xxx = mdct_kernel(x, w, n, n2, n4, n8);
int xx = 0;
// step 8
{
int B = n2;
int o1 = n4, o2 = o1 - 1;
int o3 = n4 + n2, o4 = o3 - 1;
for (int i = 0; i < n4; i++) {
float temp1 = (xxx[xx] * trig[B + 1] - xxx[xx + 1] * trig[B]);
float temp2 = -(xxx[xx] * trig[B] + xxx[xx + 1] * trig[B + 1]);
out[o1] = -temp1;
out[o2] = temp1;
out[o3] = temp2;
out[o4] = temp2;
o1++;
o2--;
o3++;
o4--;
xx += 2;
B += 2;
}
}
}
private float[] mdct_kernel(float[] x, float[] w, int n, int n2, int n4, int n8) {
// step 2
int xA = n4;
int xB = 0;
int w2 = n4;
int A = n2;
for (int i = 0; i < n4;) {
float x0 = x[xA] - x[xB];
float x1;
w[w2 + i] = x[xA++] + x[xB++];
x1 = x[xA] - x[xB];
A -= 4;
w[i++] = x0 * trig[A] + x1 * trig[A + 1];
w[i] = x1 * trig[A] - x0 * trig[A + 1];
w[w2 + i] = x[xA++] + x[xB++];
i++;
}
// step 3
{
for (int i = 0; i < log2n - 3; i++) {
int k0 = n >>> (i + 2);
int k1 = 1 << (i + 3);
int wbase = n2 - 2;
A = 0;
float[] temp;
for (int r = 0; r < (k0 >>> 2); r++) {
int w1 = wbase;
w2 = w1 - (k0 >> 1);
float AEv = trig[A], wA;
float AOv = trig[A + 1], wB;
wbase -= 2;
k0++;
for (int s = 0; s < (2 << i); s++) {
wB = w[w1] - w[w2];
x[w1] = w[w1] + w[w2];
wA = w[++w1] - w[++w2];
x[w1] = w[w1] + w[w2];
x[w2] = wA * AEv - wB * AOv;
x[w2 - 1] = wB * AEv + wA * AOv;
w1 -= k0;
w2 -= k0;
}
k0--;
A += k1;
}
temp = w;
w = x;
x = temp;
}
}
// step 4, 5, 6, 7
{
int C = n;
int bit = 0;
int x1 = 0;
int x2 = n2 - 1;
for (int i = 0; i < n8; i++) {
int t1 = bitrev[bit++];
int t2 = bitrev[bit++];
float wA = w[t1] - w[t2 + 1];
float wB = w[t1 - 1] + w[t2];
float wC = w[t1] + w[t2 + 1];
float wD = w[t1 - 1] - w[t2];
float wACE = wA * trig[C];
float wBCE = wB * trig[C++];
float wACO = wA * trig[C];
float wBCO = wB * trig[C++];
x[x1++] = (wC + wACO + wBCE) * .5f;
x[x2--] = (-wD + wBCO - wACE) * .5f;
x[x1++] = (wD + wBCO - wACE) * .5f;
x[x2--] = (wC - wACO - wBCE) * .5f;
}
}
return (x);
}
}

View File

@ -0,0 +1,74 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/* JOrbis
* Copyright (C) 2000 ymnk, JCraft,Inc.
*
* Written by: 2000 ymnk<ymnk@jcraft.com>
*
* Many thanks to
* Monty <monty@xiph.org> and
* The XIPHOPHORUS Company http://www.xiph.org/ .
* JOrbis has been based on their awesome works, Vorbis codec.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package com.jcraft.jorbis;
// psychoacoustic setup
class PsyInfo {
int athp;
int decayp;
int smoothp;
int noisefitp;
int noisefit_subblock;
float noisefit_threshdB;
float ath_att;
int tonemaskp;
float[] toneatt_125Hz = new float[5];
float[] toneatt_250Hz = new float[5];
float[] toneatt_500Hz = new float[5];
float[] toneatt_1000Hz = new float[5];
float[] toneatt_2000Hz = new float[5];
float[] toneatt_4000Hz = new float[5];
float[] toneatt_8000Hz = new float[5];
int peakattp;
float[] peakatt_125Hz = new float[5];
float[] peakatt_250Hz = new float[5];
float[] peakatt_500Hz = new float[5];
float[] peakatt_1000Hz = new float[5];
float[] peakatt_2000Hz = new float[5];
float[] peakatt_4000Hz = new float[5];
float[] peakatt_8000Hz = new float[5];
int noisemaskp;
float[] noiseatt_125Hz = new float[5];
float[] noiseatt_250Hz = new float[5];
float[] noiseatt_500Hz = new float[5];
float[] noiseatt_1000Hz = new float[5];
float[] noiseatt_2000Hz = new float[5];
float[] noiseatt_4000Hz = new float[5];
float[] noiseatt_8000Hz = new float[5];
float max_curve_dB;
float attack_coeff;
float decay_coeff;
void free() {
}
}

View File

@ -0,0 +1,42 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/* JOrbis
* Copyright (C) 2000 ymnk, JCraft,Inc.
*
* Written by: 2000 ymnk<ymnk@jcraft.com>
*
* Many thanks to
* Monty <monty@xiph.org> and
* The XIPHOPHORUS Company http://www.xiph.org/ .
* JOrbis has been based on their awesome works, Vorbis codec.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package com.jcraft.jorbis;
class PsyLook {
int n;
PsyInfo vi;
float[][][] tonecurves;
float[][] peakatt;
float[][][] noisecurves;
float[] ath;
int[] octave;
void init(PsyInfo vi, int n, int rate) {
}
}

View File

@ -0,0 +1,326 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/* JOrbis
* Copyright (C) 2000 ymnk, JCraft,Inc.
*
* Written by: 2000 ymnk<ymnk@jcraft.com>
*
* Many thanks to
* Monty <monty@xiph.org> and
* The XIPHOPHORUS Company http://www.xiph.org/ .
* JOrbis has been based on their awesome works, Vorbis codec.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package com.jcraft.jorbis;
import com.jcraft.jogg.*;
class Residue0 extends FuncResidue {
void pack(Object vr, Buffer opb) {
InfoResidue0 info = (InfoResidue0) vr;
int acc = 0;
opb.write(info.begin, 24);
opb.write(info.end, 24);
opb.write(info.grouping - 1, 24); /*
* residue vectors to group and code with a partitioned book
*/
opb.write(info.partitions - 1, 6); /* possible partition choices */
opb.write(info.groupbook, 8); /* group huffman book */
/*
* secondstages is a bitmask; as encoding progresses pass by pass, a bitmask of
* one indicates this partition class has bits to write this pass
*/
for (int j = 0; j < info.partitions; j++) {
int i = info.secondstages[j];
if (Util.ilog(i) > 3) {
/* yes, this is a minor hack due to not thinking ahead */
opb.write(i, 3);
opb.write(1, 1);
opb.write(i >>> 3, 5);
} else {
opb.write(i, 4); /* trailing zero */
}
acc += Util.icount(i);
}
for (int j = 0; j < acc; j++) {
opb.write(info.booklist[j], 8);
}
}
Object unpack(Info vi, Buffer opb) {
int acc = 0;
InfoResidue0 info = new InfoResidue0();
info.begin = opb.read(24);
info.end = opb.read(24);
info.grouping = opb.read(24) + 1;
info.partitions = opb.read(6) + 1;
info.groupbook = opb.read(8);
for (int j = 0; j < info.partitions; j++) {
int cascade = opb.read(3);
if (opb.read(1) != 0) {
cascade |= (opb.read(5) << 3);
}
info.secondstages[j] = cascade;
acc += Util.icount(cascade);
}
for (int j = 0; j < acc; j++) {
info.booklist[j] = opb.read(8);
}
if (info.groupbook >= vi.books) {
free_info(info);
return (null);
}
for (int j = 0; j < acc; j++) {
if (info.booklist[j] >= vi.books) {
free_info(info);
return (null);
}
}
return (info);
}
Object look(DspState vd, InfoMode vm, Object vr) {
InfoResidue0 info = (InfoResidue0) vr;
LookResidue0 look = new LookResidue0();
int acc = 0;
int dim;
int maxstage = 0;
look.info = info;
look.map = vm.mapping;
look.parts = info.partitions;
look.fullbooks = vd.fullbooks;
look.phrasebook = vd.fullbooks[info.groupbook];
dim = look.phrasebook.dim;
look.partbooks = new int[look.parts][];
for (int j = 0; j < look.parts; j++) {
int i = info.secondstages[j];
int stages = Util.ilog(i);
if (stages != 0) {
if (stages > maxstage)
maxstage = stages;
look.partbooks[j] = new int[stages];
for (int k = 0; k < stages; k++) {
if ((i & (1 << k)) != 0) {
look.partbooks[j][k] = info.booklist[acc++];
}
}
}
}
look.partvals = (int) Math.rint(Math.pow(look.parts, dim));
look.stages = maxstage;
look.decodemap = new int[look.partvals][];
for (int j = 0; j < look.partvals; j++) {
int val = j;
int mult = look.partvals / look.parts;
look.decodemap[j] = new int[dim];
for (int k = 0; k < dim; k++) {
int deco = val / mult;
val -= deco * mult;
mult /= look.parts;
look.decodemap[j][k] = deco;
}
}
return (look);
}
void free_info(Object i) {
}
void free_look(Object i) {
}
private static int[][][] _01inverse_partword = new int[2][][]; // _01inverse is synchronized for
// re-using partword
synchronized static int _01inverse(Block vb, Object vl, float[][] in, int ch, int decodepart) {
int i, j, k, l, s;
LookResidue0 look = (LookResidue0) vl;
InfoResidue0 info = look.info;
// move all this setup out later
int samples_per_partition = info.grouping;
int partitions_per_word = look.phrasebook.dim;
int n = info.end - info.begin;
int partvals = n / samples_per_partition;
int partwords = (partvals + partitions_per_word - 1) / partitions_per_word;
if (_01inverse_partword.length < ch) {
_01inverse_partword = new int[ch][][];
}
for (j = 0; j < ch; j++) {
if (_01inverse_partword[j] == null || _01inverse_partword[j].length < partwords) {
_01inverse_partword[j] = new int[partwords][];
}
}
for (s = 0; s < look.stages; s++) {
// each loop decodes on partition codeword containing
// partitions_pre_word partitions
for (i = 0, l = 0; i < partvals; l++) {
if (s == 0) {
// fetch the partition word for each channel
for (j = 0; j < ch; j++) {
int temp = look.phrasebook.decode(vb.opb);
if (temp == -1) {
return (0);
}
_01inverse_partword[j][l] = look.decodemap[temp];
if (_01inverse_partword[j][l] == null) {
return (0);
}
}
}
// now we decode residual values for the partitions
for (k = 0; k < partitions_per_word && i < partvals; k++, i++)
for (j = 0; j < ch; j++) {
int offset = info.begin + i * samples_per_partition;
int index = _01inverse_partword[j][l][k];
if ((info.secondstages[index] & (1 << s)) != 0) {
CodeBook stagebook = look.fullbooks[look.partbooks[index][s]];
if (stagebook != null) {
if (decodepart == 0) {
if (stagebook.decodevs_add(in[j], offset, vb.opb, samples_per_partition) == -1) {
return (0);
}
} else if (decodepart == 1) {
if (stagebook.decodev_add(in[j], offset, vb.opb, samples_per_partition) == -1) {
return (0);
}
}
}
}
}
}
}
return (0);
}
static int[][] _2inverse_partword = null;
synchronized static int _2inverse(Block vb, Object vl, float[][] in, int ch) {
int i, k, l, s;
LookResidue0 look = (LookResidue0) vl;
InfoResidue0 info = look.info;
// move all this setup out later
int samples_per_partition = info.grouping;
int partitions_per_word = look.phrasebook.dim;
int n = info.end - info.begin;
int partvals = n / samples_per_partition;
int partwords = (partvals + partitions_per_word - 1) / partitions_per_word;
if (_2inverse_partword == null || _2inverse_partword.length < partwords) {
_2inverse_partword = new int[partwords][];
}
for (s = 0; s < look.stages; s++) {
for (i = 0, l = 0; i < partvals; l++) {
if (s == 0) {
// fetch the partition word for each channel
int temp = look.phrasebook.decode(vb.opb);
if (temp == -1) {
return (0);
}
_2inverse_partword[l] = look.decodemap[temp];
if (_2inverse_partword[l] == null) {
return (0);
}
}
// now we decode residual values for the partitions
for (k = 0; k < partitions_per_word && i < partvals; k++, i++) {
int offset = info.begin + i * samples_per_partition;
int index = _2inverse_partword[l][k];
if ((info.secondstages[index] & (1 << s)) != 0) {
CodeBook stagebook = look.fullbooks[look.partbooks[index][s]];
if (stagebook != null) {
if (stagebook.decodevv_add(in, offset, ch, vb.opb, samples_per_partition) == -1) {
return (0);
}
}
}
}
}
}
return (0);
}
int inverse(Block vb, Object vl, float[][] in, int[] nonzero, int ch) {
int used = 0;
for (int i = 0; i < ch; i++) {
if (nonzero[i] != 0) {
in[used++] = in[i];
}
}
if (used != 0)
return (_01inverse(vb, vl, in, used, 0));
else
return (0);
}
class LookResidue0 {
InfoResidue0 info;
int map;
int parts;
int stages;
CodeBook[] fullbooks;
CodeBook phrasebook;
int[][] partbooks;
int partvals;
int[][] decodemap;
int postbits;
int phrasebits;
int frames;
}
class InfoResidue0 {
// block-partitioned VQ coded straight residue
int begin;
int end;
// first stage (lossless partitioning)
int grouping; // group n vectors per partition
int partitions; // possible codebooks for a partition
int groupbook; // huffbook for partitioning
int[] secondstages = new int[64]; // expanded out to pointers in lookup
int[] booklist = new int[256]; // list of second stage books
// encode-only heuristic settings
float[] entmax = new float[64]; // book entropy threshholds
float[] ampmax = new float[64]; // book amp threshholds
int[] subgrp = new int[64]; // book heuristic subgroup size
int[] blimit = new int[64]; // subgroup position limits
}
}

View File

@ -0,0 +1,44 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/* JOrbis
* Copyright (C) 2000 ymnk, JCraft,Inc.
*
* Written by: 2000 ymnk<ymnk@jcraft.com>
*
* Many thanks to
* Monty <monty@xiph.org> and
* The XIPHOPHORUS Company http://www.xiph.org/ .
* JOrbis has been based on their awesome works, Vorbis codec.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package com.jcraft.jorbis;
class Residue1 extends Residue0 {
int inverse(Block vb, Object vl, float[][] in, int[] nonzero, int ch) {
int used = 0;
for (int i = 0; i < ch; i++) {
if (nonzero[i] != 0) {
in[used++] = in[i];
}
}
if (used != 0) {
return (_01inverse(vb, vl, in, used, 1));
} else {
return 0;
}
}
}

View File

@ -0,0 +1,41 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/* JOrbis
* Copyright (C) 2000 ymnk, JCraft,Inc.
*
* Written by: 2000 ymnk<ymnk@jcraft.com>
*
* Many thanks to
* Monty <monty@xiph.org> and
* The XIPHOPHORUS Company http://www.xiph.org/ .
* JOrbis has been based on their awesome works, Vorbis codec.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package com.jcraft.jorbis;
class Residue2 extends Residue0 {
int inverse(Block vb, Object vl, float[][] in, int[] nonzero, int ch) {
int i = 0;
for (i = 0; i < ch; i++)
if (nonzero[i] != 0)
break;
if (i == ch)
return (0); /* no nonzero vectors */
return (_2inverse(vb, vl, in, ch));
}
}

View File

@ -0,0 +1,436 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/* JOrbis
* Copyright (C) 2000 ymnk, JCraft,Inc.
*
* Written by: 2000 ymnk<ymnk@jcraft.com>
*
* Many thanks to
* Monty <monty@xiph.org> and
* The XIPHOPHORUS Company http://www.xiph.org/ .
* JOrbis has been based on their awesome works, Vorbis codec.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package com.jcraft.jorbis;
import com.jcraft.jogg.*;
class StaticCodeBook {
int dim; // codebook dimensions (elements per vector)
int entries; // codebook entries
int[] lengthlist; // codeword lengths in bits
// mapping
int maptype; // 0=none
// 1=implicitly populated values from map column
// 2=listed arbitrary values
// The below does a linear, single monotonic sequence mapping.
int q_min; // packed 32 bit float; quant value 0 maps to minval
int q_delta; // packed 32 bit float; val 1 - val 0 == delta
int q_quant; // bits: 0 < quant <= 16
int q_sequencep; // bitflag
// additional information for log (dB) mapping; the linear mapping
// is assumed to actually be values in dB. encodebias is used to
// assign an error weight to 0 dB. We have two additional flags:
// zeroflag indicates if entry zero is to represent -Inf dB; negflag
// indicates if we're to represent negative linear values in a
// mirror of the positive mapping.
int[] quantlist; // map == 1: (int)(entries/dim) element column map
// map == 2: list of dim*entries quantized entry vals
StaticCodeBook() {
}
int pack(Buffer opb) {
int i;
boolean ordered = false;
opb.write(0x564342, 24);
opb.write(dim, 16);
opb.write(entries, 24);
// pack the codewords. There are two packings; length ordered and
// length random. Decide between the two now.
for (i = 1; i < entries; i++) {
if (lengthlist[i] < lengthlist[i - 1])
break;
}
if (i == entries)
ordered = true;
if (ordered) {
// length ordered. We only need to say how many codewords of
// each length. The actual codewords are generated
// deterministically
int count = 0;
opb.write(1, 1); // ordered
opb.write(lengthlist[0] - 1, 5); // 1 to 32
for (i = 1; i < entries; i++) {
int _this = lengthlist[i];
int _last = lengthlist[i - 1];
if (_this > _last) {
for (int j = _last; j < _this; j++) {
opb.write(i - count, Util.ilog(entries - count));
count = i;
}
}
}
opb.write(i - count, Util.ilog(entries - count));
} else {
// length random. Again, we don't code the codeword itself, just
// the length. This time, though, we have to encode each length
opb.write(0, 1); // unordered
// algortihmic mapping has use for 'unused entries', which we tag
// here. The algorithmic mapping happens as usual, but the unused
// entry has no codeword.
for (i = 0; i < entries; i++) {
if (lengthlist[i] == 0)
break;
}
if (i == entries) {
opb.write(0, 1); // no unused entries
for (i = 0; i < entries; i++) {
opb.write(lengthlist[i] - 1, 5);
}
} else {
opb.write(1, 1); // we have unused entries; thus we tag
for (i = 0; i < entries; i++) {
if (lengthlist[i] == 0) {
opb.write(0, 1);
} else {
opb.write(1, 1);
opb.write(lengthlist[i] - 1, 5);
}
}
}
}
// is the entry number the desired return value, or do we have a
// mapping? If we have a mapping, what type?
opb.write(maptype, 4);
switch (maptype) {
case 0:
// no mapping
break;
case 1:
case 2:
// implicitly populated value mapping
// explicitly populated value mapping
if (quantlist == null) {
// no quantlist? error
return (-1);
}
// values that define the dequantization
opb.write(q_min, 32);
opb.write(q_delta, 32);
opb.write(q_quant - 1, 4);
opb.write(q_sequencep, 1);
{
int quantvals = 0;
switch (maptype) {
case 1:
// a single column of (c->entries/c->dim) quantized values for
// building a full value list algorithmically (square lattice)
quantvals = maptype1_quantvals();
break;
case 2:
// every value (c->entries*c->dim total) specified explicitly
quantvals = entries * dim;
break;
}
// quantized values
for (i = 0; i < quantvals; i++) {
opb.write(Math.abs(quantlist[i]), q_quant);
}
}
break;
default:
// error case; we don't have any other map types now
return (-1);
}
return (0);
}
// unpacks a codebook from the packet buffer into the codebook struct,
// readies the codebook auxiliary structures for decode
int unpack(Buffer opb) {
int i;
// memset(s,0,sizeof(static_codebook));
// make sure alignment is correct
if (opb.read(24) != 0x564342) {
// goto _eofout;
clear();
return (-1);
}
// first the basic parameters
dim = opb.read(16);
entries = opb.read(24);
if (entries == -1) {
// goto _eofout;
clear();
return (-1);
}
// codeword ordering.... length ordered or unordered?
switch (opb.read(1)) {
case 0:
// unordered
lengthlist = new int[entries];
// allocated but unused entries?
if (opb.read(1) != 0) {
// yes, unused entries
for (i = 0; i < entries; i++) {
if (opb.read(1) != 0) {
int num = opb.read(5);
if (num == -1) {
// goto _eofout;
clear();
return (-1);
}
lengthlist[i] = num + 1;
} else {
lengthlist[i] = 0;
}
}
} else {
// all entries used; no tagging
for (i = 0; i < entries; i++) {
int num = opb.read(5);
if (num == -1) {
// goto _eofout;
clear();
return (-1);
}
lengthlist[i] = num + 1;
}
}
break;
case 1:
// ordered
{
int length = opb.read(5) + 1;
lengthlist = new int[entries];
for (i = 0; i < entries;) {
int num = opb.read(Util.ilog(entries - i));
if (num == -1) {
// goto _eofout;
clear();
return (-1);
}
for (int j = 0; j < num; j++, i++) {
lengthlist[i] = length;
}
length++;
}
}
break;
default:
// EOF
return (-1);
}
// Do we have a mapping to unpack?
switch ((maptype = opb.read(4))) {
case 0:
// no mapping
break;
case 1:
case 2:
// implicitly populated value mapping
// explicitly populated value mapping
q_min = opb.read(32);
q_delta = opb.read(32);
q_quant = opb.read(4) + 1;
q_sequencep = opb.read(1);
{
int quantvals = 0;
switch (maptype) {
case 1:
quantvals = maptype1_quantvals();
break;
case 2:
quantvals = entries * dim;
break;
}
// quantized values
quantlist = new int[quantvals];
for (i = 0; i < quantvals; i++) {
quantlist[i] = opb.read(q_quant);
}
if (quantlist[quantvals - 1] == -1) {
// goto _eofout;
clear();
return (-1);
}
}
break;
default:
// goto _eofout;
clear();
return (-1);
}
// all set
return (0);
// _errout:
// _eofout:
// vorbis_staticbook_clear(s);
// return(-1);
}
// there might be a straightforward one-line way to do the below
// that's portable and totally safe against roundoff, but I haven't
// thought of it. Therefore, we opt on the side of caution
private int maptype1_quantvals() {
int vals = (int) (Math.floor(Math.pow(entries, 1. / dim)));
// the above *should* be reliable, but we'll not assume that FP is
// ever reliable when bitstream sync is at stake; verify via integer
// means that vals really is the greatest value of dim for which
// vals^b->bim <= b->entries
// treat the above as an initial guess
while (true) {
int acc = 1;
int acc1 = 1;
for (int i = 0; i < dim; i++) {
acc *= vals;
acc1 *= vals + 1;
}
if (acc <= entries && acc1 > entries) {
return (vals);
} else {
if (acc > entries) {
vals--;
} else {
vals++;
}
}
}
}
void clear() {
}
// unpack the quantized list of values for encode/decode
// we need to deal with two map types: in map type 1, the values are
// generated algorithmically (each column of the vector counts through
// the values in the quant vector). in map type 2, all the values came
// in in an explicit list. Both value lists must be unpacked
float[] unquantize() {
if (maptype == 1 || maptype == 2) {
int quantvals;
float mindel = float32_unpack(q_min);
float delta = float32_unpack(q_delta);
float[] r = new float[entries * dim];
// maptype 1 and 2 both use a quantized value vector, but
// different sizes
switch (maptype) {
case 1:
// most of the time, entries%dimensions == 0, but we need to be
// well defined. We define that the possible vales at each
// scalar is values == entries/dim. If entries%dim != 0, we'll
// have 'too few' values (values*dim<entries), which means that
// we'll have 'left over' entries; left over entries use zeroed
// values (and are wasted). So don't generate codebooks like that
quantvals = maptype1_quantvals();
for (int j = 0; j < entries; j++) {
float last = 0.f;
int indexdiv = 1;
for (int k = 0; k < dim; k++) {
int index = (j / indexdiv) % quantvals;
float val = quantlist[index];
val = Math.abs(val) * delta + mindel + last;
if (q_sequencep != 0)
last = val;
r[j * dim + k] = val;
indexdiv *= quantvals;
}
}
break;
case 2:
for (int j = 0; j < entries; j++) {
float last = 0.f;
for (int k = 0; k < dim; k++) {
float val = quantlist[j * dim + k];
// if((j*dim+k)==0){System.err.println(" | 0 -> "+val+" | ");}
val = Math.abs(val) * delta + mindel + last;
if (q_sequencep != 0)
last = val;
r[j * dim + k] = val;
// if((j*dim+k)==0){System.err.println(" $ r[0] -> "+r[0]+" | ");}
}
}
// System.err.println("\nr[0]="+r[0]);
}
return (r);
}
return (null);
}
// 32 bit float (not IEEE; nonnormalized mantissa +
// biased exponent) : neeeeeee eeemmmmm mmmmmmmm mmmmmmmm
// Why not IEEE? It's just not that important here.
static final int VQ_FEXP = 10;
static final int VQ_FMAN = 21;
static final int VQ_FEXP_BIAS = 768; // bias toward values smaller than 1.
// doesn't currently guard under/overflow
static long float32_pack(float val) {
int sign = 0;
int exp;
int mant;
if (val < 0) {
sign = 0x80000000;
val = -val;
}
exp = (int) Math.floor(Math.log(val) / Math.log(2));
mant = (int) Math.rint(Math.pow(val, (VQ_FMAN - 1) - exp));
exp = (exp + VQ_FEXP_BIAS) << VQ_FMAN;
return (sign | exp | mant);
}
static float float32_unpack(int val) {
float mant = val & 0x1fffff;
float exp = (val & 0x7fe00000) >>> VQ_FMAN;
if ((val & 0x80000000) != 0)
mant = -mant;
return (ldexp(mant, ((int) exp) - (VQ_FMAN - 1) - VQ_FEXP_BIAS));
}
static float ldexp(float foo, int e) {
return (float) (foo * Math.pow(2, e));
}
}

View File

@ -0,0 +1,52 @@
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/* JOrbis
* Copyright (C) 2000 ymnk, JCraft,Inc.
*
* Written by: 2000 ymnk<ymnk@jcraft.com>
*
* Many thanks to
* Monty <monty@xiph.org> and
* The XIPHOPHORUS Company http://www.xiph.org/ .
* JOrbis has been based on their awesome works, Vorbis codec.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package com.jcraft.jorbis;
import com.jcraft.jogg.*;
class Time0 extends FuncTime {
void pack(Object i, Buffer opb) {
}
Object unpack(Info vi, Buffer opb) {
return "";
}
Object look(DspState vd, InfoMode mi, Object i) {
return "";
}
void free_info(Object i) {
}
void free_look(Object i) {
}
int inverse(Block vb, Object i, float[] in, float[] out) {
return 0;
}
}

View File

@ -0,0 +1,30 @@
package com.jcraft.jorbis;
class Util {
static int ilog(int v) {
int ret = 0;
while (v != 0) {
ret++;
v >>>= 1;
}
return (ret);
}
static int ilog2(int v) {
int ret = 0;
while (v > 1) {
ret++;
v >>>= 1;
}
return (ret);
}
static int icount(int v) {
int ret = 0;
while (v != 0) {
ret += (v & 1);
v >>>= 1;
}
return (ret);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,228 @@
package net.lax1dude.eaglercraft.v1_8.internal;
import org.teavm.jso.webgl.WebGLBuffer;
import org.teavm.jso.webgl.WebGLFramebuffer;
import org.teavm.jso.webgl.WebGLProgram;
import org.teavm.jso.webgl.WebGLRenderbuffer;
import org.teavm.jso.webgl.WebGLShader;
import org.teavm.jso.webgl.WebGLTexture;
import org.teavm.jso.webgl.WebGLUniformLocation;
import net.lax1dude.eaglercraft.v1_8.internal.wasm_gc_teavm.WebGLQuery;
import net.lax1dude.eaglercraft.v1_8.internal.wasm_gc_teavm.WebGLVertexArray;
/**
* Copyright (c) 2022-2023 lax1dude. All Rights Reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
class OpenGLObjects {
static class BufferGL implements IBufferGL {
private static int hashGen = 0;
final WebGLBuffer ptr;
final int hash;
BufferGL(WebGLBuffer ptr) {
this.ptr = ptr;
this.hash = ++hashGen;
}
public int hashCode() {
return hash;
}
@Override
public void free() {
PlatformOpenGL._wglDeleteBuffers(this);
}
}
static class BufferArrayGL implements IBufferArrayGL {
private static int hashGen = 0;
final WebGLVertexArray ptr;
final int hash;
BufferArrayGL(WebGLVertexArray ptr) {
this.ptr = ptr;
this.hash = ++hashGen;
}
public int hashCode() {
return hash;
}
@Override
public void free() {
PlatformOpenGL._wglDeleteVertexArrays(this);
}
}
static class TextureGL implements ITextureGL {
private static int hashGen = 0;
final WebGLTexture ptr;
final int hash;
TextureGL(WebGLTexture ptr) {
this.ptr = ptr;
this.hash = ++hashGen;
}
public int hashCode() {
return hash;
}
@Override
public void free() {
PlatformOpenGL._wglDeleteTextures(this);
}
}
static class ProgramGL implements IProgramGL {
private static int hashGen = 0;
final WebGLProgram ptr;
final int hash;
ProgramGL(WebGLProgram ptr) {
this.ptr = ptr;
this.hash = ++hashGen;
}
public int hashCode() {
return hash;
}
@Override
public void free() {
PlatformOpenGL._wglDeleteProgram(this);
}
}
static class UniformGL implements IUniformGL {
private static int hashGen = 0;
final WebGLUniformLocation ptr;
final int hash;
UniformGL(WebGLUniformLocation ptr) {
this.ptr = ptr;
this.hash = ++hashGen;
}
public int hashCode() {
return hash;
}
@Override
public void free() {
}
}
static class ShaderGL implements IShaderGL {
private static int hashGen = 0;
final WebGLShader ptr;
final int hash;
ShaderGL(WebGLShader ptr) {
this.ptr = ptr;
this.hash = ++hashGen;
}
public int hashCode() {
return hash;
}
@Override
public void free() {
PlatformOpenGL._wglDeleteShader(this);
}
}
static class FramebufferGL implements IFramebufferGL {
private static int hashGen = 0;
final WebGLFramebuffer ptr;
final int hash;
FramebufferGL(WebGLFramebuffer ptr) {
this.ptr = ptr;
this.hash = ++hashGen;
}
public int hashCode() {
return hash;
}
@Override
public void free() {
PlatformOpenGL._wglDeleteFramebuffer(this);
}
}
static class RenderbufferGL implements IRenderbufferGL {
private static int hashGen = 0;
final WebGLRenderbuffer ptr;
final int hash;
RenderbufferGL(WebGLRenderbuffer ptr) {
this.ptr = ptr;
this.hash = ++hashGen;
}
public int hashCode() {
return hash;
}
@Override
public void free() {
PlatformOpenGL._wglDeleteRenderbuffer(this);
}
}
static class QueryGL implements IQueryGL {
private static int hashGen = 0;
final WebGLQuery ptr;
final int hash;
QueryGL(WebGLQuery ptr) {
this.ptr = ptr;
this.hash = ++hashGen;
}
public int hashCode() {
return hash;
}
@Override
public void free() {
PlatformOpenGL._wglDeleteQueries(this);
}
}
}

View File

@ -0,0 +1,308 @@
package net.lax1dude.eaglercraft.v1_8.internal;
import java.net.URI;
import java.net.URISyntaxException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.teavm.interop.Import;
import org.teavm.jso.JSBody;
import org.teavm.jso.JSObject;
import org.teavm.jso.JSProperty;
import org.teavm.jso.browser.Storage;
import org.teavm.jso.browser.Window;
import org.teavm.jso.canvas.CanvasRenderingContext2D;
import org.teavm.jso.canvas.ImageData;
import org.teavm.jso.core.JSString;
import org.teavm.jso.dom.html.HTMLCanvasElement;
import org.teavm.jso.dom.html.HTMLDocument;
import org.teavm.jso.typedarrays.ArrayBuffer;
import org.teavm.jso.typedarrays.Uint8Array;
import org.teavm.jso.typedarrays.Uint8ClampedArray;
import net.lax1dude.eaglercraft.v1_8.Base64;
import net.lax1dude.eaglercraft.v1_8.internal.buffer.ByteBuffer;
import net.lax1dude.eaglercraft.v1_8.internal.buffer.WASMGCBufferAllocator;
import net.lax1dude.eaglercraft.v1_8.internal.buffer.WASMGCDirectArrayConverter;
import net.lax1dude.eaglercraft.v1_8.internal.wasm_gc_teavm.BetterJSStringConverter;
import net.lax1dude.eaglercraft.v1_8.internal.wasm_gc_teavm.TeaVMUtils;
/**
* Copyright (c) 2022-2024 lax1dude. All Rights Reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
public class PlatformApplication {
public static void openLink(String url) {
if(url.indexOf(':') == -1) {
url = "http://" + url;
}
URI parsedURL;
try {
parsedURL = new URI(url);
}catch(URISyntaxException ex) {
PlatformRuntime.logger.error("Refusing to open invalid URL: {}", url);
return;
}
try {
PlatformRuntime.win.open(parsedURL.toString(), "_blank", "noopener,noreferrer");
}catch(Throwable t) {
PlatformRuntime.logger.error("Exception opening link!");
}
}
public static void setClipboard(String text) {
long start = PlatformRuntime.steadyTimeMillis();
boolean b = false;
try {
b = setClipboard0(BetterJSStringConverter.stringToJS(text));
}catch(Throwable t) {
PlatformRuntime.logger.error("Exception setting clipboard data");
}
if(!b) {
try {
Window.prompt("Here is the text you're trying to copy:", text);
}catch(Throwable t2) {
}
}
if(PlatformRuntime.steadyTimeMillis() - start > 500l) {
PlatformInput.unpressCTRL = true;
}
}
@Import(module = "platformApplication", name = "setClipboard")
private static native boolean setClipboard0(JSString str);
public static String getClipboard() {
long start = PlatformRuntime.steadyTimeMillis();
String ret = null;
try {
ret = BetterJSStringConverter.stringFromJS(getClipboard0());
}catch(Throwable t) {
PlatformRuntime.logger.error("Exception getting clipboard data");
}
if(ret == null) {
try {
ret = Window.prompt("Please enter the text to paste:");
}catch(Throwable t2) {
}
}
if(PlatformRuntime.steadyTimeMillis() - start > 500l) {
PlatformInput.unpressCTRL = true;
}
return ret != null ? ret : "";
}
@Import(module = "platformApplication", name = "getClipboard")
private static native JSString getClipboard0();
public static void setLocalStorage(String name, byte[] data) {
setLocalStorage(name, data, true);
}
public static void setLocalStorage(String name, byte[] data, boolean hooks) {
IClientConfigAdapter adapter = PlatformRuntime.getClientConfigAdapter();
String eagName = adapter.getLocalStorageNamespace() + "." + name;
String b64 = data != null ? Base64.encodeBase64String(data) : null;
try {
Storage s = Window.current().getLocalStorage();
if(s != null) {
if(b64 != null) {
s.setItem(eagName, b64);
}else {
s.removeItem(eagName);
}
}
}catch(Throwable t) {
}
if(hooks) {
adapter.getHooks().callLocalStorageSavedHook(name, b64);
}
}
public static byte[] getLocalStorage(String name) {
return getLocalStorage(name, true);
}
public static byte[] getLocalStorage(String name, boolean hooks) {
IClientConfigAdapter adapter = PlatformRuntime.getClientConfigAdapter();
String eagName = adapter.getLocalStorageNamespace() + "." + name;
byte[] hooked = null;
if(hooks) {
String hookedStr = adapter.getHooks().callLocalStorageLoadHook(eagName);
if(hookedStr != null) {
try {
hooked = Base64.decodeBase64(hookedStr);
}catch(Throwable t) {
PlatformRuntime.logger.error("Invalid Base64 recieved from local storage hook!");
hooked = null;
}
}
}
if(hooked == null) {
try {
Storage s = Window.current().getLocalStorage();
if(s != null) {
String str = s.getItem(eagName);
if(str != null) {
return Base64.decodeBase64(str);
}else {
return null;
}
}else {
return null;
}
}catch(Throwable t) {
return null;
}
}else {
return hooked;
}
}
public static final void displayFileChooser(String mime, String ext) {
displayFileChooser0(BetterJSStringConverter.stringToJS(mime), BetterJSStringConverter.stringToJS(ext));
}
@Import(module = "platformApplication", name = "displayFileChooser")
private static native void displayFileChooser0(JSString mime, JSString ext);
@Import(module = "platformApplication", name = "fileChooserHasResult")
public static native boolean fileChooserHasResult();
public static FileChooserResult getFileChooserResult() {
JSFileChooserResult jsResult = getFileChooserResult0();
if(jsResult != null) {
return new FileChooserResult(jsResult.getFileName(),
WASMGCDirectArrayConverter.externU8ArrayToByteArray(new Uint8Array(jsResult.getFileData())));
}else {
return null;
}
}
private interface JSFileChooserResult extends JSObject {
@JSProperty
String getFileName();
@JSProperty
ArrayBuffer getFileData();
}
@Import(module = "platformApplication", name = "getFileChooserResult")
private static native JSFileChooserResult getFileChooserResult0();
@Import(module = "platformApplication", name = "clearFileChooserResult")
public static native void clearFileChooserResult();
@Import(module = "platformApplication", name = "getFaviconURL")
public static native JSString faviconURLTeaVM();
public static void showPopup(String msg) {
Window.alert(msg);
}
@JSBody(params = { "doc", "str" }, script = "doc.write(str);doc.close();")
private static native void documentWrite(HTMLDocument doc, String str);
public static void openCreditsPopup(String text) {
Window currentWin = PlatformRuntime.win;
int w = (int)(850 * PlatformInput.getDPI());
int h = (int)(700 * PlatformInput.getDPI());
int x = (currentWin.getScreen().getWidth() - w) / 2;
int y = (currentWin.getScreen().getHeight() - h) / 2;
Window newWin = Window.current().open("", "_blank", "top=" + y + ",left=" + x + ",width=" + w + ",height=" + h + ",menubar=0,status=0,titlebar=0,toolbar=0");
if(newWin == null || TeaVMUtils.isNotTruthy(newWin)) {
Window.alert("ERROR: Popup blocked!\n\nPlease make sure you have popups enabled for this site!");
return;
}
newWin.focus();
documentWrite(newWin.getDocument(), "<!DOCTYPE html><html><head><meta charset=\"UTF-8\" />"
+ "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" /><title>EaglercraftX 1.8 Credits</title>"
+ "<link type=\"image/png\" rel=\"shortcut icon\" href=\""
+ BetterJSStringConverter.stringFromJS(PlatformApplication.faviconURLTeaVM()) + "\" />"
+ "</head><body><pre style=\"font:15px Consolas,monospace;\">" + text + "</pre></body></html>");
}
public static void downloadFileWithName(String str, byte[] dat) {
ByteBuffer buf = WASMGCDirectArrayConverter.byteArrayToBuffer(dat);
try {
downloadFileWithNameTeaVM(BetterJSStringConverter.stringToJS(str), WASMGCBufferAllocator.getUnsignedByteBufferView(buf));
}finally {
PlatformRuntime.freeByteBuffer(buf);
}
}
@Import(module = "platformApplication", name = "downloadFileWithNameU8")
public static native void downloadFileWithNameTeaVM(JSString str, Uint8Array dat);
@Import(module = "platformApplication", name = "downloadFileWithNameA")
public static native void downloadFileWithNameTeaVM(JSString str, ArrayBuffer dat);
private static final DateFormat dateFormatSS = new SimpleDateFormat("yyyy-MM-dd_HH.mm.ss");
@JSBody(params = { }, script = "return { willReadFrequently: true };")
static native JSObject youEagler();
public static String saveScreenshot() {
PlatformOpenGL._wglBindFramebuffer(0x8D40, null);
int w = PlatformInput.getWindowWidth();
int h = PlatformInput.getWindowHeight();
ByteBuffer buf = PlatformRuntime.allocateByteBuffer(w * h * 4);
PlatformOpenGL._wglReadPixels(0, 0, w, h, 6408, 5121, buf);
for(int i = 3, l = buf.remaining(); i < l; i += 4) {
buf.put(i, (byte)0xFF);
}
String name = "screenshot_" + dateFormatSS.format(new Date()).toString() + ".png";
HTMLCanvasElement copyCanvas = (HTMLCanvasElement) Window.current().getDocument().createElement("canvas");
copyCanvas.setWidth(w);
copyCanvas.setHeight(h);
CanvasRenderingContext2D ctx = (CanvasRenderingContext2D) copyCanvas.getContext("2d", youEagler());
ImageData imgData = ctx.createImageData(w, h);
Uint8ClampedArray dest = imgData.getData();
int ww = w * 4;
for(int i = 0, j; i < h; ++i) {
j = (h - i - 1) * ww;
buf.limit(j + ww);
buf.position(j);
dest.set(WASMGCBufferAllocator.getUnsignedClampedByteBufferView(buf), i * ww);
}
ctx.putImageData(imgData, 0, 0);
PlatformRuntime.freeByteBuffer(buf);
downloadScreenshotWithNameTeaVM(BetterJSStringConverter.stringToJS(name), copyCanvas);
return name;
}
@Import(module = "platformApplication", name = "downloadScreenshot")
private static native void downloadScreenshotWithNameTeaVM(JSString str, HTMLCanvasElement cvs);
@Import(module = "platformApplication", name = "showDebugConsole")
public static native void showDebugConsole();
public static void addLogMessage(String text, boolean err) {
}
@Import(module = "platformApplication", name = "isShowingDebugConsole")
public static native boolean isShowingDebugConsole();
@JSBody(params = { "str" }, script = "window.minecraftServer = str;")
public static native void setMCServerWindowGlobal(String str);
}

View File

@ -0,0 +1,220 @@
package net.lax1dude.eaglercraft.v1_8.internal;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import org.teavm.interop.Address;
import org.teavm.interop.Import;
import org.teavm.interop.Unmanaged;
import org.teavm.jso.JSObject;
import org.teavm.jso.JSProperty;
import org.teavm.jso.core.JSString;
import org.teavm.jso.typedarrays.Uint8Array;
import org.teavm.jso.typedarrays.Uint8ClampedArray;
import net.lax1dude.eaglercraft.v1_8.EaglerInputStream;
import net.lax1dude.eaglercraft.v1_8.internal.buffer.ByteBuffer;
import net.lax1dude.eaglercraft.v1_8.internal.buffer.WASMGCBufferAllocator;
import net.lax1dude.eaglercraft.v1_8.internal.buffer.WASMGCDirectArrayConverter;
import net.lax1dude.eaglercraft.v1_8.internal.wasm_gc_teavm.BetterJSStringConverter;
import net.lax1dude.eaglercraft.v1_8.internal.wasm_gc_teavm.ClientMain;
import net.lax1dude.eaglercraft.v1_8.internal.wasm_gc_teavm.EPKLoader;
import net.lax1dude.eaglercraft.v1_8.log4j.LogManager;
import net.lax1dude.eaglercraft.v1_8.log4j.Logger;
import net.lax1dude.eaglercraft.v1_8.opengl.ImageData;
/**
* Copyright (c) 2022-2024 lax1dude. All Rights Reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
public class PlatformAssets {
static final Logger logger = LogManager.getLogger("PlatformAssets");
private static final byte[] MISSING_FILE = new byte[0];
static Map<String,byte[]> assets = new HashMap<>();
public static boolean getResourceExists(String path) {
if(path.startsWith("/")) {
path = path.substring(1);
}
byte[] ret = assets.get(path);
if(ret != null && ret != MISSING_FILE) {
return true;
}else {
if(path.startsWith("assets/minecraft/lang/") && !path.endsWith(".mcmeta")) {
byte[] file = PlatformRuntime.downloadRemoteURIByteArray(
ClientMain.configLocalesFolder + "/" + path.substring(22));
if(file != null) {
assets.put(path, file);
return true;
}else {
assets.put(path, MISSING_FILE);
return false;
}
}else {
return false;
}
}
}
public static byte[] getResourceBytes(String path) {
if(path.startsWith("/")) {
path = path.substring(1);
}
byte[] data = assets.get(path);
if(data == null && path.startsWith("assets/minecraft/lang/") && !path.endsWith(".mcmeta")) {
byte[] file = PlatformRuntime.downloadRemoteURIByteArray(
ClientMain.configLocalesFolder + "/" + path.substring(22));
if(file != null) {
assets.put(path, file);
return data;
}else {
assets.put(path, MISSING_FILE);
return null;
}
}else {
return data == MISSING_FILE ? null : data;
}
}
static void readAssetsTeaVM() {
if(!assets.isEmpty()) {
assets = new HashMap<>();
}
int epkCount = getEPKFileCount();
logger.info("Reading {} EPK files", epkCount);
for(int i = 0; i < epkCount; ++i) {
JSEPKFileEntry etr = getEPKFileData(i);
String name = etr.getName();
String path = etr.getPath();
Uint8Array data = etr.getData();
int dataLen = data.getLength();
logger.info("Reading: \"{}\" @ {}", name, path.startsWith("/") ? path : ("/" + path));
ByteBuffer buf = PlatformRuntime.allocateByteBuffer(dataLen);
try {
WASMGCBufferAllocator.getUnsignedByteBufferView(buf).set(data);
EPKLoader.loadEPK(buf, path, assets);
}catch(IOException e) {
logger.error("Failed to load the EPK file!");
logger.error(e);
throw new RuntimeInitializationFailureException("Failed to read EPK file \"" + name + "\"!");
}finally {
PlatformRuntime.freeByteBuffer(buf);
}
}
logger.info("Loaded {} assets from EPK(s)", assets.size());
}
private interface JSEPKFileEntry extends JSObject {
@JSProperty
String getName();
@JSProperty
String getPath();
@JSProperty
Uint8Array getData();
}
@Import(module = "platformAssets", name = "getEPKFileData")
private static native JSEPKFileEntry getEPKFileData(int idx);
@Import(module = "platformAssets", name = "getEPKFileCount")
private static native int getEPKFileCount();
public static ImageData loadImageFile(InputStream data) {
return loadImageFile(data, "image/png");
}
public static ImageData loadImageFile(InputStream data, String mime) {
byte[] b = EaglerInputStream.inputStreamToBytesQuiet(data);
if(b != null) {
return loadImageFile(b, mime);
}else {
return null;
}
}
public static ImageData loadImageFile(byte[] data) {
return loadImageFile(data, "image/png");
}
public static ImageData loadImageFile(byte[] data, String mime) {
ByteBuffer buf = WASMGCDirectArrayConverter.byteArrayToBuffer(data);
JSImageLoadResult asyncResult;
try {
asyncResult = loadImageFile0(WASMGCBufferAllocator.getUnsignedByteBufferView(buf), BetterJSStringConverter.stringToJS(mime));
}finally {
PlatformRuntime.freeByteBuffer(buf);
}
if(asyncResult == null) {
return null;
}
int w = asyncResult.getWidth();
int h = asyncResult.getHeight();
int len = w * h;
ByteBuffer dataDest = PlatformRuntime.allocateByteBuffer(len << 2);
try {
loadImageFile1(asyncResult, WASMGCBufferAllocator.getUnsignedClampedByteBufferView(dataDest));
int[] pixelsArray = new int[len];
copyPixelArrayFast(pixelsArray, WASMGCBufferAllocator.getByteBufferAddress(dataDest), len);
return new ImageData(w, h, pixelsArray, true);
}finally {
PlatformRuntime.freeByteBuffer(dataDest);
}
}
private interface JSImageLoadResult extends JSObject {
@JSProperty
int getWidth();
@JSProperty
int getHeight();
}
@Import(module = "platformAssets", name = "loadImageFile0")
private static native JSImageLoadResult loadImageFile0(Uint8Array bufferData, JSString mime);
@Import(module = "platformAssets", name = "loadImageFile1")
private static native void loadImageFile1(JSImageLoadResult imageLoadResult, Uint8ClampedArray dataDest);
@Unmanaged
private static void copyPixelArrayFast(int[] pixelsArray, Address addr, int count) {
Address addrEnd = addr.add(count << 2);
int dstOffset = 0;
while(addr.isLessThan(addrEnd)) {
pixelsArray[dstOffset] = addr.getInt();
++dstOffset;
addr = addr.add(4);
}
}
}

View File

@ -0,0 +1,530 @@
package net.lax1dude.eaglercraft.v1_8.internal;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.teavm.interop.Import;
import org.teavm.jso.JSBody;
import org.teavm.jso.JSObject;
import org.teavm.jso.JSProperty;
import org.teavm.jso.core.JSString;
import org.teavm.jso.typedarrays.Float32Array;
import org.teavm.jso.typedarrays.Uint8Array;
import org.teavm.jso.webaudio.AudioBuffer;
import org.teavm.jso.webaudio.AudioBufferSourceNode;
import org.teavm.jso.webaudio.AudioContext;
import org.teavm.jso.webaudio.AudioListener;
import org.teavm.jso.webaudio.GainNode;
import org.teavm.jso.webaudio.MediaStream;
import org.teavm.jso.webaudio.MediaStreamAudioDestinationNode;
import org.teavm.jso.webaudio.PannerNode;
import net.lax1dude.eaglercraft.v1_8.EagRuntime;
import net.lax1dude.eaglercraft.v1_8.internal.buffer.ByteBuffer;
import net.lax1dude.eaglercraft.v1_8.internal.buffer.WASMGCBufferAllocator;
import net.lax1dude.eaglercraft.v1_8.internal.buffer.WASMGCDirectArrayConverter;
import net.lax1dude.eaglercraft.v1_8.internal.wasm_gc_teavm.BetterJSStringConverter;
import net.lax1dude.eaglercraft.v1_8.internal.wasm_gc_teavm.JOrbisAudioBufferDecoder;
import net.lax1dude.eaglercraft.v1_8.internal.wasm_gc_teavm.WASMGCClientConfigAdapter;
import net.lax1dude.eaglercraft.v1_8.log4j.LogManager;
import net.lax1dude.eaglercraft.v1_8.log4j.Logger;
import net.minecraft.util.MathHelper;
/**
* Copyright (c) 2022-2024 lax1dude. All Rights Reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
public class PlatformAudio {
static final Logger logger = LogManager.getLogger("BrowserAudio");
static AudioContext audioctx = null;
static MediaStreamAudioDestinationNode recDestNode = null;
static MediaStream recDestMediaStream = null;
static AudioBuffer silence = null;
static AudioBufferSourceNode recDestSilenceNode = null;
static GainNode micRecGain = null;
static GainNode gameRecGain = null;
private static final Map<String, BrowserAudioResource> soundCache = new HashMap<>();
private static final List<BrowserAudioHandle> activeSounds = new LinkedList<>();
private static long cacheFreeTimer = 0l;
private static long activeFreeTimer = 0l;
private static boolean oggSupport = false;
static void initialize() {
oggSupport = false;
audioctx = getContext();
if(audioctx == null) {
logger.error("Could not initialize audio context!");
return;
}
if(((WASMGCClientConfigAdapter)PlatformRuntime.getClientConfigAdapter()).isUseJOrbisAudioDecoderTeaVM()) {
logger.info("Note: Using embedded JOrbis OGG decoder");
}else {
byte[] fileData = EagRuntime.getRequiredResourceBytes("/assets/eagler/audioctx_test_ogg.dat");
ByteBuffer buf = WASMGCDirectArrayConverter.byteArrayToBuffer(fileData);
try {
AudioBuffer audioBuffer = decodeAudioBrowserAsync(WASMGCBufferAllocator.getUnsignedByteBufferView(buf),
BetterJSStringConverter.stringToJS("audioctx_test_ogg.dat"));
if(audioBuffer != null && audioBuffer.getLength() > 0) {
oggSupport = true;
}
}catch(Throwable t) {
}finally {
PlatformRuntime.freeByteBuffer(buf);
}
if(!oggSupport) {
logger.error("OGG file support detected as false! Using embedded JOrbis OGG decoder");
}
}
}
@Import(module = "platformAudio", name = "getContext")
private static native AudioContext getContext();
protected static class BrowserAudioResource implements IAudioResource {
protected AudioBuffer buffer;
protected long cacheHit = 0l;
protected BrowserAudioResource(AudioBuffer buffer) {
this.buffer = buffer;
}
}
protected interface JSBrowserAudioHandleCB extends JSObject {
@JSProperty
boolean getIsEnded();
@JSProperty
void setIsEnded(boolean b);
}
@JSBody(script = "return { isEnded: false };")
protected static native JSBrowserAudioHandleCB createHandleCBInstance();
protected static class BrowserAudioHandle implements IAudioHandle {
protected final BrowserAudioResource resource;
protected AudioBufferSourceNode source;
protected final PannerNode panner;
protected final GainNode gain;
protected float pitch;
protected boolean repeat;
protected boolean isPaused = false;
protected boolean isDisposed = false;
protected JSBrowserAudioHandleCB isEnded;
protected BrowserAudioHandle(BrowserAudioResource resource, AudioBufferSourceNode source, PannerNode panner,
GainNode gain, float pitch, boolean repeat) {
this.resource = resource;
this.source = source;
this.panner = panner;
this.gain = gain;
this.pitch = pitch;
this.repeat = repeat;
this.isEnded = createHandleCBInstance();
registerIsEndedHandler(source, isEnded);
}
@Override
public void pause(boolean setPaused) {
if(setPaused) {
if(!isPaused) {
isPaused = true;
source.getPlaybackRate().setValue(0.0f);
}
}else {
if(isPaused) {
isPaused = false;
source.getPlaybackRate().setValue(pitch);
}
}
}
@Override
public void repeat(boolean en) {
repeat = en;
if(!isEnded.getIsEnded()) {
source.setLoop(en);
}
}
@Override
public void restart() {
if(isEnded.getIsEnded()) {
isEnded.setIsEnded(false);
registerIsEndedHandler(source, isEnded);
isPaused = false;
AudioBufferSourceNode src = audioctx.createBufferSource();
resource.cacheHit = PlatformRuntime.steadyTimeMillis();
src.setBuffer(resource.buffer);
src.getPlaybackRate().setValue(pitch);
source.disconnect();
src.connect(panner == null ? gain : panner);
if(isDisposed) {
isDisposed = false;
activeSounds.add(this);
gain.connect(audioctx.getDestination());
if(gameRecGain != null) {
gain.connect(gameRecGain);
}
}
source = src;
source.start();
}else {
isPaused = false;
source.getPlaybackRate().setValue(pitch);
source.start(0.0);
}
}
@Override
public void move(float x, float y, float z) {
if(panner != null) {
panner.setPosition(x, y, z);
}
}
@Override
public void pitch(float f) {
pitch = f;
if(!isPaused) {
source.getPlaybackRate().setValue(pitch);
}
}
@Override
public void gain(float f) {
if(panner != null) {
float v1 = f * 16.0f;
if(v1 < 16.0f) v1 = 16.0f;
panner.setMaxDistance(v1);
}
float v2 = f;
if(v2 > 1.0f) v2 = 1.0f;
gain.getGain().setValue(v2);
}
@Override
public void end() {
if(!isEnded.getIsEnded()) {
isEnded.setIsEnded(true);
releaseIsEndedHandler(source, isEnded);
source.stop();
}
}
@Override
public boolean shouldFree() {
return isEnded.getIsEnded();
}
private void dispose() {
if(!isDisposed) {
isDisposed = true;
gain.disconnect();
}
}
}
@Import(module = "platformAudio", name = "registerIsEndedHandler")
protected static native void registerIsEndedHandler(AudioBufferSourceNode source, JSBrowserAudioHandleCB isEnded);
@Import(module = "platformAudio", name = "releaseIsEndedHandler")
protected static native void releaseIsEndedHandler(AudioBufferSourceNode source, JSBrowserAudioHandleCB isEnded);
public static IAudioResource loadAudioData(String filename, boolean holdInCache) {
BrowserAudioResource buffer = soundCache.get(filename);
if(buffer == null) {
byte[] file = PlatformAssets.getResourceBytes(filename);
if(file == null) return null;
buffer = new BrowserAudioResource(decodeAudioData(file, filename));
if(holdInCache) {
soundCache.put(filename, buffer);
}
}
if(buffer.buffer != null) {
buffer.cacheHit = PlatformRuntime.steadyTimeMillis();
return buffer;
}else {
return null;
}
}
public static IAudioResource loadAudioDataNew(String filename, boolean holdInCache, IAudioCacheLoader loader) {
BrowserAudioResource buffer = soundCache.get(filename);
if(buffer == null) {
byte[] file = loader.loadFile(filename);
if(file == null) return null;
buffer = new BrowserAudioResource(decodeAudioData(file, filename));
if(holdInCache) {
soundCache.put(filename, buffer);
}
}
if(buffer.buffer != null) {
buffer.cacheHit = PlatformRuntime.steadyTimeMillis();
return buffer;
}else {
return null;
}
}
private static AudioBuffer decodeAudioData(byte[] data, String errorFileName) {
if(data == null) {
return null;
}
if(oggSupport) {
ByteBuffer buf = WASMGCDirectArrayConverter.byteArrayToBuffer(data);
try {
return decodeAudioBrowserAsync(WASMGCBufferAllocator.getUnsignedByteBufferView(buf),
BetterJSStringConverter.stringToJS(errorFileName));
}finally {
PlatformRuntime.freeByteBuffer(buf);
}
}else {
if (data.length > 4 && data[0] == (byte) 0x4F && data[1] == (byte) 0x67 && data[2] == (byte) 0x67
&& data[3] == (byte) 0x53) {
return JOrbisAudioBufferDecoder.decodeAudioJOrbis(data, errorFileName);
}else {
ByteBuffer buf = WASMGCDirectArrayConverter.byteArrayToBuffer(data);
try {
return decodeAudioBrowserAsync(WASMGCBufferAllocator.getUnsignedByteBufferView(buf),
BetterJSStringConverter.stringToJS(errorFileName));
}finally {
PlatformRuntime.freeByteBuffer(buf);
}
}
}
}
@Import(module = "platformAudio", name = "decodeAudioBrowser")
public static native AudioBuffer decodeAudioBrowserAsync(Uint8Array array, JSString errorFileName);
public static AudioBuffer decodeAudioBufferPCMBrowser(Float32Array array, int ch, int len, int rate) {
AudioBuffer buffer = audioctx.createBuffer(ch, len, rate);
for(int i = 0; i < ch; ++i) {
buffer.copyToChannel(new Float32Array(array.getBuffer(), array.getByteOffset() + ((i * len) << 2), len), i);
}
return buffer;
}
public static void clearAudioCache() {
long millis = PlatformRuntime.steadyTimeMillis();
if(millis - cacheFreeTimer > 30000l) {
cacheFreeTimer = millis;
Iterator<BrowserAudioResource> itr = soundCache.values().iterator();
while(itr.hasNext()) {
if(millis - itr.next().cacheHit > 600000l) { // 10 minutes
itr.remove();
}
}
}
if(millis - activeFreeTimer > 700l) {
activeFreeTimer = millis;
Iterator<BrowserAudioHandle> itr = activeSounds.iterator();
while(itr.hasNext()) {
BrowserAudioHandle h = itr.next();
if(h.shouldFree()) {
itr.remove();
h.dispose();
}
}
}
}
public static void flushAudioCache() {
soundCache.clear();
Iterator<BrowserAudioHandle> itr = activeSounds.iterator();
while(itr.hasNext()) {
itr.next().dispose();
}
activeSounds.clear();
}
public static boolean available() {
return audioctx != null;
}
@JSBody(params = { "node" }, script = "node.distanceModel = \"linear\";")
static native void setDistanceModelLinearFast(PannerNode node) ;
@JSBody(params = { "node" }, script = "node.panningModel = \"HRTF\";")
static native void setPanningModelHRTFFast(PannerNode node) ;
public static IAudioHandle beginPlayback(IAudioResource track, float x, float y, float z,
float volume, float pitch, boolean repeat) {
BrowserAudioResource internalTrack = (BrowserAudioResource) track;
internalTrack.cacheHit = PlatformRuntime.steadyTimeMillis();
AudioBufferSourceNode src = audioctx.createBufferSource();
src.setBuffer(internalTrack.buffer);
src.getPlaybackRate().setValue(pitch);
src.setLoop(repeat);
PannerNode panner = audioctx.createPanner();
panner.setPosition(x, y, z);
float v1 = volume * 16.0f;
if(v1 < 16.0f) v1 = 16.0f;
panner.setMaxDistance(v1);
panner.setRolloffFactor(1.0f);
setDistanceModelLinearFast(panner);
setPanningModelHRTFFast(panner);
panner.setConeInnerAngle(360.0f);
panner.setConeOuterAngle(0.0f);
panner.setConeOuterGain(0.0f);
panner.setOrientation(0.0f, 1.0f, 0.0f);
GainNode gain = audioctx.createGain();
float v2 = volume;
if(v2 > 1.0f) v2 = 1.0f;
gain.getGain().setValue(v2);
src.connect(panner);
panner.connect(gain);
gain.connect(audioctx.getDestination());
if(gameRecGain != null) {
gain.connect(gameRecGain);
}
src.start();
BrowserAudioHandle ret = new BrowserAudioHandle(internalTrack, src, panner, gain, pitch, repeat);
activeSounds.add(ret);
return ret;
}
public static IAudioHandle beginPlaybackStatic(IAudioResource track, float volume, float pitch, boolean repeat) {
BrowserAudioResource internalTrack = (BrowserAudioResource) track;
internalTrack.cacheHit = PlatformRuntime.steadyTimeMillis();
AudioBufferSourceNode src = audioctx.createBufferSource();
src.setBuffer(internalTrack.buffer);
src.getPlaybackRate().setValue(pitch);
src.setLoop(repeat);
GainNode gain = audioctx.createGain();
float v2 = volume;
if(v2 > 1.0f) v2 = 1.0f;
gain.getGain().setValue(v2);
src.connect(gain);
gain.connect(audioctx.getDestination());
if(gameRecGain != null) {
gain.connect(gameRecGain);
}
src.start();
BrowserAudioHandle ret = new BrowserAudioHandle(internalTrack, src, null, gain, pitch, repeat);
activeSounds.add(ret);
return ret;
}
public static void setListener(float x, float y, float z, float pitchDegrees, float yawDegrees) {
float var2 = MathHelper.cos(-yawDegrees * 0.017453292F);
float var3 = MathHelper.sin(-yawDegrees * 0.017453292F);
float var4 = -MathHelper.cos(pitchDegrees * 0.017453292F);
float var5 = MathHelper.sin(pitchDegrees * 0.017453292F);
AudioListener l = audioctx.getListener();
l.setPosition(x, y, z);
l.setOrientation(-var3 * var4, -var5, -var2 * var4, 0.0f, 1.0f, 0.0f);
}
static MediaStream initRecordingStream(float gameVol, float micVol) {
if(recDestMediaStream != null) {
return recDestMediaStream;
}
try {
if(silence == null) {
silence = audioctx.createBuffer(1, 1, 48000);
silence.copyToChannel(new Float32Array(1), 0);
}
recDestNode = audioctx.createMediaStreamDestination();
recDestSilenceNode = audioctx.createBufferSource();
recDestSilenceNode.setBuffer(silence);
recDestSilenceNode.setLoop(true);
recDestSilenceNode.start();
recDestSilenceNode.connect(recDestNode);
if(micVol > 0.0f) {
MediaStream mic = PlatformScreenRecord.getMic();
if (mic != null) {
micRecGain = audioctx.createGain();
micRecGain.getGain().setValue(micVol);
audioctx.createMediaStreamSource(mic).connect(micRecGain);
micRecGain.connect(recDestNode);
}
}
gameRecGain = audioctx.createGain();
gameRecGain.getGain().setValue(gameVol);
for(BrowserAudioHandle handle : activeSounds) {
if(handle.panner != null) {
handle.panner.connect(gameRecGain);
}else {
handle.gain.connect(gameRecGain);
}
}
PlatformVoiceClient.addRecordingDest(gameRecGain);
gameRecGain.connect(recDestNode);
recDestMediaStream = recDestNode.getStream();
return recDestMediaStream;
}catch(Throwable t) {
destroyRecordingStream();
throw t;
}
}
static void destroyRecordingStream() {
if(recDestSilenceNode != null) {
try {
recDestSilenceNode.disconnect();
}catch(Throwable t) {
}
recDestSilenceNode = null;
}
if(micRecGain != null) {
try {
micRecGain.disconnect();
}catch(Throwable t) {
}
micRecGain = null;
}
if(gameRecGain != null) {
try {
gameRecGain.disconnect();
}catch(Throwable t) {
}
for(BrowserAudioHandle handle : activeSounds) {
try {
handle.gain.disconnect(gameRecGain);
}catch(Throwable t) {
}
}
PlatformVoiceClient.removeRecordingDest(gameRecGain);
gameRecGain = null;
}
recDestNode = null;
recDestMediaStream = null;
}
}

View File

@ -0,0 +1,49 @@
package net.lax1dude.eaglercraft.v1_8.internal;
import net.lax1dude.eaglercraft.v1_8.internal.vfs2.EaglerFileSystemException;
import net.lax1dude.eaglercraft.v1_8.internal.wasm_gc_teavm.IndexedDBFilesystem;
import net.lax1dude.eaglercraft.v1_8.log4j.LogManager;
import net.lax1dude.eaglercraft.v1_8.log4j.Logger;
/**
* Copyright (c) 2024 lax1dude. All Rights Reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
public class PlatformFilesystem {
private static final Logger logger = LogManager.getLogger("PlatformFilesystem");
public static IEaglerFilesystem initializePersist(String dbName) {
try {
return IndexedDBFilesystem.createFilesystem(dbName);
}catch(Throwable t) {
logger.error("Could not open IndexedDB filesystem: {}", dbName);
logger.error(t);
return null;
}
}
public static class FilesystemDatabaseLockedException extends EaglerFileSystemException {
public FilesystemDatabaseLockedException(String message) {
super(message);
}
}
public static class FilesystemDatabaseInitializationException extends EaglerFileSystemException {
public FilesystemDatabaseInitializationException(String message) {
super(message);
}
}
}

View File

@ -0,0 +1,72 @@
package net.lax1dude.eaglercraft.v1_8.internal;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import org.teavm.interop.Import;
import org.teavm.jso.JSObject;
import org.teavm.jso.JSProperty;
import org.teavm.jso.core.JSString;
import org.teavm.jso.typedarrays.ArrayBuffer;
import org.teavm.jso.typedarrays.Uint8Array;
import net.lax1dude.eaglercraft.v1_8.EagUtils;
import net.lax1dude.eaglercraft.v1_8.EaglerInputStream;
import net.lax1dude.eaglercraft.v1_8.internal.buffer.ByteBuffer;
import net.lax1dude.eaglercraft.v1_8.internal.buffer.WASMGCBufferAllocator;
import net.lax1dude.eaglercraft.v1_8.internal.buffer.WASMGCDirectArrayConverter;
import net.lax1dude.eaglercraft.v1_8.internal.wasm_gc_teavm.BetterJSStringConverter;
import net.lax1dude.eaglercraft.v1_8.internal.wasm_gc_teavm.WASMGCWebSocketClient;
import net.lax1dude.eaglercraft.v1_8.log4j.LogManager;
import net.lax1dude.eaglercraft.v1_8.log4j.Logger;
/**
* Copyright (c) 2024 lax1dude. All Rights Reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
public class PlatformNetworking {
private static final Logger logger = LogManager.getLogger("PlatformNetworking");
public static IWebSocketClient openWebSocket(String socketURI) {
IWebSocketClient client = openWebSocketImpl(socketURI);
if(client == null) {
logger.error("Could not open WebSocket to \"{}\"!", socketURI);
}
return client;
}
public static IWebSocketClient openWebSocketUnsafe(String socketURI) {
IWebSocketClient client = openWebSocketImpl(socketURI);
if(client == null) {
throw new IllegalArgumentException("Could not open WebSocket to \"" + socketURI + "\"!");
}
return client;
}
public static IWebSocketClient openWebSocketImpl(String socketURI) {
WASMGCWebSocketClient.JSWebSocketClientHandle handle = createWebSocketHandle(
BetterJSStringConverter.stringToJS(socketURI));
if(handle != null) {
return new WASMGCWebSocketClient(handle, socketURI);
}else {
return null;
}
}
@Import(module = "platformNetworking", name = "createWebSocketHandle")
private static native WASMGCWebSocketClient.JSWebSocketClientHandle createWebSocketHandle(JSString socketURI);
}

View File

@ -0,0 +1,850 @@
package net.lax1dude.eaglercraft.v1_8.internal;
import java.util.Arrays;
import java.util.List;
import org.teavm.interop.Import;
import org.teavm.jso.JSBody;
import org.teavm.jso.core.JSArray;
import org.teavm.jso.core.JSNumber;
import org.teavm.jso.core.JSString;
import org.teavm.jso.typedarrays.ArrayBufferView;
import org.teavm.jso.typedarrays.Float32Array;
import org.teavm.jso.webgl.WebGLBuffer;
import org.teavm.jso.webgl.WebGLFramebuffer;
import org.teavm.jso.webgl.WebGLProgram;
import org.teavm.jso.webgl.WebGLRenderbuffer;
import org.teavm.jso.webgl.WebGLShader;
import org.teavm.jso.webgl.WebGLTexture;
import org.teavm.jso.webgl.WebGLUniformLocation;
import net.lax1dude.eaglercraft.v1_8.internal.buffer.ByteBuffer;
import net.lax1dude.eaglercraft.v1_8.internal.buffer.FloatBuffer;
import net.lax1dude.eaglercraft.v1_8.internal.buffer.IntBuffer;
import net.lax1dude.eaglercraft.v1_8.internal.buffer.WASMGCBufferAllocator;
import net.lax1dude.eaglercraft.v1_8.internal.wasm_gc_teavm.BetterJSStringConverter;
import net.lax1dude.eaglercraft.v1_8.internal.wasm_gc_teavm.WebGLBackBuffer;
import net.lax1dude.eaglercraft.v1_8.internal.wasm_gc_teavm.WebGLQuery;
import net.lax1dude.eaglercraft.v1_8.internal.wasm_gc_teavm.WebGLVertexArray;
/**
* Copyright (c) 2024 lax1dude. All Rights Reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
public class PlatformOpenGL {
static int glesVers = -1;
static final int VAO_IMPL_NONE = -1;
static final int VAO_IMPL_CORE = 0;
static final int VAO_IMPL_OES = 1;
static int vertexArrayImpl = VAO_IMPL_NONE;
static final int INSTANCE_IMPL_NONE = -1;
static final int INSTANCE_IMPL_CORE = 0;
static final int INSTANCE_IMPL_ANGLE = 1;
static int instancingImpl = INSTANCE_IMPL_NONE;
static final int CAP_A_BIT_EXT_GPU_SHADER5 = 1;
static final int CAP_A_BIT_OES_GPU_SHADER5 = 2;
static final int CAP_A_BIT_FBO_RENDER_MIPMAP = 4;
static final int CAP_A_BIT_TEXTURE_LOD_CAPABLE = 8;
static final int CAP_A_BIT_NPOT_CAPABLE = 16;
static final int CAP_A_BIT_HDR_FBO16F = 32;
static final int CAP_A_BIT_HDR_FBO32F = 64;
static final int CAP_A_BIT_ANISOTROPIC = 128;
static int capABits = 0;
static final int CAP_B_BIT_HDR_LINEAR16F = 1;
static final int CAP_B_BIT_HDR_LINEAR32F = 2;
static int capBBits = 0;
static void initContext() {
glesVers = getCapBits(0);
vertexArrayImpl = getCapBits(1);
instancingImpl = getCapBits(2);
capABits = getCapBits(3);
capBBits = getCapBits(4);
_wglClearColor(1.0f, 1.0f, 1.0f, 1.0f);
}
@Import(module = "platformOpenGL", name = "getCapBits")
static native int getCapBits(int idx);
@Import(module = "platformOpenGL", name = "glEnable")
public static native void _wglEnable(int glEnum);
@Import(module = "platformOpenGL", name = "glDisable")
public static native void _wglDisable(int glEnum);
@Import(module = "platformOpenGL", name = "glClearColor")
public static native void _wglClearColor(float r, float g, float b, float a);
@Import(module = "platformOpenGL", name = "glClearDepth")
public static native void _wglClearDepth(float f);
@Import(module = "platformOpenGL", name = "glClear")
public static native void _wglClear(int bits);
@Import(module = "platformOpenGL", name = "glDepthFunc")
public static native void _wglDepthFunc(int glEnum);
@Import(module = "platformOpenGL", name = "glDepthMask")
public static native void _wglDepthMask(boolean mask);
@Import(module = "platformOpenGL", name = "glCullFace")
public static native void _wglCullFace(int glEnum);
@Import(module = "platformOpenGL", name = "glViewport")
public static native void _wglViewport(int x, int y, int w, int h);
@Import(module = "platformOpenGL", name = "glBlendFunc")
public static native void _wglBlendFunc(int src, int dst);
@Import(module = "platformOpenGL", name = "glBlendFuncSeparate")
public static native void _wglBlendFuncSeparate(int srcColor, int dstColor, int srcAlpha, int dstAlpha);
@Import(module = "platformOpenGL", name = "glBlendEquation")
public static native void _wglBlendEquation(int glEnum);
@Import(module = "platformOpenGL", name = "glBlendColor")
public static native void _wglBlendColor(float r, float g, float b, float a);
@Import(module = "platformOpenGL", name = "glColorMask")
public static native void _wglColorMask(boolean r, boolean g, boolean b, boolean a);
private static final JSArray<JSNumber> drawBuffers = new JSArray<>();
@JSBody(params = { "arr", "idx", "num" }, script = "arr[idx] = num;")
private static native void setArrayInt(JSArray<JSNumber> arr, int idx, int num);
public static final void _wglDrawBuffers(int buffer) {
if(glesVers == 200) {
if(buffer != 0x8CE0) { // GL_COLOR_ATTACHMENT0
throw new UnsupportedOperationException();
}
}else {
drawBuffers.setLength(1);
setArrayInt(drawBuffers, 0, buffer);
_wglDrawBuffersN(drawBuffers);
}
}
public static final void _wglDrawBuffers(int[] buffers) {
if(glesVers == 200) {
if(buffers.length != 1 || buffers[0] != 0x8CE0) { // GL_COLOR_ATTACHMENT0
throw new UnsupportedOperationException();
}
}else {
int cnt = buffers.length;
drawBuffers.setLength(cnt);
for(int i = 0; i < cnt; ++i) {
setArrayInt(drawBuffers, i, buffers[i]);
}
_wglDrawBuffersN(drawBuffers);
}
}
@Import(module = "platformOpenGL", name = "glDrawBuffers")
private static native void _wglDrawBuffersN(JSArray<JSNumber> buffers);
@Import(module = "platformOpenGL", name = "glReadBuffer")
public static native void _wglReadBuffer(int glEnum);
public static final void _wglReadPixels(int x, int y, int width, int height, int format, int type, ByteBuffer buffer) {
_wglReadPixelsN(x, y, width, height, format, type, WASMGCBufferAllocator.getUnsignedByteBufferView(buffer));
}
public static final void _wglReadPixels_u16(int x, int y, int width, int height, int format, int type, ByteBuffer buffer) {
_wglReadPixelsN(x, y, width, height, format, type, WASMGCBufferAllocator.getUnsignedShortBufferView(buffer));
}
public static final void _wglReadPixels(int x, int y, int width, int height, int format, int type, IntBuffer buffer) {
_wglReadPixelsN(x, y, width, height, format, type, WASMGCBufferAllocator.getIntBufferView(buffer));
}
public static final void _wglReadPixels(int x, int y, int width, int height, int format, int type, FloatBuffer buffer) {
_wglReadPixelsN(x, y, width, height, format, type, WASMGCBufferAllocator.getFloatBufferView(buffer));
}
@Import(module = "platformOpenGL", name = "glReadPixels")
static native void _wglReadPixelsN(int x, int y, int width, int height, int format, int type, ArrayBufferView array);
@Import(module = "platformOpenGL", name = "glPolygonOffset")
public static native void _wglPolygonOffset(float f1, float f2);
@Import(module = "platformOpenGL", name = "glLineWidth")
public static native void _wglLineWidth(float width);
public static final IBufferGL _wglGenBuffers() {
return new OpenGLObjects.BufferGL(_wglGenBuffersN());
}
@Import(module = "platformOpenGL", name = "glGenBuffers")
static native WebGLBuffer _wglGenBuffersN();
public static final ITextureGL _wglGenTextures() {
return new OpenGLObjects.TextureGL(_wglGenTexturesN());
}
@Import(module = "platformOpenGL", name = "glGenTextures")
static native WebGLTexture _wglGenTexturesN();
public static final IBufferArrayGL _wglGenVertexArrays() {
return new OpenGLObjects.BufferArrayGL(_wglGenVertexArraysN());
}
@Import(module = "platformOpenGL", name = "glGenVertexArrays")
public static native WebGLVertexArray _wglGenVertexArraysN();
public static final IProgramGL _wglCreateProgram() {
return new OpenGLObjects.ProgramGL(_wglCreateProgramN());
}
@Import(module = "platformOpenGL", name = "glCreateProgram")
static native WebGLProgram _wglCreateProgramN();
public static final IShaderGL _wglCreateShader(int type) {
return new OpenGLObjects.ShaderGL(_wglCreateShaderN(type));
}
@Import(module = "platformOpenGL", name = "glCreateShader")
static native WebGLShader _wglCreateShaderN(int type);
public static final IFramebufferGL _wglCreateFramebuffer() {
return new OpenGLObjects.FramebufferGL(_wglCreateFramebufferN());
}
@Import(module = "platformOpenGL", name = "glCreateFramebuffer")
static native WebGLFramebuffer _wglCreateFramebufferN();
public static final IRenderbufferGL _wglCreateRenderbuffer() {
return new OpenGLObjects.RenderbufferGL(_wglCreateRenderbufferN());
}
@Import(module = "platformOpenGL", name = "glCreateRenderbuffer")
static native WebGLRenderbuffer _wglCreateRenderbufferN();
public static final IQueryGL _wglGenQueries() {
return new OpenGLObjects.QueryGL(_wglGenQueriesN());
}
@Import(module = "platformOpenGL", name = "glGenQueries")
static native WebGLQuery _wglGenQueriesN();
public static final void _wglDeleteBuffers(IBufferGL objId) {
_wglDeleteBuffersN(((OpenGLObjects.BufferGL)objId).ptr);
}
@Import(module = "platformOpenGL", name = "glDeleteBuffers")
static native void _wglDeleteBuffersN(WebGLBuffer objId);
public static final void _wglDeleteTextures(ITextureGL objId) {
_wglDeleteTexturesN(((OpenGLObjects.TextureGL)objId).ptr);
}
@Import(module = "platformOpenGL", name = "glDeleteTextures")
static native void _wglDeleteTexturesN(WebGLTexture objId);
public static final void _wglDeleteVertexArrays(IBufferArrayGL objId) {
_wglDeleteVertexArraysN(((OpenGLObjects.BufferArrayGL)objId).ptr);
}
@Import(module = "platformOpenGL", name = "glDeleteVertexArrays")
static native void _wglDeleteVertexArraysN(WebGLVertexArray objId);
public static final void _wglDeleteProgram(IProgramGL objId) {
_wglDeleteProgramN(((OpenGLObjects.ProgramGL)objId).ptr);
}
@Import(module = "platformOpenGL", name = "glDeleteProgram")
static native void _wglDeleteProgramN(WebGLProgram objId);
public static final void _wglDeleteShader(IShaderGL objId) {
_wglDeleteShaderN(((OpenGLObjects.ShaderGL)objId).ptr);
}
@Import(module = "platformOpenGL", name = "glDeleteShader")
static native void _wglDeleteShaderN(WebGLShader objId);
public static final void _wglDeleteFramebuffer(IFramebufferGL objId) {
_wglDeleteFramebufferN(((OpenGLObjects.FramebufferGL)objId).ptr);
}
@Import(module = "platformOpenGL", name = "glDeleteFramebuffer")
static native void _wglDeleteFramebufferN(WebGLFramebuffer objId);
public static final void _wglDeleteRenderbuffer(IRenderbufferGL objId) {
_wglDeleteRenderbufferN(((OpenGLObjects.RenderbufferGL)objId).ptr);
}
@Import(module = "platformOpenGL", name = "glDeleteRenderbuffer")
static native void _wglDeleteRenderbufferN(WebGLRenderbuffer objId);
public static final void _wglDeleteQueries(IQueryGL objId) {
_wglDeleteQueriesN(((OpenGLObjects.QueryGL)objId).ptr);
}
@Import(module = "platformOpenGL", name = "glDeleteQueries")
static native void _wglDeleteQueriesN(WebGLQuery objId);
public static final void _wglBindBuffer(int target, IBufferGL bufObj) {
_wglBindBufferN(target, bufObj != null ? ((OpenGLObjects.BufferGL)bufObj).ptr : null);
}
@Import(module = "platformOpenGL", name = "glBindBuffer")
static native void _wglBindBufferN(int target, WebGLBuffer bufObj);
@Import(module = "platformOpenGL", name = "glBufferData")
public static native void _wglBufferData(int target, int size, int usage);
public static final void _wglBufferData(int target, ByteBuffer buffer, int usage) {
_wglBufferDataN(target, WASMGCBufferAllocator.getUnsignedByteBufferView(buffer), usage);
}
public static final void _wglBufferData(int target, IntBuffer buffer, int usage) {
_wglBufferDataN(target, WASMGCBufferAllocator.getIntBufferView(buffer), usage);
}
public static final void _wglBufferData(int target, FloatBuffer buffer, int usage) {
_wglBufferDataN(target, WASMGCBufferAllocator.getFloatBufferView(buffer), usage);
}
@Import(module = "platformOpenGL", name = "glBufferData")
static native void _wglBufferDataN(int target, ArrayBufferView typedArray, int usage);
public static final void _wglBufferSubData(int target, int dstOffset, ByteBuffer buffer) {
_wglBufferSubDataN(target, dstOffset, WASMGCBufferAllocator.getUnsignedByteBufferView(buffer));
}
public static final void _wglBufferSubData(int target, int dstOffset, IntBuffer buffer) {
_wglBufferSubDataN(target, dstOffset, WASMGCBufferAllocator.getIntBufferView(buffer));
}
public static final void _wglBufferSubData(int target, int dstOffset, FloatBuffer buffer) {
_wglBufferSubDataN(target, dstOffset, WASMGCBufferAllocator.getFloatBufferView(buffer));
}
@Import(module = "platformOpenGL", name = "glBufferSubData")
static native void _wglBufferSubDataN(int target, int dstOffset, ArrayBufferView typedArray);
public static final void _wglBindVertexArray(IBufferArrayGL objId) {
_wglBindVertexArrayN(objId != null ? ((OpenGLObjects.BufferArrayGL)objId).ptr : null);
}
@Import(module = "platformOpenGL", name = "glBindVertexArray")
static native void _wglBindVertexArrayN(WebGLVertexArray objId);
@Import(module = "platformOpenGL", name = "glEnableVertexAttribArray")
public static native void _wglEnableVertexAttribArray(int index);
@Import(module = "platformOpenGL", name = "glDisableVertexAttribArray")
public static native void _wglDisableVertexAttribArray(int index);
@Import(module = "platformOpenGL", name = "glVertexAttribPointer")
public static native void _wglVertexAttribPointer(int index, int size, int type,
boolean normalized, int stride, int offset);
@Import(module = "platformOpenGL", name = "glVertexAttribDivisor")
public static native void _wglVertexAttribDivisor(int index, int divisor);
@Import(module = "platformOpenGL", name = "glActiveTexture")
public static native void _wglActiveTexture(int texture);
public static final void _wglBindTexture(int target, ITextureGL objId) {
_wglBindTextureN(target, objId != null ? ((OpenGLObjects.TextureGL)objId).ptr : null);
}
@Import(module = "platformOpenGL", name = "glBindTexture")
static native void _wglBindTextureN(int target, WebGLTexture objId);
@Import(module = "platformOpenGL", name = "glTexParameterf")
public static native void _wglTexParameterf(int target, int param, float value);
@Import(module = "platformOpenGL", name = "glTexParameteri")
public static native void _wglTexParameteri(int target, int param, int value);
public static final void _wglTexImage3D(int target, int level, int internalFormat, int width, int height, int depth,
int border, int format, int type, ByteBuffer data) {
_wglTexImage3DN(target, level, internalFormat, width, height, depth, border, format, type,
data != null ? WASMGCBufferAllocator.getUnsignedByteBufferView(data) : null);
}
@Import(module = "platformOpenGL", name = "glTexImage3D")
static native void _wglTexImage3DN(int target, int level, int internalFormat, int width, int height, int depth,
int border, int format, int type, ArrayBufferView typedArray);
public static final void _wglTexImage2D(int target, int level, int internalFormat, int width, int height, int border,
int format, int type, ByteBuffer data) {
_wglTexImage2DN(target, level, internalFormat, width, height, border, format, type,
data != null ? WASMGCBufferAllocator.getUnsignedByteBufferView(data) : null);
}
public static final void _wglTexImage2Du16(int target, int level, int internalFormat, int width, int height, int border,
int format, int type, ByteBuffer data) {
_wglTexImage2DN(target, level, internalFormat, width, height, border, format, type,
data != null ? WASMGCBufferAllocator.getUnsignedShortBufferView(data) : null);
}
public static final void _wglTexImage2Df32(int target, int level, int internalFormat, int width, int height, int border,
int format, int type, ByteBuffer data) {
_wglTexImage2DN(target, level, internalFormat, width, height, border, format, type,
data != null ? WASMGCBufferAllocator.getFloatBufferView(data) : null);
}
public static final void _wglTexImage2D(int target, int level, int internalFormat, int width,
int height, int border, int format, int type, IntBuffer data) {
_wglTexImage2DN(target, level, internalFormat, width, height, border, format, type,
data != null ? WASMGCBufferAllocator.getUnsignedByteBufferView(data) : null);
}
public static final void _wglTexImage2Df32(int target, int level, int internalFormat, int width,
int height, int border, int format, int type, FloatBuffer data) {
_wglTexImage2DN(target, level, internalFormat, width, height, border, format, type,
data != null ? WASMGCBufferAllocator.getFloatBufferView(data) : null);
}
@Import(module = "platformOpenGL", name = "glTexImage2D")
static native void _wglTexImage2DN(int target, int level, int internalFormat, int width, int height, int border,
int format, int type, ArrayBufferView typedArray);
public static final void _wglTexSubImage2D(int target, int level, int xoffset, int yoffset,
int width, int height, int format, int type, ByteBuffer data) {
_wglTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type,
data != null ? WASMGCBufferAllocator.getUnsignedByteBufferView(data) : null);
}
public static final void _wglTexSubImage2Du16(int target, int level, int xoffset, int yoffset,
int width, int height, int format, int type, ByteBuffer data) {
_wglTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type,
data != null ? WASMGCBufferAllocator.getUnsignedShortBufferView(data) : null);
}
public static final void _wglTexSubImage2D(int target, int level, int xoffset, int yoffset,
int width, int height, int format, int type, IntBuffer data) {
_wglTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type,
data != null ? WASMGCBufferAllocator.getUnsignedByteBufferView(data) : null);
}
public static final void _wglTexSubImage2Df32(int target, int level, int xoffset, int yoffset,
int width, int height, int format, int type, FloatBuffer data) {
_wglTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type,
data != null ? WASMGCBufferAllocator.getFloatBufferView(data) : null);
}
@Import(module = "platformOpenGL", name = "glTexSubImage2D")
static native void _wglTexSubImage2D(int target, int level, int offsetx, int offsety, int width, int height,
int format, int type, ArrayBufferView typedArray);
@Import(module = "platformOpenGL", name = "glCopyTexSubImage2D")
public static native void _wglCopyTexSubImage2D(int target, int level, int xoffset, int yoffset,
int x, int y, int width, int height);
@Import(module = "platformOpenGL", name = "glTexStorage2D")
public static native void _wglTexStorage2D(int target, int levels, int internalFormat, int w, int h);
@Import(module = "platformOpenGL", name = "glPixelStorei")
public static native void _wglPixelStorei(int pname, int value);
@Import(module = "platformOpenGL", name = "glGenerateMipmap")
public static native void _wglGenerateMipmap(int target);
public static final void _wglShaderSource(IShaderGL shader, String str) {
_wglShaderSourceN(((OpenGLObjects.ShaderGL)shader).ptr, BetterJSStringConverter.stringToJS(str));
}
@Import(module = "platformOpenGL", name = "glShaderSource")
static native void _wglShaderSourceN(WebGLShader shader, JSString str);
public static final void _wglCompileShader(IShaderGL shader) {
_wglCompileShaderN(((OpenGLObjects.ShaderGL)shader).ptr);
}
@Import(module = "platformOpenGL", name = "glCompileShader")
static native void _wglCompileShaderN(WebGLShader shader);
public static final int _wglGetShaderi(IShaderGL shader, int param) {
return _wglGetShaderiN(((OpenGLObjects.ShaderGL)shader).ptr, param);
}
@Import(module = "platformOpenGL", name = "glGetShaderi")
static native int _wglGetShaderiN(WebGLShader shader, int param);
public static final String _wglGetShaderInfoLog(IShaderGL shader) {
return BetterJSStringConverter.stringFromJS(_wglGetShaderInfoLogN(((OpenGLObjects.ShaderGL)shader).ptr));
}
@Import(module = "platformOpenGL", name = "glGetShaderInfoLog")
static native JSString _wglGetShaderInfoLogN(WebGLShader shader);
public static final void _wglUseProgram(IProgramGL prog) {
_wglUseProgramN(prog != null ? ((OpenGLObjects.ProgramGL)prog).ptr : null);
}
@Import(module = "platformOpenGL", name = "glUseProgram")
static native void _wglUseProgramN(WebGLProgram prog);
public static final void _wglAttachShader(IProgramGL prog, IShaderGL shader) {
_wglAttachShaderN(((OpenGLObjects.ProgramGL)prog).ptr, ((OpenGLObjects.ShaderGL)shader).ptr);
}
@Import(module = "platformOpenGL", name = "glAttachShader")
static native void _wglAttachShaderN(WebGLProgram prog, WebGLShader shader);
public static final void _wglDetachShader(IProgramGL prog, IShaderGL shader) {
_wglDetachShaderN(((OpenGLObjects.ProgramGL)prog).ptr, ((OpenGLObjects.ShaderGL)shader).ptr);
}
@Import(module = "platformOpenGL", name = "glDetachShader")
public static native void _wglDetachShaderN(WebGLProgram prog, WebGLShader shader);
public static final void _wglLinkProgram(IProgramGL prog) {
_wglLinkProgramN(((OpenGLObjects.ProgramGL)prog).ptr);
}
@Import(module = "platformOpenGL", name = "glLinkProgram")
static native void _wglLinkProgramN(WebGLProgram prog);
public static final int _wglGetProgrami(IProgramGL prog, int param) {
return _wglGetProgramiN(((OpenGLObjects.ProgramGL)prog).ptr, param);
}
@Import(module = "platformOpenGL", name = "glGetProgrami")
static native int _wglGetProgramiN(WebGLProgram prog, int param);
public static final String _wglGetProgramInfoLog(IProgramGL prog) {
return BetterJSStringConverter.stringFromJS(_wglGetProgramInfoLogN(((OpenGLObjects.ProgramGL)prog).ptr));
}
@Import(module = "platformOpenGL", name = "glGetProgramInfoLog")
static native JSString _wglGetProgramInfoLogN(WebGLProgram prog);
@Import(module = "platformOpenGL", name = "glDrawArrays")
public static native void _wglDrawArrays(int mode, int first, int count);
@Import(module = "platformOpenGL", name = "glDrawElements")
public static native void _wglDrawElements(int mode, int count, int type, int offset);
@Import(module = "platformOpenGL", name = "glDrawArraysInstanced")
public static native void _wglDrawArraysInstanced(int mode, int first, int count, int instanced);
@Import(module = "platformOpenGL", name = "glDrawElementsInstanced")
public static native void _wglDrawElementsInstanced(int mode, int count, int type, int offset, int instanced);
public static final void _wglBindAttribLocation(IProgramGL prog, int index, String str) {
_wglBindAttribLocationN(((OpenGLObjects.ProgramGL)prog).ptr, index, BetterJSStringConverter.stringToJS(str));
}
@Import(module = "platformOpenGL", name = "glBindAttribLocation")
static native void _wglBindAttribLocationN(WebGLProgram prog, int index, JSString str);
public static final int _wglGetAttribLocation(IProgramGL prog, String str) {
return _wglGetAttribLocationN(((OpenGLObjects.ProgramGL)prog).ptr, BetterJSStringConverter.stringToJS(str));
}
@Import(module = "platformOpenGL", name = "glGetAttribLocation")
static native int _wglGetAttribLocationN(WebGLProgram prog, JSString str);
public static final IUniformGL _wglGetUniformLocation(IProgramGL prog, String str) {
WebGLUniformLocation ret = _wglGetUniformLocationN(((OpenGLObjects.ProgramGL)prog).ptr, BetterJSStringConverter.stringToJS(str));
return ret != null ? new OpenGLObjects.UniformGL(ret) : null;
}
@Import(module = "platformOpenGL", name = "glGetUniformLocation")
static native WebGLUniformLocation _wglGetUniformLocationN(WebGLProgram prog, JSString str);
public static final int _wglGetUniformBlockIndex(IProgramGL prog, String str) {
return _wglGetUniformBlockIndexN(((OpenGLObjects.ProgramGL)prog).ptr, BetterJSStringConverter.stringToJS(str));
}
@Import(module = "platformOpenGL", name = "glGetUniformBlockIndex")
static native int _wglGetUniformBlockIndexN(WebGLProgram prog, JSString str);
public static final void _wglBindBufferRange(int target, int index, IBufferGL bufferId, int offset, int size) {
_wglBindBufferRangeN(target, index, ((OpenGLObjects.BufferGL)bufferId).ptr, offset, size);
}
@Import(module = "platformOpenGL", name = "glBindBufferRange")
static native void _wglBindBufferRangeN(int target, int index, WebGLBuffer bufferId, int offset, int size);
public static final void _wglUniformBlockBinding(IProgramGL prog, int blockIndex, int bufferIndex) {
_wglUniformBlockBindingN(((OpenGLObjects.ProgramGL)prog).ptr, blockIndex, bufferIndex);
}
@Import(module = "platformOpenGL", name = "glUniformBlockBinding")
static native void _wglUniformBlockBindingN(WebGLProgram prog, int blockIndex, int bufferIndex);
public static final void _wglUniform1f(IUniformGL uniformIndex, float x) {
if(uniformIndex != null) _wglUniform1fN(((OpenGLObjects.UniformGL)uniformIndex).ptr, x);
}
@Import(module = "platformOpenGL", name = "glUniform1f")
public static native void _wglUniform1fN(WebGLUniformLocation uniformIndex, float x);
public static final void _wglUniform2f(IUniformGL uniformIndex, float x, float y) {
if(uniformIndex != null) _wglUniform2fN(((OpenGLObjects.UniformGL)uniformIndex).ptr, x, y);
}
@Import(module = "platformOpenGL", name = "glUniform2f")
public static native void _wglUniform2fN(WebGLUniformLocation uniformIndex, float x, float y);
public static final void _wglUniform3f(IUniformGL uniformIndex, float x, float y, float z) {
if(uniformIndex != null) _wglUniform3fN(((OpenGLObjects.UniformGL)uniformIndex).ptr, x, y, z);
}
@Import(module = "platformOpenGL", name = "glUniform3f")
public static native void _wglUniform3fN(WebGLUniformLocation uniformIndex, float x, float y, float z);
public static final void _wglUniform4f(IUniformGL uniformIndex, float x, float y, float z, float w) {
if(uniformIndex != null) _wglUniform4fN(((OpenGLObjects.UniformGL)uniformIndex).ptr, x, y, z, w);
}
@Import(module = "platformOpenGL", name = "glUniform4f")
public static native void _wglUniform4fN(WebGLUniformLocation uniformIndex, float x, float y, float z, float w);
public static final void _wglUniform1i(IUniformGL uniformIndex, int x) {
if(uniformIndex != null) _wglUniform1iN(((OpenGLObjects.UniformGL)uniformIndex).ptr, x);
}
@Import(module = "platformOpenGL", name = "glUniform1i")
public static native void _wglUniform1iN(WebGLUniformLocation uniformIndex, int x);
public static final void _wglUniform2i(IUniformGL uniformIndex, int x, int y) {
if(uniformIndex != null) _wglUniform2iN(((OpenGLObjects.UniformGL)uniformIndex).ptr, x, y);
}
@Import(module = "platformOpenGL", name = "glUniform2i")
public static native void _wglUniform2iN(WebGLUniformLocation uniformIndex, int x, int y);
public static final void _wglUniform3i(IUniformGL uniformIndex, int x, int y, int z) {
if(uniformIndex != null) _wglUniform3iN(((OpenGLObjects.UniformGL)uniformIndex).ptr, x, y, z);
}
@Import(module = "platformOpenGL", name = "glUniform3i")
public static native void _wglUniform3iN(WebGLUniformLocation uniformIndex, int x, int y, int z);
public static final void _wglUniform4i(IUniformGL uniformIndex, int x, int y, int z, int w) {
if(uniformIndex != null) _wglUniform4iN(((OpenGLObjects.UniformGL)uniformIndex).ptr, x, y, z, w);
}
@Import(module = "platformOpenGL", name = "glUniform4i")
public static native void _wglUniform4iN(WebGLUniformLocation uniformIndex, int x, int y, int z, int w);
public static final void _wglUniformMatrix2fv(IUniformGL uniformIndex, boolean transpose, FloatBuffer buffer) {
if (uniformIndex != null)
_wglUniformMatrix2fvN(((OpenGLObjects.UniformGL) uniformIndex).ptr, transpose,
WASMGCBufferAllocator.getFloatBufferView(buffer));
}
@Import(module = "platformOpenGL", name = "glUniformMatrix2fv")
static native void _wglUniformMatrix2fvN(WebGLUniformLocation uniformIndex, boolean transpose, Float32Array typedArray);
public static final void _wglUniformMatrix3fv(IUniformGL uniformIndex, boolean transpose, FloatBuffer buffer) {
if (uniformIndex != null)
_wglUniformMatrix3fvN(((OpenGLObjects.UniformGL) uniformIndex).ptr, transpose,
WASMGCBufferAllocator.getFloatBufferView(buffer));
}
@Import(module = "platformOpenGL", name = "glUniformMatrix3fv")
static native void _wglUniformMatrix3fvN(WebGLUniformLocation uniformIndex, boolean transpose, Float32Array typedArray);
public static final void _wglUniformMatrix4fv(IUniformGL uniformIndex, boolean transpose, FloatBuffer buffer) {
if (uniformIndex != null)
_wglUniformMatrix4fvN(((OpenGLObjects.UniformGL) uniformIndex).ptr, transpose,
WASMGCBufferAllocator.getFloatBufferView(buffer));
}
@Import(module = "platformOpenGL", name = "glUniformMatrix4fv")
static native void _wglUniformMatrix4fvN(WebGLUniformLocation uniformIndex, boolean transpose, Float32Array typedArray);
public static final void _wglUniformMatrix3x2fv(IUniformGL uniformIndex, boolean transpose, FloatBuffer buffer) {
if (uniformIndex != null)
_wglUniformMatrix3x2fvN(((OpenGLObjects.UniformGL) uniformIndex).ptr, transpose,
WASMGCBufferAllocator.getFloatBufferView(buffer));
}
@Import(module = "platformOpenGL", name = "glUniformMatrix3x2fv")
static native void _wglUniformMatrix3x2fvN(WebGLUniformLocation uniformIndex, boolean transpose, Float32Array typedArray);
public static final void _wglUniformMatrix4x2fv(IUniformGL uniformIndex, boolean transpose, FloatBuffer buffer) {
if (uniformIndex != null)
_wglUniformMatrix4x2fvN(((OpenGLObjects.UniformGL) uniformIndex).ptr, transpose,
WASMGCBufferAllocator.getFloatBufferView(buffer));
}
@Import(module = "platformOpenGL", name = "glUniformMatrix4x2fv")
static native void _wglUniformMatrix4x2fvN(WebGLUniformLocation uniformIndex, boolean transpose, Float32Array typedArray);
public static final void _wglUniformMatrix4x3fv(IUniformGL uniformIndex, boolean transpose, FloatBuffer buffer) {
if (uniformIndex != null)
_wglUniformMatrix4x3fvN(((OpenGLObjects.UniformGL) uniformIndex).ptr, transpose,
WASMGCBufferAllocator.getFloatBufferView(buffer));
}
@Import(module = "platformOpenGL", name = "glUniformMatrix4x3fv")
static native void _wglUniformMatrix4x3fvN(WebGLUniformLocation uniformIndex, boolean transpose, Float32Array typedArray);
public static final void _wglBindFramebuffer(int target, IFramebufferGL framebuffer) {
if(framebuffer == null) {
framebuffer = WebGLBackBuffer.getBackBuffer();
}
_wglBindFramebufferN(target, ((OpenGLObjects.FramebufferGL)framebuffer).ptr);
}
public static final void _wglBindFramebufferLow(int target, IFramebufferGL framebuffer) {
_wglBindFramebufferN(target, framebuffer != null ? ((OpenGLObjects.FramebufferGL)framebuffer).ptr : null);
}
@Import(module = "platformOpenGL", name = "glBindFramebuffer")
static native void _wglBindFramebufferN(int target, WebGLFramebuffer framebuffer);
@Import(module = "platformOpenGL", name = "glCheckFramebufferStatus")
public static native int _wglCheckFramebufferStatus(int target);
@Import(module = "platformOpenGL", name = "glBlitFramebuffer")
public static native void _wglBlitFramebuffer(int srcX0, int srcY0, int srcX1, int srcY1, int dstX0, int dstY0,
int dstX1, int dstY1, int bits, int filter);
@Import(module = "platformOpenGL", name = "glRenderbufferStorage")
public static native void _wglRenderbufferStorage(int target, int internalformat, int width, int height);
public static final void _wglFramebufferTexture2D(int target, int attachment, int texTarget, ITextureGL texObj, int level) {
_wglFramebufferTexture2DN(target, attachment, texTarget, ((OpenGLObjects.TextureGL)texObj).ptr, level);
}
@Import(module = "platformOpenGL", name = "glFramebufferTexture2D")
static native void _wglFramebufferTexture2DN(int target, int attachment, int texTarget, WebGLTexture texObj, int level);
public static final void _wglFramebufferTextureLayer(int target, int attachment, ITextureGL texObj, int level, int layer) {
_wglFramebufferTextureLayerN(target, attachment, ((OpenGLObjects.TextureGL)texObj).ptr, level, layer);
}
@Import(module = "platformOpenGL", name = "glFramebufferTextureLayer")
static native void _wglFramebufferTextureLayerN(int target, int attachment, WebGLTexture texObj, int level, int layer);
public static final void _wglBindRenderbuffer(int target, IRenderbufferGL renderbuffer) {
_wglBindRenderbufferN(target, renderbuffer != null ? ((OpenGLObjects.RenderbufferGL)renderbuffer).ptr : null);
}
@Import(module = "platformOpenGL", name = "glBindRenderbuffer")
static native void _wglBindRenderbufferN(int target, WebGLRenderbuffer renderbuffer);
public static final void _wglFramebufferRenderbuffer(int target, int attachment, int renderbufferTarget,
IRenderbufferGL renderbufferId) {
_wglFramebufferRenderbufferN(target, attachment, renderbufferTarget,
renderbufferId != null ? ((OpenGLObjects.RenderbufferGL) renderbufferId).ptr : null);
}
@Import(module = "platformOpenGL", name = "glFramebufferRenderbuffer")
static native void _wglFramebufferRenderbufferN(int target, int attachment, int renderbufferTarget, WebGLRenderbuffer renderbufferId);
public static final String _wglGetString(int param) {
return BetterJSStringConverter.stringFromJS(_wglGetStringN(param));
}
@Import(module = "platformOpenGL", name = "glGetString")
static native JSString _wglGetStringN(int param);
@Import(module = "platformOpenGL", name = "glGetInteger")
public static native int _wglGetInteger(int param);
@Import(module = "platformOpenGL", name = "glGetError")
public static native int _wglGetError();
public static final int checkOpenGLESVersion() {
return glesVers;
}
public static final boolean checkEXTGPUShader5Capable() {
return (capABits & CAP_A_BIT_EXT_GPU_SHADER5) != 0;
}
public static final boolean checkOESGPUShader5Capable() {
return (capABits & CAP_A_BIT_OES_GPU_SHADER5) != 0;
}
public static final boolean checkFBORenderMipmapCapable() {
return (capABits & CAP_A_BIT_FBO_RENDER_MIPMAP) != 0;
}
public static final boolean checkVAOCapable() {
return vertexArrayImpl != VAO_IMPL_NONE;
}
public static final boolean checkInstancingCapable() {
return instancingImpl != INSTANCE_IMPL_NONE;
}
public static final boolean checkTexStorageCapable() {
return glesVers >= 300;
}
public static final boolean checkTextureLODCapable() {
return (capABits & CAP_A_BIT_TEXTURE_LOD_CAPABLE) != 0;
}
public static final boolean checkNPOTCapable() {
return (capABits & CAP_A_BIT_NPOT_CAPABLE) != 0;
}
public static final boolean checkHDRFramebufferSupport(int bits) {
switch(bits) {
case 16:
return (capABits & CAP_A_BIT_HDR_FBO16F) != 0;
case 32:
return (capABits & CAP_A_BIT_HDR_FBO32F) != 0;
default:
return false;
}
}
public static final boolean checkLinearHDRFilteringSupport(int bits) {
switch(bits) {
case 16:
return (capBBits & CAP_B_BIT_HDR_LINEAR16F) != 0;
case 32:
return (capBBits & CAP_B_BIT_HDR_LINEAR32F) != 0;
default:
return false;
}
}
// legacy
public static final boolean checkLinearHDR32FSupport() {
return (capBBits & CAP_B_BIT_HDR_LINEAR32F) != 0;
}
public static final boolean checkAnisotropicFilteringSupport() {
return (capABits & CAP_A_BIT_ANISOTROPIC) != 0;
}
public static final String[] getAllExtensions() {
return BetterJSStringConverter.stringArrayFromJS(getAllExtensions0());
}
@Import(module = "platformOpenGL", name = "getAllExtensions")
static native JSArray<JSString> getAllExtensions0();
public static final List<String> dumpActiveExtensions() {
return Arrays.asList(BetterJSStringConverter.stringArrayFromJS(dumpActiveExtensions0()));
}
@Import(module = "platformOpenGL", name = "dumpActiveExtensions")
static native JSArray<JSString> dumpActiveExtensions0();
public static final void enterVAOEmulationHook() {
WebGLBackBuffer.enterVAOEmulationPhase();
}
}

View File

@ -0,0 +1,491 @@
package net.lax1dude.eaglercraft.v1_8.internal;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Consumer;
import org.teavm.interop.Import;
import org.teavm.jso.JSBody;
import org.teavm.jso.JSObject;
import org.teavm.jso.JSProperty;
import org.teavm.jso.browser.Window;
import org.teavm.jso.core.JSString;
import org.teavm.jso.dom.html.HTMLCanvasElement;
import org.teavm.jso.dom.html.HTMLDocument;
import org.teavm.jso.dom.html.HTMLElement;
import org.teavm.jso.typedarrays.ArrayBuffer;
import org.teavm.jso.typedarrays.Uint8Array;
import com.jcraft.jzlib.DeflaterOutputStream;
import com.jcraft.jzlib.GZIPInputStream;
import com.jcraft.jzlib.GZIPOutputStream;
import com.jcraft.jzlib.InflaterInputStream;
import net.lax1dude.eaglercraft.v1_8.Filesystem;
import net.lax1dude.eaglercraft.v1_8.internal.buffer.ByteBuffer;
import net.lax1dude.eaglercraft.v1_8.internal.buffer.FloatBuffer;
import net.lax1dude.eaglercraft.v1_8.internal.buffer.IntBuffer;
import net.lax1dude.eaglercraft.v1_8.internal.buffer.WASMGCBufferAllocator;
import net.lax1dude.eaglercraft.v1_8.internal.buffer.WASMGCDirectArrayConverter;
import net.lax1dude.eaglercraft.v1_8.internal.vfs2.VFile2;
import net.lax1dude.eaglercraft.v1_8.internal.wasm_gc_teavm.BetterJSStringConverter;
import net.lax1dude.eaglercraft.v1_8.internal.wasm_gc_teavm.EarlyLoadScreen;
import net.lax1dude.eaglercraft.v1_8.internal.wasm_gc_teavm.WASMGCClientConfigAdapter;
import net.lax1dude.eaglercraft.v1_8.internal.wasm_gc_teavm.WebGLBackBuffer;
import net.lax1dude.eaglercraft.v1_8.log4j.LogManager;
import net.lax1dude.eaglercraft.v1_8.log4j.Logger;
import net.lax1dude.eaglercraft.v1_8.minecraft.EaglerFolderResourcePack;
import net.lax1dude.eaglercraft.v1_8.opengl.RealOpenGLEnums;
/**
* Copyright (c) 2022-2024 lax1dude. All Rights Reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
public class PlatformRuntime {
static final Logger logger = LogManager.getLogger("RuntimeWASMGC");
public static Window win = null;
public static HTMLDocument doc = null;
public static HTMLElement root = null;
public static HTMLElement parent = null;
public static HTMLCanvasElement canvas = null;
public static boolean webglExperimental = false;
public static void create() {
win = Window.current();
doc = win.getDocument();
root = getRootElement();
parent = getParentElement();
canvas = getCanvasElement();
PlatformApplication.setMCServerWindowGlobal(null);
PlatformOpenGL.initContext();
PlatformInput.initContext(win, parent, canvas);
// Should contain an event to update the initial screen size
pollJSEventsAfterSleep();
WebGLBackBuffer.initBackBuffer(PlatformInput.getWindowWidth(), PlatformInput.getWindowHeight());
HTMLElement el = parent.querySelector("._eaglercraftX_early_splash_element");
if(el != null) {
el.delete();
}
EarlyLoadScreen.extractingAssetsScreen();
sleep(20);
PlatformAssets.readAssetsTeaVM();
byte[] finalLoadScreen = PlatformAssets.getResourceBytes("/assets/eagler/eagtek.png");
if(finalLoadScreen != null) {
EarlyLoadScreen.loadFinal(finalLoadScreen);
EarlyLoadScreen.paintFinal(false);
}else {
PlatformOpenGL._wglClearColor(1.0f, 0.0f, 1.0f, 1.0f);
PlatformOpenGL._wglClear(RealOpenGLEnums.GL_COLOR_BUFFER_BIT);
PlatformInput.update();
}
sleep(20);
EarlyLoadScreen.destroy();
logger.info("Initializing filesystem...");
IEaglerFilesystem resourcePackFilesystem = Filesystem.getHandleFor(getClientConfigAdapter().getResourcePacksDB());
VFile2.setPrimaryFilesystem(resourcePackFilesystem);
EaglerFolderResourcePack.setSupported(true);
logger.info("Initializing sound engine...");
PlatformAudio.initialize();
PlatformScreenRecord.initContext(win, canvas);
PlatformWebRTC.initialize();
PlatformVoiceClient.initialize();
PlatformWebView.initialize();
}
@Import(module = "platformRuntime", name = "getRootElement")
private static native HTMLElement getRootElement();
@Import(module = "platformRuntime", name = "getParentElement")
private static native HTMLElement getParentElement();
@Import(module = "platformRuntime", name = "getCanvasElement")
private static native HTMLCanvasElement getCanvasElement();
public static void destroy() {
logger.fatal("Game tried to destroy the context! Browser runtime can't do that");
}
public static EnumPlatformType getPlatformType() {
return EnumPlatformType.WASM_GC;
}
public static EnumPlatformAgent getPlatformAgent() {
return EnumPlatformAgent.getFromUA(getUserAgentString());
}
@JSBody(params = { }, script = "return navigator.userAgent||null;")
public static native String getUserAgentString();
public static EnumPlatformOS getPlatformOS() {
return EnumPlatformOS.getFromUA(getUserAgentString());
}
@JSBody(params = { }, script = "return location.protocol && location.protocol.toLowerCase() === \"https:\";")
public static native boolean requireSSL();
@JSBody(params = { }, script = "return location.protocol && location.protocol.toLowerCase() === \"file:\";")
public static native boolean isOfflineDownloadURL();
public static void requestANGLE(EnumPlatformANGLE plaf) {
}
public static EnumPlatformANGLE getPlatformANGLE() {
return EnumPlatformANGLE.fromGLRendererString(getGLRenderer());
}
public static String getGLVersion() {
return PlatformOpenGL._wglGetString(RealOpenGLEnums.GL_VERSION);
}
public static String getGLRenderer() {
return PlatformOpenGL._wglGetString(RealOpenGLEnums.GL_RENDERER);
}
public static ByteBuffer allocateByteBuffer(int length) {
return WASMGCBufferAllocator.allocateByteBuffer(length);
}
public static IntBuffer allocateIntBuffer(int length) {
return WASMGCBufferAllocator.allocateIntBuffer(length);
}
public static FloatBuffer allocateFloatBuffer(int length) {
return WASMGCBufferAllocator.allocateFloatBuffer(length);
}
public static ByteBuffer castPrimitiveByteArray(byte[] array) {
return null;
}
public static IntBuffer castPrimitiveIntArray(int[] array) {
return null;
}
public static FloatBuffer castPrimitiveFloatArray(float[] array) {
return null;
}
public static byte[] castNativeByteBuffer(ByteBuffer buffer) {
return null;
}
public static int[] castNativeIntBuffer(IntBuffer buffer) {
return null;
}
public static float[] castNativeFloatBuffer(FloatBuffer buffer) {
return null;
}
public static void freeByteBuffer(ByteBuffer byteBuffer) {
WASMGCBufferAllocator.freeByteBuffer(byteBuffer);
}
public static void freeIntBuffer(IntBuffer intBuffer) {
WASMGCBufferAllocator.freeIntBuffer(intBuffer);
}
public static void freeFloatBuffer(FloatBuffer floatBuffer) {
WASMGCBufferAllocator.freeFloatBuffer(floatBuffer);
}
private interface JSAsyncDownloadEvent extends JSObject {
@JSProperty
int getRequestId();
@JSProperty
ArrayBuffer getArrayBuffer();
}
private static final int EVENT_TYPE_INPUT = 0;
private static final int EVENT_TYPE_RUNTIME = 1;
private static final int EVENT_TYPE_VOICE = 2;
private static final int EVENT_TYPE_WEBVIEW = 3;
public interface JSEagRuntimeEvent extends JSObject {
@JSProperty
int getEventType();
@JSProperty
<T> T getEventObj();
}
static void pollJSEvents() {
int cnt = getEventCount();
while(cnt-- > 0) {
JSEagRuntimeEvent evt = getNextEvent();
if(evt != null) {
switch(evt.getEventType() >>> 5) {
case EVENT_TYPE_INPUT:
PlatformInput.handleJSEvent(evt);
break;
case EVENT_TYPE_RUNTIME:
handleJSEvent(evt);
break;
case EVENT_TYPE_VOICE:
PlatformVoiceClient.handleJSEvent(evt);
break;
case EVENT_TYPE_WEBVIEW:
PlatformWebView.handleJSEvent(evt);
break;
default:
break;
}
}else {
break;
}
}
pollAsyncCallbacksTeaVM();
}
private static boolean isWakingUpFromSleep = false;
static void pollJSEventsAfterSleep() {
if(!isWakingUpFromSleep) {
try {
isWakingUpFromSleep = true;
pollJSEvents();
}finally {
isWakingUpFromSleep = false;
}
}
}
private static final int EVENT_RUNTIME_ASYNC_DOWNLOAD = 0;
private static void handleJSEvent(PlatformRuntime.JSEagRuntimeEvent evt) {
switch(evt.getEventType() & 31) {
case EVENT_RUNTIME_ASYNC_DOWNLOAD: {
JSAsyncDownloadEvent obj = evt.getEventObj();
int id = obj.getRequestId();
Consumer<ArrayBuffer> handler = waitingAsyncDownloads.get(id);
if(handler != null) {
handler.accept(obj.getArrayBuffer());
}else {
logger.warn("Ignoring unknown async download result #{}", id);
}
break;
}
}
}
@Import(module = "teavm", name = "pollAsyncCallbacks")
private static native JSEagRuntimeEvent pollAsyncCallbacksTeaVM();
@Import(module = "platformRuntime", name = "getEventCount")
private static native int getEventCount();
@Import(module = "platformRuntime", name = "getNextEvent")
private static native JSEagRuntimeEvent getNextEvent();
private static final Map<Integer, Consumer<ArrayBuffer>> waitingAsyncDownloads = new HashMap<>();
private static int asyncDownloadID = 0;
private static void queueAsyncDownload(String uri, boolean forceCache, Consumer<ArrayBuffer> cb) {
int id = ++asyncDownloadID;
waitingAsyncDownloads.put(id, cb);
queueAsyncDownload0(BetterJSStringConverter.stringToJS(uri), forceCache, id);
}
public static void downloadRemoteURIByteArray(String assetPackageURI, final Consumer<byte[]> cb) {
downloadRemoteURIByteArray(assetPackageURI, false, cb);
}
public static void downloadRemoteURIByteArray(String assetPackageURI, boolean useCache, final Consumer<byte[]> cb) {
queueAsyncDownload(assetPackageURI, useCache, arr -> {
if(arr == null) {
cb.accept(null);
}else {
cb.accept(WASMGCDirectArrayConverter.externU8ArrayToByteArray(new Uint8Array(arr)));
}
});
}
public static void downloadRemoteURI(String assetPackageURI, Consumer<ArrayBuffer> cb) {
queueAsyncDownload(assetPackageURI, false, cb);
}
public static void downloadRemoteURI(String assetPackageURI, boolean useCache, Consumer<ArrayBuffer> cb) {
queueAsyncDownload(assetPackageURI, useCache, cb);
}
public static byte[] downloadRemoteURIByteArray(String assetPackageURI) {
return downloadRemoteURIByteArray(assetPackageURI, false);
}
public static byte[] downloadRemoteURIByteArray(String assetPackageURI, boolean forceCache) {
ArrayBuffer arrayBuffer = downloadSync(BetterJSStringConverter.stringToJS(assetPackageURI), forceCache);
pollJSEventsAfterSleep();
if(arrayBuffer == null) {
return null;
}
return WASMGCDirectArrayConverter.externU8ArrayToByteArray(new Uint8Array(arrayBuffer));
}
public static ArrayBuffer downloadRemoteURI(String assetPackageURI) {
ArrayBuffer ret = downloadSync(BetterJSStringConverter.stringToJS(assetPackageURI), false);
pollJSEventsAfterSleep();
return ret;
}
public static ArrayBuffer downloadRemoteURI(String assetPackageURI, boolean forceCache) {
ArrayBuffer ret = downloadSync(BetterJSStringConverter.stringToJS(assetPackageURI), forceCache);
pollJSEventsAfterSleep();
return ret;
}
@Import(module = "platformRuntime", name = "queueAsyncDownload")
private static native void queueAsyncDownload0(JSString uri, boolean forceCache, int id);
@Import(module = "platformRuntime", name = "download")
private static native ArrayBuffer downloadSync(JSString uri, boolean forceCache);
public static boolean isDebugRuntime() {
return false;
}
public static void writeCrashReport(String crashDump) {
writeCrashReport0(BetterJSStringConverter.stringToJS(crashDump));
}
@Import(module = "platformRuntime", name = "writeCrashReport")
private static native void writeCrashReport0(JSString crashDump);
public static void getStackTrace(Throwable t, Consumer<String> ret) {
StackTraceElement[] el = t.getStackTrace();
if(el.length > 0) {
for(int i = 0; i < el.length; ++i) {
ret.accept(el[i].toString());
}
}else {
ret.accept("[no stack trace]");
}
}
public static boolean printJSExceptionIfBrowser(Throwable t) {
return false;
}
private static String currentThreadName = "main";
public static String currentThreadName() {
return currentThreadName;
}
public static void setThreadName(String string) {
currentThreadName = string;
}
public static long maxMemory() {
return 1073741824l;
}
public static long totalMemory() {
return 1073741824l;
}
public static long freeMemory() {
return 1073741824l;
}
public static String getCallingClass(int i) {
return null;
}
public static long randomSeed() {
return (long)(Math.random() * 9007199254740991.0);
}
public static void exit() {
logger.fatal("Game is attempting to exit!");
}
public static IClientConfigAdapter getClientConfigAdapter() {
return WASMGCClientConfigAdapter.instance;
}
@Import(module = "platformRuntime", name = "steadyTimeMillis")
static native double steadyTimeMillis0();
public static long steadyTimeMillis() {
return (long)steadyTimeMillis0();
}
public static long nanoTime() {
return (long)(steadyTimeMillis0() * 1000000.0);
}
public static void sleep(int millis) {
sleep0(millis);
pollJSEventsAfterSleep();
}
@Import(module = "platformRuntime", name = "sleep")
private static native void sleep0(int millis);
public static void immediateContinue() {
immediateContinue0();
pollJSEventsAfterSleep();
}
@Import(module = "platformRuntime", name = "immediateContinue")
private static native void immediateContinue0();
public static void postCreate() {
}
public static void setDisplayBootMenuNextRefresh(boolean en) {
}
public static OutputStream newDeflaterOutputStream(OutputStream os) throws IOException {
return new DeflaterOutputStream(os);
}
public static OutputStream newGZIPOutputStream(OutputStream os) throws IOException {
return new GZIPOutputStream(os);
}
public static InputStream newInflaterInputStream(InputStream is) throws IOException {
return new InflaterInputStream(is);
}
public static InputStream newGZIPInputStream(InputStream is) throws IOException {
return new GZIPInputStream(is);
}
}

View File

@ -0,0 +1,206 @@
package net.lax1dude.eaglercraft.v1_8.internal;
import java.util.EnumSet;
import java.util.Set;
import org.teavm.interop.Import;
import org.teavm.jso.JSBody;
import org.teavm.jso.JSObject;
import org.teavm.jso.browser.Window;
import org.teavm.jso.canvas.CanvasRenderingContext2D;
import org.teavm.jso.core.JSString;
import org.teavm.jso.dom.html.HTMLCanvasElement;
import org.teavm.jso.webaudio.MediaStream;
import net.lax1dude.eaglercraft.v1_8.EaglercraftVersion;
import net.lax1dude.eaglercraft.v1_8.internal.wasm_gc_teavm.BetterJSStringConverter;
import net.lax1dude.eaglercraft.v1_8.log4j.LogManager;
import net.lax1dude.eaglercraft.v1_8.log4j.Logger;
import net.lax1dude.eaglercraft.v1_8.profile.EaglerProfile;
import net.lax1dude.eaglercraft.v1_8.recording.EnumScreenRecordingCodec;
/**
* Copyright (c) 2024 lax1dude. All Rights Reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
public class PlatformScreenRecord {
static final Logger logger = LogManager.getLogger("PlatformScreenRecord");
static Window win;
static HTMLCanvasElement canvas;
static boolean support;
static final Set<EnumScreenRecordingCodec> supportedCodecs = EnumSet.noneOf(EnumScreenRecordingCodec.class);
static float currentGameVolume = 1.0f;
static float currentMicVolume = 0.0f;
static MediaStream recStream = null;
static HTMLCanvasElement downscaleCanvas = null;
static CanvasRenderingContext2D downscaleCanvasCtx = null;
static long lastDownscaleFrameCaptured = 0l;
static boolean currentMicLock = false;
static JSObject mediaRec = null;
static ScreenRecordParameters currentParameters = null;
static void initContext(Window theWin, HTMLCanvasElement theCanvas) {
win = theWin;
canvas = theCanvas;
supportedCodecs.clear();
support = hasMediaRecorder(theWin, theCanvas);
if(support) {
logger.info("MediaRecorder is supported, checking codecs...");
EnumScreenRecordingCodec[] allCodecs = EnumScreenRecordingCodec.values();
for(int i = 0; i < allCodecs.length; ++i) {
if(checkCodecSupported(theWin, allCodecs[i].mimeType)) {
supportedCodecs.add(allCodecs[i]);
}
}
if(!supportedCodecs.isEmpty()) {
logger.info("Found {} codecs that are probably supported!", supportedCodecs.size());
}else {
logger.error("No supported codecs found!");
support = false;
}
}
}
@Import(module = "platformScreenRecord", name = "getMic")
public static native MediaStream getMic();
@JSBody(params = { "win", "canvas" }, script = "return (typeof win.MediaRecorder !== \"undefined\") && (typeof win.MediaRecorder.isTypeSupported === \"function\") && (typeof canvas.captureStream === \"function\");")
private static native boolean hasMediaRecorder(Window win, HTMLCanvasElement canvas);
@JSBody(params = { "win", "mime" }, script = "return win.MediaRecorder.isTypeSupported(mime);")
private static native boolean checkCodecSupported(Window win, String mime);
public static boolean isSupported() {
return support;
}
public static boolean isCodecSupported(EnumScreenRecordingCodec codec) {
return supportedCodecs.contains(codec);
}
static void captureFrameHook() {
if(mediaRec != null && currentParameters != null && currentParameters.resolutionDivisior > 1 && downscaleCanvas != null && downscaleCanvasCtx != null) {
if(currentParameters.captureFrameRate > 0) {
long curTime = PlatformRuntime.steadyTimeMillis();
if(curTime - lastDownscaleFrameCaptured < (long)(1000 / currentParameters.captureFrameRate)) {
return;
}
lastDownscaleFrameCaptured = curTime;
}
int oldWidth = downscaleCanvas.getWidth();
int oldHeight = downscaleCanvas.getHeight();
float divisor = (float)Math.sqrt(1.0 / Math.pow(2.0, currentParameters.resolutionDivisior - 1));
int newWidth = (int)(PlatformInput.getWindowWidth() * divisor);
int newHeight = (int)(PlatformInput.getWindowHeight() * divisor);
if(oldWidth != newWidth || oldHeight != newHeight) {
downscaleCanvas.setWidth(newWidth);
downscaleCanvas.setHeight(newHeight);
}
downscaleCanvasCtx.drawImage(canvas, 0, 0, newWidth, newHeight);
}
}
public static void setGameVolume(float volume) {
currentGameVolume = volume;
if(PlatformAudio.gameRecGain != null) {
PlatformAudio.gameRecGain.getGain().setValue(volume);
}
}
public static void setMicrophoneVolume(float volume) {
currentMicVolume = volume;
if(PlatformAudio.micRecGain != null) {
PlatformAudio.micRecGain.getGain().setValue(volume);
}
}
@Import(module = "platformScreenRecord", name = "setDataAvailableHandler")
private static native void setupDataAvailableHandler(JSObject mediaRec, boolean isWebM, JSString nameStr);
@JSBody(params = { }, script = "return { alpha: false, desynchronized: true };")
private static native JSObject youEagler();
@JSBody(params = { "canvas", "fps", "audio" }, script = "var stream = fps <= 0 ? canvas.captureStream() : canvas.captureStream(fps); stream.addTrack(audio.getTracks()[0]); return stream;")
private static native MediaStream captureStreamAndAddAudio(HTMLCanvasElement canvas, int fps, MediaStream audio);
@JSBody(params = { "stream", "codec", "videoBitrate", "audioBitrate" }, script = "var rec = new MediaRecorder(stream, { mimeType: codec, videoBitsPerSecond: videoBitrate, audioBitsPerSecond: audioBitrate }); rec.start(); return rec;")
private static native JSObject createMediaRecorder(MediaStream stream, String codec, int videoBitrate, int audioBitrate);
@JSBody(params = { "rec" }, script = "rec.stop();")
private static native void stopRec(JSObject rec);
public static void startRecording(ScreenRecordParameters params) {
if(!support) {
throw new IllegalStateException("Screen recording is not supported");
}
if(isRecording()) {
throw new IllegalStateException("Already recording!");
}
if(params.captureFrameRate <= 0 && (!PlatformInput.vsync || !PlatformInput.vsyncSupport)) {
throw new IllegalStateException("V-Sync is not enabled, please enable it in \"Video Settings\"");
}
if(params.resolutionDivisior > 1) {
float divisor = (float)Math.sqrt(1.0 / Math.pow(2.0, params.resolutionDivisior - 1));
int newWidth = (int)(PlatformInput.getWindowWidth() * divisor);
int newHeight = (int)(PlatformInput.getWindowHeight() * divisor);
if(downscaleCanvas == null) {
downscaleCanvas = (HTMLCanvasElement) win.getDocument().createElement("canvas");
downscaleCanvas.setWidth(newWidth);
downscaleCanvas.setHeight(newHeight);
downscaleCanvasCtx = (CanvasRenderingContext2D) downscaleCanvas.getContext("2d", youEagler());
if(downscaleCanvasCtx == null) {
downscaleCanvas = null;
throw new IllegalStateException("Could not create downscaler canvas!");
}
}else {
downscaleCanvas.setWidth(newWidth);
downscaleCanvas.setHeight(newHeight);
}
}
currentMicLock = currentMicVolume <= 0.0f;
recStream = captureStreamAndAddAudio(params.resolutionDivisior > 1 ? downscaleCanvas : canvas,
Math.max(params.captureFrameRate, 0),
PlatformAudio.initRecordingStream(currentGameVolume, currentMicVolume));
mediaRec = createMediaRecorder(recStream, params.codec.mimeType, params.videoBitsPerSecond * 1000,
params.audioBitsPerSecond * 1000);
currentParameters = params;
setupDataAvailableHandler(mediaRec, "video/webm".equals(params.codec.container),
BetterJSStringConverter.stringToJS(EaglercraftVersion.screenRecordingFilePrefix + " - " + EaglerProfile.getName()
+ " - ${date}." + params.codec.fileExt));
}
public static void endRecording() {
if(mediaRec != null) {
stopRec(mediaRec);
mediaRec = null;
PlatformAudio.destroyRecordingStream();
}
currentParameters = null;
}
public static boolean isRecording() {
return mediaRec != null;
}
public static boolean isMicVolumeLocked() {
return mediaRec != null && currentMicLock;
}
public static boolean isVSyncLocked() {
return mediaRec != null && currentParameters != null && currentParameters.captureFrameRate == -1;
}
}

View File

@ -0,0 +1,67 @@
package net.lax1dude.eaglercraft.v1_8.internal;
import net.lax1dude.eaglercraft.v1_8.update.UpdateCertificate;
import net.lax1dude.eaglercraft.v1_8.update.UpdateProgressStruct;
import net.lax1dude.eaglercraft.v1_8.update.UpdateResultObj;
/**
* Copyright (c) 2024 lax1dude. All Rights Reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
public class PlatformUpdateSvc {
private static final UpdateProgressStruct dummyStruct = new UpdateProgressStruct();
public static boolean supported() {
return false;
}
public static void initialize() {
}
public static byte[] getClientSignatureData() {
return null;
}
public static byte[] getClientBundleData() {
return null;
}
public static void startClientUpdateFrom(UpdateCertificate clientUpdate) {
}
public static UpdateProgressStruct getUpdatingStatus() {
return dummyStruct;
}
public static UpdateResultObj getUpdateResult() {
return null;
}
public static void installSignedClient(UpdateCertificate clientCert, byte[] clientPayload, boolean setDefault,
boolean setTimeout) {
}
public static void quine(String filename, byte[] cert, byte[] data, String date) {
}
public static void quine(UpdateCertificate clientUpdate, byte[] data) {
}
}

View File

@ -0,0 +1,473 @@
package net.lax1dude.eaglercraft.v1_8.internal;
import java.util.HashMap;
import java.util.Map;
import org.json.JSONArray;
import org.json.JSONObject;
import org.teavm.interop.Import;
import org.teavm.jso.JSBody;
import org.teavm.jso.JSObject;
import org.teavm.jso.JSProperty;
import org.teavm.jso.core.JSString;
import org.teavm.jso.typedarrays.Uint8Array;
import org.teavm.jso.webaudio.AnalyserNode;
import org.teavm.jso.webaudio.AudioContext;
import org.teavm.jso.webaudio.AudioNode;
import org.teavm.jso.webaudio.GainNode;
import org.teavm.jso.webaudio.MediaStream;
import org.teavm.jso.webaudio.MediaStreamAudioDestinationNode;
import org.teavm.jso.webaudio.MediaStreamAudioSourceNode;
import org.teavm.jso.webaudio.PannerNode;
import net.lax1dude.eaglercraft.v1_8.EaglercraftUUID;
import net.lax1dude.eaglercraft.v1_8.internal.wasm_gc_teavm.BetterJSStringConverter;
import net.lax1dude.eaglercraft.v1_8.log4j.LogManager;
import net.lax1dude.eaglercraft.v1_8.log4j.Logger;
import net.lax1dude.eaglercraft.v1_8.voice.EnumVoiceChannelReadyState;
import net.lax1dude.eaglercraft.v1_8.voice.EnumVoiceChannelType;
import net.lax1dude.eaglercraft.v1_8.voice.VoiceClientController;
/**
* Copyright (c) 2024 lax1dude. All Rights Reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
public class PlatformVoiceClient {
private static final Logger logger = LogManager.getLogger("PlatformVoiceClient");
static boolean support = false;
private interface JSVoicePeerHandle extends JSObject {
@JSProperty
int getObjId();
void setRemoteDescription(JSString desc);
void addRemoteICECandidate(JSString ice);
void closeHandle();
}
private interface JSVoicePeerICEEvent extends JSObject {
@JSProperty
int getObjId();
@JSProperty
JSString getData();
}
private interface JSVoicePeerOpenEvent extends JSObject {
@JSProperty
int getObjId();
@JSProperty
MediaStream getStream();
}
private interface JSVoicePeerCloseEvent extends JSObject {
@JSProperty
int getObjId();
}
private static final int EVENT_VOICE_ICE = 0;
private static final int EVENT_VOICE_DESC = 1;
private static final int EVENT_VOICE_OPEN = 2;
private static final int EVENT_VOICE_CLOSE = 3;
static void handleJSEvent(PlatformRuntime.JSEagRuntimeEvent evt) {
switch(evt.getEventType() & 31) {
case EVENT_VOICE_ICE: {
JSVoicePeerICEEvent obj = evt.getEventObj();
VoicePeer peer = peerListI.get(obj.getObjId());
if(peer != null) {
peer.handleEventLocalICECandidate(BetterJSStringConverter.stringFromJS(obj.getData()));
}
break;
}
case EVENT_VOICE_DESC: {
JSVoicePeerICEEvent obj = evt.getEventObj();
VoicePeer peer = peerListI.get(obj.getObjId());
if(peer != null) {
peer.handleEventLocalDescription(BetterJSStringConverter.stringFromJS(obj.getData()));
}
break;
}
case EVENT_VOICE_OPEN: {
JSVoicePeerOpenEvent obj = evt.getEventObj();
VoicePeer peer = peerListI.get(obj.getObjId());
if(peer != null) {
peer.handleEventOpened(obj.getStream());
}
break;
}
case EVENT_VOICE_CLOSE: {
JSVoicePeerCloseEvent obj = evt.getEventObj();
VoicePeer peer = peerListI.remove(obj.getObjId());
if(peer != null) {
peerList.remove(peer.peerId);
peer.handleEventClosed();
}
break;
}
}
}
static final Map<EaglercraftUUID, VoicePeer> peerList = new HashMap<>();
static final Map<Integer, VoicePeer> peerListI = new HashMap<>();
private static class VoicePeer {
private final int objId;
private final EaglercraftUUID peerId;
private final JSVoicePeerHandle jsHandle;
private MediaStream rawStream = null;
private AnalyserNode analyser = null;
private GainNode gain = null;
private PannerNode panner = null;
private AudioNode recNode = null;
private boolean dead = false;
private VoicePeer(int objId, EaglercraftUUID peerId, JSVoicePeerHandle jsHandle) {
this.objId = objId;
this.peerId = peerId;
this.jsHandle = jsHandle;
}
private void handleEventLocalICECandidate(String data) {
VoiceClientController.sendPacketICE(peerId, data);
}
private void handleEventLocalDescription(String data) {
VoiceClientController.sendPacketDesc(peerId, data);
}
private void handlePacketRemoteDescription(String descJSON) {
jsHandle.setRemoteDescription(BetterJSStringConverter.stringToJS(descJSON));
}
private void handlePacketRemoteICECandidate(String candidate) {
jsHandle.addRemoteICECandidate(BetterJSStringConverter.stringToJS(candidate));
}
private void handleEventOpened(MediaStream stream) {
rawStream = stream;
MediaStreamAudioSourceNode audioNode = PlatformAudio.audioctx.createMediaStreamSource(stream);
AnalyserNode analyser = PlatformAudio.audioctx.createAnalyser();
analyser.setSmoothingTimeConstant(0f);
analyser.setFftSize(32);
audioNode.connect(analyser);
if (VoiceClientController.getVoiceChannel() == EnumVoiceChannelType.GLOBAL) {
GainNode gain = PlatformAudio.audioctx.createGain();
gain.getGain().setValue(VoiceClientController.getVoiceListenVolume());
audioNode.connect(gain);
gain.connect(PlatformAudio.audioctx.getDestination());
if(PlatformAudio.gameRecGain != null) {
gain.connect(PlatformAudio.gameRecGain);
}
VoiceClientController.getVoiceListening().add(peerId);
this.analyser = analyser;
this.gain = gain;
this.recNode = gain;
} else if (VoiceClientController.getVoiceChannel() == EnumVoiceChannelType.PROXIMITY) {
PannerNode panner = PlatformAudio.audioctx.createPanner();
panner.setRolloffFactor(1f);
PlatformAudio.setDistanceModelLinearFast(panner);
PlatformAudio.setPanningModelHRTFFast(panner);
panner.setConeInnerAngle(360f);
panner.setConeOuterAngle(0f);
panner.setConeOuterGain(0f);
panner.setOrientation(0f, 1f, 0f);
panner.setPosition(0, 0, 0);
float vol = VoiceClientController.getVoiceListenVolume();
panner.setMaxDistance(vol * 2 * VoiceClientController.getVoiceProximity() + 0.1f);
GainNode gain = PlatformAudio.audioctx.createGain();
gain.getGain().setValue(vol);
audioNode.connect(gain);
gain.connect(panner);
panner.connect(PlatformAudio.audioctx.getDestination());
if(PlatformAudio.gameRecGain != null) {
panner.connect(PlatformAudio.gameRecGain);
}
VoiceClientController.getVoiceListening().add(peerId);
this.analyser = analyser;
this.panner = panner;
this.gain = gain;
this.recNode = panner;
}
if (VoiceClientController.getVoiceMuted().contains(peerId)) mute(true);
}
private void mute(boolean muted) {
if(rawStream != null) {
PlatformVoiceClient.mute(rawStream, muted);
}
}
private void handlePacketClosed() {
closeHandle(true);
}
private void handleEventClosed() {
if(!dead) {
dead = true;
cleanup(false);
}
}
private void closeHandle(boolean quiet) {
if(!dead) {
dead = true;
jsHandle.closeHandle();
peerListI.remove(objId);
peerList.remove(peerId);
cleanup(quiet);
}
}
private void cleanup(boolean quiet) {
if(analyser != null) {
analyser.disconnect();
analyser = null;
}
if(gain != null) {
gain.disconnect();
gain = null;
}
if(panner != null) {
panner.disconnect();
panner = null;
}
if(rawStream != null) {
rawStream = null;
}
VoiceClientController.getVoiceListening().remove(peerId);
if (!quiet) {
VoiceClientController.sendPacketDisconnectPeer(peerId);
}
}
}
static void initialize() {
support = PlatformWebRTC.supported() && isSupported0();
peerList.clear();
peerListI.clear();
}
public static boolean isSupported() {
return support;
}
@Import(module = "platformVoiceClient", name = "isSupported")
private static native boolean isSupported0();
public static String iceServers = null;
public static boolean hasInit = false;
public static MediaStreamAudioDestinationNode localMediaStream;
public static GainNode localMediaStreamGain;
public static MediaStream localRawMediaStream;
public static EnumVoiceChannelReadyState readyState = EnumVoiceChannelReadyState.NONE;
public static AudioContext microphoneVolumeAudioContext = null;
public static void setICEServers(String[] urls) {
if (urls == null || urls.length == 0) {
iceServers = null;
return;
}
JSONArray arr = new JSONArray();
for (String url : urls) {
String[] etr = url.split(";");
if (etr.length == 1) {
JSONObject m = new JSONObject();
m.put("urls", etr[0]);
arr.put(m);
} else if (etr.length == 3) {
JSONObject m = new JSONObject();
m.put("urls", etr[0]);
m.put("username", etr[1]);
m.put("credential", etr[2]);
arr.put(m);
}
}
iceServers = arr.toString();
}
@JSBody(params = { "rawStream", "muted" }, script = "return rawStream.getAudioTracks()[0].enabled = !muted;")
static native void mute(MediaStream rawStream, boolean muted);
public static void activateVoice(boolean talk) {
if (hasInit) {
mute(localRawMediaStream, !talk);
}
}
public static void initializeDevices() {
if (!hasInit) {
localRawMediaStream = PlatformScreenRecord.getMic();
if (localRawMediaStream == null) {
readyState = EnumVoiceChannelReadyState.ABORTED;
return;
}
microphoneVolumeAudioContext = new AudioContext();
mute(localRawMediaStream, true);
localMediaStream = microphoneVolumeAudioContext.createMediaStreamDestination();
localMediaStreamGain = microphoneVolumeAudioContext.createGain();
microphoneVolumeAudioContext.createMediaStreamSource(localRawMediaStream).connect(localMediaStreamGain);
localMediaStreamGain.connect(localMediaStream);
localMediaStreamGain.getGain().setValue(1.0F);
readyState = EnumVoiceChannelReadyState.DEVICE_INITIALIZED;
hasInit = true;
} else {
readyState = EnumVoiceChannelReadyState.DEVICE_INITIALIZED;
}
}
public static void tickVoiceClient() {
for (VoicePeer voicePlayer : peerList.values()) {
AnalyserNode analyser = voicePlayer.analyser;
if(analyser != null) {
Uint8Array array = new Uint8Array(analyser.getFrequencyBinCount());
analyser.getByteFrequencyData(array);
int len = array.getLength();
for (int i = 0; i < len; i++) {
if (array.get(i) >= 0.1f) {
VoiceClientController.getVoiceSpeaking().add(voicePlayer.peerId);
break;
}
}
}
}
}
public static void setMicVolume(float val) {
if (hasInit) {
if(val > 0.5F) val = 0.5F + (val - 0.5F) * 2.0F;
if(val > 1.5F) val = 1.5F;
if(val < 0.0F) val = 0.0F;
localMediaStreamGain.getGain().setValue(val * 2.0F);
}
}
public static EnumVoiceChannelReadyState getReadyState() {
return readyState;
}
public static void signalConnect(EaglercraftUUID peerId, boolean offer) {
if(iceServers == null) {
logger.error("No ICE servers provided for {}", peerId);
return;
}
JSVoicePeerHandle peerHandle = createRTCPeerConnection(
BetterJSStringConverter.stringToJS(iceServers), offer,
localMediaStream.getStream());
if(peerHandle != null) {
int obj = peerHandle.getObjId();
VoicePeer peer = new VoicePeer(obj, peerId, peerHandle);
peerList.put(peerId, peer);
peerListI.put(obj, peer);
}else {
logger.error("Could not create peer for {}", peerId);
}
}
@Import(module = "platformVoiceClient", name = "createRTCPeerConnection")
private static native JSVoicePeerHandle createRTCPeerConnection(JSString iceServers, boolean offer, JSObject localStream);
public static void signalDescription(EaglercraftUUID peerId, String descJSON) {
VoicePeer peer = peerList.get(peerId);
if (peer != null) {
peer.handlePacketRemoteDescription(descJSON);
}
}
public static void signalDisconnect(EaglercraftUUID peerId, boolean quiet) {
VoicePeer peer = peerList.remove(peerId);
if (peer != null) {
peer.handlePacketClosed();
}
}
public static void setVoiceProximity(int prox) {
for (VoicePeer player : peerList.values()) {
if(player.panner != null) {
player.panner.setMaxDistance(VoiceClientController.getVoiceListenVolume() * 2 * prox + 0.1f);
}
}
}
public static void updateVoicePosition(EaglercraftUUID uuid, double x, double y, double z) {
VoicePeer player = peerList.get(uuid);
if (player != null && player.panner != null) player.panner.setPosition((float) x, (float) y, (float) z);
}
public static void mutePeer(EaglercraftUUID peerId, boolean muted) {
VoicePeer peer = peerList.get(peerId);
if (peer != null) {
peer.mute(muted);
}
}
public static void signalICECandidate(EaglercraftUUID peerId, String candidate) {
VoicePeer peer = peerList.get(peerId);
if (peer != null) {
peer.handlePacketRemoteICECandidate(candidate);
}
}
static void addRecordingDest(AudioNode destNode) {
for(VoicePeer peer : peerList.values()) {
if(peer.recNode != null) {
peer.recNode.connect(destNode);
}
}
}
static void removeRecordingDest(AudioNode destNode) {
for(VoicePeer peer : peerList.values()) {
try {
if(peer.recNode != null) {
peer.recNode.disconnect(destNode);
}
}catch(Throwable t) {
}
}
}
public static void setVoiceListenVolume(float f) {
for (VoicePeer peer : peerList.values()) {
if(peer.gain != null) {
float val = f;
if(val > 0.5f) val = 0.5f + (val - 0.5f) * 3.0f;
if(val > 2.0f) val = 2.0f;
if(val < 0.0f) val = 0.0f;
peer.gain.getGain().setValue(val * 2.0f);
}
if(peer.panner != null) {
peer.panner.setMaxDistance(f * 2 * VoiceClientController.getVoiceProximity() + 0.1f);
}
}
}
}

View File

@ -0,0 +1,360 @@
package net.lax1dude.eaglercraft.v1_8.internal;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.teavm.interop.Import;
import org.teavm.jso.JSObject;
import org.teavm.jso.JSProperty;
import org.teavm.jso.core.JSArray;
import org.teavm.jso.core.JSString;
import org.teavm.jso.typedarrays.Uint8Array;
import net.lax1dude.eaglercraft.v1_8.internal.buffer.ByteBuffer;
import net.lax1dude.eaglercraft.v1_8.internal.buffer.WASMGCBufferAllocator;
import net.lax1dude.eaglercraft.v1_8.internal.buffer.WASMGCDirectArrayConverter;
import net.lax1dude.eaglercraft.v1_8.internal.wasm_gc_teavm.BetterJSStringConverter;
import net.lax1dude.eaglercraft.v1_8.log4j.LogManager;
import net.lax1dude.eaglercraft.v1_8.log4j.Logger;
import net.lax1dude.eaglercraft.v1_8.sp.lan.LANPeerEvent;
/**
* Copyright (c) 2024 lax1dude. All Rights Reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
public class PlatformWebRTC {
private static final Logger logger = LogManager.getLogger("PlatformWebRTC");
private static boolean isSupported = false;
public static void initialize() {
isSupported = supported0();
}
public static boolean supported() {
return isSupported;
}
@Import(module = "platformWebRTC", name = "supported")
private static native boolean supported0();
public static void runScheduledTasks() {
}
public static void startRTCLANClient() {
}
@Import(module = "platformWebRTC", name = "clientLANReadyState")
public static native int clientLANReadyState();
@Import(module = "platformWebRTC", name = "clientLANCloseConnection")
public static native void clientLANCloseConnection();
public static void clientLANSendPacket(byte[] pkt) {
ByteBuffer buf = WASMGCDirectArrayConverter.byteArrayToBuffer(pkt);
try {
clientLANSendPacket0(WASMGCBufferAllocator.getUnsignedByteBufferView(buf));
}finally {
PlatformRuntime.freeByteBuffer(buf);
}
}
@Import(module = "platformWebRTC", name = "clientLANSendPacket")
private static native void clientLANSendPacket0(Uint8Array pkt);
public static byte[] clientLANReadPacket() {
Uint8Array arr = clientLANReadPacket0();
if(arr != null) {
return WASMGCDirectArrayConverter.externU8ArrayToByteArray(arr);
}else {
return null;
}
}
@Import(module = "platformWebRTC", name = "clientLANReadPacket")
private static native Uint8Array clientLANReadPacket0();
@Import(module = "platformWebRTC", name = "clientLANAvailable")
private static native int clientLANAvailable();
public static List<byte[]> clientLANReadAllPacket() {
int cnt = clientLANAvailable();
if(cnt == 0) {
return null;
}
byte[][] ret = new byte[cnt][];
for(int i = 0; i < cnt; ++i) {
ret[i] = clientLANReadPacket();
}
return Arrays.asList(ret);
}
public static void clientLANSetICEServersAndConnect(String[] servers) {
clientLANSetICEServersAndConnect0(BetterJSStringConverter.stringArrayToJS(servers));
}
@Import(module = "platformWebRTC", name = "clientLANSetICEServersAndConnect")
private static native void clientLANSetICEServersAndConnect0(JSArray<JSString> servers);
@Import(module = "platformWebRTC", name = "clearLANClientState")
public static native void clearLANClientState();
public static String clientLANAwaitICECandidate() {
return BetterJSStringConverter.stringFromJS(clientLANAwaitICECandidate0());
}
@Import(module = "platformWebRTC", name = "clientLANAwaitICECandidate")
private static native JSString clientLANAwaitICECandidate0();
public static String clientLANAwaitDescription() {
return BetterJSStringConverter.stringFromJS(clientLANAwaitDescription0());
}
@Import(module = "platformWebRTC", name = "clientLANAwaitDescription")
private static native JSString clientLANAwaitDescription0();
@Import(module = "platformWebRTC", name = "clientLANAwaitChannel")
public static native boolean clientLANAwaitChannel();
@Import(module = "platformWebRTC", name = "clientLANClosed")
public static native boolean clientLANClosed();
public static void clientLANSetICECandidate(String candidate) {
clientLANSetICECandidate0(BetterJSStringConverter.stringToJS(candidate));
}
@Import(module = "platformWebRTC", name = "clientLANSetICECandidate")
private static native void clientLANSetICECandidate0(JSString candidate);
public static void clientLANSetDescription(String description) {
clientLANSetDescription0(BetterJSStringConverter.stringToJS(description));
}
@Import(module = "platformWebRTC", name = "clientLANSetDescription")
private static native void clientLANSetDescription0(JSString description);
public static void startRTCLANServer() {
}
public static void serverLANInitializeServer(String[] servers) {
serverLANInitializeServer0(BetterJSStringConverter.stringArrayToJS(servers));
}
@Import(module = "platformWebRTC", name = "serverLANInitializeServer")
private static native void serverLANInitializeServer0(JSArray<JSString> servers);
@Import(module = "platformWebRTC", name = "serverLANCloseServer")
public static native void serverLANCloseServer();
private static final int EVENT_WEBRTC_ICE = 0;
private static final int EVENT_WEBRTC_DESC = 1;
private static final int EVENT_WEBRTC_OPEN = 2;
private static final int EVENT_WEBRTC_PACKET = 3;
private static final int EVENT_WEBRTC_CLOSE = 4;
private interface JSLANPeerEvent extends JSObject {
@JSProperty
int getType();
@JSProperty
<T> T getData();
}
private interface JSLANPeerHandle extends JSObject {
@JSProperty
JSString getPeerId();
int countAvailableEvents();
JSLANPeerEvent nextEvent();
void writePacket(Uint8Array arr);
void handleRemoteICECandidates(JSString iceCandidates);
void handleRemoteDescription(JSString description);
void mapIPC(String ipcChannel);
void disconnect();
}
private static class LANPeer {
private final String peerId;
private final JSLANPeerHandle handle;
private boolean dead = false;
private LANPeer(String peerId, JSLANPeerHandle handle) {
this.peerId = peerId;
this.handle = handle;
}
private LANPeerEvent getEvent() {
if(dead) return null;
JSLANPeerEvent peerEvt = handle.nextEvent();
switch(peerEvt.getType()) {
case EVENT_WEBRTC_ICE: {
return new LANPeerEvent.LANPeerICECandidateEvent(peerId,
BetterJSStringConverter.stringFromJS(peerEvt.getData()));
}
case EVENT_WEBRTC_DESC: {
return new LANPeerEvent.LANPeerDescriptionEvent(peerId,
BetterJSStringConverter.stringFromJS(peerEvt.getData()));
}
case EVENT_WEBRTC_OPEN: {
return new LANPeerEvent.LANPeerDataChannelEvent(peerId);
}
case EVENT_WEBRTC_PACKET: {
return new LANPeerEvent.LANPeerPacketEvent(peerId,
WASMGCDirectArrayConverter.externU8ArrayToByteArray(peerEvt.getData()));
}
case EVENT_WEBRTC_CLOSE: {
return new LANPeerEvent.LANPeerDisconnectEvent(peerId);
}
default: {
throw new IllegalStateException();
}
}
}
private List<LANPeerEvent> getAllEvent() {
if(dead) return null;
int cnt = handle.countAvailableEvents();
if(cnt == 0) {
return null;
}
LANPeerEvent[] lst = new LANPeerEvent[cnt];
for(int i = 0; i < cnt; ++i) {
lst[i] = getEvent();
}
return Arrays.asList(lst);
}
private void writePacket(byte[] pkt) {
if(dead) return;
ByteBuffer buf = WASMGCDirectArrayConverter.byteArrayToBuffer(pkt);
try {
handle.writePacket(WASMGCBufferAllocator.getUnsignedByteBufferView(buf));
}finally {
PlatformRuntime.freeByteBuffer(buf);
}
}
private void handleRemoteICECandidates(String iceCandidates) {
if(dead) return;
handle.handleRemoteICECandidates(BetterJSStringConverter.stringToJS(iceCandidates));
}
private void handleRemoteDescription(String description) {
if(dead) return;
handle.handleRemoteDescription(BetterJSStringConverter.stringToJS(description));
}
private void mapIPC(String ipcChannel) {
if(dead) return;
handle.mapIPC(ipcChannel);
}
private void disconnect() {
if(!dead) {
dead = true;
handle.disconnect();
}
}
}
private static final Map<String, LANPeer> lanServerPeers = new HashMap<>();
public static void serverLANCreatePeer(String peer) {
JSLANPeerHandle handle = serverLANCreatePeer0(BetterJSStringConverter.stringToJS(peer));
if(handle != null) {
lanServerPeers.put(peer, new LANPeer(peer, handle));
}else {
logger.error("Failed to create peer for client \"{}\"!", peer);
}
}
@Import(module = "platformWebRTC", name = "serverLANCreatePeer")
private static native JSLANPeerHandle serverLANCreatePeer0(JSString peer);
public static LANPeerEvent serverLANGetEvent(String peer) {
LANPeer lanPeer = lanServerPeers.get(peer);
if(lanPeer != null) {
return lanPeer.getEvent();
}else {
return null;
}
}
public static List<LANPeerEvent> serverLANGetAllEvent(String peer) {
LANPeer lanPeer = lanServerPeers.get(peer);
if(lanPeer != null) {
return lanPeer.getAllEvent();
}else {
return null;
}
}
public static void serverLANWritePacket(String peer, byte[] data) {
LANPeer lanPeer = lanServerPeers.get(peer);
if(lanPeer != null) {
lanPeer.writePacket(data);
}
}
public static void serverLANPeerICECandidates(String peer, String iceCandidates) {
LANPeer lanPeer = lanServerPeers.get(peer);
if(lanPeer != null) {
lanPeer.handleRemoteICECandidates(iceCandidates);
}
}
public static void serverLANPeerDescription(String peer, String description) {
LANPeer lanPeer = lanServerPeers.get(peer);
if(lanPeer != null) {
lanPeer.handleRemoteDescription(description);
}
}
public static void serverLANPeerMapIPC(String peer, String ipcChannel) {
LANPeer lanPeer = lanServerPeers.get(peer);
if(lanPeer != null) {
lanPeer.mapIPC(ipcChannel);
}
}
public static void serverLANDisconnectPeer(String peer) {
LANPeer lanPeer = lanServerPeers.remove(peer);
if(lanPeer != null) {
lanPeer.disconnect();
}
}
public static int countPeers() {
return lanServerPeers.size();
}
}

View File

@ -0,0 +1,426 @@
package net.lax1dude.eaglercraft.v1_8.internal;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import org.teavm.interop.Import;
import org.teavm.jso.JSBody;
import org.teavm.jso.JSObject;
import org.teavm.jso.JSProperty;
import org.teavm.jso.core.JSString;
import org.teavm.jso.typedarrays.ArrayBuffer;
import org.teavm.jso.typedarrays.Uint8Array;
import net.lax1dude.eaglercraft.v1_8.internal.PlatformRuntime.JSEagRuntimeEvent;
import net.lax1dude.eaglercraft.v1_8.internal.buffer.ByteBuffer;
import net.lax1dude.eaglercraft.v1_8.internal.buffer.WASMGCBufferAllocator;
import net.lax1dude.eaglercraft.v1_8.internal.buffer.WASMGCDirectArrayConverter;
import net.lax1dude.eaglercraft.v1_8.internal.wasm_gc_teavm.BetterJSStringConverter;
import net.lax1dude.eaglercraft.v1_8.log4j.LogManager;
import net.lax1dude.eaglercraft.v1_8.log4j.Logger;
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.client.CPacketWebViewMessageEnV4EAG;
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.client.CPacketWebViewMessageV4EAG;
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketWebViewMessageV4EAG;
import net.lax1dude.eaglercraft.v1_8.webview.PermissionsCache;
import net.lax1dude.eaglercraft.v1_8.webview.WebViewOverlayController.IPacketSendCallback;
/**
* Copyright (c) 2024 lax1dude. All Rights Reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
public class PlatformWebView {
private static final Logger logger = LogManager.getLogger("PlatformWebView");
private static String currentMessageChannelName = null;
private static IPacketSendCallback packetSendCallback = null;
private static boolean supportedState = false;
private static boolean showingState = false;
private static boolean supportForce = false;
private static boolean enableCSP = true;
private static boolean cspSupport = false;
private static final int EVENT_CHANNEL_OPEN = 0;
private static final int EVENT_CHANNEL_CLOSE = 1;
private interface JSWebViewChannelEvent extends JSObject {
@JSProperty
int getEventType();
@JSProperty
JSString getChannelName();
}
private static final int EVENT_MESSAGE_STRING = 0;
private static final int EVENT_MESSAGE_BINARY = 1;
private interface JSWebViewMessageEvent extends JSObject {
@JSProperty
int getEventType();
@JSProperty
JSString getChannelName();
@JSProperty("eventData")
JSString getEventDataString();
@JSProperty("eventData")
ArrayBuffer getEventDataBinary();
}
private static final int EVENT_WEBVIEW_CHANNEL = 0;
private static final int EVENT_WEBVIEW_MESSAGE = 1;
private static final int EVENT_WEBVIEW_PERMISSION_ALLOW = 2;
private static final int EVENT_WEBVIEW_PERMISSION_BLOCK = 3;
private static final int EVENT_WEBVIEW_PERMISSION_CLEAR = 4;
private static final List<Runnable> messageQueue = new LinkedList<>();
private static Runnable setJavaScriptAllowedCurrent = null;
private static Runnable setJavaScriptBlockedCurrent = null;
private static Runnable setJavaScriptClearedCurrent = null;
private static int webviewResetSerial = 0;
@Import(module = "platformWebView", name = "checkSupported")
private static native boolean checkSupported();
@Import(module = "platformWebView", name = "checkCSPSupported")
private static native boolean checkCSPSupported();
static void initialize() {
currentMessageChannelName = null;
packetSendCallback = null;
setJavaScriptAllowedCurrent = null;
setJavaScriptBlockedCurrent = null;
setJavaScriptClearedCurrent = null;
supportedState = checkSupported();
cspSupport = checkCSPSupported();
if(!supportedState) {
logger.error("This browser does not meet the safety requirements for webview support, this feature will be disabled");
}else if(!cspSupport) {
logger.warn("This browser does not support CSP attribute on iframes! (try Chrome)");
}
}
public static boolean supported() {
return supportedState;
}
public static boolean isShowing() {
return showingState;
}
public static void setPacketSendCallback(IPacketSendCallback callback) {
packetSendCallback = callback;
}
public static void handleJSEvent(JSEagRuntimeEvent evt) {
switch(evt.getEventType() & 31) {
case EVENT_WEBVIEW_CHANNEL: {
JSWebViewChannelEvent obj = evt.getEventObj();
final String channel = BetterJSStringConverter.stringFromJS(obj.getChannelName());
switch(obj.getEventType()) {
case EVENT_CHANNEL_OPEN: {
messageQueue.add(() -> {
sendMessageEnToServer(true, channel);
});
break;
}
case EVENT_CHANNEL_CLOSE: {
messageQueue.add(() -> {
sendMessageEnToServer(false, channel);
});
break;
}
}
break;
}
case EVENT_WEBVIEW_MESSAGE: {
JSWebViewMessageEvent obj = evt.getEventObj();
final String channel = BetterJSStringConverter.stringFromJS(obj.getChannelName());
switch(obj.getEventType()) {
case EVENT_MESSAGE_STRING: {
final String data = BetterJSStringConverter.stringFromJS(obj.getEventDataString());
messageQueue.add(() -> {
sendMessageToServer(channel, CPacketWebViewMessageV4EAG.TYPE_STRING, data.getBytes(StandardCharsets.UTF_8));
});
break;
}
case EVENT_MESSAGE_BINARY: {
final byte[] dataArr = WASMGCDirectArrayConverter
.externU8ArrayToByteArray(new Uint8Array(obj.getEventDataBinary()));
messageQueue.add(() -> {
sendMessageToServer(channel, CPacketWebViewMessageV4EAG.TYPE_BINARY, dataArr);
});
break;
}
}
break;
}
case EVENT_WEBVIEW_PERMISSION_ALLOW: {
if(setJavaScriptAllowedCurrent != null) {
messageQueue.add(setJavaScriptAllowedCurrent);
}
break;
}
case EVENT_WEBVIEW_PERMISSION_BLOCK: {
if(setJavaScriptBlockedCurrent != null) {
messageQueue.add(setJavaScriptBlockedCurrent);
}
break;
}
case EVENT_WEBVIEW_PERMISSION_CLEAR: {
if(setJavaScriptClearedCurrent != null) {
messageQueue.add(setJavaScriptClearedCurrent);
}
break;
}
}
}
public static void runTick() {
if(!showingState) {
return;
}
List<Runnable> lst = null;
if(messageQueue.isEmpty()) {
return;
}
lst = new ArrayList<>(messageQueue);
messageQueue.clear();
for(int i = 0, l = lst.size(); i < l; ++i) {
try {
lst.get(i).run();
}catch(Throwable t) {
logger.error("Caught exception processing webview message!");
logger.error(t);
}
}
}
public static void handleMessageFromServer(SPacketWebViewMessageV4EAG packet) {
if(showingState && currentMessageChannelName != null) {
if(packet.type == SPacketWebViewMessageV4EAG.TYPE_STRING) {
sendStringMessage(BetterJSStringConverter.stringToJS(currentMessageChannelName),
BetterJSStringConverter.stringToJS(new String(packet.data, StandardCharsets.UTF_8)));
}else if(packet.type == SPacketWebViewMessageV4EAG.TYPE_BINARY) {
ByteBuffer buf = WASMGCDirectArrayConverter.byteArrayToBuffer(packet.data);
try {
sendBinaryMessage(BetterJSStringConverter.stringToJS(currentMessageChannelName),
WASMGCBufferAllocator.getUnsignedByteBufferView(buf));
}finally {
PlatformRuntime.freeByteBuffer(buf);
}
}
}else {
logger.error("Server tried to send the WebView a message, but the message channel is not open!");
}
}
@Import(module = "platformWebView", name = "sendStringMessage")
private static native void sendStringMessage(JSString ch, JSString str);
@Import(module = "platformWebView", name = "sendBinaryMessage")
private static native void sendBinaryMessage(JSString ch, Uint8Array bin);
private static void sendMessageToServer(String channelName, int type, byte[] data) {
if(channelName.length() > 255) {
logger.error("WebView tried to send a message packet, but channel name is too long, max is 255 characters!");
return;
}
if(!channelName.equals(currentMessageChannelName)) {
logger.error("WebView tried to send a message packet, but the channel is not open!");
return;
}
if(packetSendCallback != null) {
if(!packetSendCallback.sendPacket(new CPacketWebViewMessageV4EAG(type, data))) {
logger.error("WebView tried to send a packet to the server, but the server does not support this protocol!");
}
}else {
logger.error("WebView tried to send a message, but no callback for sending packets is set!");
}
}
private static void sendMessageEnToServer(boolean messageChannelOpen, String channelName) {
if(channelName.length() > 255) {
logger.error("WebView tried to {} a channel, but channel name is too long, max is 255 characters!", messageChannelOpen ? "open" : "close");
return;
}
if(messageChannelOpen && currentMessageChannelName != null) {
logger.error("WebView tried to open channel, but a channel is already open!");
sendMessageEnToServer(false, currentMessageChannelName);
}
if(!messageChannelOpen && currentMessageChannelName != null && !currentMessageChannelName.equals(channelName)) {
logger.error("WebView tried to close the wrong channel!");
}
if(!messageChannelOpen && currentMessageChannelName == null) {
logger.error("WebView tried to close channel, but the channel is not open!");
return;
}
if(packetSendCallback != null) {
if(!packetSendCallback.sendPacket(new CPacketWebViewMessageEnV4EAG(messageChannelOpen, messageChannelOpen ? channelName : null))) {
logger.error("WebView tried to send a packet to the server, but the server does not support this protocol!");
return;
}
if(messageChannelOpen) {
logger.info("WebView opened message channel to server: \"{}\"", channelName);
currentMessageChannelName = channelName;
}else {
logger.info("WebView closed message channel to server: \"{}\"", currentMessageChannelName);
currentMessageChannelName = null;
}
}else {
logger.error("WebView tried to send a message, but no callback for sending packets is set!");
}
}
private interface JSWebViewOptions extends JSObject {
@JSProperty("uri")
void setURI(JSString title);
@JSProperty
void setBlob(Uint8Array data);
}
@JSBody(params = { "a", "b", "c", "d", "e" }, script = "return { contentMode: a, fallbackTitle: b, "
+ "scriptEnabled: c, strictCSPEnable: d, serverMessageAPIEnabled: e};")
private static native JSWebViewOptions makeOptions(int contentMode, JSString fallbackTitle, boolean scriptEnabled,
boolean strictCSPEnable, boolean serverMessageAPIEnabled);
private static int hashPermissionFlags(WebViewOptions opts) {
int i = (opts.scriptEnabled ? 1 : 0);
i |= ((enableCSP && cspSupport && opts.strictCSPEnable) ? 0 : 2);
i |= (opts.serverMessageAPIEnabled ? 4 : 0);
return i;
}
public static void beginShowing(final WebViewOptions options, int x, int y, int w, int h) {
if(!supported()) {
return;
}
if(showingState) {
endShowing();
}
showingState = true;
++webviewResetSerial;
messageQueue.clear();
int state = BEGIN_SHOWING_DIRECT;
if(options.scriptEnabled) {
PermissionsCache.Permission perm = PermissionsCache.getJavaScriptAllowed(options.permissionsOriginUUID, hashPermissionFlags(options));
if(perm == null) {
state = BEGIN_SHOWING_ENABLE_JAVASCRIPT;
}else if(!perm.choice) {
state = BEGIN_SHOWING_CONTENT_BLOCKED;
}
}
boolean isBlob = options.contentMode == EnumWebViewContentMode.BLOB_BASED;
JSWebViewOptions opts = makeOptions(isBlob ? 1 : 0, BetterJSStringConverter.stringToJS(options.fallbackTitle),
options.scriptEnabled, options.strictCSPEnable, options.serverMessageAPIEnabled);
if(isBlob) {
ByteBuffer buf = WASMGCDirectArrayConverter.byteArrayToBuffer(options.blob);
try {
opts.setBlob(WASMGCBufferAllocator.getUnsignedByteBufferView(buf));
beginShowing0(state, opts, x, y, w, h);
}finally {
PlatformRuntime.freeByteBuffer(buf);
}
}else {
opts.setURI(BetterJSStringConverter.stringToJS(options.url.toString()));
beginShowing0(state, opts, x, y, w, h);
}
final int serial = webviewResetSerial;
setJavaScriptAllowedCurrent = () -> {
if(serial == webviewResetSerial) {
PermissionsCache.setJavaScriptAllowed(options.permissionsOriginUUID, hashPermissionFlags(options), true);
}
};
setJavaScriptBlockedCurrent = () -> {
if(serial == webviewResetSerial) {
PermissionsCache.setJavaScriptAllowed(options.permissionsOriginUUID, hashPermissionFlags(options), false);
}
};
setJavaScriptClearedCurrent = () -> {
if(serial == webviewResetSerial) {
PermissionsCache.clearJavaScriptAllowed(options.permissionsOriginUUID);
}
};
}
private static final int BEGIN_SHOWING_DIRECT = 0;
private static final int BEGIN_SHOWING_ENABLE_JAVASCRIPT = 1;
private static final int BEGIN_SHOWING_CONTENT_BLOCKED = 2;
@Import(module = "platformWebView", name = "beginShowing")
private static native void beginShowing0(int state, JSWebViewOptions options, int x, int y, int w, int h);
@Import(module = "platformWebView", name = "resize")
public static native void resize(int x, int y, int w, int h);
public static void endShowing() {
if(!supported()) {
return;
}
++webviewResetSerial;
if(showingState) {
showingState = false;
endShowing0();
}
if(currentMessageChannelName != null) {
sendMessageEnToServer(false, currentMessageChannelName);
}
messageQueue.clear();
setJavaScriptAllowedCurrent = null;
setJavaScriptBlockedCurrent = null;
setJavaScriptClearedCurrent = null;
}
@Import(module = "platformWebView", name = "endShowing")
private static native void endShowing0();
public static boolean fallbackSupported() {
return false;
}
public static void launchFallback(WebViewOptions options) {
}
public static boolean fallbackRunning() {
return false;
}
public static String getFallbackURL() {
return null;
}
public static void endFallbackServer() {
}
}

View File

@ -0,0 +1,385 @@
package net.lax1dude.eaglercraft.v1_8.internal.buffer;
import org.teavm.interop.Address;
import org.teavm.interop.DirectMalloc;
/**
* Copyright (c) 2024 lax1dude. All Rights Reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
public class DirectMallocByteBuffer implements ByteBuffer {
final Address address;
final boolean original;
private final int capacity;
private int position;
private int limit;
private int mark;
DirectMallocByteBuffer(Address address, int capacity, boolean original) {
this(address, capacity, 0, capacity, -1, original);
}
DirectMallocByteBuffer(Address address, int capacity, int position, int limit, int mark, boolean original) {
this.address = address;
this.capacity = capacity;
this.position = position;
this.limit = limit;
this.mark = mark;
this.original = original;
}
@Override
public int capacity() {
return capacity;
}
@Override
public int position() {
return position;
}
@Override
public int limit() {
return limit;
}
@Override
public int remaining() {
return limit - position;
}
@Override
public boolean hasRemaining() {
return position < limit;
}
@Override
public boolean hasArray() {
return false;
}
@Override
public byte[] array() {
throw new UnsupportedOperationException();
}
@Override
public boolean isDirect() {
return true;
}
@Override
public ByteBuffer duplicate() {
return new DirectMallocByteBuffer(address, capacity, position, limit, mark, false);
}
@Override
public byte get() {
if(position >= limit) throw Buffer.makeIOOBE(position);
return address.add(position++).getByte();
}
@Override
public ByteBuffer put(byte b) {
if(position >= limit) throw Buffer.makeIOOBE(position);
address.add(position++).putByte(b);
return this;
}
@Override
public byte get(int index) {
if(index < 0 || index >= limit) throw Buffer.makeIOOBE(index);
return address.add(index).getByte();
}
@Override
public ByteBuffer put(int index, byte b) {
if(index < 0 || index >= limit) throw Buffer.makeIOOBE(index);
address.add(index).putByte(b);
return this;
}
@Override
public ByteBuffer get(byte[] dst, int offset, int length) {
if(position + length > limit) throw Buffer.makeIOOBE(position + length - 1);
if(offset < 0) throw Buffer.makeIOOBE(offset);
if(offset + length > dst.length) throw Buffer.makeIOOBE(offset + length - 1);
WASMGCDirectArrayCopy.memcpy(dst, offset, address.add(position), length);
position += length;
return this;
}
@Override
public ByteBuffer get(byte[] dst) {
int dstLen = dst.length;
if(position + dstLen > limit) throw Buffer.makeIOOBE(position + dstLen - 1);
WASMGCDirectArrayCopy.memcpy(dst, 0, address.add(position), dstLen);
position += dstLen;
return this;
}
@Override
public ByteBuffer put(ByteBuffer src) {
if(src instanceof DirectMallocByteBuffer) {
DirectMallocByteBuffer c = (DirectMallocByteBuffer)src;
int l = c.limit - c.position;
if(position + l > limit) throw Buffer.makeIOOBE(position + l - 1);
DirectMalloc.memcpy(address.add(position), c.address.add(c.position), l);
position += l;
c.position += l;
}else {
int l = src.remaining();
if(position + l > limit) throw Buffer.makeIOOBE(position + l - 1);
Address addrBase = address.add(position);
for(int i = 0; i < l; ++i) {
addrBase.add(i).putByte(src.get());
}
position += l;
}
return this;
}
@Override
public ByteBuffer put(byte[] src, int offset, int length) {
if(position + length > limit) throw Buffer.makeIOOBE(position + length - 1);
if(offset < 0) throw Buffer.makeIOOBE(offset);
if(offset + length > src.length) throw Buffer.makeIOOBE(offset + length - 1);
WASMGCDirectArrayCopy.memcpy(address.add(position), src, offset, length);
position += length;
return this;
}
@Override
public ByteBuffer put(byte[] src) {
int srcLen = src.length;
if(position + srcLen > limit) throw Buffer.makeIOOBE(position + srcLen - 1);
WASMGCDirectArrayCopy.memcpy(address.add(position), src, 0, srcLen);
position += srcLen;
return this;
}
@Override
public char getChar() {
if(position + 2 > limit) throw Buffer.makeIOOBE(position);
char c = address.add(position).getChar();
position += 2;
return c;
}
@Override
public ByteBuffer putChar(char value) {
if(position + 2 > limit) throw Buffer.makeIOOBE(position);
address.add(position).putChar(value);
position += 2;
return this;
}
@Override
public char getChar(int index) {
if(index < 0 || index + 2 > limit) throw Buffer.makeIOOBE(index);
return address.add(index).getChar();
}
@Override
public ByteBuffer putChar(int index, char value) {
if(index < 0 || index + 2 > limit) throw Buffer.makeIOOBE(index);
address.add(index).putChar(value);
return this;
}
@Override
public short getShort() {
if(position + 2 > limit) throw Buffer.makeIOOBE(position);
short s = address.add(position).getShort();
position += 2;
return s;
}
@Override
public ByteBuffer putShort(short value) {
if(position + 2 > limit) throw Buffer.makeIOOBE(position);
address.add(position).putShort(value);
position += 2;
return this;
}
@Override
public short getShort(int index) {
if(index < 0 || index + 2 > limit) throw Buffer.makeIOOBE(index);
return address.add(index).getShort();
}
@Override
public ByteBuffer putShort(int index, short value) {
if(index < 0 || index + 2 > limit) throw Buffer.makeIOOBE(index);
address.add(index).putShort(value);
return this;
}
@Override
public ShortBuffer asShortBuffer() {
return new DirectMallocShortBuffer(address, capacity >> 1, false);
}
@Override
public int getInt() {
if(position + 4 > limit) throw Buffer.makeIOOBE(position);
int i = address.add(position).getInt();
position += 4;
return i;
}
@Override
public ByteBuffer putInt(int value) {
if(position + 4 > limit) throw Buffer.makeIOOBE(position);
address.add(position).putInt(value);
position += 4;
return this;
}
@Override
public int getInt(int index) {
if(index < 0 || index + 4 > limit) throw Buffer.makeIOOBE(index);
return address.add(index).getInt();
}
@Override
public ByteBuffer putInt(int index, int value) {
if(index < 0 || index + 4 > limit) throw Buffer.makeIOOBE(index);
address.add(index).putInt(value);
return this;
}
@Override
public IntBuffer asIntBuffer() {
return new DirectMallocIntBuffer(address, capacity >> 2, false);
}
@Override
public long getLong() {
if(position + 8 > limit) throw Buffer.makeIOOBE(position);
long l = address.add(position).getLong();
position += 8;
return l;
}
@Override
public ByteBuffer putLong(long value) {
if(position + 8 > limit) throw Buffer.makeIOOBE(position);
address.add(position).putLong(value);
position += 8;
return this;
}
@Override
public long getLong(int index) {
if(index < 0 || index + 8 > limit) throw Buffer.makeIOOBE(index);
return address.add(index).getLong();
}
@Override
public ByteBuffer putLong(int index, long value) {
if(index < 0 || index + 8 > limit) throw Buffer.makeIOOBE(index);
address.add(index).putLong(value);
return this;
}
@Override
public float getFloat() {
if(position + 4 > limit) throw Buffer.makeIOOBE(position);
float f = address.add(position).getFloat();
position += 4;
return f;
}
@Override
public ByteBuffer putFloat(float value) {
if(position + 4 > limit) throw Buffer.makeIOOBE(position);
address.add(position).putFloat(value);
position += 4;
return this;
}
@Override
public float getFloat(int index) {
if(index < 0 || index + 4 > limit) throw Buffer.makeIOOBE(index);
return address.add(index).getFloat();
}
@Override
public ByteBuffer putFloat(int index, float value) {
if(index < 0 || index + 4 > limit) throw Buffer.makeIOOBE(index);
address.add(index).putFloat(value);
return this;
}
@Override
public FloatBuffer asFloatBuffer() {
return new DirectMallocFloatBuffer(address, capacity >> 2, false);
}
@Override
public ByteBuffer mark() {
mark = position;
return this;
}
@Override
public ByteBuffer reset() {
int m = mark;
if(m < 0) throw new IndexOutOfBoundsException("Invalid mark: " + m);
position = m;
return this;
}
@Override
public ByteBuffer clear() {
position = 0;
limit = capacity;
mark = -1;
return this;
}
@Override
public ByteBuffer flip() {
limit = position;
position = 0;
mark = -1;
return this;
}
@Override
public ByteBuffer rewind() {
position = 0;
mark = -1;
return this;
}
@Override
public ByteBuffer limit(int newLimit) {
if(newLimit < 0 || newLimit > capacity) throw Buffer.makeIOOBE(newLimit);
limit = newLimit;
return this;
}
@Override
public ByteBuffer position(int newPosition) {
if(newPosition < 0 || newPosition > limit) throw Buffer.makeIOOBE(newPosition);
position = newPosition;
return this;
}
}

View File

@ -0,0 +1,238 @@
package net.lax1dude.eaglercraft.v1_8.internal.buffer;
import org.teavm.interop.Address;
import org.teavm.interop.DirectMalloc;
/**
* Copyright (c) 2024 lax1dude. All Rights Reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
public class DirectMallocFloatBuffer implements FloatBuffer {
final Address address;
final boolean original;
private final int capacity;
private int position;
private int limit;
private int mark;
private static final int SHIFT = 2;
DirectMallocFloatBuffer(Address address, int capacity, boolean original) {
this(address, capacity, 0, capacity, -1, original);
}
DirectMallocFloatBuffer(Address address, int capacity, int position, int limit, int mark, boolean original) {
this.address = address;
this.capacity = capacity;
this.position = position;
this.limit = limit;
this.mark = mark;
this.original = original;
}
@Override
public int capacity() {
return capacity;
}
@Override
public int position() {
return position;
}
@Override
public int limit() {
return limit;
}
@Override
public int remaining() {
return limit - position;
}
@Override
public boolean hasRemaining() {
return position < limit;
}
@Override
public boolean hasArray() {
return false;
}
@Override
public float[] array() {
throw new UnsupportedOperationException();
}
@Override
public FloatBuffer duplicate() {
return new DirectMallocFloatBuffer(address, capacity, position, limit, mark, false);
}
@Override
public float get() {
if(position >= limit) throw Buffer.makeIOOBE(position);
return address.add((position++) << SHIFT).getFloat();
}
@Override
public FloatBuffer put(float b) {
if(position >= limit) throw Buffer.makeIOOBE(position);
address.add((position++) << SHIFT).putFloat(b);
return this;
}
@Override
public float get(int index) {
if(index < 0 || index >= limit) throw Buffer.makeIOOBE(index);
return address.add(index << SHIFT).getFloat();
}
@Override
public FloatBuffer put(int index, float b) {
if(index < 0 || index >= limit) throw Buffer.makeIOOBE(index);
address.add(index << SHIFT).putFloat(b);
return this;
}
@Override
public float getElement(int index) {
if(index < 0 || index >= limit) throw Buffer.makeIOOBE(index);
return address.add(index << SHIFT).getFloat();
}
@Override
public void putElement(int index, float value) {
if(index < 0 || index >= limit) throw Buffer.makeIOOBE(index);
address.add(index << SHIFT).putFloat(value);
}
@Override
public FloatBuffer get(float[] dst, int offset, int length) {
if(position + length > limit) throw Buffer.makeIOOBE(position + length - 1);
if(offset < 0) throw Buffer.makeIOOBE(offset);
if(offset + length > dst.length) throw Buffer.makeIOOBE(offset + length - 1);
WASMGCDirectArrayCopy.memcpy(dst, offset, address.add(position << SHIFT), length);
position += length;
return this;
}
@Override
public FloatBuffer get(float[] dst) {
int dstLen = dst.length;
if(position + dstLen > limit) throw Buffer.makeIOOBE(position + dstLen - 1);
WASMGCDirectArrayCopy.memcpy(dst, 0, address.add(position << SHIFT), dstLen);
position += dstLen;
return this;
}
@Override
public FloatBuffer put(FloatBuffer src) {
if(src instanceof DirectMallocFloatBuffer) {
DirectMallocFloatBuffer c = (DirectMallocFloatBuffer)src;
int l = c.limit - c.position;
if(position + l > limit) throw Buffer.makeIOOBE(position + l - 1);
DirectMalloc.memcpy(address.add(position << SHIFT), c.address.add(c.position << SHIFT), l << SHIFT);
position += l;
c.position += l;
}else {
int l = src.remaining();
if(position + l > limit) throw Buffer.makeIOOBE(position + l - 1);
Address addrBase = address.add(position << SHIFT);
for(int i = 0, ll = l << SHIFT; i < ll; i += 4) {
addrBase.add(i).putFloat(src.get());
}
position += l;
}
return this;
}
@Override
public FloatBuffer put(float[] src, int offset, int length) {
if(position + length > limit) throw Buffer.makeIOOBE(position + length - 1);
if(offset < 0) throw Buffer.makeIOOBE(offset);
if(offset + length > src.length) throw Buffer.makeIOOBE(offset + length - 1);
WASMGCDirectArrayCopy.memcpy(address.add(position << SHIFT), src, offset, length);
position += length;
return this;
}
@Override
public FloatBuffer put(float[] src) {
if(position + src.length > limit) throw Buffer.makeIOOBE(position + src.length - 1);
WASMGCDirectArrayCopy.memcpy(address.add(position << SHIFT), src, 0, src.length);
position += src.length;
return this;
}
@Override
public boolean isDirect() {
return true;
}
@Override
public FloatBuffer mark() {
mark = position;
return this;
}
@Override
public FloatBuffer reset() {
int m = mark;
if(m < 0) throw new IndexOutOfBoundsException("Invalid mark: " + m);
position = m;
return this;
}
@Override
public FloatBuffer clear() {
position = 0;
limit = capacity;
mark = -1;
return this;
}
@Override
public FloatBuffer flip() {
limit = position;
position = 0;
mark = -1;
return this;
}
@Override
public FloatBuffer rewind() {
position = 0;
mark = -1;
return this;
}
@Override
public FloatBuffer limit(int newLimit) {
if(newLimit < 0 || newLimit > capacity) throw Buffer.makeIOOBE(newLimit);
limit = newLimit;
return this;
}
@Override
public FloatBuffer position(int newPosition) {
if(newPosition < 0 || newPosition > limit) throw Buffer.makeIOOBE(newPosition);
position = newPosition;
return this;
}
}

View File

@ -0,0 +1,239 @@
package net.lax1dude.eaglercraft.v1_8.internal.buffer;
import org.teavm.interop.Address;
import org.teavm.interop.DirectMalloc;
/**
* Copyright (c) 2024 lax1dude. All Rights Reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
public class DirectMallocIntBuffer implements IntBuffer {
final Address address;
final boolean original;
private final int capacity;
private int position;
private int limit;
private int mark;
private static final int SHIFT = 2;
DirectMallocIntBuffer(Address address, int capacity, boolean original) {
this(address, capacity, 0, capacity, -1, original);
}
DirectMallocIntBuffer(Address address, int capacity, int position, int limit, int mark, boolean original) {
this.address = address;
this.capacity = capacity;
this.position = position;
this.limit = limit;
this.mark = mark;
this.original = original;
}
@Override
public int capacity() {
return capacity;
}
@Override
public int position() {
return position;
}
@Override
public int limit() {
return limit;
}
@Override
public int remaining() {
return limit - position;
}
@Override
public boolean hasRemaining() {
return position < limit;
}
@Override
public boolean hasArray() {
return false;
}
@Override
public int[] array() {
throw new UnsupportedOperationException();
}
@Override
public IntBuffer duplicate() {
return new DirectMallocIntBuffer(address, capacity, position, limit, mark, false);
}
@Override
public int get() {
if(position >= limit) throw Buffer.makeIOOBE(position);
return address.add((position++) << SHIFT).getInt();
}
@Override
public IntBuffer put(int b) {
if(position >= limit) throw Buffer.makeIOOBE(position);
address.add((position++) << SHIFT).putInt(b);
return this;
}
@Override
public int get(int index) {
if(index < 0 || index >= limit) throw Buffer.makeIOOBE(index);
return address.add(index << SHIFT).getInt();
}
@Override
public IntBuffer put(int index, int b) {
if(index < 0 || index >= limit) throw Buffer.makeIOOBE(index);
address.add(index << SHIFT).putInt(b);
return this;
}
@Override
public int getElement(int index) {
if(index < 0 || index >= limit) throw Buffer.makeIOOBE(index);
return address.add(index << SHIFT).getInt();
}
@Override
public void putElement(int index, int value) {
if(index < 0 || index >= limit) throw Buffer.makeIOOBE(index);
address.add(index << SHIFT).putInt(value);
}
@Override
public IntBuffer get(int[] dst, int offset, int length) {
if(position + length > limit) throw Buffer.makeIOOBE(position + length - 1);
if(offset < 0) throw Buffer.makeIOOBE(offset);
if(offset + length > dst.length) throw Buffer.makeIOOBE(offset + length - 1);
WASMGCDirectArrayCopy.memcpy(dst, offset, address.add(position << SHIFT), length);
position += length;
return this;
}
@Override
public IntBuffer get(int[] dst) {
int dstLen = dst.length;
if(position + dstLen > limit) throw Buffer.makeIOOBE(position + dstLen - 1);
WASMGCDirectArrayCopy.memcpy(dst, 0, address.add(position << SHIFT), dstLen);
position += dstLen;
return this;
}
@Override
public IntBuffer put(IntBuffer src) {
if(src instanceof DirectMallocIntBuffer) {
DirectMallocIntBuffer c = (DirectMallocIntBuffer)src;
int l = c.limit - c.position;
if(position + l > limit) throw Buffer.makeIOOBE(position + l - 1);
DirectMalloc.memcpy(address.add(position << SHIFT), c.address.add(c.position << SHIFT), l << SHIFT);
position += l;
c.position += l;
}else {
int l = src.remaining();
if(position + l > limit) throw Buffer.makeIOOBE(position + l - 1);
Address addrBase = address.add(position << SHIFT);
for(int i = 0, ll = l << SHIFT; i < ll; i += 4) {
addrBase.add(i).putInt(src.get());
}
position += l;
}
return this;
}
@Override
public IntBuffer put(int[] src, int offset, int length) {
if(position + length > limit) throw Buffer.makeIOOBE(position + length - 1);
if(offset < 0) throw Buffer.makeIOOBE(offset);
if(offset + length > src.length) throw Buffer.makeIOOBE(offset + length - 1);
WASMGCDirectArrayCopy.memcpy(address.add(position << SHIFT), src, offset, length);
position += length;
return this;
}
@Override
public IntBuffer put(int[] src) {
int srcLen = src.length;
if(position + srcLen > limit) throw Buffer.makeIOOBE(position + srcLen - 1);
WASMGCDirectArrayCopy.memcpy(address.add(position << SHIFT), src, 0, srcLen);
position += src.length;
return this;
}
@Override
public boolean isDirect() {
return true;
}
@Override
public IntBuffer mark() {
mark = position;
return this;
}
@Override
public IntBuffer reset() {
int m = mark;
if(m < 0) throw new IndexOutOfBoundsException("Invalid mark: " + m);
position = m;
return this;
}
@Override
public IntBuffer clear() {
position = 0;
limit = capacity;
mark = -1;
return this;
}
@Override
public IntBuffer flip() {
limit = position;
position = 0;
mark = -1;
return this;
}
@Override
public IntBuffer rewind() {
position = 0;
mark = -1;
return this;
}
@Override
public IntBuffer limit(int newLimit) {
if(newLimit < 0 || newLimit > capacity) throw Buffer.makeIOOBE(newLimit);
limit = newLimit;
return this;
}
@Override
public IntBuffer position(int newPosition) {
if(newPosition < 0 || newPosition > limit) throw Buffer.makeIOOBE(newPosition);
position = newPosition;
return this;
}
}

View File

@ -0,0 +1,239 @@
package net.lax1dude.eaglercraft.v1_8.internal.buffer;
import org.teavm.interop.Address;
import org.teavm.interop.DirectMalloc;
/**
* Copyright (c) 2024 lax1dude. All Rights Reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
public class DirectMallocShortBuffer implements ShortBuffer {
final Address address;
final boolean original;
private final int capacity;
private int position;
private int limit;
private int mark;
private static final int SHIFT = 1;
DirectMallocShortBuffer(Address address, int capacity, boolean original) {
this(address, capacity, 0, capacity, -1, original);
}
DirectMallocShortBuffer(Address address, int capacity, int position, int limit, int mark, boolean original) {
this.address = address;
this.capacity = capacity;
this.position = position;
this.limit = limit;
this.mark = mark;
this.original = original;
}
@Override
public int capacity() {
return capacity;
}
@Override
public int position() {
return position;
}
@Override
public int limit() {
return limit;
}
@Override
public int remaining() {
return limit - position;
}
@Override
public boolean hasRemaining() {
return position < limit;
}
@Override
public boolean hasArray() {
return false;
}
@Override
public short[] array() {
throw new UnsupportedOperationException();
}
@Override
public ShortBuffer duplicate() {
return new DirectMallocShortBuffer(address, capacity, position, limit, mark, false);
}
@Override
public short get() {
if(position >= limit) throw Buffer.makeIOOBE(position);
return address.add((position++) << SHIFT).getShort();
}
@Override
public ShortBuffer put(short b) {
if(position >= limit) throw Buffer.makeIOOBE(position);
address.add((position++) << SHIFT).putShort(b);
return this;
}
@Override
public short get(int index) {
if(index < 0 || index >= limit) throw Buffer.makeIOOBE(index);
return address.add(index << SHIFT).getShort();
}
@Override
public ShortBuffer put(int index, short b) {
if(index < 0 || index >= limit) throw Buffer.makeIOOBE(index);
address.add(index << SHIFT).putShort(b);
return this;
}
@Override
public short getElement(int index) {
if(index < 0 || index >= limit) throw Buffer.makeIOOBE(index);
return address.add(index << SHIFT).getShort();
}
@Override
public void putElement(int index, short value) {
if(index < 0 || index >= limit) throw Buffer.makeIOOBE(index);
address.add(index << SHIFT).putShort(value);
}
@Override
public ShortBuffer get(short[] dst, int offset, int length) {
if(position + length > limit) throw Buffer.makeIOOBE(position + length - 1);
if(offset < 0) throw Buffer.makeIOOBE(offset);
if(offset + length > dst.length) throw Buffer.makeIOOBE(offset + length - 1);
WASMGCDirectArrayCopy.memcpy(dst, offset, address.add(position << SHIFT), length);
position += length;
return this;
}
@Override
public ShortBuffer get(short[] dst) {
int dstLen = dst.length;
if(position + dstLen > limit) throw Buffer.makeIOOBE(position + dstLen - 1);
WASMGCDirectArrayCopy.memcpy(dst, 0, address.add(position << SHIFT), dstLen);
position += dstLen;
return this;
}
@Override
public ShortBuffer put(ShortBuffer src) {
if(src instanceof DirectMallocShortBuffer) {
DirectMallocShortBuffer c = (DirectMallocShortBuffer)src;
int l = c.limit - c.position;
if(position + l > limit) throw Buffer.makeIOOBE(position + l - 1);
DirectMalloc.memcpy(address.add(position << SHIFT), c.address.add(c.position << SHIFT), l << SHIFT);
position += l;
c.position += l;
}else {
int l = src.remaining();
if(position + l > limit) throw Buffer.makeIOOBE(position + l - 1);
Address addrBase = address.add(position << SHIFT);
for(int i = 0, ll = l << SHIFT; i < ll; i += 2) {
addrBase.add(i).putShort(src.get());
}
position += l;
}
return this;
}
@Override
public ShortBuffer put(short[] src, int offset, int length) {
if(position + length > limit) throw Buffer.makeIOOBE(position + length - 1);
if(offset < 0) throw Buffer.makeIOOBE(offset);
if(offset + length > src.length) throw Buffer.makeIOOBE(offset + length - 1);
WASMGCDirectArrayCopy.memcpy(address.add(position << SHIFT), src, offset, length);
position += length;
return this;
}
@Override
public ShortBuffer put(short[] src) {
int srcLen = src.length;
if(position + srcLen > limit) throw Buffer.makeIOOBE(position + srcLen - 1);
WASMGCDirectArrayCopy.memcpy(address.add(position << SHIFT), src, 0, srcLen);
position += src.length;
return this;
}
@Override
public boolean isDirect() {
return true;
}
@Override
public ShortBuffer mark() {
mark = position;
return this;
}
@Override
public ShortBuffer reset() {
int m = mark;
if(m < 0) throw new IndexOutOfBoundsException("Invalid mark: " + m);
position = m;
return this;
}
@Override
public ShortBuffer clear() {
position = 0;
limit = capacity;
mark = -1;
return this;
}
@Override
public ShortBuffer flip() {
limit = position;
position = 0;
mark = -1;
return this;
}
@Override
public ShortBuffer rewind() {
position = 0;
mark = -1;
return this;
}
@Override
public ShortBuffer limit(int newLimit) {
if(newLimit < 0 || newLimit > capacity) throw Buffer.makeIOOBE(newLimit);
limit = newLimit;
return this;
}
@Override
public ShortBuffer position(int newPosition) {
if(newPosition < 0 || newPosition > limit) throw Buffer.makeIOOBE(newPosition);
position = newPosition;
return this;
}
}

View File

@ -0,0 +1,269 @@
package net.lax1dude.eaglercraft.v1_8.internal.buffer;
import org.teavm.interop.Address;
import org.teavm.interop.DirectMalloc;
import org.teavm.interop.Import;
import org.teavm.jso.typedarrays.Float32Array;
import org.teavm.jso.typedarrays.Int16Array;
import org.teavm.jso.typedarrays.Int32Array;
import org.teavm.jso.typedarrays.Int8Array;
import org.teavm.jso.typedarrays.Uint16Array;
import org.teavm.jso.typedarrays.Uint8Array;
import org.teavm.jso.typedarrays.Uint8ClampedArray;
/**
* Copyright (c) 2024 lax1dude. All Rights Reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
public class WASMGCBufferAllocator {
private static final boolean enableBufferOverflowCheck = false;
public static Address malloc(int size) {
if(size == 0) {
throw new IllegalArgumentException("Tried to allocate 0 bytes!");
}
Address addr;
if(enableBufferOverflowCheck) {
addr = DirectMalloc.malloc(size + 12);
if(addr.toInt() == 0) {
throw new OutOfMemoryError("DirectMalloc returned null pointer!");
}
int tag = (int)(Math.random() * 2147483647.0);
addr.putInt(size);
addr.add(4).putInt(tag);
addr.add(size + 8).putInt(tag);
addr = addr.add(8);
}else {
addr = DirectMalloc.malloc(size);
if(addr.toInt() == 0) {
throw new OutOfMemoryError("DirectMalloc returned null pointer!");
}
}
return addr;
}
public static Address calloc(int size) {
Address addr;
if(enableBufferOverflowCheck) {
if(size == 0) {
throw new OutOfMemoryError("Tried to allocate 0 bytes!");
}
addr = DirectMalloc.calloc(size + 12);
if(addr.toInt() == 0) {
throw new OutOfMemoryError("DirectMalloc returned null pointer!");
}
int tag = (int)(Math.random() * 2147483647.0);
addr.putInt(size);
addr.add(4).putInt(tag);
addr.add(size + 8).putInt(tag);
addr = addr.add(8);
}else {
addr = DirectMalloc.calloc(size);
if(addr.toInt() == 0) {
throw new OutOfMemoryError("DirectMalloc returned null pointer!");
}
}
return addr;
}
public static void free(Address ptr) {
if(ptr.toInt() != 0) {
if(enableBufferOverflowCheck) {
ptr = ptr.add(-8);
int size = ptr.getInt();
int tag = ptr.add(4).getInt();
if(tag != ptr.add(size + 8).getInt()) {
throw new RuntimeException("Detected a buffer write overflow");
}
DirectMalloc.free(ptr);
}else {
DirectMalloc.free(ptr);
}
}
}
public static ByteBuffer allocateByteBuffer(int size) {
return new DirectMallocByteBuffer(malloc(size), size, true);
}
public static ShortBuffer allocateShortBuffer(int size) {
return new DirectMallocShortBuffer(malloc(size << 1), size, true);
}
public static IntBuffer allocateIntBuffer(int size) {
return new DirectMallocIntBuffer(malloc(size << 2), size, true);
}
public static FloatBuffer allocateFloatBuffer(int size) {
return new DirectMallocFloatBuffer(malloc(size << 2), size, true);
}
public static void freeByteBuffer(ByteBuffer buffer) {
DirectMallocByteBuffer buf = (DirectMallocByteBuffer)buffer;
if(buf.original) {
WASMGCBufferAllocator.free(buf.address);
}else {
throwNotOriginal(buf);
}
}
public static void freeShortBuffer(ShortBuffer buffer) {
DirectMallocShortBuffer buf = (DirectMallocShortBuffer)buffer;
if(buf.original) {
WASMGCBufferAllocator.free(buf.address);
}else {
throwNotOriginal(buf);
}
}
public static void freeIntBuffer(IntBuffer buffer) {
DirectMallocIntBuffer buf = (DirectMallocIntBuffer)buffer;
if(buf.original) {
WASMGCBufferAllocator.free(buf.address);
}else {
throwNotOriginal(buf);
}
}
public static void freeFloatBuffer(FloatBuffer buffer) {
DirectMallocFloatBuffer buf = (DirectMallocFloatBuffer)buffer;
if(buf.original) {
WASMGCBufferAllocator.free(buf.address);
}else {
throwNotOriginal(buf);
}
}
public static Address getByteBufferAddress(ByteBuffer buffer) {
DirectMallocByteBuffer buf = (DirectMallocByteBuffer)buffer;
return buf.address.add(buf.position());
}
public static Address getShortBufferAddress(ShortBuffer buffer) {
DirectMallocShortBuffer buf = (DirectMallocShortBuffer)buffer;
return buf.address.add(buf.position() << 1);
}
public static Address getIntBufferAddress(IntBuffer buffer) {
DirectMallocIntBuffer buf = (DirectMallocIntBuffer)buffer;
return buf.address.add(buf.position() << 2);
}
public static Address getFloatBufferAddress(FloatBuffer buffer) {
DirectMallocFloatBuffer buf = (DirectMallocFloatBuffer)buffer;
return buf.address.add(buf.position() << 2);
}
public static Int8Array getByteBufferView(ByteBuffer buffer) {
DirectMallocByteBuffer buf = (DirectMallocByteBuffer)buffer;
return getByteBufferView0(buf.address.add(buf.position()), buf.remaining());
}
@Import(module = "WASMGCBufferAllocator", name = "getByteBufferView")
public static native Int8Array getByteBufferView0(Address addr, int length);
public static Uint8Array getUnsignedByteBufferView(ByteBuffer buffer) {
DirectMallocByteBuffer buf = (DirectMallocByteBuffer)buffer;
return getUnsignedByteBufferView0(buf.address.add(buf.position()), buf.remaining());
}
public static Uint8Array getUnsignedByteBufferView(ShortBuffer buffer) {
DirectMallocIntBuffer buf = (DirectMallocIntBuffer)buffer;
return getUnsignedByteBufferView0(buf.address.add(buf.position()), buf.remaining() << 1);
}
public static Uint8Array getUnsignedByteBufferView(IntBuffer buffer) {
DirectMallocIntBuffer buf = (DirectMallocIntBuffer)buffer;
return getUnsignedByteBufferView0(buf.address.add(buf.position()), buf.remaining() << 2);
}
@Import(module = "WASMGCBufferAllocator", name = "getUnsignedByteBufferView")
public static native Uint8Array getUnsignedByteBufferView0(Address addr, int length);
public static Uint8ClampedArray getUnsignedClampedByteBufferView(ByteBuffer buffer) {
DirectMallocByteBuffer buf = (DirectMallocByteBuffer)buffer;
return getUnsignedClampedByteBufferView0(buf.address.add(buf.position()), buf.remaining());
}
@Import(module = "WASMGCBufferAllocator", name = "getUnsignedClampedByteBufferView")
public static native Uint8ClampedArray getUnsignedClampedByteBufferView0(Address addr, int length);
public static Int16Array getShortBufferView(ShortBuffer buffer) {
DirectMallocShortBuffer buf = (DirectMallocShortBuffer)buffer;
return getShortBufferView0(buf.address.add(buf.position() << 1), buf.remaining());
}
public static Int16Array getShortBufferView(ByteBuffer buffer) {
DirectMallocByteBuffer buf = (DirectMallocByteBuffer)buffer;
return getShortBufferView0(buf.address.add(buf.position()), buf.remaining() >> 1);
}
@Import(module = "WASMGCBufferAllocator", name = "getShortBufferView")
public static native Int16Array getShortBufferView0(Address addr, int length);
public static Uint16Array getUnsignedShortBufferView(ShortBuffer buffer) {
DirectMallocShortBuffer buf = (DirectMallocShortBuffer)buffer;
return getUnsignedShortBufferView0(buf.address.add(buf.position() << 1), buf.remaining());
}
public static Uint16Array getUnsignedShortBufferView(ByteBuffer buffer) {
DirectMallocByteBuffer buf = (DirectMallocByteBuffer)buffer;
return getUnsignedShortBufferView0(buf.address.add(buf.position()), buf.remaining() >> 1);
}
@Import(module = "WASMGCBufferAllocator", name = "getUnsignedShortBufferView")
public static native Uint16Array getUnsignedShortBufferView0(Address addr, int length);
public static Int32Array getIntBufferView(IntBuffer buffer) {
DirectMallocIntBuffer buf = (DirectMallocIntBuffer)buffer;
return getIntBufferView0(buf.address.add(buf.position() << 2), buf.remaining());
}
public static Int32Array getIntBufferView(ByteBuffer buffer) {
DirectMallocByteBuffer buf = (DirectMallocByteBuffer)buffer;
return getIntBufferView0(buf.address.add(buf.position()), buf.remaining() >> 2);
}
@Import(module = "WASMGCBufferAllocator", name = "getIntBufferView")
public static native Int32Array getIntBufferView0(Address addr, int length);
public static Float32Array getFloatBufferView(FloatBuffer buffer) {
DirectMallocFloatBuffer buf = (DirectMallocFloatBuffer)buffer;
return getFloatBufferView0(buf.address.add(buf.position() << 2), buf.remaining());
}
public static Float32Array getFloatBufferView(ByteBuffer buffer) {
DirectMallocByteBuffer buf = (DirectMallocByteBuffer)buffer;
return getFloatBufferView0(buf.address.add(buf.position()), buf.remaining() >> 2);
}
@Import(module = "WASMGCBufferAllocator", name = "getFloatBufferView")
public static native Float32Array getFloatBufferView0(Address addr, int length);
private static void throwNotOriginal(Object clazz) {
throw notOriginal(clazz);
}
public static class WrongBufferClassType extends RuntimeException {
public WrongBufferClassType(String msg) {
super(msg);
}
}
private static WrongBufferClassType notOriginal(Object clazz) {
return new WrongBufferClassType("Tried to pass a " + clazz.getClass().getSimpleName() + " which was not the original buffer");
}
}

View File

@ -0,0 +1,397 @@
package net.lax1dude.eaglercraft.v1_8.internal.buffer;
import org.teavm.interop.Address;
import org.teavm.interop.DirectMalloc;
import org.teavm.jso.typedarrays.Float32Array;
import org.teavm.jso.typedarrays.Int16Array;
import org.teavm.jso.typedarrays.Int32Array;
import org.teavm.jso.typedarrays.Int8Array;
import org.teavm.jso.typedarrays.Uint16Array;
import org.teavm.jso.typedarrays.Uint8Array;
import org.teavm.jso.typedarrays.Uint8ClampedArray;
/**
* Copyright (c) 2024 lax1dude. All Rights Reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
public class WASMGCDirectArrayConverter {
public static ByteBuffer byteArrayToBuffer(byte[] byteArray) {
int len = byteArray.length;
Address ret = WASMGCBufferAllocator.malloc(len);
WASMGCDirectArrayCopy.memcpy(ret, byteArray, 0, len);
return new DirectMallocByteBuffer(ret, len, true);
}
public static ByteBuffer byteArrayToBuffer(byte[] byteArray, int offset, int length) {
if(offset < 0) throw Buffer.makeIOOBE(offset);
if(offset + length > byteArray.length) throw Buffer.makeIOOBE(offset + length - 1);
Address ret = WASMGCBufferAllocator.malloc(length);
WASMGCDirectArrayCopy.memcpy(ret, byteArray, offset, length);
return new DirectMallocByteBuffer(ret, length, true);
}
public static ShortBuffer shortArrayToBuffer(short[] shortArray) {
int len = shortArray.length;
Address ret = WASMGCBufferAllocator.malloc(len << 1);
WASMGCDirectArrayCopy.memcpy(ret, shortArray, 0, len);
return new DirectMallocShortBuffer(ret, len, true);
}
public static ShortBuffer shortArrayToBuffer(short[] shortArray, int offset, int length) {
if(offset < 0) throw Buffer.makeIOOBE(offset);
if(offset + length > shortArray.length) throw Buffer.makeIOOBE(offset + length - 1);
Address ret = WASMGCBufferAllocator.malloc(length << 1);
WASMGCDirectArrayCopy.memcpy(ret, shortArray, offset, length);
return new DirectMallocShortBuffer(ret, length, true);
}
public static IntBuffer intArrayToBuffer(int[] intArray) {
int len = intArray.length;
Address ret = WASMGCBufferAllocator.malloc(len << 2);
WASMGCDirectArrayCopy.memcpy(ret, intArray, 0, len);
return new DirectMallocIntBuffer(ret, len, true);
}
public static IntBuffer intArrayToBuffer(int[] intArray, int offset, int length) {
if(offset < 0) throw Buffer.makeIOOBE(offset);
if(offset + length > intArray.length) throw Buffer.makeIOOBE(offset + length - 1);
Address ret = WASMGCBufferAllocator.malloc(length << 2);
WASMGCDirectArrayCopy.memcpy(ret, intArray, offset, length);
return new DirectMallocIntBuffer(ret, length, true);
}
public static FloatBuffer floatArrayToBuffer(float[] floatArray) {
int len = floatArray.length;
Address ret = WASMGCBufferAllocator.malloc(len << 2);
WASMGCDirectArrayCopy.memcpy(ret, floatArray, 0, len);
return new DirectMallocFloatBuffer(ret, len, true);
}
public static FloatBuffer floatArrayToBuffer(float[] floatArray, int offset, int length) {
if(offset < 0) throw Buffer.makeIOOBE(offset);
if(offset + length > floatArray.length) throw Buffer.makeIOOBE(offset + length - 1);
Address ret = WASMGCBufferAllocator.malloc(length << 2);
WASMGCDirectArrayCopy.memcpy(ret, floatArray, offset, length);
return new DirectMallocFloatBuffer(ret, length, true);
}
private static final Uint8Array UINT8ZeroLength = new Uint8Array(0);
private static final Uint8ClampedArray UINT8CZeroLength = new Uint8ClampedArray(0);
private static final Int8Array INT8ZeroLength = new Int8Array(0);
private static final Uint16Array UINT16ZeroLength = new Uint16Array(0);
private static final Int16Array INT16ZeroLength = new Int16Array(0);
private static final Int32Array INT32ZeroLength = new Int32Array(0);
private static final Float32Array FLOAT32ZeroLength = new Float32Array(0);
public static Uint8Array byteArrayToExternU8Array(byte[] byteArray) {
int len = byteArray.length;
if(len == 0) {
return UINT8ZeroLength;
}
Address addr = WASMGCBufferAllocator.malloc(len);
WASMGCDirectArrayCopy.memcpy(addr, byteArray, 0, len);
Uint8Array ret = new Uint8Array(len);
ret.set(WASMGCBufferAllocator.getUnsignedByteBufferView0(addr, len));
WASMGCBufferAllocator.free(addr);
return ret;
}
public static Uint8ClampedArray byteArrayToExternU8CArray(byte[] byteArray) {
int len = byteArray.length;
if(len == 0) {
return UINT8CZeroLength;
}
Address addr = WASMGCBufferAllocator.malloc(len);
WASMGCDirectArrayCopy.memcpy(addr, byteArray, 0, len);
Uint8ClampedArray ret = new Uint8ClampedArray(len);
ret.set(WASMGCBufferAllocator.getUnsignedClampedByteBufferView0(addr, len));
WASMGCBufferAllocator.free(addr);
return ret;
}
public static Int8Array byteArrayToExternI8Array(byte[] byteArray) {
int len = byteArray.length;
if(len == 0) {
return INT8ZeroLength;
}
Address addr = WASMGCBufferAllocator.malloc(len);
WASMGCDirectArrayCopy.memcpy(addr, byteArray, 0, len);
Int8Array ret = new Int8Array(len);
ret.set(WASMGCBufferAllocator.getByteBufferView0(addr, len));
WASMGCBufferAllocator.free(addr);
return ret;
}
public static Uint16Array byteArrayToExternU16Array(byte[] byteArray) {
int len = byteArray.length & 0xFFFFFFFE;
if(len == 0) {
return UINT16ZeroLength;
}
Address addr = WASMGCBufferAllocator.malloc(len);
WASMGCDirectArrayCopy.memcpy(addr, byteArray, 0, len);
len >>= 1;
Uint16Array ret = new Uint16Array(len);
ret.set(WASMGCBufferAllocator.getUnsignedShortBufferView0(addr, len));
WASMGCBufferAllocator.free(addr);
return ret;
}
public static Int16Array byteArrayToExternI16Array(byte[] byteArray) {
int len = byteArray.length & 0xFFFFFFFE;
if(len == 0) {
return INT16ZeroLength;
}
Address addr = WASMGCBufferAllocator.malloc(len);
WASMGCDirectArrayCopy.memcpy(addr, byteArray, 0, len);
len >>= 1;
Int16Array ret = new Int16Array(len);
ret.set(WASMGCBufferAllocator.getShortBufferView0(addr, len));
WASMGCBufferAllocator.free(addr);
return ret;
}
public static Uint16Array shortArrayToExternU16Array(short[] shortArray) {
int len = shortArray.length;
if(len == 0) {
return UINT16ZeroLength;
}
Address addr = WASMGCBufferAllocator.malloc(len << 1);
WASMGCDirectArrayCopy.memcpy(addr, shortArray, 0, len);
Uint16Array ret = new Uint16Array(len);
ret.set(WASMGCBufferAllocator.getUnsignedShortBufferView0(addr, len));
WASMGCBufferAllocator.free(addr);
return ret;
}
public static Int16Array shortArrayToExternI16Array(short[] shortArray) {
int len = shortArray.length;
if(len == 0) {
return INT16ZeroLength;
}
Address addr = WASMGCBufferAllocator.malloc(len << 1);
WASMGCDirectArrayCopy.memcpy(addr, shortArray, 0, len);
Int16Array ret = new Int16Array(len);
ret.set(WASMGCBufferAllocator.getShortBufferView0(addr, len));
WASMGCBufferAllocator.free(addr);
return ret;
}
public static Int32Array byteArrayToExternI32Array(byte[] byteArray) {
int len = byteArray.length & 0xFFFFFFFC;
if(len == 0) {
return INT32ZeroLength;
}
Address addr = WASMGCBufferAllocator.malloc(len);
WASMGCDirectArrayCopy.memcpy(addr, byteArray, 0, len);
len >>= 2;
Int32Array ret = new Int32Array(len);
ret.set(WASMGCBufferAllocator.getIntBufferView0(addr, len));
WASMGCBufferAllocator.free(addr);
return ret;
}
public static Int32Array intArrayToExternI32Array(int[] intArray) {
int len = intArray.length;
if(len == 0) {
return INT32ZeroLength;
}
Address addr = WASMGCBufferAllocator.malloc(len << 2);
WASMGCDirectArrayCopy.memcpy(addr, intArray, 0, len);
Int32Array ret = new Int32Array(len);
ret.set(WASMGCBufferAllocator.getIntBufferView0(addr, len));
WASMGCBufferAllocator.free(addr);
return ret;
}
public static Float32Array byteArrayToExternF32Array(byte[] byteArray) {
int len = byteArray.length & 0xFFFFFFFC;
if(len == 0) {
return FLOAT32ZeroLength;
}
Address addr = WASMGCBufferAllocator.malloc(len);
WASMGCDirectArrayCopy.memcpy(addr, byteArray, 0, len);
len >>= 2;
Float32Array ret = new Float32Array(len);
ret.set(WASMGCBufferAllocator.getFloatBufferView0(addr, len));
WASMGCBufferAllocator.free(addr);
return ret;
}
public static Float32Array floatArrayToExternF32Array(float[] floatArray) {
int len = floatArray.length;
if(len == 0) {
return FLOAT32ZeroLength;
}
Address addr = WASMGCBufferAllocator.malloc(len << 2);
WASMGCDirectArrayCopy.memcpy(addr, floatArray, 0, len);
Float32Array ret = new Float32Array(len);
ret.set(WASMGCBufferAllocator.getFloatBufferView0(addr, len));
WASMGCBufferAllocator.free(addr);
return ret;
}
private static final byte[] byteZeroLength = new byte[0];
private static final short[] shortZeroLength = new short[0];
private static final int[] intZeroLength = new int[0];
private static final float[] floatZeroLength = new float[0];
public static byte[] externU8ArrayToByteArray(Uint8Array U8Array) {
int len = U8Array.getLength();
if(len == 0) {
return byteZeroLength;
}
Address addr = WASMGCBufferAllocator.malloc(len);
WASMGCBufferAllocator.getUnsignedByteBufferView0(addr, len).set(U8Array);
byte[] ret = new byte[len];
WASMGCDirectArrayCopy.memcpy(ret, 0, addr, len);
WASMGCBufferAllocator.free(addr);
return ret;
}
public static byte[] externU8CArrayToByteArray(Uint8ClampedArray U8CArray) {
int len = U8CArray.getLength();
if(len == 0) {
return byteZeroLength;
}
Address addr = WASMGCBufferAllocator.malloc(len);
WASMGCBufferAllocator.getUnsignedClampedByteBufferView0(addr, len).set(U8CArray);
byte[] ret = new byte[len];
WASMGCDirectArrayCopy.memcpy(ret, 0, addr, len);
WASMGCBufferAllocator.free(addr);
return ret;
}
public static byte[] externI8ArrayToByteArray(Int8Array I8Array) {
int len = I8Array.getLength();
if(len == 0) {
return byteZeroLength;
}
Address addr = WASMGCBufferAllocator.malloc(len);
WASMGCBufferAllocator.getByteBufferView0(addr, len).set(I8Array);
byte[] ret = new byte[len];
WASMGCDirectArrayCopy.memcpy(ret, 0, addr, len);
WASMGCBufferAllocator.free(addr);
return ret;
}
public static byte[] externU16ArrayToByteArray(Uint16Array U16Array) {
int len = U16Array.getLength();
if(len == 0) {
return byteZeroLength;
}
int len2 = len << 1;
Address addr = WASMGCBufferAllocator.malloc(len2);
WASMGCBufferAllocator.getUnsignedShortBufferView0(addr, len).set(U16Array);
byte[] ret = new byte[len2];
WASMGCDirectArrayCopy.memcpy(ret, 0, addr, len2);
WASMGCBufferAllocator.free(addr);
return ret;
}
public static byte[] externI16ArrayToByteArray(Int16Array I16Array) {
int len = I16Array.getLength();
if(len == 0) {
return byteZeroLength;
}
int len2 = len << 1;
Address addr = WASMGCBufferAllocator.malloc(len2);
WASMGCBufferAllocator.getShortBufferView0(addr, len).set(I16Array);
byte[] ret = new byte[len2];
WASMGCDirectArrayCopy.memcpy(ret, 0, addr, len2);
WASMGCBufferAllocator.free(addr);
return ret;
}
public static short[] externU16ArrayToShortArray(Uint16Array U16Array) {
int len = U16Array.getLength();
if(len == 0) {
return shortZeroLength;
}
Address addr = WASMGCBufferAllocator.malloc(len << 1);
WASMGCBufferAllocator.getUnsignedShortBufferView0(addr, len).set(U16Array);
short[] ret = new short[len];
WASMGCDirectArrayCopy.memcpy(ret, 0, addr, len);
WASMGCBufferAllocator.free(addr);
return ret;
}
public static short[] externI16ArrayToShortArray(Int16Array I16Array) {
int len = I16Array.getLength();
if(len == 0) {
return shortZeroLength;
}
Address addr = WASMGCBufferAllocator.malloc(len << 1);
WASMGCBufferAllocator.getShortBufferView0(addr, len).set(I16Array);
short[] ret = new short[len];
WASMGCDirectArrayCopy.memcpy(ret, 0, addr, len);
WASMGCBufferAllocator.free(addr);
return ret;
}
public static byte[] externI32ArrayToByteArray(Int32Array I32Array) {
int len = I32Array.getLength();
if(len == 0) {
return byteZeroLength;
}
int len2 = len << 2;
Address addr = WASMGCBufferAllocator.malloc(len2);
WASMGCBufferAllocator.getIntBufferView0(addr, len).set(I32Array);
byte[] ret = new byte[len2];
WASMGCDirectArrayCopy.memcpy(ret, 0, addr, len2);
WASMGCBufferAllocator.free(addr);
return ret;
}
public static int[] externI32ArrayToIntArray(Int32Array I32Array) {
int len = I32Array.getLength();
if(len == 0) {
return intZeroLength;
}
Address addr = WASMGCBufferAllocator.malloc(len << 2);
WASMGCBufferAllocator.getIntBufferView0(addr, len).set(I32Array);
int[] ret = new int[len];
WASMGCDirectArrayCopy.memcpy(ret, 0, addr, len);
WASMGCBufferAllocator.free(addr);
return ret;
}
public static byte[] externF32ArrayToByteArray(Float32Array F32Array) {
int len = F32Array.getLength();
if(len == 0) {
return byteZeroLength;
}
int len2 = len << 2;
Address addr = WASMGCBufferAllocator.malloc(len2);
WASMGCBufferAllocator.getFloatBufferView0(addr, len).set(F32Array);
byte[] ret = new byte[len2];
WASMGCDirectArrayCopy.memcpy(ret, 0, addr, len2);
WASMGCBufferAllocator.free(addr);
return ret;
}
public static float[] externF32ArrayToFloatArray(Float32Array F32Array) {
int len = F32Array.getLength();
if(len == 0) {
return floatZeroLength;
}
Address addr = WASMGCBufferAllocator.malloc(len << 2);
WASMGCBufferAllocator.getFloatBufferView0(addr, len).set(F32Array);
float[] ret = new float[len];
WASMGCDirectArrayCopy.memcpy(ret, 0, addr, len);
WASMGCBufferAllocator.free(addr);
return ret;
}
}

View File

@ -0,0 +1,123 @@
package net.lax1dude.eaglercraft.v1_8.internal.buffer;
import org.teavm.interop.Address;
import org.teavm.interop.Unmanaged;
/**
* Copyright (c) 2024 lax1dude. All Rights Reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
public class WASMGCDirectArrayCopy {
@Unmanaged
public static void memcpy(Address dest, byte[] src, int srcOffset, int count) {
Address destEnd = dest.add(count);
while(dest.isLessThan(destEnd)) {
dest.putByte(src[srcOffset]);
++srcOffset;
dest = dest.add(1);
}
}
@Unmanaged
public static void memcpy(Address dest, short[] src, int srcOffset, int count) {
Address destEnd = dest.add(count << 1);
while(dest.isLessThan(destEnd)) {
dest.putShort(src[srcOffset]);
++srcOffset;
dest = dest.add(2);
}
}
@Unmanaged
public static void memcpy(Address dest, char[] src, int srcOffset, int count) {
Address destEnd = dest.add(count << 1);
while(dest.isLessThan(destEnd)) {
dest.putChar(src[srcOffset]);
++srcOffset;
dest = dest.add(2);
}
}
@Unmanaged
public static void memcpy(Address dest, int[] src, int srcOffset, int count) {
Address destEnd = dest.add(count << 2);
while(dest.isLessThan(destEnd)) {
dest.putInt(src[srcOffset]);
++srcOffset;
dest = dest.add(4);
}
}
@Unmanaged
public static void memcpy(Address dest, float[] src, int srcOffset, int count) {
Address destEnd = dest.add(count << 2);
while(dest.isLessThan(destEnd)) {
dest.putFloat(src[srcOffset]);
++srcOffset;
dest = dest.add(4);
}
}
@Unmanaged
public static void memcpy(byte[] dest, int destOffset, Address src, int count) {
Address srcEnd = src.add(count);
while(src.isLessThan(srcEnd)) {
dest[destOffset] = src.getByte();
++destOffset;
src = src.add(1);
}
}
@Unmanaged
public static void memcpy(short[] dest, int destOffset, Address src, int count) {
Address srcEnd = src.add(count << 1);
while(src.isLessThan(srcEnd)) {
dest[destOffset] = src.getShort();
++destOffset;
src = src.add(2);
}
}
@Unmanaged
public static void memcpy(char[] dest, int destOffset, Address src, int count) {
Address srcEnd = src.add(count << 1);
while(src.isLessThan(srcEnd)) {
dest[destOffset] = src.getChar();
++destOffset;
src = src.add(2);
}
}
@Unmanaged
public static void memcpy(int[] dest, int destOffset, Address src, int count) {
Address srcEnd = src.add(count << 2);
while(src.isLessThan(srcEnd)) {
dest[destOffset] = src.getInt();
++destOffset;
src = src.add(4);
}
}
@Unmanaged
public static void memcpy(float[] dest, int destOffset, Address src, int count) {
Address srcEnd = src.add(count << 2);
while(src.isLessThan(srcEnd)) {
dest[destOffset] = src.getFloat();
++destOffset;
src = src.add(4);
}
}
}

View File

@ -0,0 +1,81 @@
package net.lax1dude.eaglercraft.v1_8.internal.wasm_gc_teavm;
import org.teavm.interop.Address;
import org.teavm.interop.DirectMalloc;
import org.teavm.interop.Import;
import org.teavm.interop.Unmanaged;
import org.teavm.jso.core.JSArray;
import org.teavm.jso.core.JSString;
import net.lax1dude.eaglercraft.v1_8.EagUtils;
import net.lax1dude.eaglercraft.v1_8.internal.buffer.WASMGCBufferAllocator;
/**
* Copyright (c) 2024 lax1dude. All Rights Reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
public class BetterJSStringConverter {
private static final TextDecoder textDecoder = new TextDecoder("utf-16");
@Unmanaged
public static JSString stringToJS(String input) {
if(input == null) return null;
int len = input.length();
Address tmpAddr = WASMGCBufferAllocator.malloc(len << 1);
for(int i = 0; i < len; ++i) {
tmpAddr.add(i << 1).putChar(input.charAt(i));
}
JSString ret = textDecoder.decode(WASMGCBufferAllocator.getUnsignedByteBufferView0(tmpAddr, len << 1));
WASMGCBufferAllocator.free(tmpAddr);
return ret;
}
@Unmanaged
public static JSArray<JSString> stringArrayToJS(String[] input) {
if(input == null) return null;
int len = input.length;
JSArray<JSString> ret = new JSArray<>(len);
for(int i = 0; i < len; ++i) {
ret.set(i, stringToJS(input[i]));
}
return ret;
}
@Unmanaged
public static String stringFromJS(JSString input) {
if(input == null) return null;
int len = input.getLength();
char[] chars = new char[len];
for(int i = 0; i < len; ++i) {
chars[i] = charCodeAt(input, i);
}
return new String(chars);
}
@Import(module = "teavmJso", name = "charAt")
private static native char charCodeAt(JSString str, int idx);
@Unmanaged
public static String[] stringArrayFromJS(JSArray<JSString> input) {
if(input == null) return null;
int len = input.getLength();
String[] ret = new String[len];
for(int i = 0; i < len; ++i) {
ret[i] = stringFromJS(input.get(i));
}
return ret;
}
}

View File

@ -0,0 +1,96 @@
package net.lax1dude.eaglercraft.v1_8.internal.wasm_gc_teavm;
import java.io.PrintStream;
import org.teavm.interop.Import;
import org.teavm.jso.JSObject;
import org.teavm.jso.browser.Window;
import net.lax1dude.eaglercraft.v1_8.EagRuntime;
import net.lax1dude.eaglercraft.v1_8.internal.PlatformRuntime;
import net.lax1dude.eaglercraft.v1_8.internal.wasm_gc_teavm.opts.JSEaglercraftXOptsRoot;
import net.minecraft.client.main.Main;
/**
* Copyright (c) 2024 lax1dude. All Rights Reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
public class ClientMain {
private static final PrintStream systemOut = System.out;
private static final PrintStream systemErr = System.err;
public static String configLocalesFolder = null;
public static void _main() {
try {
systemOut.println("ClientMain: [INFO] eaglercraftx wasm gc is starting...");
JSObject opts = getEaglerXOpts();
if(opts == null) {
systemErr.println("ClientMain: [ERROR] the \"window.eaglercraftXOpts\" variable is undefined");
systemErr.println("ClientMain: [ERROR] eaglercraftx cannot start");
Window.alert("ERROR: game cannot start, the \"window.eaglercraftXOpts\" variable is undefined");
return;
}
try {
JSEaglercraftXOptsRoot eaglercraftOpts = (JSEaglercraftXOptsRoot)opts;
configLocalesFolder = eaglercraftOpts.getLocalesURI("lang");
if(configLocalesFolder.endsWith("/")) {
configLocalesFolder = configLocalesFolder.substring(0, configLocalesFolder.length() - 1);
}
((WASMGCClientConfigAdapter)WASMGCClientConfigAdapter.instance).loadNative(eaglercraftOpts);
systemOut.println("ClientMain: [INFO] configuration was successful");
}catch(Throwable t) {
systemErr.println("ClientMain: [ERROR] the \"window.eaglercraftXOpts\" variable is invalid");
EagRuntime.debugPrintStackTraceToSTDERR(t);
systemErr.println("ClientMain: [ERROR] eaglercraftx cannot start");
Window.alert("ERROR: game cannot start, the \"window.eaglercraftXOpts\" variable is invalid: " + t.toString());
return;
}
systemOut.println("ClientMain: [INFO] initializing eaglercraftx runtime");
try {
EagRuntime.create();
}catch(Throwable t) {
systemErr.println("ClientMain: [ERROR] eaglercraftx's runtime could not be initialized!");
EagRuntime.debugPrintStackTraceToSTDERR(t);
PlatformRuntime.writeCrashReport("EaglercraftX's runtime could not be initialized!\n\n" + EagRuntime.getStackTrace(t));
systemErr.println("ClientMain: [ERROR] eaglercraftx cannot start");
return;
}
systemOut.println("ClientMain: [INFO] launching eaglercraftx main thread");
try {
Main.appMain();
}catch(Throwable t) {
systemErr.println("ClientMain: [ERROR] unhandled exception caused main thread to exit");
EagRuntime.debugPrintStackTraceToSTDERR(t);
PlatformRuntime.writeCrashReport("Unhandled exception caused main thread to exit!\n\n" + EagRuntime.getStackTrace(t));
}
}finally {
systemErr.println("ClientMain: [ERROR] eaglercraftx main thread has exited");
}
}
@Import(module = "platformRuntime", name = "getEaglercraftXOpts")
private static native JSObject getEaglerXOpts();
}

View File

@ -0,0 +1,191 @@
package net.lax1dude.eaglercraft.v1_8.internal.wasm_gc_teavm;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import com.jcraft.jzlib.CRC32;
import com.jcraft.jzlib.GZIPInputStream;
import com.jcraft.jzlib.InflaterInputStream;
import net.lax1dude.eaglercraft.v1_8.internal.buffer.ByteBuffer;
import net.lax1dude.eaglercraft.v1_8.internal.buffer.EaglerBufferInputStream;
/**
* Copyright (c) 2022-2024 lax1dude. All Rights Reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
public class EPKLoader {
public static final void loadEPK(ByteBuffer epkFile, Map<String, byte[]> loadedFiles) throws IOException {
loadEPK(epkFile, "", loadedFiles);
}
public static final void loadEPK(ByteBuffer epkFile, String path, Map<String, byte[]> loadedFiles) throws IOException {
int byteLength = epkFile.remaining();
int l = byteLength - 16;
if(l < 1) {
throw new IOException("EPK file is incomplete");
}
EaglerBufferInputStream is = new EaglerBufferInputStream(epkFile);
byte[] header = new byte[8];
is.read(header);
String type = readASCII(header);
if(!"EAGPKG$$".equals(type)) {
throw new IOException("Invalid EPK file type '" + type + "'");
}
byte[] endCode = new byte[] { (byte)':', (byte)':', (byte)':', (byte)'Y',
(byte)'E', (byte)'E', (byte)':', (byte)'>' };
for(int i = 0; i < 8; ++i) {
if(epkFile.get(byteLength - 8 + i) != endCode[i]) {
throw new IOException("EPK file is missing EOF code (:::YEE:>)");
}
}
epkFile.limit(epkFile.limit() - 8);
String vers = readASCII(is);
if(!vers.startsWith("ver2.")) {
throw new IOException("Unknown or invalid EPK version: " + vers);
}
is.skip(is.read()); // skip filename
is.skip(loadShort(is)); // skip comment
is.skip(8); // skip millis date
int numFiles = loadInt(is);
char compressionType = (char)is.read();
InputStream zis;
switch(compressionType) {
case 'G':
zis = new GZIPInputStream(is);
break;
case 'Z':
zis = new InflaterInputStream(is);
break;
case '0':
zis = is;
break;
default:
throw new IOException("Invalid or unsupported EPK compression: " + compressionType);
}
int blockFile = ('F' << 24) | ('I' << 16) | ('L' << 8) | 'E';
int blockEnd = ('E' << 24) | ('N' << 16) | ('D' << 8) | '$';
int blockHead = ('H' << 24) | ('E' << 16) | ('A' << 8) | 'D';
if(path.length() > 0 && !path.endsWith("/")) {
path = path + "/";
}
CRC32 crc32 = new CRC32();
int blockType;
for(int i = 0; i < numFiles; ++i) {
blockType = loadInt(zis);
if(blockType == blockEnd) {
throw new IOException("Unexpected END when there are still " + (numFiles - i) + " files remaining");
}
String name = readASCII(zis);
int len = loadInt(zis);
if(i == 0) {
if(blockType == blockHead) {
byte[] readType = new byte[len];
zis.read(readType);
if(!"file-type".equals(name) || !"epk/resources".equals(readASCII(readType))) {
throw new IOException("EPK is not of file-type 'epk/resources'!");
}
if(zis.read() != '>') {
throw new IOException("Object '" + name + "' is incomplete");
}
continue;
}else {
throw new IOException("File '" + name + "' did not have a file-type block as the first entry in the file");
}
}
if(blockType == blockFile) {
if(len < 5) {
throw new IOException("File '" + name + "' is incomplete");
}
int expectedCRC = loadInt(zis);
byte[] load = new byte[len - 5];
zis.read(load);
if(len > 5) {
crc32.reset();
crc32.update(load, 0, load.length);
if(expectedCRC != (int)crc32.getValue()) {
throw new IOException("File '" + name + "' has an invalid checksum");
}
}
if(zis.read() != ':') {
throw new IOException("File '" + name + "' is incomplete");
}
loadedFiles.put(path + name, load);
}else {
zis.skip(len);
}
if(zis.read() != '>') {
throw new IOException("Object '" + name + "' is incomplete");
}
}
if(loadInt(zis) != blockEnd) {
throw new IOException("EPK missing END$ object");
}
zis.close();
}
private static final int loadShort(InputStream is) throws IOException {
return (is.read() << 8) | is.read();
}
private static final int loadInt(InputStream is) throws IOException {
return (is.read() << 24) | (is.read() << 16) | (is.read() << 8) | is.read();
}
private static final String readASCII(byte[] bytesIn) throws IOException {
char[] charIn = new char[bytesIn.length];
for(int i = 0; i < bytesIn.length; ++i) {
charIn[i] = (char)((int)bytesIn[i] & 0xFF);
}
return new String(charIn);
}
private static final String readASCII(InputStream bytesIn) throws IOException {
int len = bytesIn.read();
char[] charIn = new char[len];
for(int i = 0; i < len; ++i) {
charIn[i] = (char)(bytesIn.read() & 0xFF);
}
return new String(charIn);
}
}

View File

@ -0,0 +1,262 @@
package net.lax1dude.eaglercraft.v1_8.internal.wasm_gc_teavm;
import static net.lax1dude.eaglercraft.v1_8.internal.PlatformOpenGL.*;
import static net.lax1dude.eaglercraft.v1_8.opengl.RealOpenGLEnums.*;
import net.lax1dude.eaglercraft.v1_8.Base64;
import net.lax1dude.eaglercraft.v1_8.internal.IBufferArrayGL;
import net.lax1dude.eaglercraft.v1_8.internal.IBufferGL;
import net.lax1dude.eaglercraft.v1_8.internal.IProgramGL;
import net.lax1dude.eaglercraft.v1_8.internal.IShaderGL;
import net.lax1dude.eaglercraft.v1_8.internal.ITextureGL;
import net.lax1dude.eaglercraft.v1_8.internal.PlatformAssets;
import net.lax1dude.eaglercraft.v1_8.internal.PlatformInput;
import net.lax1dude.eaglercraft.v1_8.internal.PlatformRuntime;
import net.lax1dude.eaglercraft.v1_8.internal.buffer.ByteBuffer;
import net.lax1dude.eaglercraft.v1_8.internal.buffer.FloatBuffer;
import net.lax1dude.eaglercraft.v1_8.internal.buffer.IntBuffer;
import net.lax1dude.eaglercraft.v1_8.opengl.EaglercraftGPU;
import net.lax1dude.eaglercraft.v1_8.opengl.ImageData;
/**
* Copyright (c) 2022-2024 lax1dude. All Rights Reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
public class EarlyLoadScreen {
public static final String loadScreen = "iVBORw0KGgoAAAANSUhEUgAAAMAAAADACAYAAABS3GwHAAAACXBIWXMAAAsTAAALEwEAmpwYAAAHx0lEQVR42u3da27jIBRAYbfqFp1FuovM/GLEMIDBhsRJviNVapsYY8y5vPz4ut/v9wX4UL4VAQgAEAAgAEAAgAAAAQACAAQACAAQACAAQACAAAABAAIABAAIABAAIABAAIAAAAEAAgAEAAgAEAAgAEAAgAAAAQACAAQACAAQACAAQACAAAABAAIABAAIABAAIABAAIAAAAEAAgAEAAgAEAAgAAgAEAAgAEAAgAAAAQACAAQACAAQACAAQACAAMBr86MI3ovf39/i/9Z1XdZ1VUgEeN/Kf7vdqt8hgC7QW6OCE+CjK/+2bcv9fieCLtDjux9x/1t/u1xOveWSlisBXmQASoB/+fr6+vv7/X7vHteE8hxZrrpAkyo/2mU42soSgAAfN8YZ3aoSQOV/GNu2ZX9vGdjPEuBnVmXIVYqePly8famCne0TtuS1tt/a9kfSbWnqZw2u9yQesc91XZv7/iO2a+I+iG3b7uu63pdl2f1Z17WaTksaaXrbtk3JaynvR/O5l6/WtPaON3d8tf3v7e9d+RkVPeIVyDRKpREtfL+nGdxL7/f3d9m2bTdS5VZL4/Rz0fcRszm32604jZrLUyi/UXlb1/WlunKhTE63iCMif0tkao1IaXqlqFWKlr2RsTUPpXRLrUnYpqVlircfdby9LUCpbHpa1lyeW8tgL51SmZ9N+2dE5GqJlrkI0xJxaumV0ixt0xrd07TDdrl+aDoeGNnfbzne0RE1HqSOaF3SljptyXP7qF3QN3zi4Yw9LdF0r5+Zs7u175mLirU85KJiLbK3pt2bj1qZ1CJaz356WoD0u2ejaq11XNf1708uf73jqqeOAXotbIlgZ/t0tfSPRulZ050j0jubRjz2CGU/clyRRvvwv1LPIR4X5r6TtlJPmwY9W5la54vfea5+Zhm2dnniyj+j3GtdxCsMzL+vWAmuyujK2dLXnVGGYSZsduXPlV0625Vbk0nlnFlXhrYAezdjPFOa2sD4GRetlY5hdhnmpoHjKcXZlb927Llp4JCvWYHy8leDxpHgbCH0zBo9s3vyiLK8QiBIxwiPaHWnjwFGZbjl9r5RAtxut92Fp5GLTqPHP735qpXDrK5QbjFz27b/Wp802IXu2Yz6cGoadDmwCHV0enVJFpbCfkqLQ6Mvg9g7riPToEfyfrYMl4ZLOUadw1rZh33H/ytNjcbnunfavakeX02As3P1rZVoT4KeVdBXESDN05HV4pFXDaQrxqkE6TnISfC0dYAZA5PSSu3orkeYiSil/Sl3cm3b9t+NKbMHxHtTpenvcT7C33Gez+b1e3QFvvrUY2nhZ/Qi0KtMC+f6/KWpytnnsjWoXuKWyNaZkyud/HTh55mVvTYt++h8zDiXlTFnkwS1wfhlBZgxj917acNe9H9mZWuJvjPuez0azJ5RPj1T3kMe/zJyUNMzkMpdJts6MNybyckNXo/cwLI0XtZ8ZkaldBwt2x65RHvGMRwZoO9dWLh3CfqofC0zZhtKU5fpiWkVIE4n3b423Zemf0SA5cQdVenxt9x70FJ+8TEfkbxUuXqDytnp0L2p0kewzJjeOnMSWtKKt92rQCNageXEDTot05xH1iZy5Xf2lsra9iMrZDjW2dG9ha/7wLuNS5ctpDevt9y2WBu0ptvnxh2l75YutOrtu+/1m+N8tw66022PlGHrcfVuP+NCwNrg+2ETFPcPI45yLSu8s1Yg8UY3xb8K6WP2WualrzJjhDl8f2Ll721iPeiWAG8hwMw+LQhw6co/cpWaPO/DR4wBchU23APQMiMy43EhuAZDp0FfaQxwRCJjAQK8xTigp0uk4hPgowbH+vkEAD4GL8gAAQACAAQACAAQACAAQACAAAABAAIABAAIABAAIABAAIAAAAEAAgAEAK7NJR6M9S6PLQzPHZr1sulSuXmCxQu3APHz+sNP6wOspr09/CL76ym3Tzr2t2sBHhk13+UYwgsmnvFeXwI8qUtRinZxZNq27e/3tm3Lvg8gjWRpxc09Rj3eb2l/ufTiZ5CG78Sfn305eO7durX8tH4W8pB+Pz32vTQJcGAcED+0Nv5//Pbw9GTl+sKh8sVRMo2WoWkPJy0WpiRB6XVFpa5IvF28v3RfvX36mpylBwKXPktbkjiI1I69liYBTg6E4wqTkyOWolRB4nTSE5XuszaI3dvfngRppM1F+9auTG4fuW1raeXendYiWk+aBBjQf44jZW/TWoriV3gRddwi9L57IPfY9lA5Q3nF6YZyq33WIkLt/NTSJMCAcUD4/Wzhxt2o3Hjg0a3emSdPt7Q2t9vtn3KrfXY0L7U091rWo599xBggjSgh0pSa79aTl4ugaR8913qU9ld6vWlvd6bn+7mB+96MUHpcLULtHftemlqAAwKEwVd6MtNBbK4C7kWLuMkuDT5zA+za/nKzMC0VOu0CtXQhal2UeKCfG2PUPsvNZrUcey3NV8Dj0Z/cvctNQ77DmogWAM0S7M0gQQvwluS6HFZ0CQA8DJdDgwAAAQACAAQACAAQACAAQACAAAABAAIABAAIABAAIABAAIAAAAEAAgAEAAgAEAAgAEAAgAAAAQACAAQACAAQACAAQACAAAABAAIABAAIABAAIABAAIAAAAEAAgAEAAgAEAAgAEAAgAAAAYBlWf4A1W4Hx65cJAoAAAAASUVORK5CYII=";
private static IBufferGL vbo = null;
private static IProgramGL program = null;
private static ITextureGL finalTexture = null;
public static void extractingAssetsScreen() {
boolean gles3 = checkOpenGLESVersion() >= 300;
boolean vaos = checkVAOCapable();
// create textures:
ITextureGL tex = _wglGenTextures();
_wglActiveTexture(GL_TEXTURE0);
_wglBindTexture(GL_TEXTURE_2D, tex);
_wglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
_wglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
_wglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
_wglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
ImageData img = PlatformAssets.loadImageFile(Base64.decodeBase64(loadScreen));
ByteBuffer upload = PlatformRuntime.allocateByteBuffer(192*192*4);
IntBuffer pixelUpload = upload.asIntBuffer();
pixelUpload.put(img.pixels);
pixelUpload.flip();
_wglTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 192, 192, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixelUpload);
// create vertex buffer:
FloatBuffer vertexUpload = upload.asFloatBuffer();
vertexUpload.clear();
vertexUpload.put(0.0f); vertexUpload.put(0.0f);
vertexUpload.put(0.0f); vertexUpload.put(1.0f);
vertexUpload.put(1.0f); vertexUpload.put(0.0f);
vertexUpload.put(1.0f); vertexUpload.put(0.0f);
vertexUpload.put(0.0f); vertexUpload.put(1.0f);
vertexUpload.put(1.0f); vertexUpload.put(1.0f);
vertexUpload.flip();
vbo = _wglGenBuffers();
_wglBindBuffer(GL_ARRAY_BUFFER, vbo);
_wglBufferData(GL_ARRAY_BUFFER, vertexUpload, GL_STATIC_DRAW);
PlatformRuntime.freeByteBuffer(upload);
// compile the splash shader:
IShaderGL vert = _wglCreateShader(GL_VERTEX_SHADER);
_wglShaderSource(vert, gles3
? "#version 300 es\nprecision mediump float; layout(location = 0) in vec2 a_pos; out vec2 v_pos; void main() { gl_Position = vec4(((v_pos = a_pos) - 0.5) * vec2(2.0, -2.0), 0.0, 1.0); }"
: "#version 100\nprecision mediump float; attribute vec2 a_pos; varying vec2 v_pos; void main() { gl_Position = vec4(((v_pos = a_pos) - 0.5) * vec2(2.0, -2.0), 0.0, 1.0); }");
_wglCompileShader(vert);
IShaderGL frag = _wglCreateShader(GL_FRAGMENT_SHADER);
_wglShaderSource(frag, gles3
? "#version 300 es\nprecision mediump float; precision mediump sampler2D; in vec2 v_pos; layout(location = 0) out vec4 fragColor; uniform sampler2D tex; uniform vec2 aspect; void main() { fragColor = vec4(textureLod(tex, clamp(v_pos * aspect - ((aspect - 1.0) * 0.5), 0.02, 0.98), 0.0).rgb, 1.0); }"
: "#version 100\nprecision mediump float; precision mediump sampler2D; varying vec2 v_pos; uniform sampler2D tex; uniform vec2 aspect; void main() { gl_FragColor = vec4(texture2D(tex, clamp(v_pos * aspect - ((aspect - 1.0) * 0.5), 0.02, 0.98)).rgb, 1.0); }");
_wglCompileShader(frag);
program = _wglCreateProgram();
_wglAttachShader(program, vert);
_wglAttachShader(program, frag);
if(!gles3) {
_wglBindAttribLocation(program, 0, "a_pos");
}
_wglLinkProgram(program);
_wglDetachShader(program, vert);
_wglDetachShader(program, frag);
_wglDeleteShader(vert);
_wglDeleteShader(frag);
_wglUseProgram(program);
_wglUniform1i(_wglGetUniformLocation(program, "tex"), 0);
int width = PlatformInput.getWindowWidth();
int height = PlatformInput.getWindowHeight();
float x, y;
if(width > height) {
x = (float)width / (float)height;
y = 1.0f;
}else {
x = 1.0f;
y = (float)height / (float)width;
}
_wglActiveTexture(GL_TEXTURE0);
_wglBindTexture(GL_TEXTURE_2D, tex);
_wglViewport(0, 0, width, height);
_wglClearColor(1.0f, 1.0f, 1.0f, 1.0f);
_wglClear(GL_COLOR_BUFFER_BIT);
_wglUseProgram(program);
_wglUniform2f(_wglGetUniformLocation(program, "aspect"), x, y);
IBufferArrayGL vao = null;
if(vaos) {
vao = _wglGenVertexArrays();
_wglBindVertexArray(vao);
}
_wglEnableVertexAttribArray(0);
_wglVertexAttribPointer(0, 2, GL_FLOAT, false, 8, 0);
_wglDrawArrays(GL_TRIANGLES, 0, 6);
_wglDisableVertexAttribArray(0);
PlatformInput.update();
_wglUseProgram(null);
_wglBindBuffer(GL_ARRAY_BUFFER, null);
_wglBindTexture(GL_TEXTURE_2D, null);
_wglDeleteTextures(tex);
if(vaos) {
_wglDeleteVertexArrays(vao);
}
}
public static void loadFinal(byte[] finalLoadScreen) {
ImageData img = PlatformAssets.loadImageFile(finalLoadScreen);
if(img == null) {
return;
}
finalTexture = _wglGenTextures();
_wglActiveTexture(GL_TEXTURE0);
_wglBindTexture(GL_TEXTURE_2D, finalTexture);
_wglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
_wglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
_wglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
_wglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
IntBuffer upload = PlatformRuntime.allocateIntBuffer(img.width * img.height);
upload.put(img.pixels);
upload.flip();
_wglTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, img.width, img.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, upload);
PlatformRuntime.freeIntBuffer(upload);
}
public static void paintFinal(boolean softVAOs) {
if(finalTexture == null) return;
boolean vaos = checkVAOCapable();
_wglBindTexture(GL_TEXTURE_2D, finalTexture);
_wglUseProgram(program);
int width = PlatformInput.getWindowWidth();
int height = PlatformInput.getWindowHeight();
float x, y;
if(width > height) {
x = (float)width / (float)height;
y = 1.0f;
}else {
x = 1.0f;
y = (float)height / (float)width;
}
_wglActiveTexture(GL_TEXTURE0);
_wglBindTexture(GL_TEXTURE_2D, finalTexture);
_wglViewport(0, 0, width, height);
_wglClearColor(1.0f, 1.0f, 1.0f, 1.0f);
_wglClear(GL_COLOR_BUFFER_BIT);
_wglUniform2f(_wglGetUniformLocation(program, "aspect"), x, y);
IBufferArrayGL vao = null;
if(vaos) {
if(softVAOs) {
vao = EaglercraftGPU.createGLBufferArray();
EaglercraftGPU.bindGLBufferArray(vao);
}else {
vao = _wglGenVertexArrays();
_wglBindVertexArray(vao);
}
}
if(vaos && softVAOs) {
EaglercraftGPU.bindVAOGLArrayBuffer(vbo);
EaglercraftGPU.enableVertexAttribArray(0);
EaglercraftGPU.vertexAttribPointer(0, 2, GL_FLOAT, false, 8, 0);
EaglercraftGPU.doDrawArrays(GL_TRIANGLES, 0, 6);
}else {
_wglBindBuffer(GL_ARRAY_BUFFER, vbo);
_wglEnableVertexAttribArray(0);
_wglVertexAttribPointer(0, 2, GL_FLOAT, false, 8, 0);
_wglDrawArrays(GL_TRIANGLES, 0, 6);
}
if(!softVAOs) {
_wglDisableVertexAttribArray(0);
}
PlatformInput.update();
_wglUseProgram(null);
if(!(vaos && softVAOs)) {
_wglBindBuffer(GL_ARRAY_BUFFER, null);
}
_wglBindTexture(GL_TEXTURE_2D, null);
if(softVAOs) {
EaglercraftGPU.clearCurrentBinding(EaglercraftGPU.CLEAR_BINDING_ACTIVE_TEXTURE | EaglercraftGPU.CLEAR_BINDING_TEXTURE0);
}
if(vaos) {
if(softVAOs) {
EaglercraftGPU.destroyGLBufferArray(vao);
}else {
_wglDeleteVertexArrays(vao);
}
}
}
public static void destroy() {
if(vbo != null) {
_wglDeleteBuffers(vbo);
vbo = null;
}
if(program != null) {
_wglDeleteProgram(program);
program = null;
}
if(finalTexture != null) {
_wglDeleteTextures(finalTexture);
finalTexture = null;
}
}
}

View File

@ -0,0 +1,199 @@
package net.lax1dude.eaglercraft.v1_8.internal.wasm_gc_teavm;
import org.teavm.interop.Import;
import org.teavm.jso.JSObject;
import org.teavm.jso.JSProperty;
import org.teavm.jso.core.JSString;
import org.teavm.jso.indexeddb.IDBDatabase;
import org.teavm.jso.typedarrays.ArrayBuffer;
import org.teavm.jso.typedarrays.Uint8Array;
import net.lax1dude.eaglercraft.v1_8.internal.IEaglerFilesystem;
import net.lax1dude.eaglercraft.v1_8.internal.PlatformRuntime;
import net.lax1dude.eaglercraft.v1_8.internal.VFSFilenameIterator;
import net.lax1dude.eaglercraft.v1_8.internal.buffer.ByteBuffer;
import net.lax1dude.eaglercraft.v1_8.internal.buffer.WASMGCBufferAllocator;
import net.lax1dude.eaglercraft.v1_8.internal.vfs2.EaglerFileSystemException;
import net.lax1dude.eaglercraft.v1_8.internal.PlatformFilesystem.FilesystemDatabaseInitializationException;
import net.lax1dude.eaglercraft.v1_8.internal.PlatformFilesystem.FilesystemDatabaseLockedException;
/**
* Copyright (c) 2024 lax1dude. All Rights Reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
public class IndexedDBFilesystem implements IEaglerFilesystem {
public static IEaglerFilesystem createFilesystem(String dbName) {
String filesystemDB = "_net_lax1dude_eaglercraft_v1_8_internal_PlatformFilesystem_1_8_8_" + dbName;
JSDatabaseOpen dbOpen = openDB(BetterJSStringConverter.stringToJS(filesystemDB));
if(dbOpen.getFailedLocked()) {
throw new FilesystemDatabaseLockedException(dbOpen.getFailedError());
}
if(dbOpen.getFailedInit()) {
throw new FilesystemDatabaseInitializationException(dbOpen.getFailedError());
}
IDBDatabase database = dbOpen.getDatabase();
if(database == null) {
throw new NullPointerException("IDBDatabase is null!");
}
return new IndexedDBFilesystem(dbName, filesystemDB, database);
}
private interface JSDatabaseOpen extends JSObject {
@JSProperty
boolean getFailedInit();
@JSProperty
boolean getFailedLocked();
@JSProperty
String getFailedError();
@JSProperty
IDBDatabase getDatabase();
}
@Import(module = "platformFilesystem", name = "openDB")
private static native JSDatabaseOpen openDB(JSString filesystemDB);
private final String name;
private final String indexedDBName;
private IDBDatabase database;
private IndexedDBFilesystem(String name, String indexedDBName, IDBDatabase database) {
this.name = name;
this.indexedDBName = indexedDBName;
this.database = database;
}
@Override
public String getFilesystemName() {
return name;
}
@Override
public String getInternalDBName() {
return "indexeddb:" + indexedDBName;
}
@Override
public boolean isRamdisk() {
return false;
}
@Override
public boolean eaglerDelete(String pathName) {
return eaglerDelete(database, BetterJSStringConverter.stringToJS(pathName));
}
@Import(module = "platformFilesystem", name = "eaglerDelete")
private static native boolean eaglerDelete(IDBDatabase database, JSString pathName);
@Override
public ByteBuffer eaglerRead(String pathName) {
ArrayBuffer ar = eaglerRead(database, BetterJSStringConverter.stringToJS(pathName));
if(ar == null) {
return null;
}
Uint8Array arr = new Uint8Array(ar);
ByteBuffer buf = PlatformRuntime.allocateByteBuffer(arr.getLength());
WASMGCBufferAllocator.getUnsignedByteBufferView(buf).set(arr);
return buf;
}
@Import(module = "platformFilesystem", name = "eaglerRead")
private static native ArrayBuffer eaglerRead(IDBDatabase database, JSString pathName);
@Override
public void eaglerWrite(String pathName, ByteBuffer data) {
int len = data.remaining();
Uint8Array arr = new Uint8Array(len);
arr.set(WASMGCBufferAllocator.getByteBufferView(data));
if(!eaglerWrite(database, BetterJSStringConverter.stringToJS(pathName), arr.getBuffer())) {
throw new EaglerFileSystemException("Failed to write " + len + " byte file to indexeddb table: " + pathName);
}
}
@Import(module = "platformFilesystem", name = "eaglerWrite")
private static native boolean eaglerWrite(IDBDatabase database, JSString pathName, ArrayBuffer arr);
@Override
public boolean eaglerExists(String pathName) {
return eaglerExists(database, BetterJSStringConverter.stringToJS(pathName));
}
@Import(module = "platformFilesystem", name = "eaglerExists")
private static native boolean eaglerExists(IDBDatabase database, JSString pathName);
@Override
public boolean eaglerMove(String pathNameOld, String pathNameNew) {
return eaglerMove(database, BetterJSStringConverter.stringToJS(pathNameOld), BetterJSStringConverter.stringToJS(pathNameNew));
}
@Import(module = "platformFilesystem", name = "eaglerMove")
private static native boolean eaglerMove(IDBDatabase database, JSString pathNameOld, JSString pathNameNew);
@Override
public int eaglerCopy(String pathNameOld, String pathNameNew) {
return eaglerCopy(database, BetterJSStringConverter.stringToJS(pathNameOld), BetterJSStringConverter.stringToJS(pathNameNew));
}
@Import(module = "platformFilesystem", name = "eaglerCopy")
private static native int eaglerCopy(IDBDatabase database, JSString pathNameOld, JSString pathNameNew);
@Override
public int eaglerSize(String pathName) {
return eaglerSize(database, BetterJSStringConverter.stringToJS(pathName));
}
@Import(module = "platformFilesystem", name = "eaglerSize")
private static native int eaglerSize(IDBDatabase database, JSString pathName);
private interface JSDatabaseIteratorResult extends JSObject {
@JSProperty
int getLength();
String getRow(int idx);
}
@Override
public void eaglerIterate(String pathName, VFSFilenameIterator itr, boolean recursive) {
JSDatabaseIteratorResult result = eaglerIterate(database, BetterJSStringConverter.stringToJS(pathName), recursive);
if(result != null) {
for(int i = 0, len = result.getLength(); i < len; ++i) {
itr.next(result.getRow(i));
}
}
}
@Import(module = "platformFilesystem", name = "eaglerIterate")
private static native JSDatabaseIteratorResult eaglerIterate(IDBDatabase database, JSString pathName, boolean recursive);
@Override
public void closeHandle() {
if(database != null) {
database.close();
database = null;
}
}
}

View File

@ -0,0 +1,384 @@
package net.lax1dude.eaglercraft.v1_8.internal.wasm_gc_teavm;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
import org.teavm.jso.webaudio.AudioBuffer;
import com.jcraft.jogg.Packet;
import com.jcraft.jogg.Page;
import com.jcraft.jogg.StreamState;
import com.jcraft.jogg.SyncState;
import com.jcraft.jorbis.Block;
import com.jcraft.jorbis.Comment;
import com.jcraft.jorbis.DspState;
import com.jcraft.jorbis.Info;
import net.lax1dude.eaglercraft.v1_8.EaglerInputStream;
import net.lax1dude.eaglercraft.v1_8.internal.PlatformAudio;
import net.lax1dude.eaglercraft.v1_8.internal.PlatformRuntime;
import net.lax1dude.eaglercraft.v1_8.internal.buffer.FloatBuffer;
import net.lax1dude.eaglercraft.v1_8.internal.buffer.WASMGCBufferAllocator;
import net.lax1dude.eaglercraft.v1_8.log4j.LogManager;
import net.lax1dude.eaglercraft.v1_8.log4j.Logger;
/**
* Copyright (c) 2024 lax1dude. All Rights Reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
public class JOrbisAudioBufferDecoder {
private EaglerInputStream inputStream;
private boolean endOfStream = false;
private byte[] buffer = null;
private int bufferSize;
private int count = 0;
private int index = 0;
private float[][] convertedBuffer = null;
private float[][][] pcmInfo;
private int[] pcmIndex;
private Packet joggPacket = new Packet();
private Page joggPage = new Page();
private StreamState joggStreamState = new StreamState();
private SyncState joggSyncState = new SyncState();
private DspState jorbisDspState = new DspState();
private Block jorbisBlock;
private Comment jorbisComment;
private Info jorbisInfo;
private String errorString;
private static final Logger logger = LogManager.getLogger("JOrbisAudioBufferDecoder");
private static final JOrbisAudioBufferDecoder instance = new JOrbisAudioBufferDecoder();
public static AudioBuffer decodeAudioJOrbis(byte[] data, String errorString) {
JOrbisAudioBufferDecoder dec = instance;
synchronized(dec) {
if(!dec.init(data, errorString)) {
logger.error("[{}]: Invalid header detected", errorString);
return null;
}
int ch = -1;
int len = 0;
List<float[][]> lst = new LinkedList<>();
float[][] b;
while((b = dec.readBytes()) != null) {
if(ch == -1) {
ch = b.length;
}
len += b[0].length;
lst.add(b);
}
if(dec.jorbisInfo.channels != ch) {
logger.warn("[{}]: Number of channels in header does not match the stream", errorString);
}
if(ch == -1 || len == 0) {
logger.error("[{}]: Empty file", errorString);
return null;
}
FloatBuffer buf = PlatformRuntime.allocateFloatBuffer(ch * len);
try {
int len2 = 0;
for(float[][] fl : lst) {
for(int i = 0; i < ch; ++i) {
buf.position(i * len + len2);
buf.put(fl[i]);
}
len2 += fl[0].length;
}
buf.clear();
return PlatformAudio.decodeAudioBufferPCMBrowser(WASMGCBufferAllocator.getFloatBufferView(buf), ch,
len, dec.jorbisInfo.rate);
}finally {
PlatformRuntime.freeFloatBuffer(buf);
}
}
}
private JOrbisAudioBufferDecoder() {
this.jorbisBlock = new Block(this.jorbisDspState);
this.jorbisComment = new Comment();
this.jorbisInfo = new Info();
}
private boolean init(byte[] data, String errorString) {
this.inputStream = new EaglerInputStream(data);
this.errorString = errorString;
if (this.joggStreamState != null) {
this.joggStreamState.clear();
}
if (this.jorbisBlock != null) {
this.jorbisBlock.clear();
}
if (this.jorbisDspState != null) {
this.jorbisDspState.clear();
}
if (this.jorbisInfo != null) {
this.jorbisInfo.clear();
}
if (this.joggSyncState != null) {
this.joggSyncState.clear();
}
if (this.inputStream != null) {
try {
this.inputStream.close();
} catch (IOException var7) {
}
}
this.bufferSize = 8192;
this.buffer = null;
this.count = 0;
this.index = 0;
this.joggStreamState = new StreamState();
this.jorbisBlock = new Block(this.jorbisDspState);
this.jorbisDspState = new DspState();
this.jorbisInfo = new Info();
this.joggSyncState = new SyncState();
this.endOfStream = false;
this.joggSyncState.init();
this.joggSyncState.buffer(this.bufferSize);
this.buffer = this.joggSyncState.data;
vigg: {
this.index = this.joggSyncState.buffer(this.bufferSize);
int bytes = this.inputStream.read(this.joggSyncState.data, this.index, this.bufferSize);
if (bytes < 0) {
bytes = 0;
}
this.joggSyncState.wrote(bytes);
if (this.joggSyncState.pageout(this.joggPage) != 1) {
if (bytes < this.bufferSize) {
break vigg;
} else {
logger.error("[{}]: Ogg header not recognized in method 'readHeader'.", errorString);
return false;
}
} else {
this.joggStreamState.init(this.joggPage.serialno());
this.jorbisInfo.init();
this.jorbisComment.init();
if (this.joggStreamState.pagein(this.joggPage) < 0) {
logger.error("[{}]: Problem with first Ogg header page in method 'readHeader'.", errorString);
return false;
} else if (this.joggStreamState.packetout(this.joggPacket) != 1) {
logger.error("[{}]: Problem with first Ogg header packet in method 'readHeader'.", errorString);
return false;
} else if (this.jorbisInfo.synthesis_headerin(this.jorbisComment, this.joggPacket) < 0) {
logger.error("[{}]: File does not contain Vorbis header in method 'readHeader'.", errorString);
return false;
} else {
int i = 0;
while (i < 2) {
label73: while (true) {
int result;
do {
if (i >= 2) {
break label73;
}
result = this.joggSyncState.pageout(this.joggPage);
if (result == 0) {
break label73;
}
} while (result != 1);
this.joggStreamState.pagein(this.joggPage);
while (i < 2) {
result = this.joggStreamState.packetout(this.joggPacket);
if (result == 0) {
break;
}
if (result == -1) {
logger.error("[{}]: Secondary Ogg header corrupt in method 'readHeader'.", errorString);
return false;
}
this.jorbisInfo.synthesis_headerin(this.jorbisComment, this.joggPacket);
++i;
}
}
this.index = this.joggSyncState.buffer(this.bufferSize);
bytes = this.inputStream.read(this.joggSyncState.data, this.index, this.bufferSize);
if (bytes < 0) {
bytes = 0;
}
if (bytes == 0 && i < 2) {
logger.error(
"[{}]: End of file reached before finished reading Ogg header in method 'readHeader'",
errorString);
return false;
}
this.joggSyncState.wrote(bytes);
}
this.index = this.joggSyncState.buffer(this.bufferSize);
this.buffer = this.joggSyncState.data;
}
}
}
this.jorbisDspState.synthesis_init(this.jorbisInfo);
this.jorbisBlock.init(this.jorbisDspState);
int channels = this.jorbisInfo.channels;
int rate = this.jorbisInfo.rate;
this.pcmInfo = new float[1][][];
this.pcmIndex = new int[channels];
if(convertedBuffer == null || convertedBuffer.length != this.jorbisInfo.channels || (convertedBuffer.length > 0 && convertedBuffer[0].length != this.bufferSize)) {
this.convertedBuffer = new float[this.jorbisInfo.channels][this.bufferSize];
}
return true;
}
private float[][] readBytes() {
if (this.endOfStream) {
return null;
} else {
float[][] returnBuffer = null;
switch (this.joggSyncState.pageout(this.joggPage)) {
default:
this.joggStreamState.pagein(this.joggPage);
if (this.joggPage.granulepos() == 0L) {
this.endOfStream = true;
return null;
} else {
label99: {
while (true) {
switch (this.joggStreamState.packetout(this.joggPacket)) {
case -1:
break;
case 0:
if (this.joggPage.eos() != 0) {
this.endOfStream = true;
}
break label99;
default:
if (this.jorbisBlock.synthesis(this.joggPacket) == 0) {
this.jorbisDspState.synthesis_blockin(this.jorbisBlock);
}
int samples;
while ((samples = this.jorbisDspState.synthesis_pcmout(this.pcmInfo,
this.pcmIndex)) > 0) {
float[][] pcmf = this.pcmInfo[0];
int bout = samples < bufferSize ? samples : this.bufferSize;
for (int i = 0; i < this.jorbisInfo.channels; ++i) {
float[] f1 = convertedBuffer[i];
float[] f2 = pcmf[i];
int mono = this.pcmIndex[i];
for (int j = 0; j < bout; ++j) {
f1[j] = f2[mono + j];
}
}
this.jorbisDspState.synthesis_read(bout);
returnBuffer = appendFloatArrays(returnBuffer, this.convertedBuffer, bout);
}
}
}
}
}
case -1:
case 0:
if (!this.endOfStream) {
this.index = this.joggSyncState.buffer(this.bufferSize);
this.buffer = this.joggSyncState.data;
try {
this.count = this.inputStream.read(this.buffer, this.index, this.bufferSize);
} catch (Exception var11) {
return null;
}
if (this.count == -1) {
return returnBuffer;
}
this.joggSyncState.wrote(this.count);
if (this.count == 0) {
this.endOfStream = true;
}
}
return returnBuffer;
}
}
}
private static float[][] appendFloatArrays(float[][] arrayOne, float[][] arrayTwo, int arrayTwoBytes) {
int bytes = arrayTwoBytes;
int l;
if (arrayTwo != null && (l = arrayTwo[0].length) != 0) {
if (l < arrayTwoBytes) {
bytes = l;
}
} else {
bytes = 0;
}
if ((arrayOne != null || arrayTwo != null) && bytes > 0) {
float[][] newArray;
if (arrayOne == null) {
int ch = arrayTwo.length;
int len1 = arrayTwo[0].length;
newArray = new float[ch][bytes];
for(int i = 0; i < ch; ++i) {
System.arraycopy(arrayTwo[i], 0, newArray[i], 0, bytes);
}
arrayTwo = null;
} else {
int ch = arrayOne.length;
int len1 = arrayOne[0].length;
if (arrayTwo != null && bytes > 0) {
newArray = new float[ch][len1 + bytes];
for(int i = 0; i < ch; ++i) {
System.arraycopy(arrayOne[i], 0, newArray[i], 0, len1);
System.arraycopy(arrayTwo[i], 0, newArray[i], len1, bytes);
}
arrayOne = null;
arrayTwo = null;
} else {
newArray = new float[ch][len1];
for(int i = 0; i < ch; ++i) {
System.arraycopy(arrayOne[i], 0, newArray[i], 0, len1);
}
arrayOne = null;
}
}
return newArray;
} else {
return null;
}
}
}

View File

@ -0,0 +1,328 @@
package net.lax1dude.eaglercraft.v1_8.internal.wasm_gc_teavm;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import com.google.common.collect.Sets;
/**
* Copyright (c) 2024 lax1dude. All Rights Reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
public class LegacyKeycodeTranslator {
public static class LegacyKeycode {
public final int keyCode;
public final int location;
private LegacyKeycode(int keyCode, int location) {
this.keyCode = keyCode;
this.location = location;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (!(obj instanceof LegacyKeycode))
return false;
LegacyKeycode other = (LegacyKeycode) obj;
if (keyCode != other.keyCode)
return false;
if (location != other.location)
return false;
return true;
}
}
private static final Set<String> numpadVolatile = Sets.newHashSet(
"Comma", "Minus", "Period", "Slash", "Equal", "Enter", "Digit0", "Digit1", "Digit2", "Digit3",
"Digit4", "Digit5", "Digit6", "Digit7", "Digit8", "Digit9", "IntlYen");
private final Map<String,LegacyKeycode> codeLookupBase = new HashMap<>();
private final Map<String,LegacyKeycode> codeLookupLayout = new HashMap<>();
public LegacyKeycodeTranslator() {
codeLookupBase.put("Digit0", new LegacyKeycode(0x30, 0));
codeLookupBase.put("Digit1", new LegacyKeycode(0x31, 0));
codeLookupBase.put("Digit2", new LegacyKeycode(0x32, 0));
codeLookupBase.put("Digit3", new LegacyKeycode(0x33, 0));
codeLookupBase.put("Digit4", new LegacyKeycode(0x34, 0));
codeLookupBase.put("Digit5", new LegacyKeycode(0x35, 0));
codeLookupBase.put("Digit6", new LegacyKeycode(0x36, 0));
codeLookupBase.put("Digit7", new LegacyKeycode(0x37, 0));
codeLookupBase.put("Digit8", new LegacyKeycode(0x38, 0));
codeLookupBase.put("Digit9", new LegacyKeycode(0x39, 0));
codeLookupBase.put("KeyA", new LegacyKeycode(0x41, 0));
codeLookupBase.put("KeyB", new LegacyKeycode(0x42, 0));
codeLookupBase.put("KeyC", new LegacyKeycode(0x43, 0));
codeLookupBase.put("KeyD", new LegacyKeycode(0x44, 0));
codeLookupBase.put("KeyE", new LegacyKeycode(0x45, 0));
codeLookupBase.put("KeyF", new LegacyKeycode(0x46, 0));
codeLookupBase.put("KeyG", new LegacyKeycode(0x47, 0));
codeLookupBase.put("KeyH", new LegacyKeycode(0x48, 0));
codeLookupBase.put("KeyI", new LegacyKeycode(0x49, 0));
codeLookupBase.put("KeyJ", new LegacyKeycode(0x4A, 0));
codeLookupBase.put("KeyK", new LegacyKeycode(0x4B, 0));
codeLookupBase.put("KeyL", new LegacyKeycode(0x4C, 0));
codeLookupBase.put("KeyM", new LegacyKeycode(0x4D, 0));
codeLookupBase.put("KeyN", new LegacyKeycode(0x4E, 0));
codeLookupBase.put("KeyO", new LegacyKeycode(0x4F, 0));
codeLookupBase.put("KeyP", new LegacyKeycode(0x50, 0));
codeLookupBase.put("KeyQ", new LegacyKeycode(0x51, 0));
codeLookupBase.put("KeyR", new LegacyKeycode(0x52, 0));
codeLookupBase.put("KeyS", new LegacyKeycode(0x53, 0));
codeLookupBase.put("KeyT", new LegacyKeycode(0x54, 0));
codeLookupBase.put("KeyU", new LegacyKeycode(0x55, 0));
codeLookupBase.put("KeyV", new LegacyKeycode(0x56, 0));
codeLookupBase.put("KeyW", new LegacyKeycode(0x57, 0));
codeLookupBase.put("KeyX", new LegacyKeycode(0x58, 0));
codeLookupBase.put("KeyY", new LegacyKeycode(0x59, 0));
codeLookupBase.put("KeyZ", new LegacyKeycode(0x5A, 0));
codeLookupBase.put("Comma", new LegacyKeycode(0xBC, 0));
codeLookupBase.put("Period", new LegacyKeycode(0xBE, 0));
codeLookupBase.put("Semicolon", new LegacyKeycode(0xBA, 0));
codeLookupBase.put("Quote", new LegacyKeycode(0xDE, 0));
codeLookupBase.put("BracketLeft", new LegacyKeycode(0xDB, 0));
codeLookupBase.put("BracketRight", new LegacyKeycode(0xDD, 0));
codeLookupBase.put("Backquote", new LegacyKeycode(0xC0, 0));
codeLookupBase.put("Backslash", new LegacyKeycode(0xDC, 0));
codeLookupBase.put("IntlBackslash", new LegacyKeycode(0xDC, 0));
codeLookupBase.put("Minus", new LegacyKeycode(0xBD, 0));
codeLookupBase.put("Equal", new LegacyKeycode(0xBB, 0));
codeLookupBase.put("Slash", new LegacyKeycode(0xBF, 0));
codeLookupBase.put("IntlRo", new LegacyKeycode(0xC1, 0));
codeLookupBase.put("IntlYen", new LegacyKeycode(0xFF, 0));
codeLookupBase.put("AltLeft", new LegacyKeycode(0x12, 1));
codeLookupBase.put("AltRight", new LegacyKeycode(0x12, 2));
codeLookupBase.put("CapsLock", new LegacyKeycode(0x14, 0));
codeLookupBase.put("ControlLeft", new LegacyKeycode(0x11, 1));
codeLookupBase.put("ControlRight", new LegacyKeycode(0x11, 2));
codeLookupBase.put("MetaLeft", new LegacyKeycode(0x5B, 1));
codeLookupBase.put("MetaRight", new LegacyKeycode(0x5C, 2));
codeLookupBase.put("ShiftLeft", new LegacyKeycode(0x10, 1));
codeLookupBase.put("ShiftRight", new LegacyKeycode(0x10, 2));
codeLookupBase.put("ContextMenu", new LegacyKeycode(0x5D, 0));
codeLookupBase.put("Enter", new LegacyKeycode(0x0D, 0));
codeLookupBase.put("Space", new LegacyKeycode(0x20, 0));
codeLookupBase.put("Backspace", new LegacyKeycode(0x08, 0));
codeLookupBase.put("Tab", new LegacyKeycode(0x09, 0));
codeLookupBase.put("Delete", new LegacyKeycode(0x2E, 0));
codeLookupBase.put("End", new LegacyKeycode(0x23, 0));
codeLookupBase.put("Help", new LegacyKeycode(0x2D, 0));
codeLookupBase.put("Home", new LegacyKeycode(0x24, 0));
codeLookupBase.put("Insert", new LegacyKeycode(0x2D, 0));
codeLookupBase.put("PageDown", new LegacyKeycode(0x22, 0));
codeLookupBase.put("PageUp", new LegacyKeycode(0x21, 0));
codeLookupBase.put("ArrowDown", new LegacyKeycode(0x28, 0));
codeLookupBase.put("ArrowLeft", new LegacyKeycode(0x25, 0));
codeLookupBase.put("ArrowRight", new LegacyKeycode(0x27, 0));
codeLookupBase.put("ArrowUp", new LegacyKeycode(0x26, 0));
codeLookupBase.put("Escape", new LegacyKeycode(0x1B, 0));
codeLookupBase.put("PrintScreen", new LegacyKeycode(0x2C, 0));
codeLookupBase.put("ScrollLock", new LegacyKeycode(0x91, 0));
codeLookupBase.put("Pause", new LegacyKeycode(0x13, 0));
codeLookupBase.put("F1", new LegacyKeycode(0x70, 0));
codeLookupBase.put("F2", new LegacyKeycode(0x71, 0));
codeLookupBase.put("F3", new LegacyKeycode(0x72, 0));
codeLookupBase.put("F4", new LegacyKeycode(0x73, 0));
codeLookupBase.put("F5", new LegacyKeycode(0x74, 0));
codeLookupBase.put("F6", new LegacyKeycode(0x75, 0));
codeLookupBase.put("F7", new LegacyKeycode(0x76, 0));
codeLookupBase.put("F8", new LegacyKeycode(0x77, 0));
codeLookupBase.put("F9", new LegacyKeycode(0x78, 0));
codeLookupBase.put("F10", new LegacyKeycode(0x79, 0));
codeLookupBase.put("F11", new LegacyKeycode(0x7A, 0));
codeLookupBase.put("F12", new LegacyKeycode(0x7B, 0));
codeLookupBase.put("F13", new LegacyKeycode(0x7C, 0));
codeLookupBase.put("F14", new LegacyKeycode(0x7D, 0));
codeLookupBase.put("F15", new LegacyKeycode(0x7E, 0));
codeLookupBase.put("F16", new LegacyKeycode(0x7F, 0));
codeLookupBase.put("F17", new LegacyKeycode(0x80, 0));
codeLookupBase.put("F18", new LegacyKeycode(0x81, 0));
codeLookupBase.put("F19", new LegacyKeycode(0x82, 0));
codeLookupBase.put("F20", new LegacyKeycode(0x83, 0));
codeLookupBase.put("F21", new LegacyKeycode(0x84, 0));
codeLookupBase.put("F22", new LegacyKeycode(0x85, 0));
codeLookupBase.put("F23", new LegacyKeycode(0x86, 0));
codeLookupBase.put("F24", new LegacyKeycode(0x87, 0));
codeLookupBase.put("NumLock", new LegacyKeycode(0x90, 3));
codeLookupBase.put("Numpad0", new LegacyKeycode(0x60, 3));
codeLookupBase.put("Numpad1", new LegacyKeycode(0x61, 3));
codeLookupBase.put("Numpad2", new LegacyKeycode(0x62, 3));
codeLookupBase.put("Numpad3", new LegacyKeycode(0x63, 3));
codeLookupBase.put("Numpad4", new LegacyKeycode(0x64, 3));
codeLookupBase.put("Numpad5", new LegacyKeycode(0x65, 3));
codeLookupBase.put("Numpad6", new LegacyKeycode(0x66, 3));
codeLookupBase.put("Numpad7", new LegacyKeycode(0x67, 3));
codeLookupBase.put("Numpad8", new LegacyKeycode(0x68, 3));
codeLookupBase.put("Numpad9", new LegacyKeycode(0x69, 3));
codeLookupBase.put("NumpadAdd", new LegacyKeycode(0x6B, 3));
codeLookupBase.put("NumpadComma", new LegacyKeycode(0xC2, 3));
codeLookupBase.put("NumpadDecimal", new LegacyKeycode(0x6E, 3));
codeLookupBase.put("NumpadDivide", new LegacyKeycode(0x6F, 3));
codeLookupBase.put("NumpadEnter", new LegacyKeycode(0x0D, 3));
codeLookupBase.put("NumpadEqual", new LegacyKeycode(0x0C, 3));
codeLookupBase.put("NumpadMultiply", new LegacyKeycode(0x6A, 3));
codeLookupBase.put("NumpadSubtract", new LegacyKeycode(0x6D, 3));
}
public LegacyKeycodeTranslator addBrowserLayoutMapping(String keyChar, String codeStr) {
LegacyKeycode mapTo = codeLookupBase.get(codeStr);
if(mapTo != null) {
String keyCode = getCodeFromLayoutChar(keyChar);
if(keyCode != null && !keyCode.equals(codeStr) && !(codeStr.startsWith("Numpad") && numpadVolatile.contains(keyCode)) && !mapTo.equals(codeLookupBase.get(keyCode))) {
codeLookupLayout.put(keyCode, mapTo);
}
}
return this;
}
public int getRemappedKeyCount() {
return codeLookupLayout.size();
}
public Map<String,LegacyKeycode> buildLayoutTable() {
if(codeLookupLayout.isEmpty()) {
return codeLookupBase;
}
Map<String,LegacyKeycode> ret = new HashMap<>();
ret.putAll(codeLookupBase);
ret.putAll(codeLookupLayout);
return ret;
}
public static String getCodeFromLayoutChar(String keyChar) {
if(keyChar.length() != 1) {
return null;
}
char c = keyChar.charAt(0);
String ret = getCodeFromLayoutChar0(c);
if(ret == null) {
ret = getCodeFromLayoutChar0(Character.toLowerCase(c));
}
return ret;
}
private static String getCodeFromLayoutChar0(char keyChar) {
switch(keyChar) {
case 'e':
return "KeyE";
case 'd':
return "KeyD";
case 'u':
return "KeyU";
case '-':
return "Minus";
case 'h':
return "KeyH";
case 'z':
return "KeyZ";
case '=':
return "Equal";
case 'p':
return "KeyP";
case ';':
return "Semicolon";
case ']':
return "BracketRight";
case '/':
return "Slash";
case '[':
return "BracketLeft";
case 'l':
return "KeyL";
case '8':
return "Digit8";
case 'w':
return "KeyW";
case 's':
return "KeyS";
case '5':
return "Digit5";
case '9':
return "Digit9";
case 'o':
return "KeyO";
case '.':
return "Period";
case '6':
return "Digit6";
case 'v':
return "KeyV";
case '3':
return "Digit3";
case '`':
return "Backquote";
case 'g':
return "KeyG";
case 'j':
return "KeyJ";
case 'q':
return "KeyQ";
case '1':
return "Digit1";
case 't':
return "KeyT";
case 'y':
return "KeyY";
case '\'':
return "Quote";
case '\\':
return "Backslash";
case 'k':
return "KeyK";
case 'f':
return "KeyF";
case 'i':
return "KeyI";
case 'r':
return "KeyR";
case 'x':
return "KeyX";
case 'a':
return "KeyA";
case '2':
return "Digit2";
case '7':
return "Digit7";
case 'm':
return "KeyM";
case '4':
return "Digit4";
case '0':
return "Digit0";
case 'n':
return "KeyN";
case 'b':
return "KeyB";
case 'c':
return "KeyC";
case ',':
return "Comma";
case '*':
return "NumpadMultiply";
case 0xA5:
return "IntlYen";
default:
return null;
}
}
}

View File

@ -0,0 +1,44 @@
package net.lax1dude.eaglercraft.v1_8.internal.wasm_gc_teavm;
import net.lax1dude.eaglercraft.v1_8.sp.server.internal.wasm_gc_teavm.WorkerMain;
/**
* Copyright (c) 2022-2024 lax1dude. All Rights Reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
public class MainClass {
public static void main(String[] args) {
WASMGCCrashReportStrings.setCrashReportStrings();
if(args.length == 1) {
if("_worker_process_".equalsIgnoreCase(args[0])) {
workerMain();
return;
}
}else if(args.length == 0) {
clientMain();
return;
}
System.out.println("???");
}
private static void clientMain() {
ClientMain._main();
}
private static void workerMain() {
WorkerMain._main();
}
}

View File

@ -0,0 +1,146 @@
package net.lax1dude.eaglercraft.v1_8.internal.wasm_gc_teavm;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import org.teavm.jso.JSObject;
import org.teavm.jso.JSProperty;
import org.teavm.jso.core.JSArrayReader;
import net.lax1dude.eaglercraft.v1_8.internal.EnumTouchEvent;
/**
* Copyright (c) 2024 lax1dude. All Rights Reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
public class SortedTouchEvent {
public interface ITouchUIDMapper {
int call(int uidIn);
}
public interface JSTouchPoint extends JSObject {
@JSProperty
int getPointX();
@JSProperty
int getPointY();
@JSProperty
float getRadius();
@JSProperty
float getForce();
@JSProperty
int getPointUID();
}
public static class TouchPoint {
public final int pointX;
public final int pointY;
public final float radius;
public final float force;
public final int uid;
public TouchPoint(int pointX, int pointY, float radius, float force, int uid) {
this.pointX = pointX;
this.pointY = pointY;
this.radius = radius;
this.force = force;
this.uid = uid;
}
}
public static final Comparator<TouchPoint> touchSortingComparator = (t1, t2) -> {
return t1.uid - t2.uid;
};
private static List<TouchPoint> convertTouchList(JSArrayReader<JSTouchPoint> jsArray, ITouchUIDMapper uidMapper,
int windowHeight, float windowDPI) {
int l = jsArray.getLength();
List<TouchPoint> ret = new ArrayList<>(l);
for(int i = 0; i < l; ++i) {
JSTouchPoint p = jsArray.get(i);
ret.add(new TouchPoint((int)(p.getPointX() * windowDPI), windowHeight - (int)(p.getPointY() * windowDPI) - 1,
p.getRadius() * windowDPI, p.getForce(), uidMapper.call(p.getPointUID())));
}
Collections.sort(ret, touchSortingComparator);
return ret;
}
public static SortedTouchEvent createTouchEvent(EnumTouchEvent type, JSArrayReader<JSTouchPoint> changedTouches,
JSArrayReader<JSTouchPoint> targetTouches, ITouchUIDMapper uidMapper, int windowHeight, float windowDPI) {
List<TouchPoint> changedTouchesList = convertTouchList(changedTouches, uidMapper, windowHeight, windowDPI);
List<TouchPoint> targetTouchesList = convertTouchList(targetTouches, uidMapper, windowHeight, windowDPI);
List<TouchPoint> eventTouchesList;
switch(type) {
case TOUCHSTART:
eventTouchesList = changedTouchesList;
break;
case TOUCHMOVE:
eventTouchesList = targetTouchesList;
break;
case TOUCHEND:
default:
eventTouchesList = changedTouchesList;
break;
}
return new SortedTouchEvent(type, changedTouchesList, targetTouchesList, eventTouchesList);
}
public final EnumTouchEvent type;
private final List<TouchPoint> changedTouchesList;
private final List<TouchPoint> targetTouchesList;
private final List<TouchPoint> eventTouchesList;
public SortedTouchEvent(EnumTouchEvent type, List<TouchPoint> changedTouchesList,
List<TouchPoint> targetTouchesList, List<TouchPoint> eventTouchesList) {
this.type = type;
this.changedTouchesList = changedTouchesList;
this.targetTouchesList = targetTouchesList;
this.eventTouchesList = eventTouchesList;
}
public int getChangedTouchesSize() {
return changedTouchesList.size();
}
public List<TouchPoint> getChangedTouches() {
return changedTouchesList;
}
public int getTargetTouchesSize() {
return targetTouchesList.size();
}
public List<TouchPoint> getTargetTouches() {
return targetTouchesList;
}
public int getEventTouchesSize() {
return eventTouchesList.size();
}
public List<TouchPoint> getEventTouches() {
return eventTouchesList;
}
}

View File

@ -0,0 +1,39 @@
package net.lax1dude.eaglercraft.v1_8.internal.wasm_gc_teavm;
import org.teavm.jso.JSBody;
import org.teavm.jso.JSObject;
import org.teavm.jso.typedarrays.ArrayBuffer;
import org.teavm.jso.typedarrays.ArrayBufferView;
import org.teavm.jso.typedarrays.Float32Array;
import org.teavm.jso.typedarrays.Int16Array;
import org.teavm.jso.typedarrays.Int32Array;
import org.teavm.jso.typedarrays.Int8Array;
import org.teavm.jso.typedarrays.Uint8Array;
/**
* Copyright (c) 2024 lax1dude. All Rights Reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
public class TeaVMUtils {
@JSBody(params = { "obj" }, script = "return !!obj;")
public static native boolean isTruthy(JSObject object);
@JSBody(params = { "obj" }, script = "return !obj;")
public static native boolean isNotTruthy(JSObject object);
@JSBody(params = { "obj" }, script = "return obj === undefined;")
public static native boolean isUndefined(JSObject object);
}

View File

@ -0,0 +1,31 @@
package net.lax1dude.eaglercraft.v1_8.internal.wasm_gc_teavm;
import org.teavm.jso.JSClass;
import org.teavm.jso.JSObject;
import org.teavm.jso.core.JSString;
import org.teavm.jso.typedarrays.Uint8Array;
/**
* Copyright (c) 2024 lax1dude. All Rights Reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
@JSClass
public class TextDecoder implements JSObject {
public TextDecoder(String encoding) {
}
public native JSString decode(Uint8Array buffer);
}

View File

@ -0,0 +1,480 @@
package net.lax1dude.eaglercraft.v1_8.internal.wasm_gc_teavm;
import java.util.ArrayList;
import java.util.List;
import net.lax1dude.eaglercraft.v1_8.EagRuntime;
import net.lax1dude.eaglercraft.v1_8.EaglercraftVersion;
import net.lax1dude.eaglercraft.v1_8.ThreadLocalRandom;
import net.lax1dude.eaglercraft.v1_8.sp.relay.RelayManager;
import org.json.JSONArray;
import org.json.JSONObject;
import org.teavm.jso.JSObject;
import org.teavm.jso.core.JSArrayReader;
import net.lax1dude.eaglercraft.v1_8.internal.IClientConfigAdapter;
import net.lax1dude.eaglercraft.v1_8.internal.IClientConfigAdapterHooks;
import net.lax1dude.eaglercraft.v1_8.internal.wasm_gc_teavm.opts.JSEaglercraftXOptsHooks;
import net.lax1dude.eaglercraft.v1_8.internal.wasm_gc_teavm.opts.JSEaglercraftXOptsRelay;
import net.lax1dude.eaglercraft.v1_8.internal.wasm_gc_teavm.opts.JSEaglercraftXOptsRoot;
import net.lax1dude.eaglercraft.v1_8.internal.wasm_gc_teavm.opts.JSEaglercraftXOptsServer;
import net.lax1dude.eaglercraft.v1_8.sp.relay.RelayEntry;
/**
* Copyright (c) 2022-2024 lax1dude. All Rights Reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
public class WASMGCClientConfigAdapter implements IClientConfigAdapter {
public static final IClientConfigAdapter instance = new WASMGCClientConfigAdapter();
private String defaultLocale = "en_US";
private List<DefaultServer> defaultServers = new ArrayList<>();
private List<RelayEntry> relays = new ArrayList<>();
private String serverToJoin = null;
private String worldsDB = "worlds";
private String resourcePacksDB = "resourcePacks";
private boolean checkGLErrors = false;
private boolean checkShaderGLErrors = false;
private boolean demoMode = EaglercraftVersion.forceDemoMode;
private boolean isEnableDownloadOfflineButton = true;
private String downloadOfflineButtonLink = null;
private boolean useSpecialCursors = false;
private boolean logInvalidCerts = false;
private boolean checkRelaysForUpdates = false;
private boolean enableSignatureBadge = false;
private boolean allowVoiceClient = true;
private boolean allowFNAWSkins = true;
private String localStorageNamespace = "_eaglercraftX";
private final WASMGCClientConfigAdapterHooks hooks = new WASMGCClientConfigAdapterHooks();
private boolean enableMinceraft = true;
private boolean enableServerCookies = true;
private boolean allowServerRedirects = true;
private boolean crashOnUncaughtExceptions = false;
private boolean openDebugConsoleOnLaunch = false;
private boolean fixDebugConsoleUnloadListener = false;
private boolean forceWebViewSupport = false;
private boolean enableWebViewCSP = false;
private boolean autoFixLegacyStyleAttr = false;
private boolean forceProfanityFilter = false;
private boolean forceWebGL1 = false;
private boolean forceWebGL2 = false;
private boolean allowExperimentalWebGL1 = false;
private boolean useWebGLExt = true;
private boolean useJOrbisAudioDecoder = false;
private boolean useXHRFetch = false;
private boolean useVisualViewport = true;
private boolean deobfStackTraces = true;
private boolean disableBlobURLs = false;
private boolean eaglerNoDelay = false;
private boolean ramdiskMode = false;
private boolean singleThreadMode = false;
private boolean enforceVSync = true;
public void loadNative(JSObject jsObject) {
JSEaglercraftXOptsRoot eaglercraftXOpts = (JSEaglercraftXOptsRoot)jsObject;
defaultLocale = eaglercraftXOpts.getLang("en_US");
serverToJoin = eaglercraftXOpts.getJoinServer(null);
worldsDB = eaglercraftXOpts.getWorldsDB("worlds");
resourcePacksDB = eaglercraftXOpts.getResourcePacksDB("resourcePacks");
checkGLErrors = eaglercraftXOpts.getCheckGLErrors(false);
checkShaderGLErrors = eaglercraftXOpts.getCheckShaderGLErrors(false);
demoMode = EaglercraftVersion.forceDemoMode || eaglercraftXOpts.getDemoMode(false);
isEnableDownloadOfflineButton = eaglercraftXOpts.getEnableDownloadOfflineButton(true);
downloadOfflineButtonLink = eaglercraftXOpts.getDownloadOfflineButtonLink(null);
useSpecialCursors = eaglercraftXOpts.getHtml5CursorSupport(false);
logInvalidCerts = EaglercraftVersion.enableUpdateService && !demoMode && eaglercraftXOpts.getLogInvalidCerts(false);
enableSignatureBadge = eaglercraftXOpts.getEnableSignatureBadge(false);
allowVoiceClient = eaglercraftXOpts.getAllowVoiceClient(true);
allowFNAWSkins = !demoMode && eaglercraftXOpts.getAllowFNAWSkins(true);
localStorageNamespace = eaglercraftXOpts.getLocalStorageNamespace(EaglercraftVersion.localStorageNamespace);
enableMinceraft = eaglercraftXOpts.getEnableMinceraft(true);
enableServerCookies = !demoMode && eaglercraftXOpts.getEnableServerCookies(true);
allowServerRedirects = eaglercraftXOpts.getAllowServerRedirects(true);
crashOnUncaughtExceptions = eaglercraftXOpts.getCrashOnUncaughtExceptions(false);
openDebugConsoleOnLaunch = eaglercraftXOpts.getOpenDebugConsoleOnLaunch(false);
fixDebugConsoleUnloadListener = eaglercraftXOpts.getFixDebugConsoleUnloadListener(false);
forceWebViewSupport = eaglercraftXOpts.getForceWebViewSupport(false);
enableWebViewCSP = eaglercraftXOpts.getEnableWebViewCSP(true);
autoFixLegacyStyleAttr = eaglercraftXOpts.getAutoFixLegacyStyleAttr(true);
forceProfanityFilter = eaglercraftXOpts.getForceProfanityFilter(false);
forceWebGL1 = eaglercraftXOpts.getForceWebGL1(false);
forceWebGL2 = eaglercraftXOpts.getForceWebGL2(false);
allowExperimentalWebGL1 = eaglercraftXOpts.getAllowExperimentalWebGL1(true);
useWebGLExt = eaglercraftXOpts.getUseWebGLExt(true);
useJOrbisAudioDecoder = eaglercraftXOpts.getUseJOrbisAudioDecoder(false);
useXHRFetch = eaglercraftXOpts.getUseXHRFetch(false);
useVisualViewport = eaglercraftXOpts.getUseVisualViewport(true);
deobfStackTraces = eaglercraftXOpts.getDeobfStackTraces(true);
disableBlobURLs = eaglercraftXOpts.getDisableBlobURLs(false);
eaglerNoDelay = eaglercraftXOpts.getEaglerNoDelay(false);
ramdiskMode = eaglercraftXOpts.getRamdiskMode(false);
singleThreadMode = eaglercraftXOpts.getSingleThreadMode(false);
enforceVSync = eaglercraftXOpts.getEnforceVSync(true);
JSEaglercraftXOptsHooks hooksObj = eaglercraftXOpts.getHooks();
if(hooksObj != null) {
hooks.loadHooks(hooksObj);
}
defaultServers.clear();
JSArrayReader<JSEaglercraftXOptsServer> serversArray = eaglercraftXOpts.getServers();
if(serversArray != null) {
for(int i = 0, l = serversArray.getLength(); i < l; ++i) {
JSEaglercraftXOptsServer serverEntry = serversArray.get(i);
boolean hideAddr = serverEntry.getHideAddr(false);
String serverAddr = serverEntry.getAddr();
if(serverAddr != null) {
String serverName = serverEntry.getName("Default Server #" + i);
defaultServers.add(new DefaultServer(serverName, serverAddr, hideAddr));
}
}
}
relays.clear();
JSArrayReader<JSEaglercraftXOptsRelay> relaysArray = eaglercraftXOpts.getRelays();
if(relaysArray != null) {
boolean gotAPrimary = false;
for(int i = 0, l = relaysArray.getLength(); i < l; ++i) {
JSEaglercraftXOptsRelay relay = relaysArray.get(i);
String addr = relay.getAddr();
if(addr != null) {
boolean p = relay.getPrimary();
if(p) {
if(gotAPrimary) {
p = false;
}else {
gotAPrimary = true;
}
}
relays.add(new RelayEntry(relay.getAddr(), relay.getComment("Default Relay #" + i), p));
}
}
}
boolean officialUpdates = !demoMode && EaglercraftVersion.updateBundlePackageName.equals("net.lax1dude.eaglercraft.v1_8.client");
if (relays.size() <= 0) {
int choice = ThreadLocalRandom.current().nextInt(3);
relays.add(new RelayEntry("wss://relay.deev.is/", "lax1dude relay #1", choice == 0));
relays.add(new RelayEntry("wss://relay.lax1dude.net/", "lax1dude relay #2", choice == 1));
relays.add(new RelayEntry("wss://relay.shhnowisnottheti.me/", "ayunami relay #1", choice == 2));
checkRelaysForUpdates = !demoMode && eaglercraftXOpts.getCheckRelaysForUpdates(officialUpdates);
}else {
if(officialUpdates) {
for(int i = 0, l = relays.size(); i < l; ++i) {
String addr = relays.get(i).address;
if(!addr.contains("deev.is") && !addr.contains("lax1dude.net") && !addr.contains("shhnowisnottheti.me")) {
officialUpdates = false;
break;
}
}
}
checkRelaysForUpdates = !demoMode && eaglercraftXOpts.getCheckRelaysForUpdates(officialUpdates);
}
RelayManager.relayManager.load(EagRuntime.getStorage("r"));
if (RelayManager.relayManager.count() <= 0) {
RelayManager.relayManager.loadDefaults();
RelayManager.relayManager.save();
}
}
@Override
public String getDefaultLocale() {
return defaultLocale;
}
@Override
public List<DefaultServer> getDefaultServerList() {
return defaultServers;
}
@Override
public String getServerToJoin() {
return serverToJoin;
}
@Override
public String getWorldsDB() {
return worldsDB;
}
@Override
public String getResourcePacksDB() {
return resourcePacksDB;
}
@Override
public JSONObject getIntegratedServerOpts() {
return null;
}
@Override
public List<RelayEntry> getRelays() {
return relays;
}
@Override
public boolean isCheckGLErrors() {
return checkGLErrors;
}
@Override
public boolean isCheckShaderGLErrors() {
return checkShaderGLErrors;
}
@Override
public boolean isDemo() {
return demoMode;
}
@Override
public boolean allowUpdateSvc() {
return false;
}
@Override
public boolean allowUpdateDL() {
return false;
}
@Override
public boolean isEnableDownloadOfflineButton() {
return isEnableDownloadOfflineButton;
}
@Override
public String getDownloadOfflineButtonLink() {
return downloadOfflineButtonLink;
}
@Override
public boolean useSpecialCursors() {
return useSpecialCursors;
}
@Override
public boolean isLogInvalidCerts() {
return logInvalidCerts;
}
@Override
public boolean isCheckRelaysForUpdates() {
return false;
}
@Override
public boolean isEnableSignatureBadge() {
return enableSignatureBadge;
}
@Override
public boolean isAllowVoiceClient() {
return allowVoiceClient;
}
@Override
public boolean isAllowFNAWSkins() {
return allowFNAWSkins;
}
@Override
public String getLocalStorageNamespace() {
return localStorageNamespace;
}
@Override
public boolean isEnableMinceraft() {
return enableMinceraft;
}
@Override
public boolean isEnableServerCookies() {
return enableServerCookies;
}
@Override
public boolean isAllowServerRedirects() {
return allowServerRedirects;
}
@Override
public boolean isOpenDebugConsoleOnLaunch() {
return openDebugConsoleOnLaunch;
}
public boolean isFixDebugConsoleUnloadListenerTeaVM() {
return fixDebugConsoleUnloadListener;
}
@Override
public boolean isForceWebViewSupport() {
return forceWebViewSupport;
}
@Override
public boolean isEnableWebViewCSP() {
return enableWebViewCSP;
}
public boolean isAutoFixLegacyStyleAttrTeaVM() {
return autoFixLegacyStyleAttr;
}
public boolean isForceWebGL1TeaVM() {
return forceWebGL1;
}
public boolean isForceWebGL2TeaVM() {
return forceWebGL2;
}
public boolean isAllowExperimentalWebGL1TeaVM() {
return allowExperimentalWebGL1;
}
public boolean isUseWebGLExtTeaVM() {
return useWebGLExt;
}
public boolean isUseJOrbisAudioDecoderTeaVM() {
return useJOrbisAudioDecoder;
}
public boolean isUseXHRFetchTeaVM() {
return useXHRFetch;
}
public boolean isDeobfStackTracesTeaVM() {
return deobfStackTraces;
}
public boolean isUseVisualViewportTeaVM() {
return useVisualViewport;
}
public boolean isDisableBlobURLsTeaVM() {
return disableBlobURLs;
}
public boolean isSingleThreadModeTeaVM() {
return singleThreadMode;
}
@Override
public boolean isAllowBootMenu() {
return false;
}
@Override
public boolean isForceProfanityFilter() {
return forceProfanityFilter;
}
@Override
public boolean isEaglerNoDelay() {
return eaglerNoDelay;
}
@Override
public boolean isRamdiskMode() {
return ramdiskMode;
}
@Override
public boolean isEnforceVSync() {
return enforceVSync;
}
@Override
public IClientConfigAdapterHooks getHooks() {
return hooks;
}
public JSONObject toJSONObject() {
JSONObject jsonObject = new JSONObject();
jsonObject.put("lang", defaultLocale);
jsonObject.put("joinServer", serverToJoin);
jsonObject.put("worldsDB", worldsDB);
jsonObject.put("resourcePacksDB", resourcePacksDB);
jsonObject.put("checkGLErrors", checkGLErrors);
jsonObject.put("checkShaderGLErrors", checkShaderGLErrors);
jsonObject.put("demoMode", demoMode);
jsonObject.put("enableDownloadOfflineButton", isEnableDownloadOfflineButton);
jsonObject.put("downloadOfflineButtonLink", downloadOfflineButtonLink);
jsonObject.put("html5CursorSupport", useSpecialCursors);
jsonObject.put("logInvalidCerts", logInvalidCerts);
jsonObject.put("checkRelaysForUpdates", checkRelaysForUpdates);
jsonObject.put("enableSignatureBadge", enableSignatureBadge);
jsonObject.put("allowVoiceClient", allowVoiceClient);
jsonObject.put("allowFNAWSkins", allowFNAWSkins);
jsonObject.put("localStorageNamespace", localStorageNamespace);
jsonObject.put("enableMinceraft", enableMinceraft);
jsonObject.put("enableServerCookies", enableServerCookies);
jsonObject.put("allowServerRedirects", allowServerRedirects);
jsonObject.put("crashOnUncaughtExceptions", crashOnUncaughtExceptions);
jsonObject.put("openDebugConsoleOnLaunch", openDebugConsoleOnLaunch);
jsonObject.put("fixDebugConsoleUnloadListener", fixDebugConsoleUnloadListener);
jsonObject.put("forceWebViewSupport", forceWebViewSupport);
jsonObject.put("enableWebViewCSP", enableWebViewCSP);
jsonObject.put("autoFixLegacyStyleAttr", autoFixLegacyStyleAttr);
jsonObject.put("forceProfanityFilter", forceProfanityFilter);
jsonObject.put("forceWebGL1", forceWebGL1);
jsonObject.put("forceWebGL2", forceWebGL2);
jsonObject.put("allowExperimentalWebGL1", allowExperimentalWebGL1);
jsonObject.put("useWebGLExt", useWebGLExt);
jsonObject.put("useJOrbisAudioDecoder", useJOrbisAudioDecoder);
jsonObject.put("useXHRFetch", useXHRFetch);
jsonObject.put("useVisualViewport", useVisualViewport);
jsonObject.put("deobfStackTraces", deobfStackTraces);
jsonObject.put("disableBlobURLs", disableBlobURLs);
jsonObject.put("eaglerNoDelay", eaglerNoDelay);
jsonObject.put("ramdiskMode", ramdiskMode);
jsonObject.put("singleThreadMode", singleThreadMode);
jsonObject.put("enforceVSync", enforceVSync);
JSONArray serversArr = new JSONArray();
for(int i = 0, l = defaultServers.size(); i < l; ++i) {
DefaultServer srv = defaultServers.get(i);
JSONObject obj = new JSONObject();
obj.put("addr", srv.addr);
obj.put("hideAddr", srv.hideAddress);
obj.put("name", srv.name);
serversArr.put(obj);
}
jsonObject.put("servers", serversArr);
JSONArray relaysArr = new JSONArray();
for(int i = 0, l = relays.size(); i < l; ++i) {
RelayEntry rl = relays.get(i);
JSONObject obj = new JSONObject();
obj.put("addr", rl.address);
obj.put("comment", rl.comment);
obj.put("primary", rl.primary);
relaysArr.put(obj);
}
jsonObject.put("relays", relaysArr);
return jsonObject;
}
@Override
public String toString() {
return toJSONObject().toString();
}
public String toStringFormatted() {
return toJSONObject().toString(4);
}
}

View File

@ -0,0 +1,122 @@
package net.lax1dude.eaglercraft.v1_8.internal.wasm_gc_teavm;
import java.util.function.Consumer;
import org.teavm.jso.JSFunctor;
import org.teavm.jso.JSObject;
import net.lax1dude.eaglercraft.v1_8.internal.IClientConfigAdapterHooks;
import net.lax1dude.eaglercraft.v1_8.internal.wasm_gc_teavm.opts.JSEaglercraftXOptsHooks;
import net.lax1dude.eaglercraft.v1_8.log4j.LogManager;
import net.lax1dude.eaglercraft.v1_8.log4j.Logger;
/**
* Copyright (c) 2024 lax1dude. All Rights Reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
public class WASMGCClientConfigAdapterHooks implements IClientConfigAdapterHooks {
private static final Logger logger = LogManager.getLogger("TeaVMClientConfigAdapterHooks");
private LocalStorageSaveHook saveHook = null;
private LocalStorageLoadHook loadHook = null;
private CrashReportHook crashHook = null;
private ScreenChangeHook screenChangedHook = null;
@JSFunctor
private static interface LocalStorageSaveHook extends JSObject {
void call(String key, String base64);
}
@Override
public void callLocalStorageSavedHook(String key, String base64) {
if(saveHook != null) {
try {
saveHook.call(key, base64);
}catch(Throwable t) {
logger.error("Caught exception while invoking eaglercraftXOpts \"localStorageSaved\" hook!");
logger.error(t);
}
}
}
@JSFunctor
private static interface LocalStorageLoadHook extends JSObject {
String call(String key);
}
@Override
public String callLocalStorageLoadHook(String key) {
if(loadHook != null) {
try {
return loadHook.call(key);
}catch(Throwable t) {
logger.error("Caught exception while invoking eaglercraftXOpts \"localStorageLoaded\" hook!");
logger.error(t);
return null;
}
}else {
return null;
}
}
@JSFunctor
private static interface ScreenChangeHook extends JSObject {
String call(String screenName, int scaledWidth, int scaledHeight, int realWidth, int realHeight,
int scaleFactor);
}
@Override
public void callScreenChangedHook(String screenName, int scaledWidth, int scaledHeight, int realWidth,
int realHeight, int scaleFactor) {
if(screenChangedHook != null) {
try {
screenChangedHook.call(screenName, scaledWidth, scaledHeight, realWidth, realHeight, scaleFactor);
}catch(Throwable t) {
logger.error("Caught exception while invoking eaglercraftXOpts \"screenChanged\" hook!");
logger.error(t);
}
}
}
@JSFunctor
private static interface CrashReportHook extends JSObject {
void call(String crashReport, CustomMessageCB customMessageCB);
}
@JSFunctor
private static interface CustomMessageCB extends JSObject {
void call(String msg);
}
@Override
public void callCrashReportHook(String crashReport, Consumer<String> customMessageCB) {
if(crashHook != null) {
try {
crashHook.call(crashReport, (msg) -> customMessageCB.accept(msg));
}catch(Throwable t) {
logger.error("Caught exception while invoking eaglercraftXOpts \"screenChanged\" hook!");
logger.error(t);
}
}
}
public void loadHooks(JSEaglercraftXOptsHooks hooks) {
saveHook = (LocalStorageSaveHook)hooks.getLocalStorageSavedHook();
loadHook = (LocalStorageLoadHook)hooks.getLocalStorageLoadedHook();
crashHook = (CrashReportHook)hooks.getCrashReportHook();
screenChangedHook = (ScreenChangeHook)hooks.getScreenChangedHook();
}
}

View File

@ -0,0 +1,38 @@
package net.lax1dude.eaglercraft.v1_8.internal.wasm_gc_teavm;
import org.teavm.interop.Import;
import org.teavm.jso.core.JSString;
import net.lax1dude.eaglercraft.v1_8.EaglercraftVersion;
/**
* Copyright (c) 2024 lax1dude. All Rights Reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
public class WASMGCCrashReportStrings {
private static final int CRASH_REPORT_EAGLER_VERSION = 0;
private static final int CRASH_REPORT_EAGLER_VENDOR = 1;
private static final int CRASH_REPORT_MINECRAFT_VERSION = 2;
@Import(module = "platformRuntime", name = "setCrashReportString")
private static native void setCrashReportString(int id, JSString str);
public static void setCrashReportStrings() {
setCrashReportString(CRASH_REPORT_EAGLER_VERSION, JSString.valueOf(EaglercraftVersion.projectForkVersion));
setCrashReportString(CRASH_REPORT_EAGLER_VENDOR, JSString.valueOf(EaglercraftVersion.projectForkVendor));
setCrashReportString(CRASH_REPORT_MINECRAFT_VERSION, JSString.valueOf("1.8.8"));
}
}

View File

@ -0,0 +1,242 @@
package net.lax1dude.eaglercraft.v1_8.internal.wasm_gc_teavm;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.teavm.jso.JSObject;
import org.teavm.jso.JSProperty;
import org.teavm.jso.typedarrays.Uint8Array;
import net.lax1dude.eaglercraft.v1_8.EagUtils;
import net.lax1dude.eaglercraft.v1_8.internal.EnumEaglerConnectionState;
import net.lax1dude.eaglercraft.v1_8.internal.IWebSocketClient;
import net.lax1dude.eaglercraft.v1_8.internal.IWebSocketFrame;
import net.lax1dude.eaglercraft.v1_8.internal.PlatformRuntime;
import net.lax1dude.eaglercraft.v1_8.internal.buffer.ByteBuffer;
import net.lax1dude.eaglercraft.v1_8.internal.buffer.WASMGCBufferAllocator;
import net.lax1dude.eaglercraft.v1_8.internal.buffer.WASMGCDirectArrayConverter;
/**
* Copyright (c) 2024 lax1dude. All Rights Reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
public class WASMGCWebSocketClient implements IWebSocketClient {
public interface JSWebSocketClientHandle extends JSObject {
@JSProperty
int getState();
void closeSocket();
void sendStringFrame(String str);
void sendBinaryFrame(Uint8Array arr);
int availableFrames();
WASMGCWebSocketFrame.JSWebSocketFrame getNextFrame();
WASMGCWebSocketFrame.JSWebSocketFrame[] getAllFrames();
void clearFrames();
int availableStringFrames();
WASMGCWebSocketFrame.JSWebSocketFrame getNextStringFrame();
WASMGCWebSocketFrame.JSWebSocketFrame[] getAllStringFrames();
void clearStringFrames();
int availableBinaryFrames();
WASMGCWebSocketFrame.JSWebSocketFrame getNextBinaryFrame();
WASMGCWebSocketFrame.JSWebSocketFrame[] getAllBinaryFrames();
void clearBinaryFrames();
}
private final JSWebSocketClientHandle handle;
private final String uri;
public WASMGCWebSocketClient(JSWebSocketClientHandle handle, String uri) {
this.handle = handle;
this.uri = uri;
}
@Override
public EnumEaglerConnectionState getState() {
int state = handle.getState();
switch(state) {
case 0:
default:
return EnumEaglerConnectionState.CLOSED;
case 1:
return EnumEaglerConnectionState.CONNECTING;
case 2:
return EnumEaglerConnectionState.CONNECTED;
case 3:
return EnumEaglerConnectionState.FAILED;
}
}
@Override
public boolean connectBlocking(int timeoutMS) {
long startTime = PlatformRuntime.steadyTimeMillis();
int state;
for(;;) {
state = handle.getState();
if(state != 1) { // CONNECTING
break;
}
EagUtils.sleep(50);
if(PlatformRuntime.steadyTimeMillis() - startTime > timeoutMS) {
state = 3;
break;
}
}
return state == 2;
}
@Override
public boolean isOpen() {
return handle.getState() == 2;
}
@Override
public boolean isClosed() {
int state = handle.getState();
return state != 1 && state != 2;
}
@Override
public void close() {
handle.closeSocket();
}
@Override
public int availableFrames() {
return handle.availableFrames();
}
@Override
public IWebSocketFrame getNextFrame() {
WASMGCWebSocketFrame.JSWebSocketFrame nextFrame = handle.getNextFrame();
return nextFrame != null ? new WASMGCWebSocketFrame(nextFrame) : null;
}
@Override
public List<IWebSocketFrame> getNextFrames() {
WASMGCWebSocketFrame.JSWebSocketFrame[] arrJS = handle.getAllFrames();
if(arrJS == null) {
return null;
}
int len = arrJS.length;
IWebSocketFrame[] arr = new IWebSocketFrame[len];
for(int i = 0; i < len; ++i) {
arr[i] = new WASMGCWebSocketFrame(arrJS[i]);
}
return Arrays.asList(arr);
}
@Override
public void clearFrames() {
handle.clearFrames();
}
@Override
public int availableStringFrames() {
return handle.availableStringFrames();
}
@Override
public IWebSocketFrame getNextStringFrame() {
WASMGCWebSocketFrame.JSWebSocketFrame nextFrame = handle.getNextStringFrame();
return nextFrame != null ? new WASMGCWebSocketFrame(nextFrame) : null;
}
@Override
public List<IWebSocketFrame> getNextStringFrames() {
WASMGCWebSocketFrame.JSWebSocketFrame[] arrJS = handle.getAllStringFrames();
if(arrJS == null) {
return null;
}
int len = arrJS.length;
IWebSocketFrame[] arr = new IWebSocketFrame[len];
for(int i = 0; i < len; ++i) {
arr[i] = new WASMGCWebSocketFrame(arrJS[i]);
}
return Arrays.asList(arr);
}
@Override
public void clearStringFrames() {
handle.clearStringFrames();
}
@Override
public int availableBinaryFrames() {
return handle.availableBinaryFrames();
}
@Override
public IWebSocketFrame getNextBinaryFrame() {
WASMGCWebSocketFrame.JSWebSocketFrame nextFrame = handle.getNextBinaryFrame();
return nextFrame != null ? new WASMGCWebSocketFrame(nextFrame) : null;
}
@Override
public List<IWebSocketFrame> getNextBinaryFrames() {
WASMGCWebSocketFrame.JSWebSocketFrame[] arrJS = handle.getAllBinaryFrames();
if(arrJS == null) {
return null;
}
int len = arrJS.length;
IWebSocketFrame[] arr = new IWebSocketFrame[len];
for(int i = 0; i < len; ++i) {
arr[i] = new WASMGCWebSocketFrame(arrJS[i]);
}
return Arrays.asList(arr);
}
@Override
public void clearBinaryFrames() {
handle.clearStringFrames();
}
@Override
public void send(String str) {
handle.sendStringFrame(str);
}
@Override
public void send(byte[] bytes) {
ByteBuffer buf = WASMGCDirectArrayConverter.byteArrayToBuffer(bytes);
try {
handle.sendBinaryFrame(WASMGCBufferAllocator.getUnsignedByteBufferView(buf));
}finally {
PlatformRuntime.freeByteBuffer(buf);
}
}
@Override
public String getCurrentURI() {
return uri;
}
}

View File

@ -0,0 +1,116 @@
package net.lax1dude.eaglercraft.v1_8.internal.wasm_gc_teavm;
import java.io.InputStream;
import org.teavm.jso.JSObject;
import org.teavm.jso.JSProperty;
import org.teavm.jso.core.JSString;
import org.teavm.jso.typedarrays.ArrayBuffer;
import org.teavm.jso.typedarrays.Uint8Array;
import net.lax1dude.eaglercraft.v1_8.EaglerInputStream;
import net.lax1dude.eaglercraft.v1_8.internal.IWebSocketFrame;
import net.lax1dude.eaglercraft.v1_8.internal.buffer.WASMGCDirectArrayConverter;
/**
* Copyright (c) 2024 lax1dude. All Rights Reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
public class WASMGCWebSocketFrame implements IWebSocketFrame {
public interface JSWebSocketFrame extends JSObject {
@JSProperty
int getType();
@JSProperty("data")
ArrayBuffer getDataBinary();
@JSProperty("data")
JSString getDataJSString();
@JSProperty
double getTimestamp();
}
private final JSWebSocketFrame handle;
private final boolean isStr;
private int cachedLength = -1;
private String cachedString = null;
private byte[] cachedByteArray = null;
public WASMGCWebSocketFrame(JSWebSocketFrame handle) {
this.handle = handle;
this.isStr = handle.getType() == 0;
}
@Override
public boolean isString() {
return isStr;
}
@Override
public String getString() {
if(cachedString == null) {
cachedString = getString0();
}
return cachedString;
}
private String getString0() {
if(!isStr) return null;
return BetterJSStringConverter.stringFromJS(handle.getDataJSString());
}
@Override
public byte[] getByteArray() {
if(cachedByteArray == null) {
cachedByteArray = getByteArray0();
}
return cachedByteArray;
}
private byte[] getByteArray0() {
if(isStr) return null;
return WASMGCDirectArrayConverter.externU8ArrayToByteArray(new Uint8Array(handle.getDataBinary()));
}
@Override
public InputStream getInputStream() {
return new EaglerInputStream(getByteArray());
}
@Override
public int getLength() {
if(cachedLength == -1) {
cachedLength = getLength0();
}
return cachedLength;
}
private int getLength0() {
if(isStr) {
return handle.getDataJSString().getLength();
}else {
return handle.getDataBinary().getByteLength();
}
}
@Override
public long getTimestamp() {
return (long)handle.getTimestamp();
}
}

View File

@ -0,0 +1,297 @@
package net.lax1dude.eaglercraft.v1_8.internal.wasm_gc_teavm;
import static net.lax1dude.eaglercraft.v1_8.internal.PlatformOpenGL.*;
import static net.lax1dude.eaglercraft.v1_8.opengl.RealOpenGLEnums.*;
import net.lax1dude.eaglercraft.v1_8.internal.IBufferArrayGL;
import net.lax1dude.eaglercraft.v1_8.internal.IBufferGL;
import net.lax1dude.eaglercraft.v1_8.internal.IFramebufferGL;
import net.lax1dude.eaglercraft.v1_8.internal.IProgramGL;
import net.lax1dude.eaglercraft.v1_8.internal.IRenderbufferGL;
import net.lax1dude.eaglercraft.v1_8.internal.IShaderGL;
import net.lax1dude.eaglercraft.v1_8.internal.ITextureGL;
import net.lax1dude.eaglercraft.v1_8.internal.PlatformRuntime;
import net.lax1dude.eaglercraft.v1_8.internal.buffer.ByteBuffer;
import net.lax1dude.eaglercraft.v1_8.opengl.EaglercraftGPU;
import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager;
/**
* Copyright (c) 2022-2024 lax1dude. All Rights Reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
public class WebGLBackBuffer {
private static int glesVers = -1;
private static IFramebufferGL eagFramebuffer;
private static int width;
private static int height;
// GLES 3.0+
private static IRenderbufferGL gles3ColorRenderbuffer;
private static IRenderbufferGL gles3DepthRenderbuffer;
// GLES 2.0
private static ITextureGL gles2ColorTexture;
private static IRenderbufferGL gles2DepthRenderbuffer;
private static IProgramGL gles2BlitProgram;
private static IBufferArrayGL gles2BlitVAO;
private static IBufferGL gles2BlitVBO;
private static boolean isVAOCapable = false;
private static boolean isEmulatedVAOPhase = false;
private static final int _GL_FRAMEBUFFER = 0x8D40;
private static final int _GL_RENDERBUFFER = 0x8D41;
private static final int _GL_COLOR_ATTACHMENT0 = 0x8CE0;
private static final int _GL_DEPTH_ATTACHMENT = 0x8D00;
private static final int _GL_DEPTH_COMPONENT16 = 0x81A5;
private static final int _GL_DEPTH_COMPONENT32F = 0x8CAC;
private static final int _GL_READ_FRAMEBUFFER = 0x8CA8;
private static final int _GL_DRAW_FRAMEBUFFER = 0x8CA9;
public static void initBackBuffer(int sw, int sh) {
glesVers = checkOpenGLESVersion();
eagFramebuffer = _wglCreateFramebuffer();
isVAOCapable = checkVAOCapable();
isEmulatedVAOPhase = false;
width = sw;
height = sh;
if(glesVers >= 300) {
gles3ColorRenderbuffer = _wglCreateRenderbuffer();
gles3DepthRenderbuffer = _wglCreateRenderbuffer();
_wglBindFramebuffer(_GL_FRAMEBUFFER, eagFramebuffer);
_wglBindRenderbuffer(_GL_RENDERBUFFER, gles3ColorRenderbuffer);
_wglRenderbufferStorage(_GL_RENDERBUFFER, GL_RGBA8, sw, sh);
_wglFramebufferRenderbuffer(_GL_FRAMEBUFFER, _GL_COLOR_ATTACHMENT0, _GL_RENDERBUFFER, gles3ColorRenderbuffer);
_wglBindRenderbuffer(_GL_RENDERBUFFER, gles3DepthRenderbuffer);
_wglRenderbufferStorage(_GL_RENDERBUFFER, _GL_DEPTH_COMPONENT32F, sw, sh);
_wglFramebufferRenderbuffer(_GL_FRAMEBUFFER, _GL_DEPTH_ATTACHMENT, _GL_RENDERBUFFER, gles3DepthRenderbuffer);
_wglDrawBuffers(_GL_COLOR_ATTACHMENT0);
}else {
gles2ColorTexture = _wglGenTextures();
gles2DepthRenderbuffer = _wglCreateRenderbuffer();
_wglBindFramebuffer(_GL_FRAMEBUFFER, eagFramebuffer);
_wglBindTexture(GL_TEXTURE_2D, gles2ColorTexture);
_wglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
_wglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
_wglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
_wglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
_wglTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, sw, sh, 0, GL_RGBA, GL_UNSIGNED_BYTE, (ByteBuffer)null);
_wglFramebufferTexture2D(_GL_FRAMEBUFFER, _GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, gles2ColorTexture, 0);
_wglBindRenderbuffer(_GL_RENDERBUFFER, gles2DepthRenderbuffer);
_wglRenderbufferStorage(_GL_RENDERBUFFER, _GL_DEPTH_COMPONENT16, sw, sh);
_wglFramebufferRenderbuffer(_GL_FRAMEBUFFER, _GL_DEPTH_ATTACHMENT, _GL_RENDERBUFFER, gles2DepthRenderbuffer);
ByteBuffer upload = PlatformRuntime.allocateByteBuffer(48);
upload.putFloat(0.0f); upload.putFloat(0.0f);
upload.putFloat(1.0f); upload.putFloat(0.0f);
upload.putFloat(0.0f); upload.putFloat(1.0f);
upload.putFloat(1.0f); upload.putFloat(0.0f);
upload.putFloat(1.0f); upload.putFloat(1.0f);
upload.putFloat(0.0f); upload.putFloat(1.0f);
upload.flip();
gles2BlitVBO = _wglGenBuffers();
EaglercraftGPU.bindVAOGLArrayBufferNow(gles2BlitVBO);
_wglBufferData(GL_ARRAY_BUFFER, upload, GL_STATIC_DRAW);
PlatformRuntime.freeByteBuffer(upload);
if(isVAOCapable) {
gles2BlitVAO = _wglGenVertexArrays();
_wglBindVertexArray(gles2BlitVAO);
_wglEnableVertexAttribArray(0);
_wglVertexAttribPointer(0, 2, GL_FLOAT, false, 8, 0);
}
IShaderGL vertShader = _wglCreateShader(GL_VERTEX_SHADER);
_wglShaderSource(vertShader, "#version 100\nprecision mediump float; attribute vec2 a_pos2f; varying vec2 v_tex2f; void main() { v_tex2f = a_pos2f; gl_Position = vec4(a_pos2f * 2.0 - 1.0, 0.0, 1.0); }");
_wglCompileShader(vertShader);
IShaderGL fragShader = _wglCreateShader(GL_FRAGMENT_SHADER);
_wglShaderSource(fragShader, checkTextureLODCapable()
? "#version 100\n#extension GL_EXT_shader_texture_lod : enable\nprecision mediump float; precision mediump sampler2D; varying vec2 v_tex2f; uniform sampler2D u_samplerTex; void main() { gl_FragColor = vec4(texture2DLodEXT(u_samplerTex, v_tex2f, 0.0).rgb, 1.0); }"
: "#version 100\nprecision mediump float; precision mediump sampler2D; varying vec2 v_tex2f; uniform sampler2D u_samplerTex; void main() { gl_FragColor = vec4(texture2D(u_samplerTex, v_tex2f).rgb, 1.0); }");
_wglCompileShader(fragShader);
gles2BlitProgram = _wglCreateProgram();
_wglAttachShader(gles2BlitProgram, vertShader);
_wglAttachShader(gles2BlitProgram, fragShader);
_wglBindAttribLocation(gles2BlitProgram, 0, "a_pos2f");
_wglLinkProgram(gles2BlitProgram);
_wglDetachShader(gles2BlitProgram, vertShader);
_wglDetachShader(gles2BlitProgram, fragShader);
_wglDeleteShader(vertShader);
_wglDeleteShader(fragShader);
_wglUseProgram(gles2BlitProgram);
_wglUniform1i(_wglGetUniformLocation(gles2BlitProgram, "u_samplerTex"), 0);
}
}
public static IFramebufferGL getBackBuffer() {
return eagFramebuffer;
}
public static void enterVAOEmulationPhase() {
if(glesVers < 300) {
if(!isEmulatedVAOPhase) {
if(isVAOCapable) {
_wglDeleteVertexArrays(gles2BlitVAO);
}
gles2BlitVAO = EaglercraftGPU.createGLBufferArray();
EaglercraftGPU.bindGLBufferArray(gles2BlitVAO);
EaglercraftGPU.bindVAOGLArrayBuffer(gles2BlitVBO);
EaglercraftGPU.enableVertexAttribArray(0);
EaglercraftGPU.vertexAttribPointer(0, 2, GL_FLOAT, false, 8, 0);
isEmulatedVAOPhase = true;
}
}
}
private static void drawBlitQuad() {
if(isEmulatedVAOPhase) {
EaglercraftGPU.bindGLBufferArray(gles2BlitVAO);
EaglercraftGPU.doDrawArrays(GL_TRIANGLES, 0, 6);
}else {
if(isVAOCapable) {
_wglBindVertexArray(gles2BlitVAO);
_wglDrawArrays(GL_TRIANGLES, 0, 6);
}else {
EaglercraftGPU.bindGLArrayBuffer(gles2BlitVBO);
_wglEnableVertexAttribArray(0);
_wglVertexAttribPointer(0, 2, GL_FLOAT, false, 8, 0);
_wglDrawArrays(GL_TRIANGLES, 0, 6);
}
}
}
public static void flipBuffer(int windowWidth, int windowHeight) {
if(glesVers >= 300) {
_wglBindFramebufferLow(_GL_READ_FRAMEBUFFER, eagFramebuffer);
_wglBindFramebufferLow(_GL_DRAW_FRAMEBUFFER, null);
_wglBlitFramebuffer(0, 0, width, height, 0, 0, windowWidth, windowHeight, GL_COLOR_BUFFER_BIT, GL_NEAREST);
_wglBindFramebufferLow(_GL_FRAMEBUFFER, eagFramebuffer);
if(windowWidth != width || windowHeight != height) {
width = windowWidth;
height = windowHeight;
_wglBindRenderbuffer(_GL_RENDERBUFFER, gles3ColorRenderbuffer);
_wglRenderbufferStorage(_GL_RENDERBUFFER, GL_RGBA8, windowWidth, windowHeight);
_wglBindRenderbuffer(_GL_RENDERBUFFER, gles3DepthRenderbuffer);
_wglRenderbufferStorage(_GL_RENDERBUFFER, _GL_DEPTH_COMPONENT32F, windowWidth, windowHeight);
}
}else {
_wglBindFramebufferLow(_GL_FRAMEBUFFER, null);
_wglActiveTexture(GL_TEXTURE0);
_wglBindTexture(GL_TEXTURE_2D, gles2ColorTexture);
int[] viewportStash = null;
if(isEmulatedVAOPhase) {
viewportStash = new int[4];
EaglercraftGPU.glGetInteger(GL_VIEWPORT, viewportStash);
GlStateManager.viewport(0, 0, windowWidth, windowHeight);
GlStateManager.eagPushStateForGLES2BlitHack();
GlStateManager.disableDepth();
GlStateManager.disableBlend();
}else {
_wglViewport(0, 0, windowWidth, windowHeight);
_wglDisable(GL_DEPTH_TEST);
_wglDisable(GL_BLEND);
}
EaglercraftGPU.clearCurrentBinding(EaglercraftGPU.CLEAR_BINDING_SHADER_PROGRAM | EaglercraftGPU.CLEAR_BINDING_ARRAY_BUFFER);
EaglercraftGPU.bindGLShaderProgram(gles2BlitProgram);
drawBlitQuad();
if(windowWidth != width || windowHeight != height) {
width = windowWidth;
height = windowHeight;
_wglTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, windowWidth, windowHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, (ByteBuffer)null);
_wglBindRenderbuffer(_GL_RENDERBUFFER, gles2DepthRenderbuffer);
_wglRenderbufferStorage(_GL_RENDERBUFFER, _GL_DEPTH_COMPONENT16, windowWidth, windowHeight);
}
if(isEmulatedVAOPhase) {
EaglercraftGPU.clearCurrentBinding(EaglercraftGPU.CLEAR_BINDING_TEXTURE0 | EaglercraftGPU.CLEAR_BINDING_ACTIVE_TEXTURE | EaglercraftGPU.CLEAR_BINDING_SHADER_PROGRAM);
if(viewportStash[2] > 0) {
GlStateManager.viewport(viewportStash[0], viewportStash[1], viewportStash[2], viewportStash[3]);
}
GlStateManager.eagPopStateForGLES2BlitHack();
}else {
EaglercraftGPU.clearCurrentBinding(EaglercraftGPU.CLEAR_BINDING_TEXTURE0 | EaglercraftGPU.CLEAR_BINDING_ACTIVE_TEXTURE | EaglercraftGPU.CLEAR_BINDING_SHADER_PROGRAM | EaglercraftGPU.CLEAR_BINDING_BUFFER_ARRAY);
}
_wglBindFramebuffer(_GL_FRAMEBUFFER, eagFramebuffer);
}
}
public static void destroy() {
if(eagFramebuffer != null) {
_wglDeleteFramebuffer(eagFramebuffer);
eagFramebuffer = null;
}
if(gles3ColorRenderbuffer != null) {
_wglDeleteRenderbuffer(gles3ColorRenderbuffer);
gles3ColorRenderbuffer = null;
}
if(gles3DepthRenderbuffer != null) {
_wglDeleteRenderbuffer(gles3DepthRenderbuffer);
gles3DepthRenderbuffer = null;
}
if(gles2ColorTexture != null) {
_wglDeleteTextures(gles2ColorTexture);
gles2ColorTexture = null;
}
if(gles2DepthRenderbuffer != null) {
_wglDeleteRenderbuffer(gles2DepthRenderbuffer);
gles2DepthRenderbuffer = null;
}
if(gles2BlitProgram != null) {
_wglDeleteProgram(gles2BlitProgram);
gles2BlitProgram = null;
}
if(gles2BlitVAO != null) {
if(isEmulatedVAOPhase) {
EaglercraftGPU.destroyGLBufferArray(gles2BlitVAO);
}else if(isVAOCapable) {
_wglDeleteVertexArrays(gles2BlitVAO);
}
gles2BlitVAO = null;
}
if(gles2BlitVBO != null) {
_wglDeleteBuffers(gles2BlitVBO);
gles2BlitVBO = null;
}
width = 0;
height = 0;
isVAOCapable = false;
isEmulatedVAOPhase = false;
}
}

View File

@ -0,0 +1,21 @@
package net.lax1dude.eaglercraft.v1_8.internal.wasm_gc_teavm;
import org.teavm.jso.JSObject;
/**
* Copyright (c) 2022 lax1dude. All Rights Reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
public interface WebGLQuery extends JSObject {
}

View File

@ -0,0 +1,21 @@
package net.lax1dude.eaglercraft.v1_8.internal.wasm_gc_teavm;
import org.teavm.jso.JSObject;
/**
* Copyright (c) 2022 lax1dude. All Rights Reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
public interface WebGLVertexArray extends JSObject {
}

View File

@ -0,0 +1,35 @@
package net.lax1dude.eaglercraft.v1_8.internal.wasm_gc_teavm.opts;
import org.teavm.jso.JSBody;
import org.teavm.jso.JSObject;
/**
* Copyright (c) 2024 lax1dude. All Rights Reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
public interface JSEaglercraftXOptsHooks extends JSObject {
@JSBody(script = "return (typeof this.localStorageSaved === \"function\") ? this.localStorageSaved : null;")
JSObject getLocalStorageSavedHook();
@JSBody(script = "return (typeof this.localStorageLoaded === \"function\") ? this.localStorageLoaded : null;")
JSObject getLocalStorageLoadedHook();
@JSBody(script = "return (typeof this.crashReportShow === \"function\") ? this.crashReportShow : null;")
JSObject getCrashReportHook();
@JSBody(script = "return (typeof this.screenChanged === \"function\") ? this.screenChanged : null;")
JSObject getScreenChangedHook();
}

View File

@ -0,0 +1,32 @@
package net.lax1dude.eaglercraft.v1_8.internal.wasm_gc_teavm.opts;
import org.teavm.jso.JSBody;
import org.teavm.jso.JSObject;
/**
* Copyright (c) 2024 lax1dude. All Rights Reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
public interface JSEaglercraftXOptsRelay extends JSObject {
@JSBody(script = "return (typeof this.addr === \"string\") ? this.addr : null;")
String getAddr();
@JSBody(params = { "def" }, script = "return (typeof this.comment === \"string\") ? this.comment : def;")
String getComment(String defaultValue);
@JSBody(script = "return (typeof this.primary === \"boolean\") ? this.primary : false;")
boolean getPrimary();
}

View File

@ -0,0 +1,174 @@
package net.lax1dude.eaglercraft.v1_8.internal.wasm_gc_teavm.opts;
import org.teavm.jso.JSBody;
import org.teavm.jso.JSObject;
import org.teavm.jso.core.JSArrayReader;
/**
* Copyright (c) 2024 lax1dude. All Rights Reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
public interface JSEaglercraftXOptsRoot extends JSObject {
@JSBody(params = { "def" }, script = "return (typeof this.lang === \"string\") ? this.lang : def;")
String getLang(String defaultValue);
@JSBody(params = { "def" }, script = "return (typeof this.joinServer === \"string\") ? this.joinServer : def;")
String getJoinServer(String defaultValue);
@JSBody(params = { "def" }, script = "return (typeof this.localesURI === \"string\") ? this.localesURI : def;")
String getLocalesURI(String defaultValue);
@JSBody(params = { "def" }, script = "return (typeof this.worldsDB === \"string\") ? this.worldsDB : def;")
String getWorldsDB(String defaultValue);
@JSBody(params = { "def" }, script = "return (typeof this.resourcePacksDB === \"string\") ? this.resourcePacksDB : def;")
String getResourcePacksDB(String defaultValue);
@JSBody(params = { "def" }, script = "return (typeof this.demoMode === \"boolean\") ? this.demoMode : def;")
boolean getDemoMode(boolean defaultValue);
@JSBody(script = "return (typeof this.servers === \"object\") ? this.servers : null;")
JSArrayReader<JSEaglercraftXOptsServer> getServers();
@JSBody(script = "return (typeof this.relays === \"object\") ? this.relays : null;")
JSArrayReader<JSEaglercraftXOptsRelay> getRelays();
@JSBody(params = { "def" }, script = "return (typeof this.checkGLErrors === \"boolean\") ? this.checkGLErrors : def;")
boolean getCheckGLErrors(boolean defaultValue);
@JSBody(params = { "def" }, script = "return (typeof this.checkShaderGLErrors === \"boolean\") ? this.checkShaderGLErrors : def;")
boolean getCheckShaderGLErrors(boolean defaultValue);
@JSBody(params = { "def" }, script = "return (typeof this.enableDownloadOfflineButton === \"boolean\") ? this.enableDownloadOfflineButton : def;")
boolean getEnableDownloadOfflineButton(boolean defaultValue);
@JSBody(params = { "def" }, script = "return (typeof this.downloadOfflineButtonLink === \"string\") ? this.downloadOfflineButtonLink : def;")
String getDownloadOfflineButtonLink(String defaultValue);
@JSBody(params = { "def" }, script = "return (typeof this.html5CursorSupport === \"boolean\") ? this.html5CursorSupport : def;")
boolean getHtml5CursorSupport(boolean defaultValue);
@JSBody(params = { "def" }, script = "return (typeof this.allowUpdateSvc === \"boolean\") ? this.allowUpdateSvc : def;")
boolean getAllowUpdateSvc(boolean defaultValue);
@JSBody(params = { "def" }, script = "return (typeof this.allowUpdateDL === \"boolean\") ? this.allowUpdateDL : def;")
boolean getAllowUpdateDL(boolean defaultValue);
@JSBody(params = { "def" }, script = "return (typeof this.logInvalidCerts === \"boolean\") ? this.logInvalidCerts : def;")
boolean getLogInvalidCerts(boolean defaultValue);
@JSBody(params = { "def" }, script = "return (typeof this.enableSignatureBadge === \"boolean\") ? this.enableSignatureBadge : def;")
boolean getEnableSignatureBadge(boolean defaultValue);
@JSBody(params = { "def" }, script = "return (typeof this.checkRelaysForUpdates === \"boolean\") ? this.checkRelaysForUpdates : def;")
boolean getCheckRelaysForUpdates(boolean defaultValue);
@JSBody(params = { "def" }, script = "return (typeof this.allowVoiceClient === \"boolean\") ? this.allowVoiceClient : def;")
boolean getAllowVoiceClient(boolean defaultValue);
@JSBody(params = { "def" }, script = "return (typeof this.allowFNAWSkins === \"boolean\") ? this.allowFNAWSkins : def;")
boolean getAllowFNAWSkins(boolean defaultValue);
@JSBody(script = "return (typeof this.hooks === \"object\") ? this.hooks : null;")
JSEaglercraftXOptsHooks getHooks();
@JSBody(params = { "def" }, script = "return (typeof this.localStorageNamespace === \"string\") ? this.localStorageNamespace : def;")
String getLocalStorageNamespace(String defaultValue);
@JSBody(params = { "def" }, script = "return (typeof this.enableMinceraft === \"boolean\") ? this.enableMinceraft : def;")
boolean getEnableMinceraft(boolean defaultValue);
@JSBody(params = { "def" }, script = "return (typeof this.enableServerCookies === \"boolean\") ? this.enableServerCookies : def;")
boolean getEnableServerCookies(boolean defaultValue);
@JSBody(params = { "def" }, script = "return (typeof this.allowServerRedirects === \"boolean\") ? this.allowServerRedirects : def;")
boolean getAllowServerRedirects(boolean defaultValue);
@JSBody(params = { "def" }, script = "return (typeof this.crashOnUncaughtExceptions === \"boolean\") ? this.crashOnUncaughtExceptions : def;")
boolean getCrashOnUncaughtExceptions(boolean defaultValue);
@JSBody(params = { "def" }, script = "return (typeof this.openDebugConsoleOnLaunch === \"boolean\") ? this.openDebugConsoleOnLaunch : def;")
boolean getOpenDebugConsoleOnLaunch(boolean defaultValue);
@JSBody(params = { "def" }, script = "return (typeof this.fixDebugConsoleUnloadListener === \"boolean\") ? this.fixDebugConsoleUnloadListener : def;")
boolean getFixDebugConsoleUnloadListener(boolean defaultValue);
@JSBody(params = { "def" }, script = "return (typeof this.forceWebViewSupport === \"boolean\") ? this.forceWebViewSupport : def;")
boolean getForceWebViewSupport(boolean defaultValue);
@JSBody(params = { "def" }, script = "return (typeof this.enableWebViewCSP === \"boolean\") ? this.enableWebViewCSP : def;")
boolean getEnableWebViewCSP(boolean defaultValue);
@JSBody(params = { "def" }, script = "return (typeof this.autoFixLegacyStyleAttr === \"boolean\") ? this.autoFixLegacyStyleAttr : def;")
boolean getAutoFixLegacyStyleAttr(boolean defaultValue);
@JSBody(params = { "def" }, script = "return (typeof this.showBootMenuOnLaunch === \"boolean\") ? this.showBootMenuOnLaunch : def;")
boolean getShowBootMenuOnLaunch(boolean defaultValue);
@JSBody(params = { "def" }, script = "return (typeof this.bootMenuBlocksUnsignedClients === \"boolean\") ? this.bootMenuBlocksUnsignedClients : def;")
boolean getBootMenuBlocksUnsignedClients(boolean defaultValue);
@JSBody(params = { "def" }, script = "return (typeof this.allowBootMenu === \"boolean\") ? this.allowBootMenu : def;")
boolean getAllowBootMenu(boolean defaultValue);
@JSBody(params = { "def" }, script = "return (typeof this.forceProfanityFilter === \"boolean\") ? this.forceProfanityFilter : def;")
boolean getForceProfanityFilter(boolean defaultValue);
@JSBody(params = { "def" }, script = "return (typeof this.forceWebGL1 === \"boolean\") ? this.forceWebGL1 : def;")
boolean getForceWebGL1(boolean defaultValue);
@JSBody(params = { "def" }, script = "return (typeof this.forceWebGL2 === \"boolean\") ? this.forceWebGL2 : def;")
boolean getForceWebGL2(boolean defaultValue);
@JSBody(params = { "def" }, script = "return (typeof this.allowExperimentalWebGL1 === \"boolean\") ? this.allowExperimentalWebGL1 : def;")
boolean getAllowExperimentalWebGL1(boolean defaultValue);
@JSBody(params = { "def" }, script = "return (typeof this.useWebGLExt === \"boolean\") ? this.useWebGLExt : def;")
boolean getUseWebGLExt(boolean defaultValue);
@JSBody(params = { "def" }, script = "return (typeof this.useDelayOnSwap === \"boolean\") ? this.useDelayOnSwap : def;")
boolean getUseDelayOnSwap(boolean defaultValue);
@JSBody(params = { "def" }, script = "return (typeof this.useJOrbisAudioDecoder === \"boolean\") ? this.useJOrbisAudioDecoder : def;")
boolean getUseJOrbisAudioDecoder(boolean defaultValue);
@JSBody(params = { "def" }, script = "return (typeof this.useXHRFetch === \"boolean\") ? this.useXHRFetch : def;")
boolean getUseXHRFetch(boolean defaultValue);
@JSBody(params = { "def" }, script = "return (typeof this.useVisualViewport === \"boolean\") ? this.useVisualViewport : def;")
boolean getUseVisualViewport(boolean defaultValue);
@JSBody(params = { "def" }, script = "return (typeof this.deobfStackTraces === \"boolean\") ? this.deobfStackTraces : def;")
boolean getDeobfStackTraces(boolean deobfStackTraces);
@JSBody(params = { "def" }, script = "return (typeof this.disableBlobURLs === \"boolean\") ? this.disableBlobURLs : def;")
boolean getDisableBlobURLs(boolean deobfStackTraces);
@JSBody(params = { "def" }, script = "return (typeof this.eaglerNoDelay === \"boolean\") ? this.eaglerNoDelay : def;")
boolean getEaglerNoDelay(boolean deobfStackTraces);
@JSBody(params = { "def" }, script = "return (typeof this.ramdiskMode === \"boolean\") ? this.ramdiskMode : def;")
boolean getRamdiskMode(boolean deobfStackTraces);
@JSBody(params = { "def" }, script = "return (typeof this.singleThreadMode === \"boolean\") ? this.singleThreadMode : def;")
boolean getSingleThreadMode(boolean deobfStackTraces);
@JSBody(params = { "def" }, script = "return (typeof this.enforceVSync === \"boolean\") ? this.enforceVSync : def;")
boolean getEnforceVSync(boolean enforceVSync);
@JSBody(params = { "def" }, script = "return (typeof this.enableEPKVersionCheck === \"boolean\") ? this.enableEPKVersionCheck : def;")
boolean getEnableEPKVersionCheck(boolean deobfStackTraces);
}

View File

@ -0,0 +1,32 @@
package net.lax1dude.eaglercraft.v1_8.internal.wasm_gc_teavm.opts;
import org.teavm.jso.JSBody;
import org.teavm.jso.JSObject;
/**
* Copyright (c) 2024 lax1dude. All Rights Reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
public interface JSEaglercraftXOptsServer extends JSObject {
@JSBody(script = "return (typeof this.addr === \"string\") ? this.addr : null;")
String getAddr();
@JSBody(params = { "def" }, script = "return (typeof this.hideAddr === \"boolean\") ? this.hideAddr : def;")
boolean getHideAddr(boolean defaultValue);
@JSBody(params = { "def" }, script = "return (typeof this.name === \"string\") ? this.name : def;")
String getName(String defaultValue);
}

View File

@ -0,0 +1,143 @@
package net.lax1dude.eaglercraft.v1_8.sp.internal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import org.teavm.interop.Import;
import org.teavm.jso.core.JSString;
import org.teavm.jso.typedarrays.Uint8Array;
import net.lax1dude.eaglercraft.v1_8.internal.IPCPacketData;
import net.lax1dude.eaglercraft.v1_8.internal.PlatformRuntime;
import net.lax1dude.eaglercraft.v1_8.internal.buffer.ByteBuffer;
import net.lax1dude.eaglercraft.v1_8.internal.buffer.WASMGCBufferAllocator;
import net.lax1dude.eaglercraft.v1_8.internal.buffer.WASMGCDirectArrayConverter;
import net.lax1dude.eaglercraft.v1_8.internal.wasm_gc_teavm.BetterJSStringConverter;
import net.lax1dude.eaglercraft.v1_8.internal.wasm_gc_teavm.WASMGCClientConfigAdapter;
import net.lax1dude.eaglercraft.v1_8.log4j.LogManager;
import net.lax1dude.eaglercraft.v1_8.log4j.Logger;
import net.lax1dude.eaglercraft.v1_8.sp.server.internal.wasm_gc_teavm.JS_IPCPacketData;
import net.lax1dude.eaglercraft.v1_8.sp.server.internal.wasm_gc_teavm.SingleThreadWorker;
/**
* Copyright (c) 2024 lax1dude. All Rights Reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
public class ClientPlatformSingleplayer {
private static final Logger logger = LogManager.getLogger("ClientPlatformSingleplayer");
private static boolean isSingleThreadMode = false;
private static final LinkedList<IPCPacketData> singleThreadMessageQueue = new LinkedList<>();
public static void startIntegratedServer(boolean singleThreadMode) {
singleThreadMode |= ((WASMGCClientConfigAdapter)PlatformRuntime.getClientConfigAdapter()).isSingleThreadModeTeaVM();
if(singleThreadMode) {
if(!isSingleThreadMode) {
SingleThreadWorker.singleThreadStartup(singleThreadMessageQueue::add);
isSingleThreadMode = true;
}
}else {
if(!startIntegratedServer0()) {
logger.error("Failed to start integrated server!");
logger.error("Falling back to single thread mode...");
startIntegratedServer(true);
}else {
logger.info("Integrated server started");
}
}
}
@Import(module = "clientPlatformSingleplayer", name = "startIntegratedServer")
private static native boolean startIntegratedServer0();
public static void sendPacket(IPCPacketData packet) {
if(isSingleThreadMode) {
SingleThreadWorker.sendPacketToWorker(packet);
}else {
ByteBuffer buf = WASMGCDirectArrayConverter.byteArrayToBuffer(packet.contents);
try {
sendPacket0(BetterJSStringConverter.stringToJS(packet.channel), WASMGCBufferAllocator.getUnsignedByteBufferView(buf));
}finally {
PlatformRuntime.freeByteBuffer(buf);
}
}
}
@Import(module = "clientPlatformSingleplayer", name = "sendPacket")
private static native void sendPacket0(JSString channel, Uint8Array arr);
public static List<IPCPacketData> recieveAllPacket() {
if(isSingleThreadMode) {
if(singleThreadMessageQueue.size() == 0) {
return null;
}else {
List<IPCPacketData> ret = new ArrayList<>(singleThreadMessageQueue);
singleThreadMessageQueue.clear();
return ret;
}
}else {
int cnt = getAvailablePackets();
if(cnt == 0) {
return null;
}
IPCPacketData[] ret = new IPCPacketData[cnt];
for(int i = 0; i < cnt; ++i) {
ret[i] = getNextPacket().internalize();
}
return Arrays.asList(ret);
}
}
@Import(module = "clientPlatformSingleplayer", name = "getAvailablePackets")
private static native int getAvailablePackets();
@Import(module = "clientPlatformSingleplayer", name = "getNextPacket")
private static native JS_IPCPacketData getNextPacket();
public static boolean canKillWorker() {
return !isSingleThreadMode;
}
@Import(module = "clientPlatformSingleplayer", name = "killWorker")
public static native void killWorker();
public static boolean isRunningSingleThreadMode() {
return isSingleThreadMode;
}
public static boolean isSingleThreadModeSupported() {
return true;
}
public static void updateSingleThreadMode() {
if(isSingleThreadMode) {
SingleThreadWorker.singleThreadUpdate();
}
}
public static void showCrashReportOverlay(String report, int x, int y, int w, int h) {
showCrashReportOverlay0(BetterJSStringConverter.stringToJS(report), x, y, w, h);
}
@Import(module = "clientPlatformSingleplayer", name = "showCrashReportOverlay")
private static native void showCrashReportOverlay0(JSString report, int x, int y, int w, int h);
@Import(module = "clientPlatformSingleplayer", name = "hideCrashReportOverlay")
public static native void hideCrashReportOverlay();
}

View File

@ -0,0 +1,146 @@
package net.lax1dude.eaglercraft.v1_8.sp.server.internal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.function.Consumer;
import org.teavm.interop.Import;
import org.teavm.jso.JSFunctor;
import org.teavm.jso.JSObject;
import org.teavm.jso.core.JSString;
import org.teavm.jso.typedarrays.Uint8Array;
import net.lax1dude.eaglercraft.v1_8.Filesystem;
import net.lax1dude.eaglercraft.v1_8.internal.IClientConfigAdapter;
import net.lax1dude.eaglercraft.v1_8.internal.IEaglerFilesystem;
import net.lax1dude.eaglercraft.v1_8.internal.IPCPacketData;
import net.lax1dude.eaglercraft.v1_8.internal.PlatformRuntime;
import net.lax1dude.eaglercraft.v1_8.internal.buffer.ByteBuffer;
import net.lax1dude.eaglercraft.v1_8.internal.buffer.WASMGCBufferAllocator;
import net.lax1dude.eaglercraft.v1_8.internal.buffer.WASMGCDirectArrayConverter;
import net.lax1dude.eaglercraft.v1_8.internal.vfs2.VFile2;
import net.lax1dude.eaglercraft.v1_8.internal.wasm_gc_teavm.BetterJSStringConverter;
import net.lax1dude.eaglercraft.v1_8.internal.wasm_gc_teavm.WASMGCClientConfigAdapter;
import net.lax1dude.eaglercraft.v1_8.sp.server.EaglerIntegratedServerWorker;
import net.lax1dude.eaglercraft.v1_8.sp.server.IWASMCrashCallback;
import net.lax1dude.eaglercraft.v1_8.sp.server.internal.wasm_gc_teavm.JS_IPCPacketData;
/**
* Copyright (c) 2024 lax1dude. All Rights Reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
public class ServerPlatformSingleplayer {
private static final List<IPCPacketData> messageQueue = new LinkedList<>();
private static boolean singleThreadMode = false;
private static Consumer<IPCPacketData> singleThreadCB = null;
private static IEaglerFilesystem filesystem = null;
public static void initializeContext() {
singleThreadMode = false;
singleThreadCB = null;
filesystem = Filesystem.getHandleFor(getClientConfigAdapter().getWorldsDB());
VFile2.setPrimaryFilesystem(filesystem);
}
public static IEaglerFilesystem getWorldsDatabase() {
return filesystem;
}
public static void initializeContextSingleThread(Consumer<IPCPacketData> packetSendCallback) {
singleThreadMode = true;
singleThreadCB = packetSendCallback;
filesystem = Filesystem.getHandleFor(getClientConfigAdapter().getWorldsDB());
}
public static void sendPacket(IPCPacketData packet) {
if(singleThreadMode) {
singleThreadCB.accept(packet);
}else {
ByteBuffer buf = WASMGCDirectArrayConverter.byteArrayToBuffer(packet.contents);
try {
sendPacket0(BetterJSStringConverter.stringToJS(packet.channel), WASMGCBufferAllocator.getUnsignedByteBufferView(buf));
}finally {
PlatformRuntime.freeByteBuffer(buf);
}
}
}
@Import(module = "serverPlatformSingleplayer", name = "sendPacket")
private static native void sendPacket0(JSString channel, Uint8Array arr);
public static List<IPCPacketData> recieveAllPacket() {
if(singleThreadMode) {
if(messageQueue.size() == 0) {
return null;
}else {
List<IPCPacketData> ret = new ArrayList<>(messageQueue);
messageQueue.clear();
return ret;
}
}else {
int cnt = getAvailablePackets();
if(cnt == 0) {
return null;
}
IPCPacketData[] ret = new IPCPacketData[cnt];
for(int i = 0; i < cnt; ++i) {
ret[i] = getNextPacket().internalize();
}
return Arrays.asList(ret);
}
}
@Import(module = "serverPlatformSingleplayer", name = "getAvailablePackets")
private static native int getAvailablePackets();
@Import(module = "serverPlatformSingleplayer", name = "getNextPacket")
private static native JS_IPCPacketData getNextPacket();
@Import(module = "platformRuntime", name = "immediateContinue")
public static native void immediateContinue();
public static IClientConfigAdapter getClientConfigAdapter() {
return WASMGCClientConfigAdapter.instance;
}
public static boolean isSingleThreadMode() {
return singleThreadMode;
}
public static void recievePacketSingleThreadTeaVM(IPCPacketData pkt) {
messageQueue.add(pkt);
}
public static void setCrashCallbackWASM(IWASMCrashCallback callback) {
setCrashCallbackWASM0().call(callback != null ? callback::callback : null);
}
@JSFunctor
private static interface JSWASMCrashCallback extends JSObject {
void callback(String crashReport, boolean terminated);
}
private static interface JSWASMCrashCallbackInterface extends JSObject {
void call(JSWASMCrashCallback callback);
}
@Import(module = "serverPlatformSingleplayer", name = "setCrashCallback")
private static native JSWASMCrashCallbackInterface setCrashCallbackWASM0();
}

View File

@ -0,0 +1,37 @@
package net.lax1dude.eaglercraft.v1_8.sp.server.internal.wasm_gc_teavm;
import org.teavm.jso.JSObject;
import org.teavm.jso.JSProperty;
import org.teavm.jso.typedarrays.Uint8Array;
import net.lax1dude.eaglercraft.v1_8.internal.IPCPacketData;
import net.lax1dude.eaglercraft.v1_8.internal.buffer.WASMGCDirectArrayConverter;
/**
* Copyright (c) 2024 lax1dude. All Rights Reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
public interface JS_IPCPacketData extends JSObject {
@JSProperty
String getCh();
@JSProperty
Uint8Array getData();
default IPCPacketData internalize() {
return new IPCPacketData(getCh(), WASMGCDirectArrayConverter.externU8ArrayToByteArray(getData()));
}
}

View File

@ -0,0 +1,44 @@
package net.lax1dude.eaglercraft.v1_8.sp.server.internal.wasm_gc_teavm;
import java.util.function.Consumer;
import net.lax1dude.eaglercraft.v1_8.internal.IPCPacketData;
import net.lax1dude.eaglercraft.v1_8.log4j.LogManager;
import net.lax1dude.eaglercraft.v1_8.log4j.Logger;
import net.lax1dude.eaglercraft.v1_8.sp.server.EaglerIntegratedServerWorker;
import net.lax1dude.eaglercraft.v1_8.sp.server.internal.ServerPlatformSingleplayer;
/**
* Copyright (c) 2024 lax1dude. All Rights Reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
public class SingleThreadWorker {
private static final Logger logger = LogManager.getLogger("SingleThreadWorker");
public static void singleThreadStartup(Consumer<IPCPacketData> packetSendCallback) {
logger.info("Starting single-thread mode worker...");
ServerPlatformSingleplayer.initializeContextSingleThread(packetSendCallback);
EaglerIntegratedServerWorker.singleThreadMain();
}
public static void sendPacketToWorker(IPCPacketData pkt) {
ServerPlatformSingleplayer.recievePacketSingleThreadTeaVM(pkt);
}
public static void singleThreadUpdate() {
EaglerIntegratedServerWorker.singleThreadUpdate();
}
}

View File

@ -0,0 +1,61 @@
package net.lax1dude.eaglercraft.v1_8.sp.server.internal.wasm_gc_teavm;
import java.io.PrintStream;
import org.teavm.interop.Import;
import org.teavm.jso.JSObject;
import net.lax1dude.eaglercraft.v1_8.EagRuntime;
import net.lax1dude.eaglercraft.v1_8.internal.PlatformRuntime;
import net.lax1dude.eaglercraft.v1_8.internal.wasm_gc_teavm.WASMGCClientConfigAdapter;
import net.lax1dude.eaglercraft.v1_8.sp.ipc.IPCPacket15Crashed;
import net.lax1dude.eaglercraft.v1_8.sp.ipc.IPCPacketFFProcessKeepAlive;
import net.lax1dude.eaglercraft.v1_8.sp.server.EaglerIntegratedServerWorker;
import net.lax1dude.eaglercraft.v1_8.sp.server.internal.ServerPlatformSingleplayer;
/**
* Copyright (c) 2024 lax1dude. All Rights Reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
public class WorkerMain {
public static void _main() {
PrintStream systemOut = System.out;
PrintStream systemErr = System.err;
try {
systemOut.println("WorkerMain: [INFO] eaglercraftx worker thread is starting...");
JSObject startArgs = getEaglerXOpts();
systemOut.println("WorkerMain: [INFO] reading configuration");
((WASMGCClientConfigAdapter)WASMGCClientConfigAdapter.instance).loadNative(startArgs);
systemOut.println("WorkerMain: [INFO] initializing server runtime");
ServerPlatformSingleplayer.initializeContext();
systemOut.println("WorkerMain: [INFO] starting worker thread");
PlatformRuntime.setThreadName("IntegratedServer");
EaglerIntegratedServerWorker.serverMain();
}catch(Throwable t) {
System.setOut(systemOut);
System.setErr(systemErr);
systemErr.println("WorkerMain: [ERROR] uncaught exception thrown!");
EagRuntime.debugPrintStackTraceToSTDERR(t);
EaglerIntegratedServerWorker.sendIPCPacket(new IPCPacket15Crashed("UNCAUGHT EXCEPTION CAUGHT IN WORKER PROCESS!\n\n" + EagRuntime.getStackTrace(t)));
EaglerIntegratedServerWorker.sendIPCPacket(new IPCPacketFFProcessKeepAlive(IPCPacketFFProcessKeepAlive.EXITED));
}finally {
systemErr.println("WorkerMain: [ERROR] eaglercraftx worker thread has exited");
}
}
@Import(module = "platformRuntime", name = "getEaglercraftXOpts")
private static native JSObject getEaglerXOpts();
}

View File

@ -0,0 +1,80 @@
/*
* Copyright (c) 2024 lax1dude. All Rights Reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
const WASMGCBufferAllocatorName = "WASMGCBufferAllocator";
/**
* @param {number} addr
* @param {number} length
* @return {Int8Array}
*/
eagruntimeImpl.WASMGCBufferAllocator["getByteBufferView"] = function(addr, length) {
return new Int8Array(heapArrayBuffer, addr, length);
}
/**
* @param {number} addr
* @param {number} length
* @return {Uint8Array}
*/
eagruntimeImpl.WASMGCBufferAllocator["getUnsignedByteBufferView"] = function(addr, length) {
return new Uint8Array(heapArrayBuffer, addr, length);
}
/**
* @param {number} addr
* @param {number} length
* @return {Uint8ClampedArray}
*/
eagruntimeImpl.WASMGCBufferAllocator["getUnsignedClampedByteBufferView"] = function(addr, length) {
return new Uint8ClampedArray(heapArrayBuffer, addr, length);
}
/**
* @param {number} addr
* @param {number} length
* @return {Int16Array}
*/
eagruntimeImpl.WASMGCBufferAllocator["getShortBufferView"] = function(addr, length) {
return new Int16Array(heapArrayBuffer, addr, length);
}
/**
* @param {number} addr
* @param {number} length
* @return {Uint16Array}
*/
eagruntimeImpl.WASMGCBufferAllocator["getUnsignedShortBufferView"] = function(addr, length) {
return new Uint16Array(heapArrayBuffer, addr, length);
}
/**
* @param {number} addr
* @param {number} length
* @return {Int32Array}
*/
eagruntimeImpl.WASMGCBufferAllocator["getIntBufferView"] = function(addr, length) {
return new Int32Array(heapArrayBuffer, addr, length);
}
/**
* @param {number} addr
* @param {number} length
* @return {Float32Array}
*/
eagruntimeImpl.WASMGCBufferAllocator["getFloatBufferView"] = function(addr, length) {
return new Float32Array(heapArrayBuffer, addr, length);
}

View File

@ -0,0 +1,209 @@
/*
* Copyright (c) 2024 lax1dude. All Rights Reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
const clientPlatfSPName = "clientPlatformSingleplayer";
function initializeClientPlatfSP(spImports) {
/** @type {Worker|null} */
var workerObj = null;
const clientMessageQueue = new EaglerLinkedQueue();
const workerBootstrapSource = "\"use strict\"; (function(ctx, globals) {" +
"globals.__eaglerXOnMessage = function(o) {" +
"globals.__eaglerXOnMessage = function(oo) { console.error(\"Dropped IPC packet that was sent too early!\"); };" +
"const eagRuntimeJSURL = URL.createObjectURL(new Blob([ o.eagruntimeJS ], { type: \"text/javascript;charset=utf-8\" }));" +
"ctx.getEaglercraftXOpts = function() { return o.eaglercraftXOpts; };" +
"ctx.getEagRuntimeJSURL = function() { return eagRuntimeJSURL; };" +
"ctx.getClassesWASMURL = function() { return o.classesWASM; };" +
"ctx.getClassesDeobfWASMURL = function() { return o.classesDeobfWASM; };" +
"ctx.getClassesTEADBGURL = function() { return o.classesTEADBG; };" +
"ctx.getEPKFiles = function() { return null; };" +
"ctx.getRootElement = function() { return null; };" +
"ctx.getMainArgs = function() { return [\"_worker_process_\"]; };" +
"ctx.getImageURL = function(idx) { return null; };" +
"ctx.runMain = function(mainFunc) { mainFunc(); };" +
"importScripts(eagRuntimeJSURL);" +
"};" +
"addEventListener(\"message\", function(evt) { globals.__eaglerXOnMessage(evt.data); });" +
"})(self.__eaglercraftXLoaderContext = {}, self);";
/** @type {string|null} */
var workerURL = null;
/**
* @return {Promise<boolean>}
*/
async function startIntegratedServerImpl() {
if(!workerURL) {
workerURL = URL.createObjectURL(new Blob([ workerBootstrapSource ], { "type": "text/javascript;charset=utf8" }));
}
try {
workerObj = new Worker(workerURL);
}catch(ex) {
eagStackTrace(ERROR, "Failed to create worker", ex);
return false;
}
workerObj.addEventListener("error", /** @type {function(Event)} */ (function(/** ErrorEvent */ evt) {
eagStackTrace(ERROR, "Worker Error", /** @type {Error} */ (evt.error));
}));
workerObj.addEventListener("message", /** @type {function(Event)} */ (function(/** MessageEvent */ evt) {
const channel = evt.data["ch"];
if(!channel) {
eagError("Recieved IPC packet with null channel");
return;
}
if(channel === "~!LOGGER") {
addLogMessageImpl(evt.data["txt"], evt.data["err"]);
return;
}
const buf = evt.data["dat"];
if(!buf) {
eagError("Recieved IPC packet with null buffer");
return;
}
if(serverLANPeerPassIPCFunc(channel, buf)) {
return;
}
clientMessageQueue.push({
"ch": channel,
"data": new Uint8Array(buf),
"_next": null
});
}));
const classesTEADBGCopy = new Int8Array(classesTEADBG.length);
classesTEADBGCopy.set(classesTEADBG, 0);
var eagRuntimeJS;
try {
eagRuntimeJS = await fetch(/** @type {string} */ (eagRuntimeJSURL), { "cache": "force-cache" })
.then((resp) => resp.arrayBuffer());
}catch(ex) {
eagStackTrace(ERROR, "Failed to fetch eagruntime.js contents", ex);
try {
workerObj.terminate();
}catch(exx) {
}
return false;
}
workerObj.postMessage({
"eaglercraftXOpts": eaglercraftXOpts,
"eagruntimeJS": eagRuntimeJS,
"classesWASM": classesWASMModule,
"classesDeobfWASM": classesDeobfWASMModule,
"classesTEADBG": classesTEADBGCopy.buffer
});
return true;
};
spImports["startIntegratedServer"] = new WebAssembly.Suspending(startIntegratedServerImpl);
/**
* @param {string} channel
* @param {Uint8Array} arr
*/
spImports["sendPacket"] = function(channel, arr) {
if(workerObj) {
const copiedArray = new Uint8Array(arr.length);
copiedArray.set(arr, 0);
workerObj.postMessage({
"ch": channel,
"dat": copiedArray.buffer
});
}
};
/**
* @param {string} channel
* @param {!ArrayBuffer} arr
*/
sendIPCPacketFunc = function(channel, arr) {
if(workerObj) {
workerObj.postMessage({
"ch": channel,
"dat": arr
});
}
};
spImports["getAvailablePackets"] = clientMessageQueue.getLength.bind(clientMessageQueue);
spImports["getNextPacket"] = clientMessageQueue.shift.bind(clientMessageQueue);
spImports["killWorker"] = function() {
if(workerObj) {
workerObj.terminate();
workerObj = null;
}
};
/** @type {HTMLElement} */
var integratedServerCrashPanel = null;
/**
* @param {string} report
* @param {number} x
* @param {number} y
* @param {number} w
* @param {number} h
*/
spImports["showCrashReportOverlay"] = function(report, x, y, w, h) {
if(!integratedServerCrashPanel) {
integratedServerCrashPanel = /** @type {HTMLElement} */ (document.createElement("div"));
integratedServerCrashPanel.setAttribute("style", "z-index:99;position:absolute;background-color:black;color:white;overflow-x:hidden;overflow-y:scroll;overflow-wrap:break-word;white-space:pre-wrap;font:18px sans-serif;padding:20px;display:none;");
integratedServerCrashPanel.classList.add("_eaglercraftX_integratedserver_crash_element");
parentElement.appendChild(integratedServerCrashPanel);
}
integratedServerCrashPanel.innerText = "";
integratedServerCrashPanel.innerText = "CURRENT DATE: " + (new Date()).toLocaleString() + "\n\n" + report;
const s = window.devicePixelRatio;
integratedServerCrashPanel.style.top = "" + (y / s) + "px";
integratedServerCrashPanel.style.left = "" + (x / s) + "px";
integratedServerCrashPanel.style.width = "" + ((w / s) - 20) + "px";
integratedServerCrashPanel.style.height = "" + ((h / s) - 20) + "px";
integratedServerCrashPanel.style.display = "block";
};
spImports["hideCrashReportOverlay"] = function() {
if(integratedServerCrashPanel) {
integratedServerCrashPanel.style.display = "none";
}
};
}
function initializeNoClientPlatfSP(spImports) {
setUnsupportedFunc(spImports, clientPlatfSPName, "startIntegratedServer");
setUnsupportedFunc(spImports, clientPlatfSPName, "sendPacket");
setUnsupportedFunc(spImports, clientPlatfSPName, "getAvailablePackets");
setUnsupportedFunc(spImports, clientPlatfSPName, "getNextPacket");
setUnsupportedFunc(spImports, clientPlatfSPName, "killWorker");
setUnsupportedFunc(spImports, clientPlatfSPName, "showCrashReportOverlay");
setUnsupportedFunc(spImports, clientPlatfSPName, "hideCrashReportOverlay");
}

View File

@ -0,0 +1,98 @@
/*
* Copyright (c) 2024 lax1dude. All Rights Reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
async function entryPoint() {
try {
eaglercraftXOpts = self.__eaglercraftXLoaderContext.getEaglercraftXOpts();
eagRuntimeJSURL = self.__eaglercraftXLoaderContext.getEagRuntimeJSURL();
const classesWASM = self.__eaglercraftXLoaderContext.getClassesWASMURL();
const classesDeobfWASM = self.__eaglercraftXLoaderContext.getClassesDeobfWASMURL();
const classesTEADBGURL = self.__eaglercraftXLoaderContext.getClassesTEADBGURL();
epkFileList = self.__eaglercraftXLoaderContext.getEPKFiles();
rootElement = self.__eaglercraftXLoaderContext.getRootElement();
splashURL = self.__eaglercraftXLoaderContext.getImageURL(0);
pressAnyKeyURL = self.__eaglercraftXLoaderContext.getImageURL(1);
crashURL = self.__eaglercraftXLoaderContext.getImageURL(2);
faviconURL = self.__eaglercraftXLoaderContext.getImageURL(3);
const args = self.__eaglercraftXLoaderContext.getMainArgs();
const isWorker = args[0] === "_worker_process_";
if(!isWorker) {
if(!await initializeContext()) {
return;
}
}else {
setLoggerContextName("worker");
await initializeContextWorker();
}
eagInfo("Loading EaglercraftX WASM GC binary...");
const teavm = await wasmGC.load(classesWASM, {
stackDeobfuscator: {
enabled: true,
path: classesDeobfWASM,
infoLocation: "external",
externalInfoPath: classesTEADBGURL
},
installImports: function(/** {Object} */ importObj) {
importObj[WASMGCBufferAllocatorName] = eagruntimeImpl.WASMGCBufferAllocator;
importObj[platfApplicationName] = eagruntimeImpl.platformApplication;
importObj[platfAssetsName] = eagruntimeImpl.platformAssets;
importObj[platfAudioName] = eagruntimeImpl.platformAudio;
importObj[platfFilesystemName] = eagruntimeImpl.platformFilesystem;
importObj[platfInputName] = eagruntimeImpl.platformInput;
importObj[platfNetworkingName] = eagruntimeImpl.platformNetworking;
importObj[platfOpenGLName] = eagruntimeImpl.platformOpenGL;
importObj[platfRuntimeName] = eagruntimeImpl.platformRuntime;
importObj[platfScreenRecordName] = eagruntimeImpl.platformScreenRecord;
importObj[platfVoiceClientName] = eagruntimeImpl.platformVoiceClient;
importObj[platfWebRTCName] = eagruntimeImpl.platformWebRTC;
importObj[platfWebViewName] = eagruntimeImpl.platformWebView;
importObj[clientPlatfSPName] = eagruntimeImpl.clientPlatformSingleplayer;
importObj[serverPlatfSPName] = eagruntimeImpl.serverPlatformSingleplayer;
importObj["teavm"]["notifyHeapResized"] = function() {
handleMemoryResized(teavm.exports.memory);
};
}
});
classesWASMModule = teavm.modules.classes;
classesDeobfWASMModule = teavm.modules.deobfuscator;
classesTEADBG = teavm.exports.debugInfo;
handleMemoryResized(teavm.exports.memory);
deobfuscatorFunc = /** @type {function(Array<number>):Array<Object>|null} */ (teavm.exports["deobfuscator"]);
eagInfo("Calling entry point with args: {}", JSON.stringify(args));
try {
await WebAssembly.promising(teavm.exports["main"]["__impl"])(args);
}catch(ex) {
teavm.exports["main"]["__rethrow"](ex);
}finally {
eagWarn("Main function has returned!");
}
}catch(ex) {
displayUncaughtCrashReport(ex);
}
}
if(typeof self.__eaglercraftXLoaderContext === "object") {
self.__eaglercraftXLoaderContext.runMain(entryPoint);
}else {
console.error("???");
}

View File

@ -0,0 +1,970 @@
/*
* Copyright (c) 2024 lax1dude. All Rights Reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
const eagruntimeImpl = {
WASMGCBufferAllocator: {},
platformApplication: {},
platformAssets: {},
platformAudio: {},
platformFilesystem: {},
platformInput: {},
platformNetworking: {},
platformOpenGL: {},
platformRuntime: {},
platformScreenRecord: {},
platformVoiceClient: {},
platformWebRTC: {},
platformWebView: {},
clientPlatformSingleplayer: {},
serverPlatformSingleplayer: {}
};
/** @type {WebAssembly.Module} */
var classesWASMModule = null;
/** @type {WebAssembly.Module} */
var classesDeobfWASMModule = null;
/** @type {Int8Array} */
var classesTEADBG = null;
/** @type {function(Array<number>):Array<Object>|null} */
var deobfuscatorFunc = null;
/** @type {Array} */
var epkFileList = null;
/** @type {string|null} */
var splashURL = null;
/** @type {string|null} */
var pressAnyKeyURL = null;
/** @type {string|null} */
var crashURL = null;
/** @type {string|null} */
var faviconURL = null;
/** @type {Object} */
var eaglercraftXOpts = null;
/** @type {string|null} */
var eagRuntimeJSURL = null;
/** @type {HTMLElement} */
var rootElement = null;
/** @type {HTMLElement} */
var parentElement = null;
/** @type {HTMLCanvasElement} */
var canvasElement = null;
/** @type {WebGL2RenderingContext} */
var webglContext = null;
/** @type {boolean} */
var webglExperimental = false;
/** @type {number} */
var webglGLESVer = 0;
/** @type {AudioContext} */
var audioContext = null;
/** @type {WebAssembly.Memory} */
var heapMemory = null;
/** @type {ArrayBuffer} */
var heapArrayBuffer = null;
/** @type {Uint8Array} */
var heapU8Array = null;
/** @type {Int8Array} */
var heapI8Array = null;
/** @type {Uint16Array} */
var heapU16Array = null;
/** @type {Int16Array} */
var heapI16Array = null;
/** @type {Int32Array} */
var heapI32Array = null;
/** @type {Uint32Array} */
var heapU32Array = null;
/** @type {Float32Array} */
var heapF32Array = null;
/** @type {boolean} */
var isLikelyMobileBrowser = false;
/** @type {function(string, !ArrayBuffer)|null} */
var serverLANPeerPassIPCFunc = null;
/** @type {function(string, !ArrayBuffer)|null} */
var sendIPCPacketFunc = null;
/** @type {boolean} */
var isCrashed = false;
/** @type {Array<string>} */
const crashReportStrings = [];
/** @type {function()|null} */
var removeEventHandlers = null;
const runtimeOpts = {
localStorageNamespace: "_eaglercraftX",
openDebugConsoleOnLaunch: false,
fixDebugConsoleUnloadListener: false,
forceWebViewSupport: false,
enableWebViewCSP: true,
forceWebGL1: false,
forceWebGL2: false,
allowExperimentalWebGL1: true,
useWebGLExt: true,
useDelayOnSwap: false
};
function setupRuntimeOpts() {
if(typeof eaglercraftXOpts["localStorageNamespace"] === "string") runtimeOpts.localStorageNamespace = eaglercraftXOpts["localStorageNamespace"];
if(typeof eaglercraftXOpts["openDebugConsoleOnLaunch"] === "boolean") runtimeOpts.openDebugConsoleOnLaunch = eaglercraftXOpts["openDebugConsoleOnLaunch"];
if(typeof eaglercraftXOpts["fixDebugConsoleUnloadListener"] === "boolean") runtimeOpts.fixDebugConsoleUnloadListener = eaglercraftXOpts["fixDebugConsoleUnloadListener"];
if(typeof eaglercraftXOpts["forceWebViewSupport"] === "boolean") runtimeOpts.forceWebViewSupport = eaglercraftXOpts["forceWebViewSupport"];
if(typeof eaglercraftXOpts["enableWebViewCSP"] === "boolean") runtimeOpts.enableWebViewCSP = eaglercraftXOpts["enableWebViewCSP"];
if(typeof eaglercraftXOpts["forceWebGL1"] === "boolean") runtimeOpts.forceWebGL1 = eaglercraftXOpts["forceWebGL1"];
if(typeof eaglercraftXOpts["forceWebGL2"] === "boolean") runtimeOpts.forceWebGL2 = eaglercraftXOpts["forceWebGL2"];
if(typeof eaglercraftXOpts["allowExperimentalWebGL1"] === "boolean") runtimeOpts.allowExperimentalWebGL1 = eaglercraftXOpts["allowExperimentalWebGL1"];
if(typeof eaglercraftXOpts["useWebGLExt"] === "boolean") runtimeOpts.useWebGLExt = eaglercraftXOpts["useWebGLExt"];
if(typeof eaglercraftXOpts["useDelayOnSwap"] === "boolean") runtimeOpts.useDelayOnSwap = eaglercraftXOpts["useDelayOnSwap"];
}
/**
* @return {!Promise<boolean>}
*/
async function initializeContext() {
setupRuntimeOpts();
currentRedirectorFunc = addLogMessageImpl;
window.__curEaglerX188UnloadListenerCB = function() {
//TODO: Autosave somehow?
};
if(window.__isEaglerX188UnloadListenerSet !== "yes") {
window.onbeforeunload = function(evt) {
if(window.__curEaglerX188UnloadListenerCB) {
window.__curEaglerX188UnloadListenerCB();
}
return false;
};
window.__isEaglerX188UnloadListenerSet = "yes";
}
eagInfo("Initializing EagRuntime JS context...");
await initializePlatfRuntime();
initializePlatfApplication(eagruntimeImpl.platformApplication);
initializePlatfScreenRecord(eagruntimeImpl.platformScreenRecord);
initializePlatfVoiceClient(eagruntimeImpl.platformVoiceClient);
initializePlatfWebRTC(eagruntimeImpl.platformWebRTC);
initializePlatfWebView(eagruntimeImpl.platformWebView);
initializeClientPlatfSP(eagruntimeImpl.clientPlatformSingleplayer);
initializeNoServerPlatfSP(eagruntimeImpl.serverPlatformSingleplayer);
rootElement.classList.add("_eaglercraftX_root_element");
rootElement.style.overflow = "hidden";
/** @type {HTMLElement} */
var oldSplash = null;
var node;
while(node = rootElement.lastChild) {
if(!oldSplash) {
oldSplash = /** @type {HTMLElement} */ (node);
}
rootElement.removeChild(node);
}
parentElement = /** @type {HTMLElement} */ (document.createElement("div"));
parentElement.classList.add("_eaglercraftX_wrapper_element");
parentElement.style.position = "relative";
parentElement.style.width = "100%";
parentElement.style.height = "100%";
parentElement.style.overflow = "hidden";
parentElement.style.backgroundColor = "black";
rootElement.appendChild(parentElement);
if(oldSplash) {
oldSplash.style.position = "absolute";
oldSplash.style.top = "0px";
oldSplash.style.left = "0px";
oldSplash.style.right = "0px";
oldSplash.style.bottom = "0px";
oldSplash.style.zIndex = "2";
oldSplash.classList.add("_eaglercraftX_early_splash_element");
parentElement.appendChild(oldSplash);
}
await promiseTimeout(10);
const d = window.devicePixelRatio;
const iw = parentElement.clientWidth;
const ih = parentElement.clientHeight;
const sw = (d * iw) | 0;
const sh = (d * ih) | 0;
const canvasW = sw;
const canvasH = sh;
eagInfo("Initializing audio context");
if(typeof document.exitPointerLock === "function") {
var ua = navigator.userAgent;
if(ua !== null) {
ua = ua.toLowerCase();
isLikelyMobileBrowser = ua.indexOf("mobi") !== -1 || ua.indexOf("tablet") !== -1;
}else {
isLikelyMobileBrowser = false;
}
}else {
isLikelyMobileBrowser = true;
}
var audioCtx = null;
const createAudioContext = function() {
try {
audioCtx = new AudioContext();
}catch(ex) {
eagStackTrace(ERROR, "Could not initialize audio context", ex);
}
};
if(isLikelyMobileBrowser || !navigator.userActivation || !navigator.userActivation.hasBeenActive) {
const pressAnyKeyImage = /** @type {HTMLElement} */ (document.createElement("div"));
pressAnyKeyImage.classList.add("_eaglercraftX_press_any_key_image");
pressAnyKeyImage.style.position = "absolute";
pressAnyKeyImage.style.top = "0px";
pressAnyKeyImage.style.left = "0px";
pressAnyKeyImage.style.right = "0px";
pressAnyKeyImage.style.bottom = "0px";
pressAnyKeyImage.style.width = "100%";
pressAnyKeyImage.style.height = "100%";
pressAnyKeyImage.style.zIndex = "3";
pressAnyKeyImage.style.touchAction = "pan-x pan-y";
pressAnyKeyImage.style.background = "center / contain no-repeat url(\"" + pressAnyKeyURL + "\"), left / 1000000% 100% no-repeat url(\"" + pressAnyKeyURL + "\") white";
pressAnyKeyImage.style.setProperty("image-rendering", "pixelated");
parentElement.appendChild(pressAnyKeyImage);
await new Promise(function(resolve, reject) {
var resolved = false;
var mobilePressAnyKeyScreen;
var createAudioContextHandler = function() {
if(!resolved) {
resolved = true;
if(isLikelyMobileBrowser) {
parentElement.removeChild(mobilePressAnyKeyScreen);
}else {
window.removeEventListener("keydown", /** @type {function(Event)} */ (createAudioContextHandler));
parentElement.removeEventListener("mousedown", /** @type {function(Event)} */ (createAudioContextHandler));
parentElement.removeEventListener("touchstart", /** @type {function(Event)} */ (createAudioContextHandler));
}
try {
createAudioContext();
}catch(ex) {
reject(ex);
return;
}
resolve();
}
};
if(isLikelyMobileBrowser) {
mobilePressAnyKeyScreen = /** @type {HTMLElement} */ (document.createElement("div"));
mobilePressAnyKeyScreen.classList.add("_eaglercraftX_mobile_press_any_key");
mobilePressAnyKeyScreen.setAttribute("style", "position:absolute;background-color:white;font-family:sans-serif;top:10%;left:10%;right:10%;bottom:10%;border:5px double black;padding:calc(5px + 7vh) 15px;text-align:center;font-size:20px;user-select:none;z-index:10;");
mobilePressAnyKeyScreen.innerHTML = "<h3 style=\"margin-block-start:0px;margin-block-end:0px;margin:20px 5px;\">Mobile Browser Detected</h3>"
+ "<p style=\"margin-block-start:0px;margin-block-end:0px;margin:20px 5px;\">Warning: EaglercraftX WASM-GC requires a lot of memory and may not be stable on most mobile devices!</p>"
+ "<p style=\"margin-block-start:0px;margin-block-end:0px;margin:20px 2px;\"><button style=\"font: 24px sans-serif;font-weight:bold;\" class=\"_eaglercraftX_mobile_launch_client\">Launch EaglercraftX</button></p>"
/*+ (allowBootMenu ? "<p style=\"margin-block-start:0px;margin-block-end:0px;margin:20px 2px;\"><button style=\"font: 24px sans-serif;\" class=\"_eaglercraftX_mobile_enter_boot_menu\">Enter Boot Menu</button></p>" : "")*/
+ "<p style=\"margin-block-start:0px;margin-block-end:0px;margin:25px 5px;\">(Tablets and phones with large screens work best)</p>";
mobilePressAnyKeyScreen.querySelector("._eaglercraftX_mobile_launch_client").addEventListener("click", /** @type {function(Event)} */ (createAudioContextHandler));
parentElement.appendChild(mobilePressAnyKeyScreen);
}else {
window.addEventListener("keydown", /** @type {function(Event)} */ (createAudioContextHandler));
parentElement.addEventListener("mousedown", /** @type {function(Event)} */ (createAudioContextHandler));
parentElement.addEventListener("touchstart", /** @type {function(Event)} */ (createAudioContextHandler));
}
});
parentElement.removeChild(pressAnyKeyImage);
}else {
createAudioContext();
}
if(audioCtx) {
setCurrentAudioContext(audioCtx, eagruntimeImpl.platformAudio);
}else {
setNoAudioContext(eagruntimeImpl.platformAudio);
}
eagInfo("Creating main canvas");
canvasElement = /** @type {HTMLCanvasElement} */ (document.createElement("canvas"));
canvasElement.classList.add("_eaglercraftX_canvas_element");
canvasElement.style.width = "100%";
canvasElement.style.height = "100%";
canvasElement.style.zIndex = "1";
canvasElement.style.touchAction = "pan-x pan-y";
canvasElement.style.setProperty("-webkit-touch-callout", "none");
canvasElement.style.setProperty("-webkit-tap-highlight-color", "rgba(255, 255, 255, 0)");
canvasElement.style.setProperty("image-rendering", "pixelated");
canvasElement.width = canvasW;
canvasElement.height = canvasH;
parentElement.appendChild(canvasElement);
await initPlatformInput(eagruntimeImpl.platformInput);
eagInfo("Creating WebGL context");
parentElement.addEventListener("webglcontextcreationerror", function(evt) {
eagError("[WebGL Error]: {}", evt.statusMessage);
});
/** @type {Object} */
const contextCreationHints = {
"antialias": false,
"depth": false,
"powerPreference": "high-performance",
"desynchronized": true,
"preserveDrawingBuffer": false,
"premultipliedAlpha": false,
"alpha": false
};
/** @type {number} */
var glesVer;
/** @type {boolean} */
var experimental = false;
/** @type {WebGL2RenderingContext|null} */
var webgl_;
if(runtimeOpts.forceWebGL2) {
eagInfo("Note: Forcing WebGL 2.0 context");
glesVer = 300;
webgl_ = /** @type {WebGL2RenderingContext} */ (canvasElement.getContext("webgl2", contextCreationHints));
if(!webgl_) {
showIncompatibleScreen("WebGL 2.0 is not supported on this device!");
return false;
}
}else {
if(runtimeOpts.forceWebGL1) {
eagInfo("Note: Forcing WebGL 1.0 context");
glesVer = 200;
webgl_ = /** @type {WebGL2RenderingContext} */ (canvasElement.getContext("webgl", contextCreationHints));
if(!webgl_) {
if(runtimeOpts.allowExperimentalWebGL1) {
experimental = true;
webgl_ = /** @type {WebGL2RenderingContext} */ (canvasElement.getContext("experimental-webgl", contextCreationHints));
if(!webgl_) {
showIncompatibleScreen("WebGL is not supported on this device!");
return false;
}
}else {
showIncompatibleScreen("WebGL is not supported on this device!");
return false;
}
}
}else {
glesVer = 300;
webgl_ = /** @type {WebGL2RenderingContext} */ (canvasElement.getContext("webgl2", contextCreationHints));
if(!webgl_) {
glesVer = 200;
webgl_ = /** @type {WebGL2RenderingContext} */ (canvasElement.getContext("webgl", contextCreationHints));
if(!webgl_) {
if(runtimeOpts.allowExperimentalWebGL1) {
experimental = true;
webgl_ = /** @type {WebGL2RenderingContext} */ (canvasElement.getContext("experimental-webgl", contextCreationHints));
if(!webgl_) {
showIncompatibleScreen("WebGL is not supported on this device!");
return false;
}
}else {
showIncompatibleScreen("WebGL is not supported on this device!");
return false;
}
}
}
}
}
if(experimental) {
alert("WARNING: Detected \"experimental\" WebGL 1.0 support, certain graphics API features may be missing, and therefore EaglercraftX may malfunction and crash!");
}
webglGLESVer = glesVer;
webglContext = webgl_;
webglExperimental = experimental;
setCurrentGLContext(webgl_, glesVer, runtimeOpts.useWebGLExt, eagruntimeImpl.platformOpenGL);
eagInfo("OpenGL Version: {}", eagruntimeImpl.platformOpenGL["glGetString"](0x1F02));
eagInfo("OpenGL Renderer: {}", eagruntimeImpl.platformOpenGL["glGetString"](0x1F01));
/** @type {Array<string>} */
const exts = eagruntimeImpl.platformOpenGL["dumpActiveExtensions"]();
if(exts.length === 0) {
eagInfo("Unlocked the following OpenGL ES extensions: (NONE)");
}else {
exts.sort();
eagInfo("Unlocked the following OpenGL ES extensions:");
for(var i = 0; i < exts.length; ++i) {
eagInfo(" - {}", exts[i]);
}
}
eagruntimeImpl.platformOpenGL["glClearColor"](0.0, 0.0, 0.0, 1.0);
eagruntimeImpl.platformOpenGL["glClear"](0x4000);
await promiseTimeout(20);
eagInfo("EagRuntime JS context initialization complete");
return true;
}
async function initializeContextWorker() {
setupRuntimeOpts();
/**
* @param {string} txt
* @param {boolean} err
*/
currentRedirectorFunc = function(txt, err) {
postMessage({
"ch": "~!LOGGER",
"txt": txt,
"err": err
});
};
eagInfo("Initializing EagRuntime worker JS context...");
await initializePlatfRuntime();
initializeNoPlatfApplication(eagruntimeImpl.platformApplication);
setNoAudioContext(eagruntimeImpl.platformAudio);
initNoPlatformInput(eagruntimeImpl.platformInput);
setNoGLContext(eagruntimeImpl.platformOpenGL);
initializeNoPlatfScreenRecord(eagruntimeImpl.platformScreenRecord);
initializeNoPlatfVoiceClient(eagruntimeImpl.platformVoiceClient);
initializeNoPlatfWebRTC(eagruntimeImpl.platformWebRTC);
initializeNoPlatfWebView(eagruntimeImpl.platformWebView);
initializeNoClientPlatfSP(eagruntimeImpl.clientPlatformSingleplayer);
initializeServerPlatfSP(eagruntimeImpl.serverPlatformSingleplayer);
eagInfo("EagRuntime worker JS context initialization complete");
}
/**
* @param {WebAssembly.Memory} mem
*/
function handleMemoryResized(mem) {
heapMemory = mem;
heapArrayBuffer = mem.buffer;
eagInfo("WebAssembly direct memory resized to {} MiB", ((heapArrayBuffer.byteLength / 1024.0 / 10.24) | 0) * 0.01);
heapU8Array = new Uint8Array(heapArrayBuffer);
heapI8Array = new Int8Array(heapArrayBuffer);
heapU16Array = new Uint16Array(heapArrayBuffer);
heapI16Array = new Int16Array(heapArrayBuffer);
heapU32Array = new Uint32Array(heapArrayBuffer);
heapI32Array = new Int32Array(heapArrayBuffer);
heapF32Array = new Float32Array(heapArrayBuffer);
}
const EVENT_TYPE_INPUT = 0;
const EVENT_TYPE_RUNTIME = 1;
const EVENT_TYPE_VOICE = 2;
const EVENT_TYPE_WEBVIEW = 3;
const mainEventQueue = new EaglerLinkedQueue();
/**
* @param {number} eventType
* @param {number} eventId
* @param {*} eventObj
*/
function pushEvent(eventType, eventId, eventObj) {
mainEventQueue.push({
"eventType": ((eventType << 5) | eventId),
"eventObj": eventObj,
"_next": null
});
}
let exceptionFrameRegex2 = /.+:wasm-function\[[0-9]+]:0x([0-9a-f]+).*/;
/**
* @param {string|null} stack
* @return {Array<string>}
*/
function deobfuscateStack(stack) {
if(!stack) return null;
/** @type {!Array<string>} */
const stackFrames = [];
for(let line of stack.split("\n")) {
if(deobfuscatorFunc) {
const match = exceptionFrameRegex2.exec(line);
if(match !== null && match.length >= 2) {
const val = parseInt(match[1], 16);
if(!isNaN(val)) {
try {
/** @type {Array<Object>} */
const resultList = deobfuscatorFunc([val]);
if(resultList.length > 0) {
for(let obj of resultList) {
stackFrames.push("" + obj["className"] + "." + obj["method"] + "(" + obj["file"] + ":" + obj["line"] + ")");
}
continue;
}
}catch(ex) {
}
}
}
}
line = line.trim();
if(line.startsWith("at ")) {
line = line.substring(3);
}
stackFrames.push(line);
}
return stackFrames;
}
function displayUncaughtCrashReport(error) {
const stack = error ? deobfuscateStack(error.stack) : null;
const crashContent = "Native Browser Exception\n" +
"----------------------------------\n" +
" Line: " + ((error && (typeof error.fileName === "string")) ? error.fileName : "unknown") +
":" + ((error && (typeof error.lineNumber === "number")) ? error.lineNumber : "unknown") +
":" + ((error && (typeof error.columnNumber === "number")) ? error.columnNumber : "unknown") +
"\n Type: " + ((error && (typeof error.name === "string")) ? error.name : "unknown") +
"\n Desc: " + ((error && (typeof error.message === "string")) ? error.message : "null") +
"\n----------------------------------\n\n" +
"Deobfuscated stack trace:\n at " + (stack ? stack.join("\n at ") : "null") +
"\n\nThis exception was not handled by the WASM binary\n";
if(typeof window !== "undefined") {
displayCrashReport(crashContent, true);
}else if(sendIntegratedServerCrash) {
eagError("\n{}", crashContent);
try {
sendIntegratedServerCrash(crashContent, true);
}catch(ex) {
console.log(ex);
}
}else {
eagError("\n{}", crashContent);
}
}
/**
* @param {string} crashReport
* @param {boolean} enablePrint
*/
function displayCrashReport(crashReport, enablePrint) {
eagError("Game crashed!");
var strBefore = "Game Crashed! I have fallen and I can't get up!\n\n"
+ crashReport
+ "\n\n";
var strAfter = "eaglercraft.version = \""
+ crashReportStrings[0]
+ "\"\neaglercraft.minecraft = \""
+ crashReportStrings[2]
+ "\"\neaglercraft.brand = \""
+ crashReportStrings[1]
+ "\"\n\n"
+ addWebGLToCrash()
+ "\nwindow.eaglercraftXOpts = "
+ JSON.stringify(eaglercraftXOpts)
+ "\n\ncurrentTime = "
+ (new Date()).toLocaleString()
+ "\n\n"
+ addDebugNav("userAgent")
+ addDebugNav("vendor")
+ addDebugNav("language")
+ addDebugNav("hardwareConcurrency")
+ addDebugNav("deviceMemory")
+ addDebugNav("platform")
+ addDebugNav("product")
+ addDebugNavPlugins()
+ "\n"
+ addDebug("localStorage")
+ addDebug("sessionStorage")
+ addDebug("indexedDB")
+ "\n"
+ "rootElement.clientWidth = "
+ (parentElement ? parentElement.clientWidth : "undefined")
+ "\nrootElement.clientHeight = "
+ (parentElement ? parentElement.clientHeight : "undefined")
+ "\n"
+ addDebug("innerWidth")
+ addDebug("innerHeight")
+ addDebug("outerWidth")
+ addDebug("outerHeight")
+ addDebug("devicePixelRatio")
+ addDebugScreen("availWidth")
+ addDebugScreen("availHeight")
+ addDebugScreen("colorDepth")
+ addDebugScreen("pixelDepth")
+ "\n"
+ addDebugLocation("href")
+ "\n";
var strFinal = strBefore + strAfter;
const additionalInfo = [];
try {
if((typeof eaglercraftXOpts === "object") && (typeof eaglercraftXOpts["hooks"] === "object")
&& (typeof eaglercraftXOpts["hooks"]["crashReportShow"] === "function")) {
eaglercraftXOpts["hooks"]["crashReportShow"](strFinal, function(str) {
additionalInfo.push(str);
});
}
}catch(ex) {
eagStackTrace(ERROR, "Uncaught exception invoking crash report hook", ex);
}
if(!isCrashed) {
isCrashed = true;
if(additionalInfo.length > 0) {
strFinal = strBefore + "Got the following messages from the crash report hook registered in eaglercraftXOpts:\n\n";
for(var i = 0; i < additionalInfo.length; ++i) {
strFinal += "----------[ CRASH HOOK ]----------\n"
+ additionalInfo[i]
+ "\n----------------------------------\n\n";
}
strFinal += strAfter;
}
var parentEl = parentElement || rootElement;
if(!parentEl) {
alert("Root element not found, crash report was printed to console");
eagError("\n{}", strFinal);
return;
}
if(enablePrint) {
eagError("\n{}", strFinal);
}
const img = document.createElement("img");
const div = document.createElement("div");
img.setAttribute("style", "z-index:100;position:absolute;top:10px;left:calc(50% - 151px);");
img.src = crashURL;
div.setAttribute("style", "z-index:100;position:absolute;top:135px;left:10%;right:10%;bottom:50px;background-color:white;border:1px solid #cccccc;overflow-x:hidden;overflow-y:scroll;overflow-wrap:break-word;white-space:pre-wrap;font: 14px monospace;padding:10px;");
div.classList.add("_eaglercraftX_crash_element");
parentEl.appendChild(img);
parentEl.appendChild(div);
div.appendChild(document.createTextNode(strFinal));
if(removeEventHandlers) removeEventHandlers();
window.__curEaglerX188UnloadListenerCB = null;
}else {
eagError("");
eagError("An additional crash report was supressed:");
var s = crashReport.split(/[\r\n]+/);
for(var i = 0; i < s.length; ++i) {
eagError(" {}", s[i]);
}
if(additionalInfo.length > 0) {
for(var i = 0; i < additionalInfo.length; ++i) {
var str2 = additionalInfo[i];
if(str2) {
eagError("");
eagError(" ----------[ CRASH HOOK ]----------");
s = str2.split(/[\r\n]+/);
for(var i = 0; i < s.length; ++i) {
eagError(" {}", s[i]);
}
eagError(" ----------------------------------");
}
}
}
}
}
/**
* @param {string} msg
*/
function showIncompatibleScreen(msg) {
if(!isCrashed) {
isCrashed = true;
var parentEl = parentElement || rootElement;
eagError("Compatibility error: {}", msg);
if(!parentEl) {
alert("Compatibility error: " + msg);
return;
}
const img = document.createElement("img");
const div = document.createElement("div");
img.setAttribute("style", "z-index:100;position:absolute;top:10px;left:calc(50% - 151px);");
img.src = crashURL;
div.setAttribute("style", "z-index:100;position:absolute;top:135px;left:10%;right:10%;bottom:50px;background-color:white;border:1px solid #cccccc;overflow-x:hidden;overflow-y:scroll;font:18px sans-serif;padding:40px;");
div.classList.add("_eaglercraftX_incompatible_element");
parentEl.appendChild(img);
parentEl.appendChild(div);
div.innerHTML = "<h2><svg style=\"vertical-align:middle;margin:0px 16px 8px 8px;\" xmlns=\"http://www.w3.org/2000/svg\" width=\"48\" height=\"48\" viewBox=\"0 0 48 48\" fill=\"none\"><path stroke=\"#000000\" stroke-width=\"3\" stroke-linecap=\"square\" d=\"M1.5 8.5v34h45v-28m-3-3h-10v-3m-3-3h-10m15 6h-18v-3m-3-3h-10\"/><path stroke=\"#000000\" stroke-width=\"2\" stroke-linecap=\"square\" d=\"M12 21h0m0 4h0m4 0h0m0-4h0m-2 2h0m20-2h0m0 4h0m4 0h0m0-4h0m-2 2h0\"/><path stroke=\"#000000\" stroke-width=\"2\" stroke-linecap=\"square\" d=\"M20 30h0 m2 2h0 m2 2h0 m2 2h0 m2 -2h0 m2 -2h0 m2 -2h0\"/></svg>+ This device is incompatible with Eaglercraft&ensp;:(</h2>"
+ "<div style=\"margin-left:40px;\">"
+ "<p style=\"font-size:1.2em;\"><b style=\"font-size:1.1em;\">Issue:</b> <span style=\"color:#BB0000;\" id=\"_eaglercraftX_crashReason\"></span><br /></p>"
+ "<p style=\"margin-left:10px;font:0.9em monospace;\" id=\"_eaglercraftX_crashUserAgent\"></p>"
+ "<p style=\"margin-left:10px;font:0.9em monospace;\" id=\"_eaglercraftX_crashWebGL\"></p>"
+ "<p style=\"margin-left:10px;font:0.9em monospace;\">Current Date: " + (new Date()).toLocaleString() + "</p>"
+ "<p><br /><span style=\"font-size:1.1em;border-bottom:1px dashed #AAAAAA;padding-bottom:5px;\">Things you can try:</span></p>"
+ "<ol>"
+ "<li><span style=\"font-weight:bold;\">Just try using Eaglercraft on a different device</span>, it isn't a bug it's common sense</li>"
+ "<li style=\"margin-top:7px;\">If this screen just appeared randomly, try restarting your browser or device</li>"
+ "<li style=\"margin-top:7px;\">If you are not using Chrome/Edge, try installing the latest Google Chrome</li>"
+ "<li style=\"margin-top:7px;\">If your browser is out of date, please update it to the latest version</li>"
+ "</ol>"
+ "</div>";
div.querySelector("#_eaglercraftX_crashReason").appendChild(document.createTextNode(msg));
div.querySelector("#_eaglercraftX_crashUserAgent").appendChild(document.createTextNode(getStringNav("userAgent")));
if(removeEventHandlers) removeEventHandlers();
window.__curEaglerX188UnloadListenerCB = null;
var webGLRenderer = "No GL_RENDERER string could be queried";
try {
const cvs = /** @type {HTMLCanvasElement} */ (document.createElement("canvas"));
cvs.width = 64;
cvs.height = 64;
const ctx = /** @type {WebGLRenderingContext} */ (cvs.getContext("webgl"));
if(ctx) {
/** @type {string|null} */
var r;
if(ctx.getExtension("WEBGL_debug_renderer_info")) {
r = /** @type {string|null} */ (ctx.getParameter(/* UNMASKED_RENDERER_WEBGL */ 0x9246));
}else {
r = /** @type {string|null} */ (ctx.getParameter(WebGLRenderingContext.RENDERER));
if(r) {
r += " [masked]";
}
}
if(r) {
webGLRenderer = r;
}
}
}catch(tt) {
}
div.querySelector("#_eaglercraftX_crashWebGL").appendChild(document.createTextNode(webGLRenderer));
}
}
/** @type {string|null} */
var webGLCrashStringCache = null;
/**
* @return {string}
*/
function addWebGLToCrash() {
if(webGLCrashStringCache) {
return webGLCrashStringCache;
}
try {
/** @type {WebGL2RenderingContext} */
var ctx = webglContext;
var experimental = webglExperimental;
if(!ctx) {
experimental = false;
var cvs = document.createElement("canvas");
cvs.width = 64;
cvs.height = 64;
ctx = /** @type {WebGL2RenderingContext} */ (cvs.getContext("webgl2"));
if(!ctx) {
ctx = /** @type {WebGL2RenderingContext} */ (cvs.getContext("webgl"));
if(!ctx) {
experimental = true;
ctx = /** @type {WebGL2RenderingContext} */ (cvs.getContext("experimental-webgl"));
}
}
}
if(ctx) {
var ret = "";
if(webglGLESVer > 0) {
ret += "webgl.version = "
+ ctx.getParameter(/* VERSION */ 0x1F02)
+ "\n";
}
if(ctx.getExtension("WEBGL_debug_renderer_info")) {
ret += "webgl.renderer = "
+ ctx.getParameter(/* UNMASKED_RENDERER_WEBGL */ 0x9246)
+ "\nwebgl.vendor = "
+ ctx.getParameter(/* UNMASKED_VENDOR_WEBGL */ 0x9245)
+ "\n";
}else {
ret += "webgl.renderer = "
+ ctx.getParameter(/* RENDERER */ 0x1F01)
+ " [masked]\nwebgl.vendor = "
+ ctx.getParameter(/* VENDOR */ 0x1F00)
+ " [masked]\n";
}
if(webglGLESVer > 0) {
ret += "\nwebgl.version.id = "
+ webglGLESVer
+ "\nwebgl.experimental = "
+ experimental;
if(webglGLESVer === 200) {
ret += "\nwebgl.ext.ANGLE_instanced_arrays = "
+ !!ctx.getExtension("ANGLE_instanced_arrays")
+ "\nwebgl.ext.EXT_color_buffer_half_float = "
+ !!ctx.getExtension("EXT_color_buffer_half_float")
+ "\nwebgl.ext.EXT_shader_texture_lod = "
+ !!ctx.getExtension("EXT_shader_texture_lod")
+ "\nwebgl.ext.OES_fbo_render_mipmap = "
+ !!ctx.getExtension("OES_fbo_render_mipmap")
+ "\nwebgl.ext.OES_texture_float = "
+ !!ctx.getExtension("OES_texture_float")
+ "\nwebgl.ext.OES_texture_half_float = "
+ !!ctx.getExtension("OES_texture_half_float")
+ "\nwebgl.ext.OES_texture_half_float_linear = "
+ !!ctx.getExtension("OES_texture_half_float_linear");
}else if(webglGLESVer >= 300) {
ret += "\nwebgl.ext.EXT_color_buffer_float = "
+ !!ctx.getExtension("EXT_color_buffer_float")
+ "\nwebgl.ext.EXT_color_buffer_half_float = "
+ !!ctx.getExtension("EXT_color_buffer_half_float")
+ "\nwebgl.ext.OES_texture_float_linear = "
+ !!ctx.getExtension("OES_texture_float_linear");
}
ret += "\nwebgl.ext.EXT_texture_filter_anisotropic = "
+ !!ctx.getExtension("EXT_texture_filter_anisotropic")
+ "\n";
}else {
ret += "webgl.ext.ANGLE_instanced_arrays = "
+ !!ctx.getExtension("ANGLE_instanced_arrays")
+ "\nwebgl.ext.EXT_color_buffer_float = "
+ !!ctx.getExtension("EXT_color_buffer_float")
+ "\nwebgl.ext.EXT_color_buffer_half_float = "
+ !!ctx.getExtension("EXT_color_buffer_half_float")
+ "\nwebgl.ext.EXT_shader_texture_lod = "
+ !!ctx.getExtension("EXT_shader_texture_lod")
+ "\nwebgl.ext.OES_fbo_render_mipmap = "
+ !!ctx.getExtension("OES_fbo_render_mipmap")
+ "\nwebgl.ext.OES_texture_float = "
+ !!ctx.getExtension("OES_texture_float")
+ "\nwebgl.ext.OES_texture_float_linear = "
+ !!ctx.getExtension("OES_texture_float_linear")
+ "\nwebgl.ext.OES_texture_half_float = "
+ !!ctx.getExtension("OES_texture_half_float")
+ "\nwebgl.ext.OES_texture_half_float_linear = "
+ !!ctx.getExtension("OES_texture_half_float_linear")
+ "\nwebgl.ext.EXT_texture_filter_anisotropic = "
+ !!ctx.getExtension("EXT_texture_filter_anisotropic")
+ "\n";
}
return webGLCrashStringCache = ret;
}else {
return webGLCrashStringCache = "Failed to query GPU info!\n";
}
}catch(ex) {
return webGLCrashStringCache = "ERROR: could not query webgl info - " + ex + "\n";
}
}
/**
* @param {string} k
* @return {string}
*/
function addDebugNav(k) {
var val;
try {
val = window.navigator[k];
} catch(e) {
val = "<error>";
}
return "window.navigator." + k + " = " + val + "\n";
}
/**
* @param {string} k
* @return {string}
*/
function getStringNav(k) {
try {
return window.navigator[k];
} catch(e) {
return "<error>";
}
}
/**
* @return {string}
*/
function addDebugNavPlugins() {
var val;
try {
var retObj = new Array();
if(typeof navigator.plugins === "object") {
var len = navigator.plugins.length;
if(len > 0) {
for(var idx = 0; idx < len; ++idx) {
var thePlugin = navigator.plugins[idx];
retObj.push({
"name": thePlugin.name,
"filename": thePlugin.filename,
"desc": thePlugin.description
});
}
}
}
val = JSON.stringify(retObj);
} catch(e) {
val = "<error>";
}
return "window.navigator.plugins = " + val + "\n";
}
/**
* @param {string} k
* @return {string}
*/
function addDebugScreen(k) {
var val;
try {
val = window.screen[k];
} catch(e) {
val = "<error>";
}
return "window.screen." + k + " = " + val + "\n";
}
/**
* @param {string} k
* @return {string}
*/
function addDebugLocation(k) {
var val;
try {
val = window.location[k];
} catch(e) {
val = "<error>";
}
return "window.location." + k + " = " + val + "\n";
}
/**
* @param {string} k
* @return {string}
*/
function addDebug(k) {
var val;
try {
val = window[k];
} catch(e) {
val = "<error>";
}
return "window." + k + " = " + val + "\n";
}

View File

@ -0,0 +1,233 @@
/*
* Copyright (c) 2024 lax1dude. All Rights Reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
const DEBUG = 0;
const INFO = 1;
const WARN = 2;
const ERROR = 3;
const OFF = 4;
const levels = [
"DEBUG",
"INFO",
"WARN",
"ERROR"
];
var contextName = "main";
var currentLogLevel = INFO;
/** @type {function(string, boolean)|null} */
var currentRedirectorFunc = null;
/**
* @param {string} msg
* @param {Array} args
*/
function formatArgs(msg, args) {
if(args.length > 0) {
var retString = [];
for(var i = 0; i < args.length; ++i) {
var idx = msg.indexOf("{}");
if(idx != -1) {
retString.push(msg.substring(0, idx));
retString.push(args[i]);
msg = msg.substring(idx + 2);
}else {
break;
}
}
if(retString.length > 0) {
retString.push(msg);
return retString.join("");
}else {
return msg;
}
}else {
return msg;
}
}
/**
* @param {number} lvl
* @param {string} msg
* @param {Array} args
*/
function logImpl(lvl, msg, args) {
if(lvl < currentLogLevel) {
return;
}
msg = "EagRuntimeJS: [" + (new Date()).toLocaleTimeString() + "][" + contextName +"/" + (levels[lvl] || "UNKNOWN") + "] " + formatArgs(msg, args);
if(lvl >= ERROR) {
console.error(msg);
}else {
console.log(msg);
}
if(currentRedirectorFunc) {
currentRedirectorFunc(msg, lvl >= ERROR);
}
}
/**
* @param {string} name
*/
function setLoggerContextName(name) {
contextName = name;
}
/**
* @param {string} msg
* @param {...*} args
*/
function eagDebug(msg, ...args) {
logImpl(DEBUG, msg, args);
}
/**
* @param {string} msg
* @param {...*} args
*/
function eagInfo(msg, ...args) {
logImpl(INFO, msg, args);
}
/**
* @param {string} msg
* @param {...*} args
*/
function eagWarn(msg, ...args) {
logImpl(WARN, msg, args);
}
/**
* @param {string} msg
* @param {...*} args
*/
function eagError(msg, ...args) {
logImpl(ERROR, msg, args);
}
/**
* @param {number} lvl
* @param {string} msg
* @param {...*} args
*/
function eagLog(lvl, msg, ...args) {
logImpl(lvl, msg, args);
}
/**
* @param {number} lvl
* @param {string} msg
* @param {Error} err
*/
function eagStackTrace(lvl, msg, err) {
if(err) {
if(err.message) {
eagLog(lvl, "{}: {} - \"{}\"", msg, err.name, err.message);
}else {
eagLog(lvl, "{}: {}", msg, err.name);
}
if(typeof err.stack === "string") {
const stackElements = deobfuscateStack(err.stack);
for(var i = 0; i < stackElements.length; ++i) {
eagLog(lvl, " at " + stackElements[i]);
}
}
}else {
eagLog(lvl, "{}: <null>", msg);
}
}
/**
* @param {string} modName
* @param {string} str
* @return {function()}
*/
function unsupportedFunc(modName, str) {
return function() {
eagError("Unsupported function called: {}.{}", str);
return 0;
};
}
/**
* @param {Object} importsObj
* @param {string} modName
* @param {string} str
*/
function setUnsupportedFunc(importsObj, modName, str) {
importsObj[str] = unsupportedFunc(modName, str);
}
/**
* @param {number} ms
*/
function promiseTimeout(ms) {
return new Promise(function(resolve) {
setTimeout(resolve, ms);
});
}
class EaglerLinkedQueue {
constructor() {
this.firstElement = null;
this.lastElement = null;
this.queueLength = 0;
}
/**
* @return {number}
*/
getLength() {
return this.queueLength;
}
/**
* @param {Object} obj
*/
push(obj) {
if(this.lastElement) {
this.lastElement["_next"] = obj;
}
this.lastElement = obj;
if(!this.firstElement) {
this.firstElement = obj;
}
++this.queueLength;
}
/**
* @return {Object}
*/
shift() {
if(this.firstElement) {
const ret = this.firstElement;
this.firstElement = ret["_next"] || null;
if(!this.firstElement) {
this.lastElement = null;
}else {
ret["_next"] = null;
}
--this.queueLength;
return ret;
}else {
return null;
}
}
}

View File

@ -0,0 +1,129 @@
/**
* @fileoverview eagruntime externs
* @externs
*/
/*
* Copyright (c) 2024 lax1dude. All Rights Reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
self.__eaglercraftXLoaderContext = {};
/**
* @return {Object}
*/
self.__eaglercraftXLoaderContext.getEaglercraftXOpts = function() {};
/**
* @return {string}
*/
self.__eaglercraftXLoaderContext.getEagRuntimeJSURL = function() {};
/**
* @return {string|WebAssembly.Module}
*/
self.__eaglercraftXLoaderContext.getClassesWASMURL = function() {};
/**
* @return {string|WebAssembly.Module}
*/
self.__eaglercraftXLoaderContext.getClassesDeobfWASMURL = function() {};
/**
* @return {string}
*/
self.__eaglercraftXLoaderContext.getClassesTEADBGURL = function() {};
/**
* @return {Array}
*/
self.__eaglercraftXLoaderContext.getEPKFiles = function() {};
/**
* @return {HTMLElement}
*/
self.__eaglercraftXLoaderContext.getRootElement = function() {};
/**
* @return {Array}
*/
self.__eaglercraftXLoaderContext.getMainArgs = function() {};
/**
* @param {number} img
* @return {string}
*/
self.__eaglercraftXLoaderContext.getImageURL = function(img) {};
/**
* @param {function(Array<string>)} fn
*/
self.__eaglercraftXLoaderContext.runMain = function(fn) {};
/**
* @param {Object} o
*/
self.__eaglerXOnMessage = function(o) {};
window.__isEaglerX188UnloadListenerSet = "";
/** @type {function()|null} */
window.__curEaglerX188UnloadListenerCB = function() {};
/**
* @return {Promise<Object>}
*/
window.navigator.keyboard.getLayoutMap = function() {};
/**
* @param {*} fn
* @return {function(...*)}
*/
WebAssembly.promising = function(fn) {};
WebAssembly.Suspending = class {
/**
* @param {*} fn
*/
constructor(fn) {
}
};
/**
* @param {*} tag
* @return {boolean}
*/
WebAssembly.Exception.prototype.is = function(tag) {}
/**
* @param {*} tag
* @param {number} idx
* @return {*}
*/
WebAssembly.Exception.prototype.getArg = function(tag, idx) {}
WebAssembly.Global = class {
/**
* @param {!Object} desc
* @param {*} initValue
*/
constructor(desc, initValue) {
/** @type {*} */
this.value = null;
}
};
/** @type {string|null} */
HTMLIFrameElement.prototype.csp = "";

View File

@ -0,0 +1,541 @@
/*
* The MIT license
*
* Copyright (c) 2018 Yury Sitnikov
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
const webmSections = {
0xa45dfa3: { name: 'EBML', type: 'Container' },
0x286: { name: 'EBMLVersion', type: 'Uint' },
0x2f7: { name: 'EBMLReadVersion', type: 'Uint' },
0x2f2: { name: 'EBMLMaxIDLength', type: 'Uint' },
0x2f3: { name: 'EBMLMaxSizeLength', type: 'Uint' },
0x282: { name: 'DocType', type: 'String' },
0x287: { name: 'DocTypeVersion', type: 'Uint' },
0x285: { name: 'DocTypeReadVersion', type: 'Uint' },
0x6c: { name: 'Void', type: 'Binary' },
0x3f: { name: 'CRC-32', type: 'Binary' },
0xb538667: { name: 'SignatureSlot', type: 'Container' },
0x3e8a: { name: 'SignatureAlgo', type: 'Uint' },
0x3e9a: { name: 'SignatureHash', type: 'Uint' },
0x3ea5: { name: 'SignaturePublicKey', type: 'Binary' },
0x3eb5: { name: 'Signature', type: 'Binary' },
0x3e5b: { name: 'SignatureElements', type: 'Container' },
0x3e7b: { name: 'SignatureElementList', type: 'Container' },
0x2532: { name: 'SignedElement', type: 'Binary' },
0x8538067: { name: 'Segment', type: 'Container' },
0x14d9b74: { name: 'SeekHead', type: 'Container' },
0xdbb: { name: 'Seek', type: 'Container' },
0x13ab: { name: 'SeekID', type: 'Binary' },
0x13ac: { name: 'SeekPosition', type: 'Uint' },
0x549a966: { name: 'Info', type: 'Container' },
0x33a4: { name: 'SegmentUID', type: 'Binary' },
0x3384: { name: 'SegmentFilename', type: 'String' },
0x1cb923: { name: 'PrevUID', type: 'Binary' },
0x1c83ab: { name: 'PrevFilename', type: 'String' },
0x1eb923: { name: 'NextUID', type: 'Binary' },
0x1e83bb: { name: 'NextFilename', type: 'String' },
0x444: { name: 'SegmentFamily', type: 'Binary' },
0x2924: { name: 'ChapterTranslate', type: 'Container' },
0x29fc: { name: 'ChapterTranslateEditionUID', type: 'Uint' },
0x29bf: { name: 'ChapterTranslateCodec', type: 'Uint' },
0x29a5: { name: 'ChapterTranslateID', type: 'Binary' },
0xad7b1: { name: 'TimecodeScale', type: 'Uint' },
0x489: { name: 'Duration', type: 'Float' },
0x461: { name: 'DateUTC', type: 'Date' },
0x3ba9: { name: 'Title', type: 'String' },
0xd80: { name: 'MuxingApp', type: 'String' },
0x1741: { name: 'WritingApp', type: 'String' },
// 0xf43b675: { name: 'Cluster', type: 'Container' },
0x67: { name: 'Timecode', type: 'Uint' },
0x1854: { name: 'SilentTracks', type: 'Container' },
0x18d7: { name: 'SilentTrackNumber', type: 'Uint' },
0x27: { name: 'Position', type: 'Uint' },
0x2b: { name: 'PrevSize', type: 'Uint' },
0x23: { name: 'SimpleBlock', type: 'Binary' },
0x20: { name: 'BlockGroup', type: 'Container' },
0x21: { name: 'Block', type: 'Binary' },
0x22: { name: 'BlockVirtual', type: 'Binary' },
0x35a1: { name: 'BlockAdditions', type: 'Container' },
0x26: { name: 'BlockMore', type: 'Container' },
0x6e: { name: 'BlockAddID', type: 'Uint' },
0x25: { name: 'BlockAdditional', type: 'Binary' },
0x1b: { name: 'BlockDuration', type: 'Uint' },
0x7a: { name: 'ReferencePriority', type: 'Uint' },
0x7b: { name: 'ReferenceBlock', type: 'Int' },
0x7d: { name: 'ReferenceVirtual', type: 'Int' },
0x24: { name: 'CodecState', type: 'Binary' },
0x35a2: { name: 'DiscardPadding', type: 'Int' },
0xe: { name: 'Slices', type: 'Container' },
0x68: { name: 'TimeSlice', type: 'Container' },
0x4c: { name: 'LaceNumber', type: 'Uint' },
0x4d: { name: 'FrameNumber', type: 'Uint' },
0x4b: { name: 'BlockAdditionID', type: 'Uint' },
0x4e: { name: 'Delay', type: 'Uint' },
0x4f: { name: 'SliceDuration', type: 'Uint' },
0x48: { name: 'ReferenceFrame', type: 'Container' },
0x49: { name: 'ReferenceOffset', type: 'Uint' },
0x4a: { name: 'ReferenceTimeCode', type: 'Uint' },
0x2f: { name: 'EncryptedBlock', type: 'Binary' },
0x654ae6b: { name: 'Tracks', type: 'Container' },
0x2e: { name: 'TrackEntry', type: 'Container' },
0x57: { name: 'TrackNumber', type: 'Uint' },
0x33c5: { name: 'TrackUID', type: 'Uint' },
0x3: { name: 'TrackType', type: 'Uint' },
0x39: { name: 'FlagEnabled', type: 'Uint' },
0x8: { name: 'FlagDefault', type: 'Uint' },
0x15aa: { name: 'FlagForced', type: 'Uint' },
0x1c: { name: 'FlagLacing', type: 'Uint' },
0x2de7: { name: 'MinCache', type: 'Uint' },
0x2df8: { name: 'MaxCache', type: 'Uint' },
0x3e383: { name: 'DefaultDuration', type: 'Uint' },
0x34e7a: { name: 'DefaultDecodedFieldDuration', type: 'Uint' },
0x3314f: { name: 'TrackTimecodeScale', type: 'Float' },
0x137f: { name: 'TrackOffset', type: 'Int' },
0x15ee: { name: 'MaxBlockAdditionID', type: 'Uint' },
0x136e: { name: 'Name', type: 'String' },
0x2b59c: { name: 'Language', type: 'String' },
0x6: { name: 'CodecID', type: 'String' },
0x23a2: { name: 'CodecPrivate', type: 'Binary' },
0x58688: { name: 'CodecName', type: 'String' },
0x3446: { name: 'AttachmentLink', type: 'Uint' },
0x1a9697: { name: 'CodecSettings', type: 'String' },
0x1b4040: { name: 'CodecInfoURL', type: 'String' },
0x6b240: { name: 'CodecDownloadURL', type: 'String' },
0x2a: { name: 'CodecDecodeAll', type: 'Uint' },
0x2fab: { name: 'TrackOverlay', type: 'Uint' },
0x16aa: { name: 'CodecDelay', type: 'Uint' },
0x16bb: { name: 'SeekPreRoll', type: 'Uint' },
0x2624: { name: 'TrackTranslate', type: 'Container' },
0x26fc: { name: 'TrackTranslateEditionUID', type: 'Uint' },
0x26bf: { name: 'TrackTranslateCodec', type: 'Uint' },
0x26a5: { name: 'TrackTranslateTrackID', type: 'Binary' },
0x60: { name: 'Video', type: 'Container' },
0x1a: { name: 'FlagInterlaced', type: 'Uint' },
0x13b8: { name: 'StereoMode', type: 'Uint' },
0x13c0: { name: 'AlphaMode', type: 'Uint' },
0x13b9: { name: 'OldStereoMode', type: 'Uint' },
0x30: { name: 'PixelWidth', type: 'Uint' },
0x3a: { name: 'PixelHeight', type: 'Uint' },
0x14aa: { name: 'PixelCropBottom', type: 'Uint' },
0x14bb: { name: 'PixelCropTop', type: 'Uint' },
0x14cc: { name: 'PixelCropLeft', type: 'Uint' },
0x14dd: { name: 'PixelCropRight', type: 'Uint' },
0x14b0: { name: 'DisplayWidth', type: 'Uint' },
0x14ba: { name: 'DisplayHeight', type: 'Uint' },
0x14b2: { name: 'DisplayUnit', type: 'Uint' },
0x14b3: { name: 'AspectRatioType', type: 'Uint' },
0xeb524: { name: 'ColourSpace', type: 'Binary' },
0xfb523: { name: 'GammaValue', type: 'Float' },
0x383e3: { name: 'FrameRate', type: 'Float' },
0x61: { name: 'Audio', type: 'Container' },
0x35: { name: 'SamplingFrequency', type: 'Float' },
0x38b5: { name: 'OutputSamplingFrequency', type: 'Float' },
0x1f: { name: 'Channels', type: 'Uint' },
0x3d7b: { name: 'ChannelPositions', type: 'Binary' },
0x2264: { name: 'BitDepth', type: 'Uint' },
0x62: { name: 'TrackOperation', type: 'Container' },
0x63: { name: 'TrackCombinePlanes', type: 'Container' },
0x64: { name: 'TrackPlane', type: 'Container' },
0x65: { name: 'TrackPlaneUID', type: 'Uint' },
0x66: { name: 'TrackPlaneType', type: 'Uint' },
0x69: { name: 'TrackJoinBlocks', type: 'Container' },
0x6d: { name: 'TrackJoinUID', type: 'Uint' },
0x40: { name: 'TrickTrackUID', type: 'Uint' },
0x41: { name: 'TrickTrackSegmentUID', type: 'Binary' },
0x46: { name: 'TrickTrackFlag', type: 'Uint' },
0x47: { name: 'TrickMasterTrackUID', type: 'Uint' },
0x44: { name: 'TrickMasterTrackSegmentUID', type: 'Binary' },
0x2d80: { name: 'ContentEncodings', type: 'Container' },
0x2240: { name: 'ContentEncoding', type: 'Container' },
0x1031: { name: 'ContentEncodingOrder', type: 'Uint' },
0x1032: { name: 'ContentEncodingScope', type: 'Uint' },
0x1033: { name: 'ContentEncodingType', type: 'Uint' },
0x1034: { name: 'ContentCompression', type: 'Container' },
0x254: { name: 'ContentCompAlgo', type: 'Uint' },
0x255: { name: 'ContentCompSettings', type: 'Binary' },
0x1035: { name: 'ContentEncryption', type: 'Container' },
0x7e1: { name: 'ContentEncAlgo', type: 'Uint' },
0x7e2: { name: 'ContentEncKeyID', type: 'Binary' },
0x7e3: { name: 'ContentSignature', type: 'Binary' },
0x7e4: { name: 'ContentSigKeyID', type: 'Binary' },
0x7e5: { name: 'ContentSigAlgo', type: 'Uint' },
0x7e6: { name: 'ContentSigHashAlgo', type: 'Uint' },
0xc53bb6b: { name: 'Cues', type: 'Container' },
0x3b: { name: 'CuePoint', type: 'Container' },
0x33: { name: 'CueTime', type: 'Uint' },
0x37: { name: 'CueTrackPositions', type: 'Container' },
0x77: { name: 'CueTrack', type: 'Uint' },
0x71: { name: 'CueClusterPosition', type: 'Uint' },
0x70: { name: 'CueRelativePosition', type: 'Uint' },
0x32: { name: 'CueDuration', type: 'Uint' },
0x1378: { name: 'CueBlockNumber', type: 'Uint' },
0x6a: { name: 'CueCodecState', type: 'Uint' },
0x5b: { name: 'CueReference', type: 'Container' },
0x16: { name: 'CueRefTime', type: 'Uint' },
0x17: { name: 'CueRefCluster', type: 'Uint' },
0x135f: { name: 'CueRefNumber', type: 'Uint' },
0x6b: { name: 'CueRefCodecState', type: 'Uint' },
0x941a469: { name: 'Attachments', type: 'Container' },
0x21a7: { name: 'AttachedFile', type: 'Container' },
0x67e: { name: 'FileDescription', type: 'String' },
0x66e: { name: 'FileName', type: 'String' },
0x660: { name: 'FileMimeType', type: 'String' },
0x65c: { name: 'FileData', type: 'Binary' },
0x6ae: { name: 'FileUID', type: 'Uint' },
0x675: { name: 'FileReferral', type: 'Binary' },
0x661: { name: 'FileUsedStartTime', type: 'Uint' },
0x662: { name: 'FileUsedEndTime', type: 'Uint' },
0x43a770: { name: 'Chapters', type: 'Container' },
0x5b9: { name: 'EditionEntry', type: 'Container' },
0x5bc: { name: 'EditionUID', type: 'Uint' },
0x5bd: { name: 'EditionFlagHidden', type: 'Uint' },
0x5db: { name: 'EditionFlagDefault', type: 'Uint' },
0x5dd: { name: 'EditionFlagOrdered', type: 'Uint' },
0x36: { name: 'ChapterAtom', type: 'Container' },
0x33c4: { name: 'ChapterUID', type: 'Uint' },
0x1654: { name: 'ChapterStringUID', type: 'String' },
0x11: { name: 'ChapterTimeStart', type: 'Uint' },
0x12: { name: 'ChapterTimeEnd', type: 'Uint' },
0x18: { name: 'ChapterFlagHidden', type: 'Uint' },
0x598: { name: 'ChapterFlagEnabled', type: 'Uint' },
0x2e67: { name: 'ChapterSegmentUID', type: 'Binary' },
0x2ebc: { name: 'ChapterSegmentEditionUID', type: 'Uint' },
0x23c3: { name: 'ChapterPhysicalEquiv', type: 'Uint' },
0xf: { name: 'ChapterTrack', type: 'Container' },
0x9: { name: 'ChapterTrackNumber', type: 'Uint' },
0x0: { name: 'ChapterDisplay', type: 'Container' },
0x5: { name: 'ChapString', type: 'String' },
0x37c: { name: 'ChapLanguage', type: 'String' },
0x37e: { name: 'ChapCountry', type: 'String' },
0x2944: { name: 'ChapProcess', type: 'Container' },
0x2955: { name: 'ChapProcessCodecID', type: 'Uint' },
0x50d: { name: 'ChapProcessPrivate', type: 'Binary' },
0x2911: { name: 'ChapProcessCommand', type: 'Container' },
0x2922: { name: 'ChapProcessTime', type: 'Uint' },
0x2933: { name: 'ChapProcessData', type: 'Binary' },
0x254c367: { name: 'Tags', type: 'Container' },
0x3373: { name: 'Tag', type: 'Container' },
0x23c0: { name: 'Targets', type: 'Container' },
0x28ca: { name: 'TargetTypeValue', type: 'Uint' },
0x23ca: { name: 'TargetType', type: 'String' },
0x23c5: { name: 'TagTrackUID', type: 'Uint' },
0x23c9: { name: 'TagEditionUID', type: 'Uint' },
0x23c4: { name: 'TagChapterUID', type: 'Uint' },
0x23c6: { name: 'TagAttachmentUID', type: 'Uint' },
0x27c8: { name: 'SimpleTag', type: 'Container' },
0x5a3: { name: 'TagName', type: 'String' },
0x47a: { name: 'TagLanguage', type: 'String' },
0x484: { name: 'TagDefault', type: 'Uint' },
0x487: { name: 'TagString', type: 'String' },
0x485: { name: 'TagBinary', type: 'Binary' }
};
function doInherit(newClass, baseClass) {
newClass.prototype = Object.create(baseClass.prototype);
newClass.prototype.constructor = newClass;
}
/**
* @param {string} name
* @param {string} type
* @constructor
*/
function WebmBase(name, type) {
this.name = name || 'Unknown';
this.type = type || 'Unknown';
}
WebmBase.prototype.updateBySource = function() { };
WebmBase.prototype.setSource = function(source) {
this.source = source;
this.updateBySource();
};
WebmBase.prototype.updateByData = function() { };
WebmBase.prototype.setData = function(data) {
this.data = data;
this.updateByData();
};
/**
* @param {string} name
* @param {string} type
* @extends {WebmBase}
* @constructor
*/
function WebmUint(name, type) {
WebmBase.call(this, name, type || 'Uint');
}
doInherit(WebmUint, WebmBase);
function padHex(hex) {
return hex.length % 2 === 1 ? '0' + hex : hex;
}
WebmUint.prototype.updateBySource = function() {
// use hex representation of a number instead of number value
this.data = '';
for (var i = 0; i < this.source.length; i++) {
var hex = this.source[i].toString(16);
this.data += padHex(hex);
}
};
WebmUint.prototype.updateByData = function() {
var length = this.data.length / 2;
this.source = new Uint8Array(length);
for (var i = 0; i < length; i++) {
var hex = this.data.substr(i * 2, 2);
this.source[i] = parseInt(hex, 16);
}
};
WebmUint.prototype.getValue = function() {
return parseInt(this.data, 16);
};
WebmUint.prototype.setValue = function(value) {
this.setData(padHex(value.toString(16)));
};
/**
* @param {string} name
* @param {string} type
* @extends {WebmBase}
* @constructor
*/
function WebmFloat(name, type) {
WebmBase.call(this, name, type || 'Float');
}
doInherit(WebmFloat, WebmBase);
WebmFloat.prototype.getFloatArrayType = function() {
return this.source && this.source.length === 4 ? Float32Array : Float64Array;
};
WebmFloat.prototype.updateBySource = function() {
var byteArray = this.source.reverse();
var floatArrayType = this.getFloatArrayType();
var floatArray = new floatArrayType(byteArray.buffer);
this.data = floatArray[0];
};
WebmFloat.prototype.updateByData = function() {
var floatArrayType = this.getFloatArrayType();
var floatArray = new floatArrayType([ this.data ]);
var byteArray = new Uint8Array(floatArray.buffer);
this.source = byteArray.reverse();
};
WebmFloat.prototype.getValue = function() {
return this.data;
};
WebmFloat.prototype.setValue = function(value) {
this.setData(value);
};
/**
* @param {string} name
* @param {string} type
* @extends {WebmBase}
* @constructor
*/
function WebmContainer(name, type) {
WebmBase.call(this, name, type || 'Container');
}
doInherit(WebmContainer, WebmBase);
WebmContainer.prototype.readByte = function() {
return this.source[this.offset++];
};
WebmContainer.prototype.readUint = function() {
var firstByte = this.readByte();
var bytes = 8 - firstByte.toString(2).length;
var value = firstByte - (1 << (7 - bytes));
for (var i = 0; i < bytes; i++) {
// don't use bit operators to support x86
value *= 256;
value += this.readByte();
}
return value;
};
WebmContainer.prototype.updateBySource = function() {
this.data = [];
var end;
for (this.offset = 0; this.offset < this.source.length; this.offset = end) {
var id = this.readUint();
var len = this.readUint();
end = Math.min(this.offset + len, this.source.length);
var data = this.source.slice(this.offset, end);
var info = webmSections[id] || { name: 'Unknown', type: 'Unknown' };
var ctr = WebmBase;
switch (info.type) {
case 'Container':
ctr = WebmContainer;
break;
case 'Uint':
ctr = WebmUint;
break;
case 'Float':
ctr = WebmFloat;
break;
}
var section = new ctr(info.name, info.type);
section.setSource(data);
this.data.push({
id: id,
idHex: id.toString(16),
data: section
});
}
};
WebmContainer.prototype.writeUint = function(x, draft) {
for (var bytes = 1, flag = 0x80; x >= flag && bytes < 8; bytes++, flag *= 0x80) { }
if (!draft) {
var value = flag + x;
for (var i = bytes - 1; i >= 0; i--) {
// don't use bit operators to support x86
var c = value % 256;
this.source[this.offset + i] = c;
value = (value - c) / 256;
}
}
this.offset += bytes;
};
WebmContainer.prototype.writeSections = function(draft) {
this.offset = 0;
for (var i = 0; i < this.data.length; i++) {
var section = this.data[i],
content = section.data.source,
contentLength = content.length;
this.writeUint(section.id, draft);
this.writeUint(contentLength, draft);
if (!draft) {
this.source.set(content, this.offset);
}
this.offset += contentLength;
}
return this.offset;
};
WebmContainer.prototype.updateByData = function() {
// run without accessing this.source to determine total length - need to know it to create Uint8Array
var length = this.writeSections('draft');
this.source = new Uint8Array(length);
// now really write data
this.writeSections(null);
};
WebmContainer.prototype.getSectionById = function(id) {
for (var i = 0; i < this.data.length; i++) {
var section = this.data[i];
if (section.id === id) {
return section.data;
}
}
return null;
};
/**
* @param {Uint8Array} source
* @extends {WebmContainer}
* @constructor
*/
function WebmFile(source) {
WebmContainer.call(this, 'File', 'File');
this.setSource(source);
}
doInherit(WebmFile, WebmContainer);
WebmFile.prototype.fixDuration = function(duration, options) {
var logger = options && options.logger;
if (logger === undefined) {
logger = function(message) {
console.log(message);
};
} else if (!logger) {
logger = function() { };
}
var segmentSection = this.getSectionById(0x8538067);
if (!segmentSection) {
logger('[fix-webm-duration] Segment section is missing');
return false;
}
var infoSection = segmentSection.getSectionById(0x549a966);
if (!infoSection) {
logger('[fix-webm-duration] Info section is missing');
return false;
}
var timeScaleSection = infoSection.getSectionById(0xad7b1);
if (!timeScaleSection) {
logger('[fix-webm-duration] TimecodeScale section is missing');
return false;
}
var durationSection = infoSection.getSectionById(0x489);
if (durationSection) {
if (durationSection.getValue() <= 0) {
logger('[fix-webm-duration] Duration section is present, but the value is empty');
durationSection.setValue(duration);
} else {
logger('[fix-webm-duration] Duration section is present');
return false;
}
} else {
logger('[fix-webm-duration] Duration section is missing');
// append Duration section
durationSection = new WebmFloat('Duration', 'Float');
durationSection.setValue(duration);
infoSection.data.push({
id: 0x489,
data: durationSection
});
}
// set default time scale to 1 millisecond (1000000 nanoseconds)
timeScaleSection.setValue(1000000);
infoSection.updateByData();
segmentSection.updateByData();
this.updateByData();
return true;
};
WebmFile.prototype.toBlob = function(mimeType) {
return new Blob([ this.source.buffer ], { type: mimeType || 'video/webm' });
};
/**
* @param {!Blob} blob
* @param {number} duration
* @param {function(!Blob)} callback
* @param {Object} options
*/
function fixWebMDuration(blob, duration, callback, options) {
try {
var reader = new FileReader();
reader.onloadend = function() {
try {
var file = new WebmFile(new Uint8Array(/** @type {ArrayBuffer} */ (reader.result)));
if (file.fixDuration(duration, options)) {
blob = file.toBlob(blob.type);
}
} catch (ex) {
// ignore
}
callback(blob);
};
reader.readAsArrayBuffer(blob);
} catch (ex) {
callback(blob);
}
}

View File

@ -0,0 +1,502 @@
/*
* Copyright (c) 2024 lax1dude. All Rights Reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
const platfApplicationName = "platformApplication";
/**
* @param {string} str
* @param {string} blobUrl
* @param {function()|null} blobRevoke
*/
function downloadFileImpl(str, blobUrl, blobRevoke) {
const el = document.createElement("a");
el.style.position = "absolute";
el.style.left = "0px";
el.style.top = "0px";
el.style.zIndex = "-100";
el.style.color = "transparent";
el.innerText = "Download File";
el.href = blobUrl;
el.target = "_blank";
el.download = str;
parentElement.appendChild(el);
setTimeout(function() {
el.click();
setTimeout(function() {
parentElement.removeChild(el);
}, 50);
if(blobRevoke) {
setTimeout(blobRevoke, 60000);
}
}, 50);
}
/** @type {number} */
const bufferSpoolSize = 256;
/** @type {number} */
const windowMaxMessages = 2048;
/** @type {number} */
var messageBufferLength = 0;
/** @type {Object|null} */
var messageBufferStart = null;
/** @type {Object|null} */
var messageBufferEnd = null;
/** @type {Window|null} */
var loggerWindow = null;
/** @type {function(string, boolean)|null} */
var loggerWindowMsgHandler = null;
/**
* @param {string} text
* @param {boolean} isErr
*/
function addLogMessageImpl(text, isErr) {
if(!loggerWindow) {
var newEl = {
"msg": text,
"err": isErr,
"next": null
};
if(messageBufferEnd) {
messageBufferEnd["next"] = newEl;
}
if(!messageBufferStart) {
messageBufferStart = newEl;
}
messageBufferEnd = newEl;
++messageBufferLength;
while(messageBufferLength > bufferSpoolSize) {
--messageBufferLength;
if(messageBufferStart) {
messageBufferStart = messageBufferStart["next"];
}
}
}else if(loggerWindowMsgHandler) {
loggerWindowMsgHandler(text, isErr);
}
}
/**
* @param {Object} applicationImports
*/
function initializePlatfApplication(applicationImports) {
/**
* @param {string} str
* @return {boolean}
*/
applicationImports["setClipboard"] = function setClipboardImpl(str) {
try {
if(window.navigator.clipboard) {
window.navigator.clipboard.writeText(str);
return true;
}
}catch(ex) {
eagError("Failed to set clipboard data!");
}
return false;
};
/**
* @return {Promise<string|null>}
*/
async function getClipboardImpl() {
var txt = null;
try {
if(window.navigator.clipboard) {
txt = await navigator.clipboard.readText();
}
}catch(ex) {
eagError("Failed to read clipboard data!");
}
return txt;
}
applicationImports["getClipboard"] = new WebAssembly.Suspending(getClipboardImpl);
/** @type {boolean} */
var fileChooserHasResult = false;
/** @type {Object|null} */
var fileChooserResultObject = null;
/** @type {HTMLInputElement|null} */
var fileChooserElement = null;
/** @type {HTMLElement|null} */
var fileChooserMobileElement = null;
/**
* @param {string} mime
* @param {string} ext
*/
applicationImports["displayFileChooser"] = function(mime, ext) {
clearFileChooserResultImpl();
if(isLikelyMobileBrowser) {
const el = fileChooserMobileElement = /** @type {HTMLElement} */ (document.createElement("div"));
el.classList.add("_eaglercraftX_mobile_file_chooser_popup");
el.style.position = "absolute";
el.style.backgroundColor = "white";
el.style.fontFamily = "sans-serif";
el.style.top = "10%";
el.style.left = "10%";
el.style.right = "10%";
el.style.border = "5px double black";
el.style.padding = "15px";
el.style.textAlign = "left";
el.style.fontSize = "20px";
el.style.userSelect = "none";
el.style.zIndex = "150";
const fileChooserTitle = document.createElement("h3");
fileChooserTitle.appendChild(document.createTextNode("File Chooser"));
el.appendChild(fileChooserTitle);
const inputElementContainer = document.createElement("p");
const inputElement = fileChooserElement = /** @type {HTMLInputElement} */ (document.createElement("input"));
inputElement.type = "file";
if(mime === null) {
inputElement.accept = "." + ext;
}else {
inputElement.accept = mime;
}
inputElement.multiple = false;
inputElementContainer.appendChild(inputElement);
el.appendChild(inputElementContainer);
const fileChooserButtons = document.createElement("p");
const fileChooserButtonCancel = document.createElement("button");
fileChooserButtonCancel.classList.add("_eaglercraftX_mobile_file_chooser_btn_cancel");
fileChooserButtonCancel.style.fontSize = "1.0em";
fileChooserButtonCancel.addEventListener("click", function(/** Event */ evt) {
if(fileChooserMobileElement === el) {
parentElement.removeChild(el);
fileChooserMobileElement = null;
fileChooserElement = null;
}
});
fileChooserButtonCancel.appendChild(document.createTextNode("Cancel"));
fileChooserButtons.appendChild(fileChooserButtonCancel);
fileChooserButtons.appendChild(document.createTextNode(" "));
const fileChooserButtonDone = document.createElement("button");
fileChooserButtonDone.classList.add("_eaglercraftX_mobile_file_chooser_btn_done");
fileChooserButtonDone.style.fontSize = "1.0em";
fileChooserButtonDone.style.fontWeight = "bold";
fileChooserButtonDone.addEventListener("click", function(/** Event */ evt) {
if(fileChooserMobileElement === el) {
if(inputElement.files.length > 0) {
const val = inputElement.files[0];
val.arrayBuffer().then(function(arr){
fileChooserHasResult = true;
fileChooserResultObject = {
"fileName": val.name,
"fileData": arr
};
}).catch(function(){
fileChooserHasResult = true;
fileChooserResultObject = null;
});
}else {
fileChooserHasResult = true;
fileChooserResultObject = null;
}
parentElement.removeChild(el);
fileChooserMobileElement = null;
fileChooserElement = null;
}
});
fileChooserButtonDone.appendChild(document.createTextNode("Done"));
fileChooserButtons.appendChild(fileChooserButtonDone);
el.appendChild(fileChooserButtons);
parentElement.appendChild(el);
}else {
const inputElement = fileChooserElement = /** @type {HTMLInputElement} */ (document.createElement("input"));
inputElement.type = "file";
inputElement.style.position = "absolute";
inputElement.style.left = "0px";
inputElement.style.top = "0px";
inputElement.style.zIndex = "-100";
if(mime === null) {
inputElement.accept = "." + ext;
}else {
inputElement.accept = mime;
}
inputElement.multiple = false;
inputElement.addEventListener("change", function(/** Event */ evt) {
if(fileChooserElement === inputElement) {
if(inputElement.files.length > 0) {
const val = inputElement.files[0];
val.arrayBuffer().then(function(arr){
fileChooserHasResult = true;
fileChooserResultObject = {
"fileName": val.name,
"fileData": arr
};
}).catch(function(){
fileChooserHasResult = true;
fileChooserResultObject = null;
});
}else {
fileChooserHasResult = true;
fileChooserResultObject = null;
}
parentElement.removeChild(inputElement);
fileChooserElement = null;
}
});
parentElement.appendChild(inputElement);
window.setTimeout(function() {
inputElement.click();
}, 50);
}
};
/**
* @return {boolean}
*/
applicationImports["fileChooserHasResult"] = function() {
return fileChooserHasResult;
};
/**
* @return {Object}
*/
applicationImports["getFileChooserResult"] = function() {
fileChooserHasResult = false;
const res = fileChooserResultObject;
fileChooserResultObject = null;
return res;
};
function clearFileChooserResultImpl() {
fileChooserHasResult = false;
fileChooserResultObject = null;
if(fileChooserMobileElement !== null) {
parentElement.removeChild(fileChooserMobileElement);
fileChooserMobileElement = null;
fileChooserElement = null;
}else if(fileChooserElement !== null) {
parentElement.removeChild(fileChooserElement);
fileChooserElement = null;
}
}
applicationImports["clearFileChooserResult"] = clearFileChooserResultImpl;
/**
* @param {string} str
* @param {Uint8Array} dat
*/
applicationImports["downloadFileWithNameU8"] = function(str, dat) {
const blobUrl = URL.createObjectURL(new Blob([dat], { "type": "application/octet-stream" }));
downloadFileImpl(str, blobUrl, function() {
URL.revokeObjectURL(blobUrl);
});
};
/**
* @param {string} str
* @param {ArrayBuffer} dat
*/
applicationImports["downloadFileWithNameA"] = function(str, dat) {
const blobUrl = URL.createObjectURL(new Blob([dat], { "type": "application/octet-stream" }));
downloadFileImpl(str, blobUrl, function() {
URL.revokeObjectURL(blobUrl);
});
};
/**
* @param {string} str
* @param {HTMLCanvasElement} cvs
*/
applicationImports["downloadScreenshot"] = function(str, cvs) {
downloadFileImpl(str, cvs.toDataURL("image/png"), null);
};
/** @type {HTMLDocument} */
var loggerDoc = null;
/** @type {HTMLElement} */
var loggerBody = null;
/** @type {HTMLElement} */
var loggerMessageContainer = null;
/** @type {string} */
const loggerLocalStorageKey = runtimeOpts.localStorageNamespace + "showDebugConsole";
/** @type {string} */
const loggerWinUnloadEvent = runtimeOpts.fixDebugConsoleUnloadListener ? "beforeunload" : "unload";
function debugConsoleLocalStorageSet(val) {
try {
if(window.localStorage) {
window.localStorage.setItem(loggerLocalStorageKey, val ? "true" : "false");
}
}catch(t) {
}
}
function debugConsoleLocalStorageGet() {
try {
if(window.localStorage) {
const val = window.localStorage.getItem(loggerLocalStorageKey);
return val && "true" === val.toLowerCase();
}else {
return false;
}
}catch(t) {
return false;
}
}
try {
window.addEventListener(loggerWinUnloadEvent, (evt) => {
destroyWindow();
});
}catch(ex) {
}
if(runtimeOpts.openDebugConsoleOnLaunch || debugConsoleLocalStorageGet()) {
showDebugConsole0();
}
function showDebugConsole() {
debugConsoleLocalStorageSet(true);
showDebugConsole0();
}
function showDebugConsole0() {
if(!loggerWindow) {
const w = Math.round(1000 * window.devicePixelRatio);
const h = Math.round(400 * window.devicePixelRatio);
const x = Math.round((window.screen.width - w) / 2.0);
const y = Math.round((window.screen.height - h) / 2.0);
loggerWindow = window.open("", "_blank", "top=" + y + ",left=" + x + ",width=" + w + ",height=" + h + ",menubar=0,status=0,titlebar=0,toolbar=0");
if(!loggerWindow) {
eagError("Logger popup was blocked!");
window.alert("ERROR: Popup blocked!\n\nPlease make sure you have popups enabled for this site!");
return;
}
loggerWindow.focus();
loggerDoc = loggerWindow.document;
loggerDoc.write("<!DOCTYPE html><html><head><meta charset=\"UTF-8\" /><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />"
+ "<title>Debug Console</title><link type=\"image/png\" rel=\"shortcut icon\" href=\"" + faviconURL + "\" />"
+ "</head><body style=\"overflow-x:hidden;overflow-y:scroll;padding:0px;\"><p id=\"loggerMessageContainer\" style=\"overflow-wrap:break-word;white-space:pre-wrap;font:14px monospace;padding:10px;\"></p></body></html>");
loggerDoc.close();
loggerBody = loggerDoc.body;
loggerMessageContainer = /** @type {HTMLElement} */ (loggerDoc.getElementById("loggerMessageContainer"));
var linkedListEl = messageBufferStart;
while(linkedListEl) {
appendLogMessage(linkedListEl["msg"] + "\n", linkedListEl["err"] ? "#DD0000" : "#000000");
linkedListEl = linkedListEl["next"];
}
messageBufferStart = null;
messageBufferEnd = null;
messageBufferLength = 0;
scrollToEnd0();
const unloadListener = (evt) => {
if(loggerWindow != null) {
loggerWindow = null;
debugConsoleLocalStorageSet(false);
}
};
loggerWindow.addEventListener("beforeunload", unloadListener);
loggerWindow.addEventListener("unload", unloadListener);
}else {
loggerWindow.focus();
}
}
/**
* @param {string} text
* @param {boolean} isErr
*/
loggerWindowMsgHandler = function(text, isErr) {
appendLogMessageAndScroll(text + "\n", isErr ? "#DD0000" : "#000000");
}
function appendLogMessageAndScroll(text, color) {
var b = isScrollToEnd();
appendLogMessage(text, color);
if(b) {
scrollToEnd0();
}
}
function appendLogMessage(text, color) {
var el = loggerDoc.createElement("span");
el.innerText = text;
el.style.color = color;
loggerMessageContainer.appendChild(el);
var children = loggerMessageContainer.children;
while(children.length > windowMaxMessages) {
children[0].remove();
}
}
/**
* @return {boolean}
*/
function isShowingDebugConsole() {
return !!loggerWindow;
}
function destroyWindow() {
if(loggerWindow) {
var w = loggerWindow;
loggerWindow = null;
loggerDoc = null;
loggerBody = null;
loggerMessageContainer = null;
w.close();
}
}
function isScrollToEnd() {
return (loggerWindow.innerHeight + loggerWindow.pageYOffset) >= loggerBody.offsetHeight;
}
function scrollToEnd0() {
setTimeout(() => {
loggerWindow.scrollTo(0, loggerBody.scrollHeight || loggerBody.clientHeight);
}, 1);
}
applicationImports["showDebugConsole"] = showDebugConsole;
applicationImports["addLogMessage"] = addLogMessageImpl;
applicationImports["isShowingDebugConsole"] = isShowingDebugConsole;
/**
* @return {string|null}
*/
applicationImports["getFaviconURL"] = function() {
return faviconURL;
};
}
/**
* @param {Object} applicationImports
*/
function initializeNoPlatfApplication(applicationImports) {
setUnsupportedFunc(applicationImports, platfApplicationName, "setClipboard");
setUnsupportedFunc(applicationImports, platfApplicationName, "getClipboard");
setUnsupportedFunc(applicationImports, platfApplicationName, "displayFileChooser");
setUnsupportedFunc(applicationImports, platfApplicationName, "fileChooserHasResult");
setUnsupportedFunc(applicationImports, platfApplicationName, "getFileChooserResult");
setUnsupportedFunc(applicationImports, platfApplicationName, "clearFileChooserResult");
setUnsupportedFunc(applicationImports, platfApplicationName, "downloadFileWithNameU8");
setUnsupportedFunc(applicationImports, platfApplicationName, "downloadFileWithNameA");
setUnsupportedFunc(applicationImports, platfApplicationName, "downloadScreenshot");
setUnsupportedFunc(applicationImports, platfApplicationName, "showDebugConsole");
setUnsupportedFunc(applicationImports, platfApplicationName, "addLogMessage");
setUnsupportedFunc(applicationImports, platfApplicationName, "isShowingDebugConsole");
setUnsupportedFunc(applicationImports, platfApplicationName, "getFaviconURL");
}

View File

@ -0,0 +1,106 @@
/*
* Copyright (c) 2024 lax1dude. All Rights Reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
const platfAssetsName = "platformAssets";
/**
* @param {number} idx
* @return {Object}
*/
eagruntimeImpl.platformAssets["getEPKFileData"] = function(idx) {
const tmp = epkFileList[idx];
epkFileList[idx] = null;
return tmp;
};
/**
* @return {number}
*/
eagruntimeImpl.platformAssets["getEPKFileCount"] = function() {
return epkFileList.length;
};
if(typeof window !== "undefined") {
/**
* @param {Uint8Array} bufferData
* @param {string} mime
* @return {Promise}
*/
function loadImageFile0Impl(bufferData, mime) {
return new Promise(function(resolve) {
const loadURL = URL.createObjectURL(new Blob([bufferData], {type: mime}));
if(loadURL) {
const toLoad = document.createElement("img");
toLoad.addEventListener("load", function(evt) {
URL.revokeObjectURL(loadURL);
resolve({
"width": toLoad.width,
"height": toLoad.height,
"img": toLoad
});
});
toLoad.addEventListener("error", function(evt) {
URL.revokeObjectURL(loadURL);
resolve(null);
});
toLoad.src = loadURL;
}else {
resolve(null);
}
});
}
eagruntimeImpl.platformAssets["loadImageFile0"] = new WebAssembly.Suspending(loadImageFile0Impl);
/** @type {HTMLCanvasElement} */
var imageLoadingCanvas = null;
/** @type {CanvasRenderingContext2D} */
var imageLoadingContext = null;
/**
* @param {Object} imageLoadResult
* @param {Uint8ClampedArray} dataDest
*/
eagruntimeImpl.platformAssets["loadImageFile1"] = function(imageLoadResult, dataDest) {
const width = imageLoadResult["width"];
const height = imageLoadResult["height"];
const img = imageLoadResult["img"];
if(img) {
if(!imageLoadingCanvas) {
imageLoadingCanvas = /** @type {HTMLCanvasElement} */ (document.createElement("canvas"));
}
if(imageLoadingCanvas.width < width) {
imageLoadingCanvas.width = width;
}
if(imageLoadingCanvas.height < height) {
imageLoadingCanvas.height = height;
}
if(!imageLoadingContext) {
imageLoadingContext = /** @type {CanvasRenderingContext2D} */ (imageLoadingCanvas.getContext("2d", { willReadFrequently: true }));
imageLoadingContext.imageSmoothingEnabled = false;
}
imageLoadingContext.clearRect(0, 0, width, height);
imageLoadingContext.drawImage(img, 0, 0, width, height);
dataDest.set(imageLoadingContext.getImageData(0, 0, width, height).data, 0);
}
};
}else {
setUnsupportedFunc(eagruntimeImpl.platformAssets, platfAssetsName, "loadImageFile0");
setUnsupportedFunc(eagruntimeImpl.platformAssets, platfAssetsName, "loadImageFile1");
}

View File

@ -0,0 +1,78 @@
/*
* Copyright (c) 2024 lax1dude. All Rights Reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
const platfAudioName = "platformAudio";
function setCurrentAudioContext(audioContext, audioImports) {
/**
* @return {AudioContext}
*/
audioImports["getContext"] = function() {
return audioContext;
};
/**
* @param {AudioBufferSourceNode} sourceNode
* @param {Object} isEnded
*/
audioImports["registerIsEndedHandler"] = function(sourceNode, isEnded) {
if(!isEnded["selfEndHandler"]) {
isEnded["selfEndHandler"] = function(evt) {
isEnded["isEnded"] = true;
}
}
sourceNode.addEventListener("ended", isEnded["selfEndHandler"]);
};
/**
* @param {AudioBufferSourceNode} sourceNode
* @param {Object} isEnded
*/
audioImports["releaseIsEndedHandler"] = function(sourceNode, isEnded) {
if(isEnded["selfEndHandler"]) {
sourceNode.removeEventListener("ended", isEnded["selfEndHandler"]);
}
};
/**
* @param {Uint8Array} fileData
* @param {string} errorFileName
* @return {Promise}
*/
function decodeAudioBrowserImpl(fileData, errorFileName) {
return new Promise(function(resolve) {
const copiedData = new Uint8Array(fileData.length);
copiedData.set(fileData, 0);
audioContext.decodeAudioData(copiedData.buffer, resolve, function(err) {
eagError("Failed to load audio: {}", errorFileName);
resolve(null);
});
});
}
audioImports["decodeAudioBrowser"] = new WebAssembly.Suspending(decodeAudioBrowserImpl);
}
function setNoAudioContext(audioImports) {
audioImports["getContext"] = function() {
return null;
};
setUnsupportedFunc(audioImports, platfAudioName, "registerIsEndedHandler");
setUnsupportedFunc(audioImports, platfAudioName, "releaseIsEndedHandler");
setUnsupportedFunc(audioImports, platfAudioName, "decodeAudioBrowser");
}

View File

@ -0,0 +1,295 @@
/*
* Copyright (c) 2024 lax1dude. All Rights Reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
const platfFilesystemName = "platformFilesystem";
/**
* @param {Object} k
* @return {string}
*/
function readDBKey(k) {
return ((typeof k) === "string") ? k : (((typeof k) === "undefined") ? null : (((typeof k[0]) === "string") ? k[0] : null));
}
/**
* @param {Object} obj
* @return {ArrayBuffer}
*/
function readDBRow(obj) {
return (typeof obj === "undefined") ? null : ((typeof obj["data"] === "undefined") ? null : obj["data"]);
}
/**
* @param {string} pat
* @param {ArrayBuffer} dat
* @return {Object}
*/
function writeDBRow(pat, dat) {
return { "path": pat, "data": dat };
}
/**
* @param {string} filesystemDB
* @return {Promise}
*/
function openDBImpl(filesystemDB) {
return new Promise(function(resolve) {
if(typeof indexedDB === "undefined") {
resolve({
"failedInit": true,
"failedLocked": false,
"failedError": "IndexedDB not supported",
"database": null
});
return;
}
let dbOpen;
try {
dbOpen = indexedDB.open(filesystemDB, 1);
}catch(err) {
resolve({
"failedInit": true,
"failedLocked": false,
"failedError": "Exception opening database",
"database": null
});
return;
}
let resultConsumer = resolve;
dbOpen.addEventListener("success", function(evt) {
if(resultConsumer) resultConsumer({
"failedInit": false,
"failedLocked": false,
"failedError": null,
"database": dbOpen.result
});
resultConsumer = null;
});
dbOpen.addEventListener("blocked", function(evt) {
if(resultConsumer) resultConsumer({
"failedInit": false,
"failedLocked": true,
"failedError": "Database is locked",
"database": null
});
resultConsumer = null;
});
dbOpen.addEventListener("error", function(evt) {
if(resultConsumer) resultConsumer({
"failedInit": true,
"failedLocked": false,
"failedError": "Opening database failed",
"database": null
});
resultConsumer = null;
});
dbOpen.addEventListener("upgradeneeded", function(evt) {
dbOpen.result.createObjectStore("filesystem", { keyPath: ["path"] });
});
});
}
eagruntimeImpl.platformFilesystem["openDB"] = new WebAssembly.Suspending(openDBImpl);
/**
* @param {IDBDatabase} database
* @param {string} pathName
* @return {Promise}
*/
function eaglerDeleteImpl(database, pathName) {
return new Promise(function(resolve) {
const tx = database.transaction("filesystem", "readwrite");
const r = tx.objectStore("filesystem").delete([pathName]);
r.addEventListener("success", function() {
resolve(true);
});
r.addEventListener("error", function() {
resolve(false);
});
});
}
eagruntimeImpl.platformFilesystem["eaglerDelete"] = new WebAssembly.Suspending(eaglerDeleteImpl);
/**
* @param {IDBDatabase} database
* @param {string} pathName
* @return {Promise}
*/
function eaglerReadImpl(database, pathName) {
return new Promise(function(resolve) {
const tx = database.transaction("filesystem", "readonly");
const r = tx.objectStore("filesystem").get([pathName]);
r.addEventListener("success", function() {
resolve(readDBRow(r.result));
});
r.addEventListener("error", function() {
resolve(null);
});
});
}
eagruntimeImpl.platformFilesystem["eaglerRead"] = new WebAssembly.Suspending(eaglerReadImpl);
/**
* @param {IDBDatabase} database
* @param {string} pathName
* @param {ArrayBuffer} arr
* @return {Promise}
*/
function eaglerWriteImpl(database, pathName, arr) {
return new Promise(function(resolve) {
const tx = database.transaction("filesystem", "readwrite");
const r = tx.objectStore("filesystem").put(writeDBRow(pathName, arr));
r.addEventListener("success", function() {
resolve(true);
});
r.addEventListener("error", function() {
resolve(false);
});
});
}
eagruntimeImpl.platformFilesystem["eaglerWrite"] = new WebAssembly.Suspending(eaglerWriteImpl);
/**
* @param {IDBDatabase} database
* @param {string} pathName
* @return {Promise}
*/
function eaglerExistsImpl(database, pathName) {
return new Promise(function(resolve) {
const tx = database.transaction("filesystem", "readonly");
const r = tx.objectStore("filesystem").count([pathName]);
r.addEventListener("success", function() {
resolve(r.result > 0);
});
r.addEventListener("error", function() {
resolve(false);
});
});
}
eagruntimeImpl.platformFilesystem["eaglerExists"] = new WebAssembly.Suspending(eaglerExistsImpl);
/**
* @param {IDBDatabase} database
* @param {string} pathNameOld
* @param {string} pathNameNew
* @return {Promise<boolean>}
*/
async function eaglerMoveImpl(database, pathNameOld, pathNameNew) {
const oldData = await eaglerReadImpl(database, pathNameOld);
if(!oldData || !(await eaglerWriteImpl(database, pathNameNew, oldData))) {
return false;
}
return await eaglerDeleteImpl(database, pathNameOld);
}
eagruntimeImpl.platformFilesystem["eaglerMove"] = new WebAssembly.Suspending(eaglerMoveImpl);
/**
* @param {IDBDatabase} database
* @param {string} pathNameOld
* @param {string} pathNameNew
* @return {Promise<boolean>}
*/
async function eaglerCopyImpl(database, pathNameOld, pathNameNew) {
const oldData = await eaglerReadImpl(database, pathNameOld);
return oldData && (await eaglerWriteImpl(database, pathNameNew, oldData));
}
eagruntimeImpl.platformFilesystem["eaglerCopy"] = new WebAssembly.Suspending(eaglerCopyImpl);
/**
* @param {IDBDatabase} database
* @param {string} pathName
* @return {Promise}
*/
function eaglerSizeImpl(database, pathName) {
return new Promise(function(resolve) {
const tx = database.transaction("filesystem", "readonly");
const r = tx.objectStore("filesystem").get([pathName]);
r.addEventListener("success", function() {
const data = readDBRow(r.result);
resolve(data ? data.byteLength : -1);
});
r.addEventListener("error", function() {
resolve(-1);
});
});
}
eagruntimeImpl.platformFilesystem["eaglerSize"] = new WebAssembly.Suspending(eaglerSizeImpl);
/**
* @param {string} str
* @return {number}
*/
function countSlashes(str) {
if(str.length === 0) return -1;
var j = 0;
for(var i = 0, l = str.length; i < l; ++i) {
if(str.charCodeAt(i) === 47) {
++j;
}
}
return j;
}
/**
* @param {IDBDatabase} database
* @param {string} pathName
* @param {number} recursive
* @return {Promise}
*/
function eaglerIterateImpl(database, pathName, recursive) {
return new Promise(function(resolve) {
const rows = [];
const tx = database.transaction("filesystem", "readonly");
const r = tx.objectStore("filesystem").openCursor();
const b = pathName.length === 0;
const pc = recursive ? -1 : countSlashes(pathName);
r.addEventListener("success", function() {
const c = r.result;
if(c === null || c.key === null) {
resolve({
"length": rows.length,
/**
* @param {number} idx
* @return {string}
*/
"getRow": function(idx) {
return rows[idx];
}
});
return;
}
const k = readDBKey(c.key);
if(k != null) {
if((b || k.startsWith(pathName)) && (recursive || countSlashes(k) === pc)) {
rows.push(k);
}
}
c.continue();
});
r.addEventListener("error", function() {
resolve(null);
});
});
}
eagruntimeImpl.platformFilesystem["eaglerIterate"] = new WebAssembly.Suspending(eaglerIterateImpl);

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,365 @@
/*
* Copyright (c) 2024 lax1dude. All Rights Reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
const platfNetworkingName = "platformNetworking";
(function() {
const WS_CLOSED = 0;
const WS_CONNECTING = 1;
const WS_CONNECTED = 2;
const WS_FAILED = 3;
function closeSocketImpl() {
this["_socket"].close();
}
/**
* @param {string} str
*/
function sendStringFrameImpl(str) {
this["_socket"].send(str);
}
/**
* @param {Uint8Array} bin
*/
function sendBinaryFrameImpl(bin) {
this["_socket"].send(bin);
}
/**
* @return {number}
*/
function availableFramesImpl() {
return this["_frameCountStr"] + this["_frameCountBin"];
}
/**
* @return {Object}
*/
function getNextFrameImpl() {
const f = this["_queue"];
if(f) {
if(f["_next"] === f && f["_prev"] === f) {
this["_queue"] = null;
}else {
this["_queue"] = f["_next"];
f["_prev"]["_next"] = f["_next"];
f["_next"]["_prev"] = f["_prev"];
}
f["_next"] = null;
f["_prev"] = null;
if(f["type"] === 0) {
--this["_frameCountStr"];
}else {
--this["_frameCountBin"];
}
return f;
}else {
return null;
}
}
/**
* @return {Array}
*/
function getAllFramesImpl() {
const len = this["_frameCountStr"] + this["_frameCountBin"];
if(len === 0) {
return null;
}
const ret = new Array(len);
var idx = 0;
var f = this["_queue"];
var g;
const ff = f;
do {
ret[idx++] = f;
g = f["_next"];
f["_next"] = null;
f["_prev"] = null;
f = g;
}while(f !== ff);
this["_queue"] = null;
this["_frameCountStr"] = 0;
this["_frameCountBin"] = 0;
return ret;
}
function clearFramesImpl() {
this["_queue"] = null;
this["_frameCountStr"] = 0;
this["_frameCountBin"] = 0;
}
/**
* @param {Object} thisObj
* @param {number} type
* @return {Object}
*/
function getNextTypedFrameImpl(thisObj, type) {
var f = thisObj["_queue"];
if(!f) {
return null;
}
var g, h;
const ff = f;
do {
g = f["_next"];
if(f["type"] === type) {
h = f["_prev"];
if(g === f && h === f) {
thisObj["_queue"] = null;
}else {
if(f === ff) {
thisObj["_queue"] = g;
}
h["_next"] = g;
g["_prev"] = h;
}
f["_next"] = null;
f["_prev"] = null;
return f;
}
f = g;
}while(f !== ff);
return null;
}
/**
* @param {Object} thisObj
* @param {number} type
* @param {Array} ret
*/
function getAllTypedFrameImpl(thisObj, type, ret) {
var idx = 0;
var f = thisObj["_queue"];
var g, h;
const ff = f;
do {
g = f["_next"];
if(f["type"] === type) {
ret[idx++] = f;
}
f = g;
}while(f !== ff);
ret.length = idx;
for(var i = 0; i < idx; ++i) {
f = ret[i];
g = f["_next"];
h = f["_prev"];
if(g === f && h === f) {
thisObj["_queue"] = null;
}else {
if(f === thisObj["_queue"]) {
thisObj["_queue"] = g;
}
h["_next"] = g;
g["_prev"] = h;
}
}
}
/**
* @return {number}
*/
function availableStringFramesImpl() {
return this["_frameCountStr"];
}
/**
* @return {Object}
*/
function getNextStringFrameImpl() {
const len = this["_frameCountStr"];
if(len === 0) {
return null;
}
const ret = getNextTypedFrameImpl(this, 0);
if(!ret) {
this["_frameCountStr"] = 0;
}else {
--this["_frameCountStr"];
}
return ret;
}
/**
* @return {Array}
*/
function getAllStringFramesImpl() {
const len = this["_frameCountStr"];
if(len === 0) {
return null;
}
const ret = new Array(len);
getAllTypedFrameImpl(this, 0, ret);
this["_frameCountStr"] = 0;
return ret;
}
function clearStringFramesImpl() {
const len = this["_frameCountStr"];
if(len === 0) {
return null;
}
const ret = new Array(len);
getAllTypedFrameImpl(this, 0, ret);
this["_frameCountStr"] = 0;
}
/**
* @return {number}
*/
function availableBinaryFramesImpl() {
return this["_frameCountBin"];
}
/**
* @return {Object}
*/
function getNextBinaryFrameImpl() {
const len = this["_frameCountBin"];
if(len === 0) {
return null;
}
const ret = getNextTypedFrameImpl(this, 1);
if(!ret) {
this["_frameCountBin"] = 0;
}else {
--this["_frameCountBin"];
}
return ret;
}
/**
* @return {Array}
*/
function getAllBinaryFramesImpl() {
const len = this["_frameCountBin"];
if(len === 0) {
return null;
}
const ret = new Array(len);
getAllTypedFrameImpl(this, 1, ret);
this["_frameCountBin"] = 0;
return ret;
}
function clearBinaryFramesImpl() {
const len = this["_frameCountBin"];
if(len === 0) {
return null;
}
const ret = new Array(len);
getAllTypedFrameImpl(this, 1, ret);
this["_frameCountBin"] = 0;
}
function addRecievedFrameImpl(dat) {
const isStr = (typeof dat === "string");
const itm = {
"type": (isStr ? 0 : 1),
"data": dat,
"timestamp": performance.now(),
"_next": null,
"_prev": null
};
const first = this["_queue"];
if(!first) {
this["_queue"] = itm;
itm["_next"] = itm;
itm["_prev"] = itm;
}else {
const last = first["_prev"];
last["_next"] = itm;
itm["_prev"] = last;
itm["_next"] = first;
first["_prev"] = itm;
}
if(isStr) {
++this["_frameCountStr"];
}else {
++this["_frameCountBin"];
}
}
/**
* @param {string} socketURI
* @return {Object}
*/
eagruntimeImpl.platformNetworking["createWebSocketHandle"] = function(socketURI) {
let sock;
try {
sock = new WebSocket(socketURI);
}catch(ex) {
eagError("Failed to create WebSocket: {}", socketURI);
eagStackTrace(ERROR, "Exception Caught", ex);
return null;
}
sock.binaryType = "arraybuffer";
const ret = {
"state": WS_CONNECTING,
"_socket": sock,
"_queue": null,
"_frameCountStr": 0,
"_frameCountBin": 0,
"_addRecievedFrame": addRecievedFrameImpl,
"closeSocket": closeSocketImpl,
"sendStringFrame": sendStringFrameImpl,
"sendBinaryFrame": sendBinaryFrameImpl,
"availableFrames": availableFramesImpl,
"getNextFrame": getNextFrameImpl,
"getAllFrames": getAllFramesImpl,
"clearFrames": clearFramesImpl,
"availableStringFrames": availableStringFramesImpl,
"getNextStringFrame": getNextStringFrameImpl,
"getAllStringFrames": getAllStringFramesImpl,
"clearStringFrames": clearStringFramesImpl,
"availableBinaryFrames": availableBinaryFramesImpl,
"getNextBinaryFrame": getNextBinaryFrameImpl,
"getAllBinaryFrames": getAllBinaryFramesImpl,
"clearBinaryFrames": clearBinaryFramesImpl
};
sock.addEventListener("open", function(evt) {
ret["state"] = WS_CONNECTED;
});
sock.addEventListener("message", function(evt) {
ret["_addRecievedFrame"](evt.data);
});
sock.addEventListener("close", function(evt) {
if(ret["state"] !== WS_FAILED) {
ret["state"] = WS_CLOSED;
}
});
sock.addEventListener("error", function(evt) {
if(ret["state"] === WS_CONNECTING) {
ret["state"] = WS_FAILED;
}
});
return ret;
};
})();

View File

@ -0,0 +1,387 @@
/*
* Copyright (c) 2024 lax1dude. All Rights Reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
const VAO_IMPL_NONE = -1;
const VAO_IMPL_CORE = 0;
const VAO_IMPL_OES = 1;
const INSTANCE_IMPL_NONE = -1;
const INSTANCE_IMPL_CORE = 0;
const INSTANCE_IMPL_ANGLE = 1;
const CAP_A_BIT_EXT_GPU_SHADER5 = 1;
const CAP_A_BIT_OES_GPU_SHADER5 = 2;
const CAP_A_BIT_FBO_RENDER_MIPMAP = 4;
const CAP_A_BIT_TEXTURE_LOD_CAPABLE = 8;
const CAP_A_BIT_NPOT_CAPABLE = 16;
const CAP_A_BIT_HDR_FBO16F = 32;
const CAP_A_BIT_HDR_FBO32F = 64;
const CAP_A_BIT_ANISOTROPIC = 128;
const CAP_B_BIT_HDR_LINEAR16F = 1;
const CAP_B_BIT_HDR_LINEAR32F = 2;
const platfOpenGLName = "platformOpenGL";
/**
* @param {WebGL2RenderingContext} ctx
* @param {number} glesVersIn
* @param {boolean} allowExts
* @param {Object} glImports
*/
function setCurrentGLContext(ctx, glesVersIn, allowExts, glImports) {
const wglExtVAO = (allowExts && glesVersIn === 200) ? ctx.getExtension("OES_vertex_array_object") : null;
const wglExtInstancing = (allowExts && glesVersIn === 200) ? ctx.getExtension("ANGLE_instanced_arrays") : null;
const hasANGLEInstancedArrays = allowExts && glesVersIn === 200 && wglExtInstancing !== null;
const hasEXTColorBufferFloat = allowExts && (glesVersIn === 310 || glesVersIn === 300) && ctx.getExtension("EXT_color_buffer_float") !== null;
const hasEXTColorBufferHalfFloat = allowExts && !hasEXTColorBufferFloat && (glesVersIn === 310 || glesVersIn === 300 || glesVersIn === 200)
&& ctx.getExtension("EXT_color_buffer_half_float") !== null;
const hasEXTShaderTextureLOD = allowExts && glesVersIn === 200 && ctx.getExtension("EXT_shader_texture_lod") !== null;
const hasOESFBORenderMipmap = allowExts && glesVersIn === 200 && ctx.getExtension("OES_fbo_render_mipmap") !== null;
const hasOESVertexArrayObject = allowExts && glesVersIn === 200 && wglExtVAO !== null;
const hasOESTextureFloat = allowExts && glesVersIn === 200 && ctx.getExtension("OES_texture_float") !== null;
const hasOESTextureFloatLinear = allowExts && glesVersIn >= 300 && ctx.getExtension("OES_texture_float_linear") !== null;
const hasOESTextureHalfFloat = allowExts && glesVersIn === 200 && ctx.getExtension("OES_texture_half_float") !== null;
const hasOESTextureHalfFloatLinear = allowExts && glesVersIn === 200 && ctx.getExtension("OES_texture_half_float_linear") !== null;
const hasEXTTextureFilterAnisotropic = allowExts && ctx.getExtension("EXT_texture_filter_anisotropic") !== null;
const hasWEBGLDebugRendererInfo = ctx.getExtension("WEBGL_debug_renderer_info") !== null;
const hasFBO16FSupport = glesVersIn >= 320 || ((glesVersIn >= 300 || hasOESTextureFloat) && (hasEXTColorBufferFloat || hasEXTColorBufferHalfFloat));
const hasFBO32FSupport = glesVersIn >= 320 || ((glesVersIn >= 300 || hasOESTextureHalfFloat) && hasEXTColorBufferFloat);
const hasLinearHDR16FSupport = glesVersIn >= 300 || hasOESTextureHalfFloatLinear;
const hasLinearHDR32FSupport = glesVersIn >= 300 && hasOESTextureFloatLinear;
const vertexArrayImpl = glesVersIn >= 300 ? VAO_IMPL_CORE : ((glesVersIn === 200 && hasOESVertexArrayObject) ? VAO_IMPL_OES : VAO_IMPL_NONE);
const instancingImpl = glesVersIn >= 300 ? INSTANCE_IMPL_CORE : ((glesVersIn === 200 && hasANGLEInstancedArrays) ? INSTANCE_IMPL_ANGLE : INSTANCE_IMPL_NONE);
const capBits = [ glesVersIn, vertexArrayImpl, instancingImpl, 0, 0 ];
if(glesVersIn >= 300 || hasOESFBORenderMipmap) capBits[3] |= CAP_A_BIT_FBO_RENDER_MIPMAP;
if(glesVersIn >= 300 || hasEXTShaderTextureLOD) capBits[3] |= CAP_A_BIT_TEXTURE_LOD_CAPABLE;
if(glesVersIn >= 300) capBits[3] |= CAP_A_BIT_NPOT_CAPABLE;
if(hasFBO16FSupport) capBits[3] |= CAP_A_BIT_HDR_FBO16F;
if(hasFBO32FSupport) capBits[3] |= CAP_A_BIT_HDR_FBO32F;
if(hasEXTTextureFilterAnisotropic) capBits[3] |= CAP_A_BIT_ANISOTROPIC;
if(hasLinearHDR16FSupport) capBits[4] |= CAP_B_BIT_HDR_LINEAR16F;
if(hasLinearHDR32FSupport) capBits[4] |= CAP_B_BIT_HDR_LINEAR32F;
/**
* @param {number} idx
* @return {number}
*/
glImports["getCapBits"] = function(idx) {
return capBits[idx];
};
glImports["glEnable"] = ctx.enable.bind(ctx);
glImports["glDisable"] = ctx.disable.bind(ctx);
glImports["glClearColor"] = ctx.clearColor.bind(ctx);
glImports["glClearDepth"] = ctx.clearDepth.bind(ctx);
glImports["glClear"] = ctx.clear.bind(ctx);
glImports["glDepthFunc"] = ctx.depthFunc.bind(ctx);
glImports["glDepthMask"] = ctx.depthMask.bind(ctx);
glImports["glCullFace"] = ctx.cullFace.bind(ctx);
glImports["glViewport"] = ctx.viewport.bind(ctx);
glImports["glBlendFunc"] = ctx.blendFunc.bind(ctx);
glImports["glBlendFuncSeparate"] = ctx.blendFuncSeparate.bind(ctx);
glImports["glBlendEquation"] = ctx.blendEquation.bind(ctx);
glImports["glBlendColor"] = ctx.blendColor.bind(ctx);
glImports["glColorMask"] = ctx.colorMask.bind(ctx);
glImports["glDrawBuffers"] = glesVersIn >= 300 ? ctx.drawBuffers.bind(ctx) : unsupportedFunc(platfOpenGLName, "glDrawBuffers");
glImports["glReadBuffer"] = glesVersIn >= 300 ? ctx.readBuffer.bind(ctx) : unsupportedFunc(platfOpenGLName, "glReadBuffer");
glImports["glReadPixels"] = ctx.readPixels.bind(ctx);
glImports["glPolygonOffset"] = ctx.polygonOffset.bind(ctx);
glImports["glLineWidth"] = ctx.lineWidth.bind(ctx);
glImports["glGenBuffers"] = ctx.createBuffer.bind(ctx);
glImports["glGenTextures"] = ctx.createTexture.bind(ctx);
glImports["glCreateProgram"] = ctx.createProgram.bind(ctx);
glImports["glCreateShader"] = ctx.createShader.bind(ctx);
glImports["glCreateFramebuffer"] = ctx.createFramebuffer.bind(ctx);
glImports["glCreateRenderbuffer"] = ctx.createRenderbuffer.bind(ctx);
glImports["glGenQueries"] = glesVersIn >= 300 ? ctx.createQuery.bind(ctx) : unsupportedFunc(platfOpenGLName, "glGenQueries");
glImports["glDeleteBuffers"] = ctx.deleteBuffer.bind(ctx);
glImports["glDeleteTextures"] = ctx.deleteTexture.bind(ctx);
glImports["glDeleteProgram"] = ctx.deleteProgram.bind(ctx);
glImports["glDeleteShader"] = ctx.deleteShader.bind(ctx);
glImports["glDeleteFramebuffer"] = ctx.deleteFramebuffer.bind(ctx);
glImports["glDeleteRenderbuffer"] = ctx.deleteRenderbuffer.bind(ctx);
glImports["glDeleteQueries"] = glesVersIn >= 300 ? ctx.deleteQuery.bind(ctx) : unsupportedFunc(platfOpenGLName, "glDeleteQueries");
glImports["glBindBuffer"] = ctx.bindBuffer.bind(ctx);
glImports["glBufferData"] = ctx.bufferData.bind(ctx);
glImports["glBufferSubData"] = ctx.bufferSubData.bind(ctx);
glImports["glEnableVertexAttribArray"] = ctx.enableVertexAttribArray.bind(ctx);
glImports["glDisableVertexAttribArray"] = ctx.disableVertexAttribArray.bind(ctx);
glImports["glVertexAttribPointer"] = ctx.vertexAttribPointer.bind(ctx);
glImports["glActiveTexture"] = ctx.activeTexture.bind(ctx);
glImports["glBindTexture"] = ctx.bindTexture.bind(ctx);
glImports["glTexParameterf"] = ctx.texParameterf.bind(ctx);
glImports["glTexParameteri"] = ctx.texParameteri.bind(ctx);
glImports["glTexImage3D"] = glesVersIn >= 300 ? ctx.texImage3D.bind(ctx) : unsupportedFunc(platfOpenGLName, "glTexImage3D");
glImports["glTexImage2D"] = ctx.texImage2D.bind(ctx);
glImports["glTexSubImage2D"] = ctx.texSubImage2D.bind(ctx);
glImports["glCopyTexSubImage2D"] = ctx.copyTexSubImage2D.bind(ctx);
glImports["glTexStorage2D"] = glesVersIn >= 300 ? ctx.texStorage2D.bind(ctx) : unsupportedFunc(platfOpenGLName, "glTexStorage2D");
glImports["glPixelStorei"] = ctx.pixelStorei.bind(ctx);
glImports["glGenerateMipmap"] = ctx.generateMipmap.bind(ctx);
glImports["glShaderSource"] = ctx.shaderSource.bind(ctx);
glImports["glCompileShader"] = ctx.compileShader.bind(ctx);
glImports["glGetShaderi"] = ctx.getShaderParameter.bind(ctx);
glImports["glGetShaderInfoLog"] = ctx.getShaderInfoLog.bind(ctx);
glImports["glUseProgram"] = ctx.useProgram.bind(ctx);
glImports["glAttachShader"] = ctx.attachShader.bind(ctx);
glImports["glDetachShader"] = ctx.detachShader.bind(ctx);
glImports["glLinkProgram"] = ctx.linkProgram.bind(ctx);
glImports["glGetProgrami"] = ctx.getProgramParameter.bind(ctx);
glImports["glGetProgramInfoLog"] = ctx.getProgramInfoLog.bind(ctx);
glImports["glDrawArrays"] = ctx.drawArrays.bind(ctx);
glImports["glDrawElements"] = ctx.drawElements.bind(ctx);
glImports["glBindAttribLocation"] = ctx.bindAttribLocation.bind(ctx);
glImports["glGetAttribLocation"] = ctx.getAttribLocation.bind(ctx);
glImports["glGetUniformLocation"] = ctx.getUniformLocation.bind(ctx);
glImports["glGetUniformBlockIndex"] = glesVersIn >= 300 ? ctx.getUniformBlockIndex.bind(ctx) : unsupportedFunc(platfOpenGLName, "glGetUniformBlockIndex");
glImports["glBindBufferRange"] = glesVersIn >= 300 ? ctx.bindBufferRange.bind(ctx) : unsupportedFunc(platfOpenGLName, "glBindBufferRange");
glImports["glUniformBlockBinding"] = glesVersIn >= 300 ? ctx.uniformBlockBinding.bind(ctx) : unsupportedFunc(platfOpenGLName, "glUniformBlockBinding");
glImports["glUniform1f"] = ctx.uniform1f.bind(ctx);
glImports["glUniform2f"] = ctx.uniform2f.bind(ctx);
glImports["glUniform3f"] = ctx.uniform3f.bind(ctx);
glImports["glUniform4f"] = ctx.uniform4f.bind(ctx);
glImports["glUniform1i"] = ctx.uniform1i.bind(ctx);
glImports["glUniform2i"] = ctx.uniform2i.bind(ctx);
glImports["glUniform3i"] = ctx.uniform3i.bind(ctx);
glImports["glUniform4i"] = ctx.uniform4i.bind(ctx);
glImports["glUniformMatrix2fv"] = ctx.uniformMatrix2fv.bind(ctx);
glImports["glUniformMatrix3fv"] = ctx.uniformMatrix3fv.bind(ctx);
glImports["glUniformMatrix4fv"] = ctx.uniformMatrix4fv.bind(ctx);
glImports["glUniformMatrix3x2fv"] = glesVersIn >= 300 ? ctx.uniformMatrix3x2fv.bind(ctx) : unsupportedFunc(platfOpenGLName, "glUniformMatrix3x2fv");
glImports["glUniformMatrix4x2fv"] = glesVersIn >= 300 ? ctx.uniformMatrix4x2fv.bind(ctx) : unsupportedFunc(platfOpenGLName, "glUniformMatrix4x2fv");
glImports["glUniformMatrix4x3fv"] = glesVersIn >= 300 ? ctx.uniformMatrix4x3fv.bind(ctx) : unsupportedFunc(platfOpenGLName, "glUniformMatrix4x3fv");
glImports["glBindFramebuffer"] = ctx.bindFramebuffer.bind(ctx);
glImports["glCheckFramebufferStatus"] = ctx.checkFramebufferStatus.bind(ctx);
glImports["glBlitFramebuffer"] = glesVersIn >= 300 ? ctx.blitFramebuffer.bind(ctx) : unsupportedFunc(platfOpenGLName, "glBlitFramebuffer");
glImports["glRenderbufferStorage"] = ctx.renderbufferStorage.bind(ctx);
glImports["glFramebufferTexture2D"] = ctx.framebufferTexture2D.bind(ctx);
glImports["glFramebufferTextureLayer"] = glesVersIn >= 300 ? ctx.framebufferTextureLayer.bind(ctx) : unsupportedFunc(platfOpenGLName, "glFramebufferTextureLayer");
glImports["glBindRenderbuffer"] = ctx.bindRenderbuffer.bind(ctx);
glImports["glFramebufferRenderbuffer"] = ctx.framebufferRenderbuffer.bind(ctx);
glImports["glGetError"] = ctx.getError.bind(ctx);
glImports["getAllExtensions"] = ctx.getSupportedExtensions.bind(ctx);
glImports["isContextLost"] = ctx.isContextLost.bind(ctx);
const exts = [];
if(hasANGLEInstancedArrays) exts.push("ANGLE_instanced_arrays");
if(hasEXTColorBufferFloat) exts.push("EXT_color_buffer_float");
if(hasEXTColorBufferHalfFloat) exts.push("EXT_color_buffer_half_float");
if(hasEXTShaderTextureLOD) exts.push("EXT_shader_texture_lod");
if(hasOESFBORenderMipmap) exts.push("OES_fbo_render_mipmap");
if(hasOESVertexArrayObject) exts.push("OES_vertex_array_object");
if(hasOESTextureFloat) exts.push("OES_texture_float");
if(hasOESTextureFloatLinear) exts.push("OES_texture_float_linear");
if(hasOESTextureHalfFloat) exts.push("OES_texture_half_float");
if(hasOESTextureHalfFloatLinear) exts.push("OES_texture_half_float_linear");
if(hasEXTTextureFilterAnisotropic) exts.push("EXT_texture_filter_anisotropic");
if(hasWEBGLDebugRendererInfo) exts.push("WEBGL_debug_renderer_info");
/**
* @return {Array}
*/
glImports["dumpActiveExtensions"] = function() {
return exts;
};
/**
* @param {number} p
* @return {number}
*/
glImports["glGetInteger"] = function(p) {
const ret = /** @type {*} */ (ctx.getParameter(p));
return (typeof ret === "number") ? (/** @type {number} */ (ret)) : 0;
};
/**
* @param {number} p
* @return {string|null}
*/
glImports["glGetString"] = function(p) {
var s;
if(hasWEBGLDebugRendererInfo) {
switch(p) {
case 0x1f00: // VENDOR
s = ctx.getParameter(0x9245); // UNMASKED_VENDOR_WEBGL
if(s == null) {
s = ctx.getParameter(0x1f00); // VENDOR
}
break;
case 0x1f01: // RENDERER
s = ctx.getParameter(0x9246); // UNMASKED_RENDERER_WEBGL
if(s == null) {
s = ctx.getParameter(0x1f01); // RENDERER
}
break;
default:
s = ctx.getParameter(p);
break;
}
}else {
s = ctx.getParameter(p);
}
if(typeof s === "string") {
return s;
}else {
return null;
}
};
switch(vertexArrayImpl) {
case VAO_IMPL_CORE:
glImports["glGenVertexArrays"] = ctx.createVertexArray.bind(ctx);
glImports["glDeleteVertexArrays"] = ctx.deleteVertexArray.bind(ctx);
glImports["glBindVertexArray"] = ctx.bindVertexArray.bind(ctx);
break;
case VAO_IMPL_OES:
glImports["glGenVertexArrays"] = wglExtVAO.createVertexArrayOES.bind(wglExtVAO);
glImports["glDeleteVertexArrays"] = wglExtVAO.deleteVertexArrayOES.bind(wglExtVAO);
glImports["glBindVertexArray"] = wglExtVAO.bindVertexArrayOES.bind(wglExtVAO);
break;
case VAO_IMPL_NONE:
default:
setUnsupportedFunc(glImports, platfOpenGLName, "glGenVertexArrays");
setUnsupportedFunc(glImports, platfOpenGLName, "glDeleteVertexArrays");
setUnsupportedFunc(glImports, platfOpenGLName, "glBindVertexArray");
break;
}
switch(instancingImpl) {
case INSTANCE_IMPL_CORE:
glImports["glVertexAttribDivisor"] = ctx.vertexAttribDivisor.bind(ctx);
glImports["glDrawArraysInstanced"] = ctx.drawArraysInstanced.bind(ctx);
glImports["glDrawElementsInstanced"] = ctx.drawElementsInstanced.bind(ctx);
break;
case INSTANCE_IMPL_ANGLE:
glImports["glVertexAttribDivisor"] = wglExtInstancing.vertexAttribDivisorANGLE.bind(wglExtInstancing);
glImports["glDrawArraysInstanced"] = wglExtInstancing.drawArraysInstancedANGLE.bind(wglExtInstancing);
glImports["glDrawElementsInstanced"] = wglExtInstancing.drawElementsInstancedANGLE.bind(wglExtInstancing);
break;
case INSTANCE_IMPL_NONE:
default:
setUnsupportedFunc(glImports, platfOpenGLName, "glVertexAttribDivisor");
setUnsupportedFunc(glImports, platfOpenGLName, "glDrawArraysInstanced");
setUnsupportedFunc(glImports, platfOpenGLName, "glDrawElementsInstanced");
break;
}
}
function setNoGLContext(glImports) {
setUnsupportedFunc(glImports, platfOpenGLName, "getCapBits");
setUnsupportedFunc(glImports, platfOpenGLName, "glEnable");
setUnsupportedFunc(glImports, platfOpenGLName, "glDisable");
setUnsupportedFunc(glImports, platfOpenGLName, "glClearColor");
setUnsupportedFunc(glImports, platfOpenGLName, "glClearDepth");
setUnsupportedFunc(glImports, platfOpenGLName, "glClear");
setUnsupportedFunc(glImports, platfOpenGLName, "glDepthFunc");
setUnsupportedFunc(glImports, platfOpenGLName, "glDepthMask");
setUnsupportedFunc(glImports, platfOpenGLName, "glCullFace");
setUnsupportedFunc(glImports, platfOpenGLName, "glViewport");
setUnsupportedFunc(glImports, platfOpenGLName, "glBlendFunc");
setUnsupportedFunc(glImports, platfOpenGLName, "glBlendFuncSeparate");
setUnsupportedFunc(glImports, platfOpenGLName, "glBlendEquation");
setUnsupportedFunc(glImports, platfOpenGLName, "glBlendColor");
setUnsupportedFunc(glImports, platfOpenGLName, "glColorMask");
setUnsupportedFunc(glImports, platfOpenGLName, "glDrawBuffers");
setUnsupportedFunc(glImports, platfOpenGLName, "glReadBuffer");
setUnsupportedFunc(glImports, platfOpenGLName, "glReadPixels");
setUnsupportedFunc(glImports, platfOpenGLName, "glPolygonOffset");
setUnsupportedFunc(glImports, platfOpenGLName, "glLineWidth");
setUnsupportedFunc(glImports, platfOpenGLName, "glGenBuffers");
setUnsupportedFunc(glImports, platfOpenGLName, "glGenTextures");
setUnsupportedFunc(glImports, platfOpenGLName, "glCreateProgram");
setUnsupportedFunc(glImports, platfOpenGLName, "glCreateShader");
setUnsupportedFunc(glImports, platfOpenGLName, "glCreateFramebuffer");
setUnsupportedFunc(glImports, platfOpenGLName, "glCreateRenderbuffer");
setUnsupportedFunc(glImports, platfOpenGLName, "glGenQueries");
setUnsupportedFunc(glImports, platfOpenGLName, "glDeleteBuffers");
setUnsupportedFunc(glImports, platfOpenGLName, "glDeleteTextures");
setUnsupportedFunc(glImports, platfOpenGLName, "glDeleteProgram");
setUnsupportedFunc(glImports, platfOpenGLName, "glDeleteShader");
setUnsupportedFunc(glImports, platfOpenGLName, "glDeleteFramebuffer");
setUnsupportedFunc(glImports, platfOpenGLName, "glDeleteRenderbuffer");
setUnsupportedFunc(glImports, platfOpenGLName, "glDeleteQueries");
setUnsupportedFunc(glImports, platfOpenGLName, "glBindBuffer");
setUnsupportedFunc(glImports, platfOpenGLName, "glBufferData");
setUnsupportedFunc(glImports, platfOpenGLName, "glBufferSubData");
setUnsupportedFunc(glImports, platfOpenGLName, "glEnableVertexAttribArray");
setUnsupportedFunc(glImports, platfOpenGLName, "glDisableVertexAttribArray");
setUnsupportedFunc(glImports, platfOpenGLName, "glVertexAttribPointer");
setUnsupportedFunc(glImports, platfOpenGLName, "glActiveTexture");
setUnsupportedFunc(glImports, platfOpenGLName, "glBindTexture");
setUnsupportedFunc(glImports, platfOpenGLName, "glTexParameterf");
setUnsupportedFunc(glImports, platfOpenGLName, "glTexParameteri");
setUnsupportedFunc(glImports, platfOpenGLName, "glTexImage3D");
setUnsupportedFunc(glImports, platfOpenGLName, "glTexImage2D");
setUnsupportedFunc(glImports, platfOpenGLName, "glTexSubImage2D");
setUnsupportedFunc(glImports, platfOpenGLName, "glCopyTexSubImage2D");
setUnsupportedFunc(glImports, platfOpenGLName, "glTexStorage2D");
setUnsupportedFunc(glImports, platfOpenGLName, "glPixelStorei");
setUnsupportedFunc(glImports, platfOpenGLName, "glGenerateMipmap");
setUnsupportedFunc(glImports, platfOpenGLName, "glShaderSource");
setUnsupportedFunc(glImports, platfOpenGLName, "glCompileShader");
setUnsupportedFunc(glImports, platfOpenGLName, "glGetShaderi");
setUnsupportedFunc(glImports, platfOpenGLName, "glGetShaderInfoLog");
setUnsupportedFunc(glImports, platfOpenGLName, "glUseProgram");
setUnsupportedFunc(glImports, platfOpenGLName, "glAttachShader");
setUnsupportedFunc(glImports, platfOpenGLName, "glDetachShader");
setUnsupportedFunc(glImports, platfOpenGLName, "glLinkProgram");
setUnsupportedFunc(glImports, platfOpenGLName, "glGetProgrami");
setUnsupportedFunc(glImports, platfOpenGLName, "glGetProgramInfoLog");
setUnsupportedFunc(glImports, platfOpenGLName, "glDrawArrays");
setUnsupportedFunc(glImports, platfOpenGLName, "glDrawElements");
setUnsupportedFunc(glImports, platfOpenGLName, "glBindAttribLocation");
setUnsupportedFunc(glImports, platfOpenGLName, "glGetAttribLocation");
setUnsupportedFunc(glImports, platfOpenGLName, "glGetUniformLocation");
setUnsupportedFunc(glImports, platfOpenGLName, "glGetUniformBlockIndex");
setUnsupportedFunc(glImports, platfOpenGLName, "glBindBufferRange");
setUnsupportedFunc(glImports, platfOpenGLName, "glUniformBlockBinding");
setUnsupportedFunc(glImports, platfOpenGLName, "glUniform1f");
setUnsupportedFunc(glImports, platfOpenGLName, "glUniform2f");
setUnsupportedFunc(glImports, platfOpenGLName, "glUniform3f");
setUnsupportedFunc(glImports, platfOpenGLName, "glUniform4f");
setUnsupportedFunc(glImports, platfOpenGLName, "glUniform1i");
setUnsupportedFunc(glImports, platfOpenGLName, "glUniform2i");
setUnsupportedFunc(glImports, platfOpenGLName, "glUniform3i");
setUnsupportedFunc(glImports, platfOpenGLName, "glUniform4i");
setUnsupportedFunc(glImports, platfOpenGLName, "glUniformMatrix2fv");
setUnsupportedFunc(glImports, platfOpenGLName, "glUniformMatrix3fv");
setUnsupportedFunc(glImports, platfOpenGLName, "glUniformMatrix4fv");
setUnsupportedFunc(glImports, platfOpenGLName, "glUniformMatrix3x2fv");
setUnsupportedFunc(glImports, platfOpenGLName, "glUniformMatrix4x2fv");
setUnsupportedFunc(glImports, platfOpenGLName, "glUniformMatrix4x3fv")
setUnsupportedFunc(glImports, platfOpenGLName, "glBindFramebuffer");
setUnsupportedFunc(glImports, platfOpenGLName, "glCheckFramebufferStatus");
setUnsupportedFunc(glImports, platfOpenGLName, "glBlitFramebuffer");
setUnsupportedFunc(glImports, platfOpenGLName, "glRenderbufferStorage");
setUnsupportedFunc(glImports, platfOpenGLName, "glFramebufferTexture2D");
setUnsupportedFunc(glImports, platfOpenGLName, "glFramebufferTextureLayer");
setUnsupportedFunc(glImports, platfOpenGLName, "glBindRenderbuffer");
setUnsupportedFunc(glImports, platfOpenGLName, "glFramebufferRenderbuffer");
setUnsupportedFunc(glImports, platfOpenGLName, "glGetInteger");
setUnsupportedFunc(glImports, platfOpenGLName, "glGetError");
setUnsupportedFunc(glImports, platfOpenGLName, "getAllExtensions");
setUnsupportedFunc(glImports, platfOpenGLName, "dumpActiveExtensions");
setUnsupportedFunc(glImports, platfOpenGLName, "glGetString");
setUnsupportedFunc(glImports, platfOpenGLName, "glGenVertexArrays");
setUnsupportedFunc(glImports, platfOpenGLName, "glDeleteVertexArrays");
setUnsupportedFunc(glImports, platfOpenGLName, "glBindVertexArray");
setUnsupportedFunc(glImports, platfOpenGLName, "glVertexAttribDivisor");
setUnsupportedFunc(glImports, platfOpenGLName, "glDrawArraysInstanced");
setUnsupportedFunc(glImports, platfOpenGLName, "glDrawElementsInstanced");
setUnsupportedFunc(glImports, platfOpenGLName, "isContextLost");
}

View File

@ -0,0 +1,208 @@
/*
* Copyright (c) 2024 lax1dude. All Rights Reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
const platfRuntimeName = "platformRuntime";
var allowImmediateContinue = false;
const immediateContinueChannel = new MessageChannel();
var immediateContinueHandler = null;
immediateContinueChannel.port2.addEventListener("message", function(evt) {
immediateContinueHandler();
});
async function initializePlatfRuntime() {
immediateContinueChannel.port1.start();
immediateContinueChannel.port2.start();
immediateContinueHandler = function() {
immediateContinueHandler = null;
};
immediateContinueChannel.port1.postMessage(0);
if(immediateContinueHandler) {
await new Promise(function(resolve) {
setTimeout(function() {
if(!immediateContinueHandler) {
allowImmediateContinue = true;
}else {
eagError("Immediate continue hack is not supported");
}
resolve();
}, 25);
});
}else {
eagError("Immediate continue hack is not supported");
}
}
/**
* @return {HTMLElement}
*/
eagruntimeImpl.platformRuntime["getRootElement"] = function() {
return rootElement;
};
/**
* @return {HTMLElement}
*/
eagruntimeImpl.platformRuntime["getParentElement"] = function() {
return parentElement;
};
/**
* @return {HTMLCanvasElement}
*/
eagruntimeImpl.platformRuntime["getCanvasElement"] = function() {
return canvasElement;
};
/**
* @return {Object}
*/
eagruntimeImpl.platformRuntime["getEaglercraftXOpts"] = function() {
return eaglercraftXOpts;
};
eagruntimeImpl.platformRuntime["getEventCount"] = mainEventQueue.getLength.bind(mainEventQueue);
eagruntimeImpl.platformRuntime["getNextEvent"] = mainEventQueue.shift.bind(mainEventQueue);
const EVENT_RUNTIME_ASYNC_DOWNLOAD = 0;
/**
* @param {string} uri
* @param {number} forceCache
* @param {number} id
*/
eagruntimeImpl.platformRuntime["queueAsyncDownload"] = function(uri, forceCache, id) {
try {
fetch(uri, /** @type {!RequestInit} */ ({
"cache": forceCache ? "force-cache" : "no-store",
"mode": "cors"
})).then(function(res) {
return res.arrayBuffer();
}).then(function(arr) {
pushEvent(EVENT_TYPE_RUNTIME, EVENT_RUNTIME_ASYNC_DOWNLOAD, {
"requestId": id,
"arrayBuffer": arr
});
}).catch(function(err) {
eagError("Failed to complete async download: {}", uri);
eagStackTrace(ERROR, "Exception Caught", /** @type {Error} */ (err));
pushEvent(EVENT_TYPE_RUNTIME, EVENT_RUNTIME_ASYNC_DOWNLOAD, {
"requestId": id,
"arrayBuffer": null
});
});
}catch(/** Error */ ex) {
eagError("Failed to fetch: {}", uri);
eagStackTrace(ERROR, "Exception Caught", ex);
pushEvent(EVENT_TYPE_RUNTIME, EVENT_RUNTIME_ASYNC_DOWNLOAD, {
"requestId": id,
"arrayBuffer": null
});
}
};
/**
* @param {string} uri
* @param {number} forceCache
* @return {Promise}
*/
function downloadImpl(uri, forceCache) {
return new Promise(function(resolve) {
try {
fetch(uri, /** @type {!RequestInit} */ ({
"cache": forceCache ? "force-cache" : "no-store",
"mode": "cors"
})).then(function(res) {
return res.arrayBuffer();
}).then(function(arr) {
resolve(arr);
}).catch(function(err) {
eagError("Failed to complete download: {}", uri);
eagStackTrace(ERROR, "Exception Caught", /** @type {Error} */ (err));
resolve(null);
});
}catch(/** Error */ ex) {
eagError("Failed to fetch: {}", uri);
eagStackTrace(ERROR, "Exception Caught", ex);
resolve(null);
}
});
}
eagruntimeImpl.platformRuntime["download"] = new WebAssembly.Suspending(downloadImpl);
/**
* @param {string} crashDump
*/
eagruntimeImpl.platformRuntime["writeCrashReport"] = function(crashDump) {
displayCrashReport(crashDump, false);
};
eagruntimeImpl.platformRuntime["steadyTimeMillis"] = performance.now.bind(performance);
/**
* @param {number} millis
* @return {Promise}
*/
function sleepImpl(millis) {
return new Promise(function(resolve) {
setTimeout(resolve, millis);
});
}
eagruntimeImpl.platformRuntime["sleep"] = new WebAssembly.Suspending(sleepImpl);
function immediateContinueResolver(resolve) {
if(allowImmediateContinue) {
immediateContinueHandler = resolve;
immediateContinueChannel.port1.postMessage(0);
}else {
setTimeout(resolve, 0);
}
}
/**
* @return {Promise}
*/
function immediateContinueImpl() {
return new Promise(immediateContinueResolver);
}
/**
* @return {Promise}
*/
function swapDelayImpl() {
if(!runtimeOpts.useDelayOnSwap) {
return immediateContinueImpl();
}else {
return sleepImpl(0);
}
}
eagruntimeImpl.platformRuntime["immediateContinue"] = new WebAssembly.Suspending(immediateContinueImpl);
/**
* @param {number} id
* @param {string} str
*/
eagruntimeImpl.platformRuntime["setCrashReportString"] = function(id, str) {
crashReportStrings[id] = str;
};

View File

@ -0,0 +1,123 @@
/*
* Copyright (c) 2024 lax1dude. All Rights Reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
const platfScreenRecordName = "platformScreenRecord";
var canMic = (typeof window !== "undefined");
var mic = null;
/**
* @return {Promise<MediaStream|null>}
*/
function getMic0() {
return new Promise(function(resolve) {
if ("navigator" in window && "mediaDevices" in window.navigator && "getUserMedia" in window.navigator.mediaDevices) {
try {
window.navigator.mediaDevices.getUserMedia({
audio: true,
video: false
}).then(function(stream) {
resolve(stream);
}).catch(function(err) {
eagError("getUserMedia Error! (async)");
eagStackTrace(ERROR, "Exception Caught", /** @type {Error} */ (err));
resolve(null);
});
} catch(e) {
eagError("getUserMedia Error!");
resolve(null);
}
} else {
eagError("No getUserMedia!");
resolve(null);
}
});
}
/**
* @return {Promise<MediaStream|null>}
*/
async function getMicImpl() {
if (canMic) {
if (mic === null) {
mic = await getMic0();
if (mic === null) {
canMic = false;
return null;
}
return mic;
}
return mic;
}
return null;
}
function initializePlatfScreenRecord(screenRecImports) {
eagruntimeImpl.platformScreenRecord["getMic"] = new WebAssembly.Suspending(getMicImpl);
/**
* @param {string} nameStr
* @return {string}
*/
function formatScreenRecDate(nameStr) {
const d = new Date();
const fmt = d.getFullYear()
+ "-" + ("0" + (d.getMonth() + 1)).slice(-2)
+ "-" + ("0" + d.getDate()).slice(-2)
+ " " + ("0" + d.getHours()).slice(-2)
+ "-" + ("0" + d.getMinutes()).slice(-2)
+ "-" + ("0" + d.getSeconds()).slice(-2);
return nameStr.replace("${date}", fmt);
}
/**
* @param {MediaRecorder} mediaRec
* @param {boolean} isWebM
* @param {string} nameStr
*/
eagruntimeImpl.platformScreenRecord["setDataAvailableHandler"] = function(mediaRec, isWebM, nameStr) {
const startTime = performance.now();
mediaRec.addEventListener("dataavailable", function(evt) {
if(isWebM) {
fixWebMDuration(/** @type {!Blob} */ (evt.data), (performance.now() - startTime) | 0, function(/** !Blob */ b) {
const blobUrl = URL.createObjectURL(b);
downloadFileImpl(formatScreenRecDate(nameStr), blobUrl, function() {
URL.revokeObjectURL(blobUrl);
});
}, {
/**
* @param {string} str
*/
logger: function(str) {
eagInfo(str);
}
});
}else {
const blobUrl = URL.createObjectURL(/** @type {!Blob} */ (evt.data));
downloadFileImpl(formatScreenRecDate(nameStr), blobUrl, function() {
URL.revokeObjectURL(blobUrl);
});
}
});
};
}
function initializeNoPlatfScreenRecord(screenRecImports) {
setUnsupportedFunc(screenRecImports, platfScreenRecordName, "getMic");
setUnsupportedFunc(screenRecImports, platfScreenRecordName, "setDataAvailableHandler");
}

View File

@ -0,0 +1,197 @@
/*
* Copyright (c) 2024 lax1dude, ayunami2000. All Rights Reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
const EVENT_VOICE_ICE = 0;
const EVENT_VOICE_DESC = 1;
const EVENT_VOICE_OPEN = 2;
const EVENT_VOICE_CLOSE = 3;
const platfVoiceClientName = "platformVoiceClient";
function initializePlatfVoiceClient(voiceClientImports) {
/**
* @return {boolean}
*/
voiceClientImports["isSupported"] = function() {
return typeof navigator.mediaDevices !== "undefined" && typeof navigator.mediaDevices.getUserMedia !== "undefined" && "srcObject" in HTMLAudioElement.prototype;
};
/**
* @param {string} desc
* @suppress {globalThis}
*/
function setRemoteDescriptionImpl(desc) {
try {
const remoteDesc = JSON.parse(desc);
this["_peerConnection"].setRemoteDescription(remoteDesc).then(() => {
if (remoteDesc.hasOwnProperty("type") && "offer" === remoteDesc["type"]) {
this["_peerConnection"].createAnswer().then((desc) => {
this["_peerConnection"].setLocalDescription(desc).then(() => {
pushEvent(EVENT_TYPE_VOICE, EVENT_VOICE_DESC, {
"objId": this["objId"],
"data": JSON.stringify(desc)
});
}).catch((err) => {
eagError("Failed to set local description for \"{}\"! {}", this["objId"], err.message);
pushEvent(EVENT_TYPE_VOICE, EVENT_VOICE_CLOSE, {
"objId": this["objId"]
});
});
}).catch((err) => {
eagError("Failed to create answer for \"{}\"! {}", this["objId"], err.message);
pushEvent(EVENT_TYPE_VOICE, EVENT_VOICE_CLOSE, {
"objId": this["objId"]
});
});
}
}).catch((err) => {
eagError("Failed to set remote description for \"{}\"! {}", this["objId"], err.message);
pushEvent(EVENT_TYPE_VOICE, EVENT_VOICE_CLOSE, {
"objId": this["objId"]
});
});
} catch (e) {
eagError(e.message);
pushEvent(EVENT_TYPE_VOICE, EVENT_VOICE_CLOSE, {
"objId": this["objId"]
});
}
}
/**
* @param {string} ice
* @suppress {globalThis}
*/
function addRemoteICECandidateImpl(ice) {
try {
this["_peerConnection"].addIceCandidate(new RTCIceCandidate(/** @type {!RTCIceCandidateInit} */ (JSON.parse(ice)))).catch((err) => {
eagError("Failed to parse ice candidate for \"{}\"! {}", this["objId"], err.message);
pushEvent(EVENT_TYPE_VOICE, EVENT_VOICE_CLOSE, {
"objId": this["objId"]
});
});
} catch (e) {
eagError(e.message);
pushEvent(EVENT_TYPE_VOICE, EVENT_VOICE_CLOSE, {
"objId": this["objId"]
});
}
}
/**
* @suppress {globalThis}
*/
function closeImpl() {
this["_peerConnection"].close();
}
let idCounter = 0;
/**
* @param {string} iceServers
* @param {number} offer
* @param {!MediaStream} localStream
* @return {Object}
*/
voiceClientImports["createRTCPeerConnection"] = function(iceServers, offer, localStream) {
try {
const peerId = idCounter++;
var ret;
const peerConnection = new RTCPeerConnection(/** @type {!RTCConfiguration} */ ({
"iceServers": JSON.parse(iceServers),
"optional": [
{
"DtlsSrtpKeyAgreement": true
}
]
}));
peerConnection.addEventListener("icecandidate", /** @type {function(Event)} */ ((/** RTCPeerConnectionIceEvent */ evt) => {
if (evt.candidate) {
pushEvent(EVENT_TYPE_VOICE, EVENT_VOICE_ICE, {
"objId": peerId,
"data": JSON.stringify({
"sdpMLineIndex": "" + evt.candidate.sdpMLineIndex,
"candidate": evt.candidate.candidate
})
});
}
}));
peerConnection.addEventListener("track", /** @type {function(Event)} */ ((/** RTCTrackEvent */ evt) => {
const rawStream = evt.streams[0];
ret["_aud"] = document.createElement("audio");
ret["_aud"]["autoplay"] = true;
ret["_aud"]["muted"] = true;
ret["_aud"]["srcObject"] = rawStream;
pushEvent(EVENT_TYPE_VOICE, EVENT_VOICE_OPEN, {
"objId": peerId,
"stream": rawStream
});
}));
localStream.getTracks().forEach(function(track) {
peerConnection.addTrack(track, localStream);
});
if (offer) {
peerConnection.createOffer().then((desc) => {
peerConnection.setLocalDescription(desc).then(() => {
pushEvent(EVENT_TYPE_VOICE, EVENT_VOICE_DESC, {
"objId": peerId,
"data": JSON.stringify(desc)
});
}).catch((err) => {
eagError("Failed to set local description for \"{}\"! {}", peerId, err.message);
pushEvent(EVENT_TYPE_VOICE, EVENT_VOICE_CLOSE, {
"objId": peerId
});
});
}).catch((err) => {
eagError("Failed to set create offer for \"{}\"! {}", peerId, err.message);
pushEvent(EVENT_TYPE_VOICE, EVENT_VOICE_CLOSE, {
"objId": peerId
});
});
}
peerConnection.addEventListener("connectionstatechange", () => {
const cs = peerConnection.connectionState;
if ("disconnected" === cs || "failed" === cs) {
pushEvent(EVENT_TYPE_VOICE, EVENT_VOICE_CLOSE, {
"objId": peerId
});
}
});
return ret = {
"objId": peerId,
"_peerConnection": peerConnection,
"setRemoteDescription": setRemoteDescriptionImpl,
"addRemoteICECandidate": addRemoteICECandidateImpl,
"closeHandle": closeImpl
};
} catch (e) {
eagError(e.message);
return null;
}
};
}
function initializeNoPlatfVoiceClient(voiceClientImports) {
setUnsupportedFunc(voiceClientImports, platfVoiceClientName, "isSupported");
setUnsupportedFunc(voiceClientImports, platfVoiceClientName, "createRTCPeerConnection");
}

View File

@ -0,0 +1,651 @@
/*
* Copyright (c) 2024 lax1dude, ayunami2000. All Rights Reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
const READYSTATE_INIT_FAILED = -2;
const READYSTATE_FAILED = -1;
const READYSTATE_DISCONNECTED = 0;
const READYSTATE_CONNECTING = 1;
const READYSTATE_CONNECTED = 2;
const EVENT_WEBRTC_ICE = 0;
const EVENT_WEBRTC_DESC = 1;
const EVENT_WEBRTC_OPEN = 2;
const EVENT_WEBRTC_PACKET = 3;
const EVENT_WEBRTC_CLOSE = 4;
const platfWebRTCName = "platformWebRTC";
/**
* @typedef {{
* peerId:string,
* peerConnection:!RTCPeerConnection,
* dataChannel:RTCDataChannel,
* ipcChannel:(string|null),
* pushEvent:function(number,*),
* disconnect:function()
* }}
*/
let LANPeerInternal;
function initializePlatfWebRTC(webrtcImports) {
const clientLANPacketBuffer = new EaglerLinkedQueue();
let lanClient;
lanClient = {
iceServers: [],
/** @type {RTCPeerConnection|null} */
peerConnection: null,
/** @type {RTCDataChannel|null} */
dataChannel: null,
readyState: READYSTATE_CONNECTING,
/** @type {string|null} */
iceCandidate: null,
/** @type {string|null} */
description: null,
dataChannelOpen: false,
dataChannelClosed: true,
disconnect: function(quiet) {
if (lanClient.dataChannel) {
try {
lanClient.dataChannel.close();
} catch (t) {
}
lanClient.dataChannel = null;
}
if (lanClient.peerConnection) {
try {
lanClient.peerConnection.close();
} catch (t) {
}
lanClient.peerConnection = null;
}
if (!quiet) lanClient.dataChannelClosed = true;
lanClient.readyState = READYSTATE_DISCONNECTED;
}
};
/**
* @return {boolean}
*/
webrtcImports["supported"] = function() {
return typeof RTCPeerConnection !== "undefined";
};
/**
* @return {number}
*/
webrtcImports["clientLANReadyState"] = function() {
return lanClient.readyState;
};
webrtcImports["clientLANCloseConnection"] = function() {
lanClient.disconnect(false);
};
/**
* @param {!Uint8Array} pkt
*/
webrtcImports["clientLANSendPacket"] = function(pkt) {
if (lanClient.dataChannel !== null && "open" === lanClient.dataChannel.readyState) {
try {
lanClient.dataChannel.send(pkt);
} catch (e) {
lanClient.disconnect(false);
}
}else {
lanClient.disconnect(false);
}
};
/**
* @return {Uint8Array}
*/
webrtcImports["clientLANReadPacket"] = function() {
const ret = clientLANPacketBuffer.shift();
return ret ? new Uint8Array(ret["data"]) : null;
};
/**
* @return {number}
*/
webrtcImports["clientLANAvailable"] = function() {
return clientLANPacketBuffer.getLength();
};
/**
* @param {!Array<string>} servers
*/
webrtcImports["clientLANSetICEServersAndConnect"] = function(servers) {
lanClient.iceServers.length = 0;
for (let url of servers) {
let etr = url.split(";");
if(etr.length === 1) {
lanClient.iceServers.push({
urls: etr[0]
});
}else if(etr.length === 3) {
lanClient.iceServers.push({
urls: etr[0],
username: etr[1],
credential: etr[2]
});
}
}
if(lanClient.readyState === READYSTATE_CONNECTED || lanClient.readyState === READYSTATE_CONNECTING) {
lanClient.disconnect(true);
}
try {
if (lanClient.dataChannel) {
try {
lanClient.dataChannel.close();
} catch (t) {
}
lanClient.dataChannel = null;
}
if (lanClient.peerConnection) {
try {
lanClient.peerConnection.close();
} catch (t) {
}
}
lanClient.peerConnection = new RTCPeerConnection({
iceServers: lanClient.iceServers,
optional: [
{
DtlsSrtpKeyAgreement: true
}
]
});
lanClient.readyState = READYSTATE_CONNECTING;
} catch (/** Error */ t) {
eagStackTrace(ERROR, "Could not create LAN client RTCPeerConnection!", t);
lanClient.readyState = READYSTATE_INIT_FAILED;
return;
}
try {
const iceCandidates = [];
lanClient.peerConnection.addEventListener("icecandidate", /** @type {function(Event)} */ ((/** !RTCPeerConnectionIceEvent */ evt) => {
if(evt.candidate) {
if(iceCandidates.length === 0) {
const candidateState = [0, 0];
const runnable = () => {
if(lanClient.peerConnection !== null && lanClient.peerConnection.connectionState !== "disconnected") {
const trial = ++candidateState[1];
if(candidateState[0] !== iceCandidates.length && trial < 3) {
candidateState[0] = iceCandidates.length;
setTimeout(runnable, 2000);
return;
}
lanClient.iceCandidate = JSON.stringify(iceCandidates);
iceCandidates.length = 0;
}
};
setTimeout(runnable, 2000);
}
iceCandidates.push({
"sdpMLineIndex": evt.candidate.sdpMLineIndex,
"candidate": evt.candidate.candidate
});
}
}));
lanClient.dataChannel = lanClient.peerConnection.createDataChannel("lan");
lanClient.dataChannel.binaryType = "arraybuffer";
let evtHandler;
evtHandler = () => {
if (iceCandidates.length > 0) {
setTimeout(evtHandler, 10);
return;
}
lanClient.dataChannelClosed = false;
lanClient.dataChannelOpen = true;
};
lanClient.dataChannel.addEventListener("open", evtHandler);
lanClient.dataChannel.addEventListener("message", /** @type {function(Event)} */ ((/** MessageEvent */ evt) => {
clientLANPacketBuffer.push({ "data": evt.data, "_next": null });
}));
lanClient.peerConnection.createOffer().then((/** !RTCSessionDescription */ desc) => {
lanClient.peerConnection.setLocalDescription(desc).then(() => {
lanClient.description = JSON.stringify(desc);
}).catch((err) => {
eagError("Failed to set local description! {}", /** @type {string} */ (err.message));
lanClient.readyState = READYSTATE_FAILED;
lanClient.disconnect(false);
});
}).catch((err) => {
eagError("Failed to set create offer! {}", /** @type {string} */ (err.message));
lanClient.readyState = READYSTATE_FAILED;
lanClient.disconnect(false);
});
lanClient.peerConnection.addEventListener("connectionstatechange", /** @type {function(Event)} */ ((evt) => {
var connectionState = lanClient.peerConnection.connectionState;
if ("disconnected" === connectionState) {
lanClient.disconnect(false);
} else if ("connected" === connectionState) {
lanClient.readyState = READYSTATE_CONNECTED;
} else if ("failed" === connectionState) {
lanClient.readyState = READYSTATE_FAILED;
lanClient.disconnect(false);
}
}));
} catch (t) {
if (lanClient.dataChannel) {
try {
lanClient.dataChannel.close();
} catch (tt) {
}
lanClient.dataChannel = null;
}
if (lanClient.peerConnection) {
try {
lanClient.peerConnection.close();
} catch (tt) {
}
lanClient.peerConnection = null;
}
eagStackTrace(ERROR, "Could not create LAN client RTCDataChannel!", t);
lanClient.readyState = READYSTATE_INIT_FAILED;
}
};
webrtcImports["clearLANClientState"] = function() {
lanClient.iceCandidate = lanClient.description = null;
lanClient.dataChannelOpen = false;
lanClient.dataChannelClosed = true;
};
/**
* @return {string|null}
*/
webrtcImports["clientLANAwaitICECandidate"] = function() {
if (lanClient.iceCandidate === null) {
return null;
}
const ret = lanClient.iceCandidate;
lanClient.iceCandidate = null;
return ret;
};
/**
* @return {string|null}
*/
webrtcImports["clientLANAwaitDescription"] = function() {
if (lanClient.description === null) {
return null;
}
const ret = lanClient.description;
lanClient.description = null;
return ret;
};
/**
* @return {boolean}
*/
webrtcImports["clientLANAwaitChannel"] = function() {
if (lanClient.dataChannelOpen) {
lanClient.dataChannelOpen = false;
return true;
}
return false;
};
/**
* @return {boolean}
*/
webrtcImports["clientLANClosed"] = function() {
return lanClient.dataChannelClosed;
};
/**
* @param {string} candidate
*/
webrtcImports["clientLANSetICECandidate"] = function(candidate) {
try {
const lst = /** @type {Array<!Object>} */ (JSON.parse(candidate));
for (var i = 0; i < lst.length; ++i) {
lanClient.peerConnection.addIceCandidate(new RTCIceCandidate(lst[i]));
}
}catch(/** Error */ ex) {
eagStackTrace(ERROR, "Uncaught exception setting remote ICE candidates", ex);
lanClient.readyState = READYSTATE_FAILED;
lanClient.disconnect(false);
}
};
/**
* @param {string} description
*/
webrtcImports["clientLANSetDescription"] = function(description) {
try {
lanClient.peerConnection.setRemoteDescription(/** @type {!RTCSessionDescription} */ (JSON.parse(description)));
}catch(/** Error */ ex) {
eagStackTrace(ERROR, "Uncaught exception setting remote description", ex);
lanClient.readyState = READYSTATE_FAILED;
lanClient.disconnect(false);
}
};
let lanServer;
lanServer = {
/** @type {!Array<Object>} */
iceServers: [],
/** @type {!Map<string, !LANPeerInternal>} */
peerList: new Map(),
/** @type {!Map<string, !LANPeerInternal>} */
ipcMapList: new Map(),
disconnect: function(/** string */ peerId) {
const thePeer = lanServer.peerList.get(peerId);
if(thePeer) {
lanServer.peerList.delete(peerId);
if(thePeer.ipcChannel) {
lanServer.ipcMapList.delete(thePeer.ipcChannel);
}
try {
thePeer.disconnect();
} catch (ignored) {}
thePeer.pushEvent(EVENT_WEBRTC_CLOSE, null);
}
}
};
/**
* @param {!Array<string>} servers
*/
webrtcImports["serverLANInitializeServer"] = function(servers) {
lanServer.iceServers.length = 0;
for (let url of servers) {
let etr = url.split(";");
if(etr.length === 1) {
lanServer.iceServers.push({
"urls": etr[0]
});
}else if(etr.length === 3) {
lanServer.iceServers.push({
"urls": etr[0],
"username": etr[1],
"credential": etr[2]
});
}
}
};
webrtcImports["serverLANCloseServer"] = function() {
for (let thePeer of Object.values(lanServer.peerList)) {
if (thePeer) {
try {
thePeer.disconnect();
} catch (e) {}
thePeer.pushEvent(EVENT_WEBRTC_CLOSE, null);
}
}
lanServer.peerList.clear();
};
/**
* @param {string} peer
*/
webrtcImports["serverLANCreatePeer"] = function(peer) {
try {
const events = new EaglerLinkedQueue();
/** @type {!LANPeerInternal} */
let peerInstance;
peerInstance = {
peerId: peer,
peerConnection: new RTCPeerConnection(/** @type {RTCConfiguration} */ ({
"iceServers": lanServer.iceServers,
"optional": [
{
"DtlsSrtpKeyAgreement": true
}
]
})),
/** @type {RTCDataChannel} */
dataChannel: null,
/** @type {string|null} */
ipcChannel: null,
pushEvent: function(type, data) {
events.push({
"type": type,
"data": data,
"_next": null
});
},
disconnect: function() {
if (peerInstance.dataChannel) peerInstance.dataChannel.close();
peerInstance.peerConnection.close();
}
};
lanServer.peerList.set(peerInstance.peerId, peerInstance);
const iceCandidates = [];
peerInstance.peerConnection.addEventListener("icecandidate", /** @type {function(Event)} */ ((/** RTCPeerConnectionIceEvent */ evt) => {
if(evt.candidate) {
if(iceCandidates.length === 0) {
const candidateState = [0, 0];
const runnable = () => {
if(peerInstance.peerConnection !== null && peerInstance.peerConnection.connectionState !== "disconnected") {
const trial = ++candidateState[1];
if(candidateState[0] !== iceCandidates.length && trial < 3) {
candidateState[0] = iceCandidates.length;
setTimeout(runnable, 2000);
return;
}
peerInstance.pushEvent(EVENT_WEBRTC_ICE, JSON.stringify(iceCandidates));
iceCandidates.length = 0;
}
};
setTimeout(runnable, 2000);
}
iceCandidates.push({
"sdpMLineIndex": evt.candidate.sdpMLineIndex,
"candidate": evt.candidate.candidate
});
}
}));
let evtHandler;
evtHandler = (/** RTCDataChannelEvent */ evt) => {
if (iceCandidates.length > 0) {
setTimeout(evtHandler, 10, evt);
return;
}
if (!evt.channel) return;
const newDataChannel = evt.channel;
if(peerInstance.dataChannel !== null) {
newDataChannel.close();
return;
}
peerInstance.dataChannel = newDataChannel;
peerInstance.pushEvent(EVENT_WEBRTC_OPEN, null);
peerInstance.dataChannel.addEventListener("message", (evt2) => {
const data = evt2.data;
if(peerInstance.ipcChannel) {
sendIPCPacketFunc(peerInstance.ipcChannel, data);
}else {
peerInstance.pushEvent(EVENT_WEBRTC_PACKET, new Uint8Array(data));
}
});
};
peerInstance.peerConnection.addEventListener("datachannel", /** @type {function(Event)} */ (evtHandler));
peerInstance.peerConnection.addEventListener("connectionstatechange", (evt) => {
const connectionState = peerInstance.peerConnection.connectionState;
if ("disconnected" === connectionState || "failed" === connectionState) {
lanServer.disconnect(peerInstance.peerId);
}
});
return {
"peerId": peerInstance.peerId,
/**
* @return {number}
*/
"countAvailableEvents": function() {
return events.getLength();
},
/**
* @return {Object}
*/
"nextEvent": function() {
return events.shift();
},
/**
* @param {!Uint8Array} dat
*/
"writePacket": function(dat) {
let b = false;
if (peerInstance.dataChannel !== null && "open" === peerInstance.dataChannel.readyState) {
try {
peerInstance.dataChannel.send(dat);
} catch (e) {
b = true;
}
} else {
b = true;
}
if(b) {
lanServer.disconnect(peerInstance.peerId);
}
},
/**
* @param {string} iceCandidates
*/
"handleRemoteICECandidates": function(iceCandidates) {
try {
const candidateList = /** @type {!Array<!RTCIceCandidateInit>} */ (JSON.parse(iceCandidates));
for (let candidate of candidateList) {
peerInstance.peerConnection.addIceCandidate(new RTCIceCandidate(candidate));
}
} catch (err) {
eagError("Failed to parse ice candidate for \"{}\"! {}", peerInstance.peerId, err.message);
lanServer.disconnect(peerInstance.peerId);
}
},
/**
* @param {string} desc
*/
"handleRemoteDescription": function(desc) {
try {
const remoteDesc = /** @type {!RTCSessionDescription} */ (JSON.parse(desc));
peerInstance.peerConnection.setRemoteDescription(remoteDesc).then(() => {
if (remoteDesc.hasOwnProperty("type") && "offer" === remoteDesc["type"]) {
peerInstance.peerConnection.createAnswer().then((desc) => {
peerInstance.peerConnection.setLocalDescription(desc).then(() => {
peerInstance.pushEvent(EVENT_WEBRTC_DESC, JSON.stringify(desc));
}).catch((err) => {
eagError("Failed to set local description for \"{}\"! {}", peerInstance.peerId, err.message);
lanServer.disconnect(peerInstance.peerId);
});
}).catch((err) => {
eagError("Failed to create answer for \"{}\"! {}", peerInstance.peerId, err.message);
lanServer.disconnect(peerInstance.peerId);
});
}
}).catch((err) => {
eagError("Failed to set remote description for \"{}\"! {}", peerInstance.peerId, err.message);
lanServer.disconnect(peerInstance.peerId);
});
} catch (err) {
eagError("Failed to parse remote description for \"{}\"! {}", peerInstance.peerId, err.message);
lanServer.disconnect(peerInstance.peerId);
}
},
/**
* @param {string|null} ipcChannel
*/
"mapIPC": function(ipcChannel) {
if(!peerInstance.ipcChannel) {
if(ipcChannel) {
peerInstance.ipcChannel = ipcChannel;
lanServer.ipcMapList.set(ipcChannel, peerInstance);
}
}else {
if(!ipcChannel) {
lanServer.ipcMapList.delete(peerInstance.ipcChannel);
peerInstance.ipcChannel = null;
}
}
},
"disconnect": function() {
lanServer.disconnect(peerInstance.peerId);
}
};
}catch(/** Error */ tt) {
eagStackTrace(ERROR, "Failed to create WebRTC LAN peer!", tt);
return null;
}
};
/**
* @param {string} channel
* @param {!ArrayBuffer} arr
*/
serverLANPeerPassIPCFunc = function(channel, arr) {
const peer = lanServer.ipcMapList.get(channel);
if(peer) {
let b = false;
if (peer.dataChannel && "open" === peer.dataChannel.readyState) {
try {
peer.dataChannel.send(arr);
} catch (e) {
b = true;
}
} else {
b = true;
}
if(b) {
lanServer.disconnect(peer.peerId);
}
return true;
}else {
return false;
}
};
}
function initializeNoPlatfWebRTC(webrtcImports) {
setUnsupportedFunc(webrtcImports, platfWebRTCName, "supported");
setUnsupportedFunc(webrtcImports, platfWebRTCName, "clientLANReadyState");
setUnsupportedFunc(webrtcImports, platfWebRTCName, "clientLANCloseConnection");
setUnsupportedFunc(webrtcImports, platfWebRTCName, "clientLANSendPacket");
setUnsupportedFunc(webrtcImports, platfWebRTCName, "clientLANReadPacket");
setUnsupportedFunc(webrtcImports, platfWebRTCName, "clientLANAvailable");
setUnsupportedFunc(webrtcImports, platfWebRTCName, "clientLANSetICEServersAndConnect");
setUnsupportedFunc(webrtcImports, platfWebRTCName, "clearLANClientState");
setUnsupportedFunc(webrtcImports, platfWebRTCName, "clientLANAwaitICECandidate");
setUnsupportedFunc(webrtcImports, platfWebRTCName, "clientLANAwaitDescription");
setUnsupportedFunc(webrtcImports, platfWebRTCName, "clientLANAwaitChannel");
setUnsupportedFunc(webrtcImports, platfWebRTCName, "clientLANClosed");
setUnsupportedFunc(webrtcImports, platfWebRTCName, "clientLANSetICECandidate");
setUnsupportedFunc(webrtcImports, platfWebRTCName, "clientLANSetDescription");
setUnsupportedFunc(webrtcImports, platfWebRTCName, "clientLANClosed");
setUnsupportedFunc(webrtcImports, platfWebRTCName, "serverLANInitializeServer");
setUnsupportedFunc(webrtcImports, platfWebRTCName, "serverLANCloseServer");
setUnsupportedFunc(webrtcImports, platfWebRTCName, "serverLANCreatePeer");
}

Some files were not shown because too many files have changed in this diff Show More