[Debugger] Rewrite script API (#2110)
* [Debugger] Rewrite script API * Update js api paths in installer script
This commit is contained in:
parent
f9597fc3c2
commit
ac94f2505e
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,3 @@
|
||||||
|
const docpath = pj64.installDirectory + "JS-API-Documentation.html";
|
||||||
|
console.log("Opening " + docpath + " ...");
|
||||||
|
exec("start " + docpath);
|
|
@ -1 +1,2 @@
|
||||||
console.log("Hello world! See apidoc.htm for documentation.");
|
console.log("Hello world! See \"JS-API-Documentation.html\" for API documentation.");
|
||||||
|
script.keepalive(true);
|
||||||
|
|
|
@ -29,6 +29,45 @@ and agreed to irrevocably license their contributions under the Duktape
|
||||||
* René Hollander <rene@rene8888.at>
|
* René Hollander <rene@rene8888.at>
|
||||||
* Julien Hamaide (https://github.com/crazyjul)
|
* Julien Hamaide (https://github.com/crazyjul)
|
||||||
* Sebastian Götte (https://github.com/jaseg)
|
* Sebastian Götte (https://github.com/jaseg)
|
||||||
|
* Tomasz Magulski (https://github.com/magul)
|
||||||
|
* \D. Bohdan (https://github.com/dbohdan)
|
||||||
|
* Ondřej Jirman (https://github.com/megous)
|
||||||
|
* Saúl Ibarra Corretgé <saghul@gmail.com>
|
||||||
|
* Jeremy HU <huxingyi@msn.com>
|
||||||
|
* Ole André Vadla Ravnås (https://github.com/oleavr)
|
||||||
|
* Harold Brenes (https://github.com/harold-b)
|
||||||
|
* Oliver Crow (https://github.com/ocrow)
|
||||||
|
* Jakub Chłapiński (https://github.com/jchlapinski)
|
||||||
|
* Brett Vickers (https://github.com/beevik)
|
||||||
|
* Dominik Okwieka (https://github.com/okitec)
|
||||||
|
* Remko Tronçon (https://el-tramo.be)
|
||||||
|
* Romero Malaquias (rbsm@ic.ufal.br)
|
||||||
|
* Michael Drake <michael.drake@codethink.co.uk>
|
||||||
|
* Steven Don (https://github.com/shdon)
|
||||||
|
* Simon Stone (https://github.com/sstone1)
|
||||||
|
* \J. McC. (https://github.com/jmhmccr)
|
||||||
|
* Jakub Nowakowski (https://github.com/jimvonmoon)
|
||||||
|
* Tommy Nguyen (https://github.com/tn0502)
|
||||||
|
* Fabrice Fontaine (https://github.com/ffontaine)
|
||||||
|
* Christopher Hiller (https://github.com/boneskull)
|
||||||
|
* Gonzalo Diethelm (https://github.com/gonzus)
|
||||||
|
* Michal Kasperek (https://github.com/michalkas)
|
||||||
|
* Andrew Janke (https://github.com/apjanke)
|
||||||
|
* Steve Fan (https://github.com/stevefan1999)
|
||||||
|
* Edward Betts (https://github.com/edwardbetts)
|
||||||
|
* Ozhan Duz (https://github.com/webfolderio)
|
||||||
|
* Akos Kiss (https://github.com/akosthekiss)
|
||||||
|
* TheBrokenRail (https://github.com/TheBrokenRail)
|
||||||
|
* Jesse Doyle (https://github.com/jessedoyle)
|
||||||
|
* Gero Kuehn (https://github.com/dc6jgk)
|
||||||
|
* James Swift (https://github.com/phraemer)
|
||||||
|
* Luis de Bethencourt (https://github.com/luisbg)
|
||||||
|
* Ian Whyman (https://github.com/v00d00)
|
||||||
|
* Rick Sayre (https://github.com/whorfin)
|
||||||
|
* Craig Leres (https://github.com/leres)
|
||||||
|
* Maurici Abad (https://github.com/mauriciabad)
|
||||||
|
* Nancy Li (https://github.com/NancyLi1013)
|
||||||
|
* William Parks (https://github.com/WilliamParks)
|
||||||
|
|
||||||
Other contributions
|
Other contributions
|
||||||
===================
|
===================
|
||||||
|
@ -66,7 +105,11 @@ bugs, provided ideas, etc; roughly in order of appearance):
|
||||||
* Michael Drake (https://github.com/tlsa)
|
* Michael Drake (https://github.com/tlsa)
|
||||||
* https://github.com/chris-y
|
* https://github.com/chris-y
|
||||||
* Laurent Zubiaur (https://github.com/lzubiaur)
|
* Laurent Zubiaur (https://github.com/lzubiaur)
|
||||||
* Ole André Vadla Ravnås (https://github.com/oleavr)
|
* Neil Kolban (https://github.com/nkolban)
|
||||||
|
* Wilhelm Wanecek (https://github.com/wanecek)
|
||||||
|
* Andrew Janke (https://github.com/apjanke)
|
||||||
|
* Unamer (https://github.com/unamer)
|
||||||
|
* Karl Dahlke (eklhad@gmail.com)
|
||||||
|
|
||||||
If you are accidentally missing from this list, send me an e-mail
|
If you are accidentally missing from this list, send me an e-mail
|
||||||
(``sami.vaarala@iki.fi``) and I'll fix the omission.
|
(``sami.vaarala@iki.fi``) and I'll fix the omission.
|
|
@ -1,10 +1,6 @@
|
||||||
===============
|
The MIT License (MIT)
|
||||||
Duktape license
|
|
||||||
===============
|
|
||||||
|
|
||||||
(http://opensource.org/licenses/MIT)
|
Copyright (c) 2013-present, Duktape authors (see AUTHORS.rst)
|
||||||
|
|
||||||
Copyright (c) 2013-2016 by Duktape authors (see AUTHORS.rst)
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
@ -22,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
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
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
THE SOFTWARE.
|
THE SOFTWARE.
|
|
@ -1,110 +0,0 @@
|
||||||
=======
|
|
||||||
Duktape
|
|
||||||
=======
|
|
||||||
|
|
||||||
Duktape is a small and portable Ecmascript E5/E5.1 implementation. It is
|
|
||||||
intended to be easily embeddable into C programs, with a C API similar in
|
|
||||||
spirit to Lua's.
|
|
||||||
|
|
||||||
Duktape supports the full E5/E5.1 feature set including errors, Unicode
|
|
||||||
strings, and regular expressions, a subset of E6 features (e.g. Proxy
|
|
||||||
objects), Khronos/ES6 ArrayBuffer/TypedView, and Node.js Buffer bindings.
|
|
||||||
|
|
||||||
Duktape also provides a number of custom features such as error tracebacks,
|
|
||||||
additional data types for better C integration, combined reference counting
|
|
||||||
and mark-and sweep garbage collector, object finalizers, co-operative
|
|
||||||
threads a.k.a. coroutines, tail calls, built-in logging and module frameworks,
|
|
||||||
a built-in debugger protocol, function bytecode dump/load, and so on.
|
|
||||||
|
|
||||||
You can browse Duktape programmer's API and other documentation at:
|
|
||||||
|
|
||||||
* http://duktape.org/
|
|
||||||
|
|
||||||
In particular, you should read the getting started section:
|
|
||||||
|
|
||||||
* http://duktape.org/guide.html#gettingstarted
|
|
||||||
|
|
||||||
More examples and how-to articles are in the Duktape Wiki:
|
|
||||||
|
|
||||||
* http://wiki.duktape.org/
|
|
||||||
|
|
||||||
Building and integrating Duktape into your project is very straightforward:
|
|
||||||
|
|
||||||
* http://duktape.org/guide.html#compiling
|
|
||||||
|
|
||||||
See Makefile.hello for a concrete example::
|
|
||||||
|
|
||||||
$ cd <dist_root>
|
|
||||||
$ make -f Makefile.hello
|
|
||||||
[...]
|
|
||||||
$ ./hello
|
|
||||||
Hello world!
|
|
||||||
2+3=5
|
|
||||||
|
|
||||||
To build an example command line tool, use the following::
|
|
||||||
|
|
||||||
$ cd <dist_root>
|
|
||||||
$ make -f Makefile.cmdline
|
|
||||||
[...]
|
|
||||||
|
|
||||||
$ ./duk
|
|
||||||
((o) Duktape
|
|
||||||
duk> print('Hello world!');
|
|
||||||
Hello world!
|
|
||||||
= undefined
|
|
||||||
|
|
||||||
$ ./duk mandel.js
|
|
||||||
[...]
|
|
||||||
|
|
||||||
This distributable contains:
|
|
||||||
|
|
||||||
* ``src/``: main Duktape library in a "single source file" format (duktape.c,
|
|
||||||
duktape.h, and duk_config.h).
|
|
||||||
|
|
||||||
* ``src-noline/``: contains a variant of ``src/duktape.c`` with no ``#line``
|
|
||||||
directives which is preferable for some users. See discussion in
|
|
||||||
https://github.com/svaarala/duktape/pull/363.
|
|
||||||
|
|
||||||
* ``src-separate/``: main Duktape library in multiple files format.
|
|
||||||
|
|
||||||
* ``config/``: genconfig utility for creating duk_config.h configuration
|
|
||||||
files, see: http://wiki.duktape.org/Configuring.html.
|
|
||||||
|
|
||||||
* ``examples/``: further examples for using Duktape. Although Duktape
|
|
||||||
itself is widely portable, some of the examples are Linux only.
|
|
||||||
For instance the ``eventloop`` example illustrates how ``setTimeout()``
|
|
||||||
and other standard timer functions could be implemented on Unix/Linux.
|
|
||||||
|
|
||||||
* ``extras/``: utilities and modules which don't comfortably fit into the
|
|
||||||
main Duktape library because of footprint or portability concerns.
|
|
||||||
Extras are maintained and bug fixed code, but don't have the same version
|
|
||||||
guarantees as the main Duktape library.
|
|
||||||
|
|
||||||
* ``polyfills/``: a few replacement suggestions for non-standard Javascript
|
|
||||||
functions provided by other implementations.
|
|
||||||
|
|
||||||
* ``debugger/``: a debugger with a web UI, see ``debugger/README.rst`` and
|
|
||||||
https://github.com/svaarala/duktape/blob/master/doc/debugger.rst for
|
|
||||||
details on Duktape debugger support. Also contains a JSON debug proxy
|
|
||||||
(one written in Node.js and another in DukLuv) to make talking to the
|
|
||||||
debug target easier.
|
|
||||||
|
|
||||||
* ``licenses/``: licensing information.
|
|
||||||
|
|
||||||
You can find release notes at:
|
|
||||||
|
|
||||||
* https://github.com/svaarala/duktape/blob/master/RELEASES.rst
|
|
||||||
|
|
||||||
This distributable contains Duktape version 1.5.1, created from git
|
|
||||||
commit 2cc76e9ff1f64869e1146ad7317d8cbe33bbd27e (v1.5.1).
|
|
||||||
|
|
||||||
Duktape is copyrighted by its authors (see ``AUTHORS.rst``) and licensed
|
|
||||||
under the MIT license (see ``LICENSE.txt``). String hashing algorithms are
|
|
||||||
based on the algorithm from Lua (MIT license), djb2 hash, and Murmurhash2
|
|
||||||
(MIT license). Duktape module loader is based on the CommonJS module
|
|
||||||
loading specification (without sharing any code), CommonJS is under the
|
|
||||||
MIT license.
|
|
||||||
|
|
||||||
Have fun!
|
|
||||||
|
|
||||||
Sami Vaarala (sami.vaarala@iki.fi)
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,477 @@
|
||||||
|
/*
|
||||||
|
* Duktape 1.x compatible module loading framework
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "duktape.h"
|
||||||
|
#include "duk_module_duktape.h"
|
||||||
|
|
||||||
|
/* (v)snprintf() is missing before MSVC 2015. Note that _(v)snprintf() does
|
||||||
|
* NOT NUL terminate on truncation, but that's OK here.
|
||||||
|
* http://stackoverflow.com/questions/2915672/snprintf-and-visual-studio-2010
|
||||||
|
*/
|
||||||
|
#if defined(_MSC_VER) && (_MSC_VER < 1900)
|
||||||
|
#define snprintf _snprintf
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if 0 /* Enable manually */
|
||||||
|
#define DUK__ASSERT(x) do { \
|
||||||
|
if (!(x)) { \
|
||||||
|
fprintf(stderr, "ASSERTION FAILED at %s:%d: " #x "\n", __FILE__, __LINE__); \
|
||||||
|
fflush(stderr); \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
#define DUK__ASSERT_TOP(ctx,val) do { \
|
||||||
|
DUK__ASSERT(duk_get_top((ctx)) == (val)); \
|
||||||
|
} while (0)
|
||||||
|
#else
|
||||||
|
#define DUK__ASSERT(x) do { (void) (x); } while (0)
|
||||||
|
#define DUK__ASSERT_TOP(ctx,val) do { (void) ctx; (void) (val); } while (0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static void duk__resolve_module_id(duk_context* ctx, const char* req_id, const char* mod_id) {
|
||||||
|
duk_uint8_t buf[DUK_COMMONJS_MODULE_ID_LIMIT];
|
||||||
|
duk_uint8_t* p;
|
||||||
|
duk_uint8_t* q;
|
||||||
|
duk_uint8_t* q_last; /* last component */
|
||||||
|
duk_int_t int_rc;
|
||||||
|
|
||||||
|
DUK__ASSERT(req_id != NULL);
|
||||||
|
/* mod_id may be NULL */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A few notes on the algorithm:
|
||||||
|
*
|
||||||
|
* - Terms are not allowed to begin with a period unless the term
|
||||||
|
* is either '.' or '..'. This simplifies implementation (and
|
||||||
|
* is within CommonJS modules specification).
|
||||||
|
*
|
||||||
|
* - There are few output bound checks here. This is on purpose:
|
||||||
|
* the resolution input is length checked and the output is never
|
||||||
|
* longer than the input. The resolved output is written directly
|
||||||
|
* over the input because it's never longer than the input at any
|
||||||
|
* point in the algorithm.
|
||||||
|
*
|
||||||
|
* - Non-ASCII characters are processed as individual bytes and
|
||||||
|
* need no special treatment. However, U+0000 terminates the
|
||||||
|
* algorithm; this is not an issue because U+0000 is not a
|
||||||
|
* desirable term character anyway.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set up the resolution input which is the requested ID directly
|
||||||
|
* (if absolute or no current module path) or with current module
|
||||||
|
* ID prepended (if relative and current module path exists).
|
||||||
|
*
|
||||||
|
* Suppose current module is 'foo/bar' and relative path is './quux'.
|
||||||
|
* The 'bar' component must be replaced so the initial input here is
|
||||||
|
* 'foo/bar/.././quux'.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (mod_id != NULL && req_id[0] == '.') {
|
||||||
|
int_rc = snprintf((char*)buf, sizeof(buf), "%s/../%s", mod_id, req_id);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
int_rc = snprintf((char*)buf, sizeof(buf), "%s", req_id);
|
||||||
|
}
|
||||||
|
if (int_rc >= (duk_int_t)sizeof(buf) || int_rc < 0) {
|
||||||
|
/* Potentially truncated, NUL not guaranteed in any case.
|
||||||
|
* The (int_rc < 0) case should not occur in practice.
|
||||||
|
*/
|
||||||
|
goto resolve_error;
|
||||||
|
}
|
||||||
|
DUK__ASSERT(strlen((const char*)buf) < sizeof(buf)); /* at most sizeof(buf) - 1 */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Resolution loop. At the top of the loop we're expecting a valid
|
||||||
|
* term: '.', '..', or a non-empty identifier not starting with a period.
|
||||||
|
*/
|
||||||
|
|
||||||
|
p = buf;
|
||||||
|
q = buf;
|
||||||
|
for (;;) {
|
||||||
|
duk_uint_fast8_t c;
|
||||||
|
|
||||||
|
/* Here 'p' always points to the start of a term.
|
||||||
|
*
|
||||||
|
* We can also unconditionally reset q_last here: if this is
|
||||||
|
* the last (non-empty) term q_last will have the right value
|
||||||
|
* on loop exit.
|
||||||
|
*/
|
||||||
|
|
||||||
|
DUK__ASSERT(p >= q); /* output is never longer than input during resolution */
|
||||||
|
|
||||||
|
q_last = q;
|
||||||
|
|
||||||
|
c = *p++;
|
||||||
|
if (c == 0) {
|
||||||
|
goto resolve_error;
|
||||||
|
}
|
||||||
|
else if (c == '.') {
|
||||||
|
c = *p++;
|
||||||
|
if (c == '/') {
|
||||||
|
/* Term was '.' and is eaten entirely (including dup slashes). */
|
||||||
|
goto eat_dup_slashes;
|
||||||
|
}
|
||||||
|
if (c == '.' && *p == '/') {
|
||||||
|
/* Term was '..', backtrack resolved name by one component.
|
||||||
|
* q[-1] = previous slash (or beyond start of buffer)
|
||||||
|
* q[-2] = last char of previous component (or beyond start of buffer)
|
||||||
|
*/
|
||||||
|
p++; /* eat (first) input slash */
|
||||||
|
DUK__ASSERT(q >= buf);
|
||||||
|
if (q == buf) {
|
||||||
|
goto resolve_error;
|
||||||
|
}
|
||||||
|
DUK__ASSERT(*(q - 1) == '/');
|
||||||
|
q--; /* Backtrack to last output slash (dups already eliminated). */
|
||||||
|
for (;;) {
|
||||||
|
/* Backtrack to previous slash or start of buffer. */
|
||||||
|
DUK__ASSERT(q >= buf);
|
||||||
|
if (q == buf) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (*(q - 1) == '/') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
q--;
|
||||||
|
}
|
||||||
|
goto eat_dup_slashes;
|
||||||
|
}
|
||||||
|
goto resolve_error;
|
||||||
|
}
|
||||||
|
else if (c == '/') {
|
||||||
|
/* e.g. require('/foo'), empty terms not allowed */
|
||||||
|
goto resolve_error;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for (;;) {
|
||||||
|
/* Copy term name until end or '/'. */
|
||||||
|
*q++ = c;
|
||||||
|
c = *p++;
|
||||||
|
if (c == 0) {
|
||||||
|
/* This was the last term, and q_last was
|
||||||
|
* updated to match this term at loop top.
|
||||||
|
*/
|
||||||
|
goto loop_done;
|
||||||
|
}
|
||||||
|
else if (c == '/') {
|
||||||
|
*q++ = '/';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* write on next loop */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
eat_dup_slashes:
|
||||||
|
for (;;) {
|
||||||
|
/* eat dup slashes */
|
||||||
|
c = *p;
|
||||||
|
if (c != '/') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
loop_done:
|
||||||
|
/* Output #1: resolved absolute name. */
|
||||||
|
DUK__ASSERT(q >= buf);
|
||||||
|
duk_push_lstring(ctx, (const char*)buf, (size_t)(q - buf));
|
||||||
|
|
||||||
|
/* Output #2: last component name. */
|
||||||
|
DUK__ASSERT(q >= q_last);
|
||||||
|
DUK__ASSERT(q_last >= buf);
|
||||||
|
duk_push_lstring(ctx, (const char*)q_last, (size_t)(q - q_last));
|
||||||
|
return;
|
||||||
|
|
||||||
|
resolve_error:
|
||||||
|
(void)duk_type_error(ctx, "cannot resolve module id: %s", (const char*)req_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Stack indices for better readability. */
|
||||||
|
#define DUK__IDX_REQUESTED_ID 0 /* module id requested */
|
||||||
|
#define DUK__IDX_REQUIRE 1 /* current require() function */
|
||||||
|
#define DUK__IDX_REQUIRE_ID 2 /* the base ID of the current require() function, resolution base */
|
||||||
|
#define DUK__IDX_RESOLVED_ID 3 /* resolved, normalized absolute module ID */
|
||||||
|
#define DUK__IDX_LASTCOMP 4 /* last component name in resolved path */
|
||||||
|
#define DUK__IDX_DUKTAPE 5 /* Duktape object */
|
||||||
|
#define DUK__IDX_MODLOADED 6 /* Duktape.modLoaded[] module cache */
|
||||||
|
#define DUK__IDX_UNDEFINED 7 /* 'undefined', artifact of lookup */
|
||||||
|
#define DUK__IDX_FRESH_REQUIRE 8 /* new require() function for module, updated resolution base */
|
||||||
|
#define DUK__IDX_EXPORTS 9 /* default exports table */
|
||||||
|
#define DUK__IDX_MODULE 10 /* module object containing module.exports, etc */
|
||||||
|
|
||||||
|
static duk_ret_t duk__require(duk_context* ctx) {
|
||||||
|
const char* str_req_id; /* requested identifier */
|
||||||
|
const char* str_mod_id; /* require.id of current module */
|
||||||
|
duk_int_t pcall_rc;
|
||||||
|
|
||||||
|
/* NOTE: we try to minimize code size by avoiding unnecessary pops,
|
||||||
|
* so the stack looks a bit cluttered in this function. DUK__ASSERT_TOP()
|
||||||
|
* assertions are used to ensure stack configuration is correct at each
|
||||||
|
* step.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Resolve module identifier into canonical absolute form.
|
||||||
|
*/
|
||||||
|
|
||||||
|
str_req_id = duk_require_string(ctx, DUK__IDX_REQUESTED_ID);
|
||||||
|
duk_push_current_function(ctx);
|
||||||
|
duk_get_prop_string(ctx, -1, "id");
|
||||||
|
str_mod_id = duk_get_string(ctx, DUK__IDX_REQUIRE_ID); /* ignore non-strings */
|
||||||
|
duk__resolve_module_id(ctx, str_req_id, str_mod_id);
|
||||||
|
str_req_id = NULL;
|
||||||
|
str_mod_id = NULL;
|
||||||
|
|
||||||
|
/* [ requested_id require require.id resolved_id last_comp ] */
|
||||||
|
DUK__ASSERT_TOP(ctx, DUK__IDX_LASTCOMP + 1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Cached module check.
|
||||||
|
*
|
||||||
|
* If module has been loaded or its loading has already begun without
|
||||||
|
* finishing, return the same cached value (module.exports). The
|
||||||
|
* value is registered when module load starts so that circular
|
||||||
|
* references can be supported to some extent.
|
||||||
|
*/
|
||||||
|
|
||||||
|
duk_push_global_stash(ctx);
|
||||||
|
duk_get_prop_string(ctx, -1, "\xff" "module:Duktape");
|
||||||
|
duk_remove(ctx, -2); /* Lookup stashed, original 'Duktape' object. */
|
||||||
|
duk_get_prop_string(ctx, DUK__IDX_DUKTAPE, "modLoaded"); /* Duktape.modLoaded */
|
||||||
|
duk_require_type_mask(ctx, DUK__IDX_MODLOADED, DUK_TYPE_MASK_OBJECT);
|
||||||
|
DUK__ASSERT_TOP(ctx, DUK__IDX_MODLOADED + 1);
|
||||||
|
|
||||||
|
duk_dup(ctx, DUK__IDX_RESOLVED_ID);
|
||||||
|
if (duk_get_prop(ctx, DUK__IDX_MODLOADED)) {
|
||||||
|
/* [ requested_id require require.id resolved_id last_comp Duktape Duktape.modLoaded Duktape.modLoaded[id] ] */
|
||||||
|
duk_get_prop_string(ctx, -1, "exports"); /* return module.exports */
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
DUK__ASSERT_TOP(ctx, DUK__IDX_UNDEFINED + 1);
|
||||||
|
|
||||||
|
/* [ requested_id require require.id resolved_id last_comp Duktape Duktape.modLoaded undefined ] */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Module not loaded (and loading not started previously).
|
||||||
|
*
|
||||||
|
* Create a new require() function with 'id' set to resolved ID
|
||||||
|
* of module being loaded. Also create 'exports' and 'module'
|
||||||
|
* tables but don't register exports to the loaded table yet.
|
||||||
|
* We don't want to do that unless the user module search callbacks
|
||||||
|
* succeeds in finding the module.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Fresh require: require.id is left configurable (but not writable)
|
||||||
|
* so that is not easy to accidentally tweak it, but it can still be
|
||||||
|
* done with Object.defineProperty().
|
||||||
|
*
|
||||||
|
* XXX: require.id could also be just made non-configurable, as there
|
||||||
|
* is no practical reason to touch it (at least from ECMAScript code).
|
||||||
|
*/
|
||||||
|
duk_push_c_function(ctx, duk__require, 1 /*nargs*/);
|
||||||
|
duk_push_string(ctx, "name");
|
||||||
|
duk_push_string(ctx, "require");
|
||||||
|
duk_def_prop(ctx, DUK__IDX_FRESH_REQUIRE, DUK_DEFPROP_HAVE_VALUE); /* not writable, not enumerable, not configurable */
|
||||||
|
duk_push_string(ctx, "id");
|
||||||
|
duk_dup(ctx, DUK__IDX_RESOLVED_ID);
|
||||||
|
duk_def_prop(ctx, DUK__IDX_FRESH_REQUIRE, DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_SET_CONFIGURABLE); /* a fresh require() with require.id = resolved target module id */
|
||||||
|
|
||||||
|
/* Module table:
|
||||||
|
* - module.exports: initial exports table (may be replaced by user)
|
||||||
|
* - module.id is non-writable and non-configurable, as the CommonJS
|
||||||
|
* spec suggests this if possible
|
||||||
|
* - module.filename: not set, defaults to resolved ID if not explicitly
|
||||||
|
* set by modSearch() (note capitalization, not .fileName, matches Node.js)
|
||||||
|
* - module.name: not set, defaults to last component of resolved ID if
|
||||||
|
* not explicitly set by modSearch()
|
||||||
|
*/
|
||||||
|
duk_push_object(ctx); /* exports */
|
||||||
|
duk_push_object(ctx); /* module */
|
||||||
|
duk_push_string(ctx, "exports");
|
||||||
|
duk_dup(ctx, DUK__IDX_EXPORTS);
|
||||||
|
duk_def_prop(ctx, DUK__IDX_MODULE, DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_SET_WRITABLE | DUK_DEFPROP_SET_CONFIGURABLE); /* module.exports = exports */
|
||||||
|
duk_push_string(ctx, "id");
|
||||||
|
duk_dup(ctx, DUK__IDX_RESOLVED_ID); /* resolved id: require(id) must return this same module */
|
||||||
|
duk_def_prop(ctx, DUK__IDX_MODULE, DUK_DEFPROP_HAVE_VALUE); /* module.id = resolved_id; not writable, not enumerable, not configurable */
|
||||||
|
duk_compact(ctx, DUK__IDX_MODULE); /* module table remains registered to modLoaded, minimize its size */
|
||||||
|
DUK__ASSERT_TOP(ctx, DUK__IDX_MODULE + 1);
|
||||||
|
|
||||||
|
/* [ requested_id require require.id resolved_id last_comp Duktape Duktape.modLoaded undefined fresh_require exports module ] */
|
||||||
|
|
||||||
|
/* Register the module table early to modLoaded[] so that we can
|
||||||
|
* support circular references even in modSearch(). If an error
|
||||||
|
* is thrown, we'll delete the reference.
|
||||||
|
*/
|
||||||
|
duk_dup(ctx, DUK__IDX_RESOLVED_ID);
|
||||||
|
duk_dup(ctx, DUK__IDX_MODULE);
|
||||||
|
duk_put_prop(ctx, DUK__IDX_MODLOADED); /* Duktape.modLoaded[resolved_id] = module */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Call user provided module search function and build the wrapped
|
||||||
|
* module source code (if necessary). The module search function
|
||||||
|
* can be used to implement pure Ecmacsript, pure C, and mixed
|
||||||
|
* ECMAScript/C modules.
|
||||||
|
*
|
||||||
|
* The module search function can operate on the exports table directly
|
||||||
|
* (e.g. DLL code can register values to it). It can also return a
|
||||||
|
* string which is interpreted as module source code (if a non-string
|
||||||
|
* is returned the module is assumed to be a pure C one). If a module
|
||||||
|
* cannot be found, an error must be thrown by the user callback.
|
||||||
|
*
|
||||||
|
* Because Duktape.modLoaded[] already contains the module being
|
||||||
|
* loaded, circular references for C modules should also work
|
||||||
|
* (although expected to be quite rare).
|
||||||
|
*/
|
||||||
|
|
||||||
|
duk_push_string(ctx, "(function(require,exports,module){");
|
||||||
|
|
||||||
|
/* Duktape.modSearch(resolved_id, fresh_require, exports, module). */
|
||||||
|
duk_get_prop_string(ctx, DUK__IDX_DUKTAPE, "modSearch"); /* Duktape.modSearch */
|
||||||
|
duk_dup(ctx, DUK__IDX_RESOLVED_ID);
|
||||||
|
duk_dup(ctx, DUK__IDX_FRESH_REQUIRE);
|
||||||
|
duk_dup(ctx, DUK__IDX_EXPORTS);
|
||||||
|
duk_dup(ctx, DUK__IDX_MODULE); /* [ ... Duktape.modSearch resolved_id last_comp fresh_require exports module ] */
|
||||||
|
pcall_rc = duk_pcall(ctx, 4 /*nargs*/); /* -> [ ... source ] */
|
||||||
|
DUK__ASSERT_TOP(ctx, DUK__IDX_MODULE + 3);
|
||||||
|
|
||||||
|
if (pcall_rc != DUK_EXEC_SUCCESS) {
|
||||||
|
/* Delete entry in Duktape.modLoaded[] and rethrow. */
|
||||||
|
goto delete_rethrow;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If user callback did not return source code, module loading
|
||||||
|
* is finished (user callback initialized exports table directly).
|
||||||
|
*/
|
||||||
|
if (!duk_is_string(ctx, -1)) {
|
||||||
|
/* User callback did not return source code, so module loading
|
||||||
|
* is finished: just update modLoaded with final module.exports
|
||||||
|
* and we're done.
|
||||||
|
*/
|
||||||
|
goto return_exports;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Finish the wrapped module source. Force module.filename as the
|
||||||
|
* function .fileName so it gets set for functions defined within a
|
||||||
|
* module. This also ensures loggers created within the module get
|
||||||
|
* the module ID (or overridden filename) as their default logger name.
|
||||||
|
* (Note capitalization: .filename matches Node.js while .fileName is
|
||||||
|
* used elsewhere in Duktape.)
|
||||||
|
*/
|
||||||
|
duk_push_string(ctx, "\n})"); /* Newline allows module last line to contain a // comment. */
|
||||||
|
duk_concat(ctx, 3);
|
||||||
|
if (!duk_get_prop_string(ctx, DUK__IDX_MODULE, "filename")) {
|
||||||
|
/* module.filename for .fileName, default to resolved ID if
|
||||||
|
* not present.
|
||||||
|
*/
|
||||||
|
duk_pop(ctx);
|
||||||
|
duk_dup(ctx, DUK__IDX_RESOLVED_ID);
|
||||||
|
}
|
||||||
|
pcall_rc = duk_pcompile(ctx, DUK_COMPILE_EVAL);
|
||||||
|
if (pcall_rc != DUK_EXEC_SUCCESS) {
|
||||||
|
goto delete_rethrow;
|
||||||
|
}
|
||||||
|
pcall_rc = duk_pcall(ctx, 0); /* -> eval'd function wrapper (not called yet) */
|
||||||
|
if (pcall_rc != DUK_EXEC_SUCCESS) {
|
||||||
|
goto delete_rethrow;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Module has now evaluated to a wrapped module function. Force its
|
||||||
|
* .name to match module.name (defaults to last component of resolved
|
||||||
|
* ID) so that it is shown in stack traces too. Note that we must not
|
||||||
|
* introduce an actual name binding into the function scope (which is
|
||||||
|
* usually the case with a named function) because it would affect the
|
||||||
|
* scope seen by the module and shadow accesses to globals of the same name.
|
||||||
|
* This is now done by compiling the function as anonymous and then forcing
|
||||||
|
* its .name without setting a "has name binding" flag.
|
||||||
|
*/
|
||||||
|
|
||||||
|
duk_push_string(ctx, "name");
|
||||||
|
if (!duk_get_prop_string(ctx, DUK__IDX_MODULE, "name")) {
|
||||||
|
/* module.name for .name, default to last component if
|
||||||
|
* not present.
|
||||||
|
*/
|
||||||
|
duk_pop(ctx);
|
||||||
|
duk_dup(ctx, DUK__IDX_LASTCOMP);
|
||||||
|
}
|
||||||
|
duk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_FORCE);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Call the wrapped module function.
|
||||||
|
*
|
||||||
|
* Use a protected call so that we can update Duktape.modLoaded[resolved_id]
|
||||||
|
* even if the module throws an error.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* [ requested_id require require.id resolved_id last_comp Duktape Duktape.modLoaded undefined fresh_require exports module mod_func ] */
|
||||||
|
DUK__ASSERT_TOP(ctx, DUK__IDX_MODULE + 2);
|
||||||
|
|
||||||
|
duk_dup(ctx, DUK__IDX_EXPORTS); /* exports (this binding) */
|
||||||
|
duk_dup(ctx, DUK__IDX_FRESH_REQUIRE); /* fresh require (argument) */
|
||||||
|
duk_get_prop_string(ctx, DUK__IDX_MODULE, "exports"); /* relookup exports from module.exports in case it was changed by modSearch */
|
||||||
|
duk_dup(ctx, DUK__IDX_MODULE); /* module (argument) */
|
||||||
|
DUK__ASSERT_TOP(ctx, DUK__IDX_MODULE + 6);
|
||||||
|
|
||||||
|
/* [ requested_id require require.id resolved_id last_comp Duktape Duktape.modLoaded undefined fresh_require exports module mod_func exports fresh_require exports module ] */
|
||||||
|
|
||||||
|
pcall_rc = duk_pcall_method(ctx, 3 /*nargs*/);
|
||||||
|
if (pcall_rc != DUK_EXEC_SUCCESS) {
|
||||||
|
/* Module loading failed. Node.js will forget the module
|
||||||
|
* registration so that another require() will try to load
|
||||||
|
* the module again. Mimic that behavior.
|
||||||
|
*/
|
||||||
|
goto delete_rethrow;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* [ requested_id require require.id resolved_id last_comp Duktape Duktape.modLoaded undefined fresh_require exports module result(ignored) ] */
|
||||||
|
DUK__ASSERT_TOP(ctx, DUK__IDX_MODULE + 2);
|
||||||
|
|
||||||
|
/* fall through */
|
||||||
|
|
||||||
|
return_exports:
|
||||||
|
duk_get_prop_string(ctx, DUK__IDX_MODULE, "exports");
|
||||||
|
duk_compact(ctx, -1); /* compact the exports table */
|
||||||
|
return 1; /* return module.exports */
|
||||||
|
|
||||||
|
delete_rethrow:
|
||||||
|
duk_dup(ctx, DUK__IDX_RESOLVED_ID);
|
||||||
|
duk_del_prop(ctx, DUK__IDX_MODLOADED); /* delete Duktape.modLoaded[resolved_id] */
|
||||||
|
(void)duk_throw(ctx); /* rethrow original error */
|
||||||
|
return 0; /* not reachable */
|
||||||
|
}
|
||||||
|
|
||||||
|
void duk_module_duktape_init(duk_context* ctx) {
|
||||||
|
/* Stash 'Duktape' in case it's modified. */
|
||||||
|
duk_push_global_stash(ctx);
|
||||||
|
duk_get_global_string(ctx, "Duktape");
|
||||||
|
duk_put_prop_string(ctx, -2, "\xff" "module:Duktape");
|
||||||
|
duk_pop(ctx);
|
||||||
|
|
||||||
|
/* Register `require` as a global function. */
|
||||||
|
duk_eval_string(ctx,
|
||||||
|
"(function(req){"
|
||||||
|
"var D=Object.defineProperty;"
|
||||||
|
"D(req,'name',{value:'require'});"
|
||||||
|
"D(this,'require',{value:req,writable:true,configurable:true});"
|
||||||
|
"D(Duktape,'modLoaded',{value:Object.create(null),writable:true,configurable:true});"
|
||||||
|
"})");
|
||||||
|
duk_push_c_function(ctx, duk__require, 1 /*nargs*/);
|
||||||
|
duk_call(ctx, 1);
|
||||||
|
duk_pop(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef DUK__ASSERT
|
||||||
|
#undef DUK__ASSERT_TOP
|
||||||
|
#undef DUK__IDX_REQUESTED_ID
|
||||||
|
#undef DUK__IDX_REQUIRE
|
||||||
|
#undef DUK__IDX_REQUIRE_ID
|
||||||
|
#undef DUK__IDX_RESOLVED_ID
|
||||||
|
#undef DUK__IDX_LASTCOMP
|
||||||
|
#undef DUK__IDX_DUKTAPE
|
||||||
|
#undef DUK__IDX_MODLOADED
|
||||||
|
#undef DUK__IDX_UNDEFINED
|
||||||
|
#undef DUK__IDX_FRESH_REQUIRE
|
||||||
|
#undef DUK__IDX_EXPORTS
|
||||||
|
#undef DUK__IDX_MODULE
|
|
@ -0,0 +1,22 @@
|
||||||
|
#if !defined(DUK_MODULE_DUKTAPE_H_INCLUDED)
|
||||||
|
#define DUK_MODULE_DUKTAPE_H_INCLUDED
|
||||||
|
|
||||||
|
#include "duktape.h"
|
||||||
|
|
||||||
|
#if defined(__cplusplus)
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Maximum length of CommonJS module identifier to resolve. Length includes
|
||||||
|
* both current module ID, requested (possibly relative) module ID, and a
|
||||||
|
* slash in between.
|
||||||
|
*/
|
||||||
|
#define DUK_COMMONJS_MODULE_ID_LIMIT 256
|
||||||
|
|
||||||
|
extern void duk_module_duktape_init(duk_context* ctx);
|
||||||
|
|
||||||
|
#if defined(__cplusplus)
|
||||||
|
}
|
||||||
|
#endif /* end 'extern "C"' wrapper */
|
||||||
|
|
||||||
|
#endif /* DUK_MODULE_DUKTAPE_H_INCLUDED */
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -37,13 +37,17 @@
|
||||||
<WarningLevel>Level3</WarningLevel>
|
<WarningLevel>Level3</WarningLevel>
|
||||||
<DisableSpecificWarnings>
|
<DisableSpecificWarnings>
|
||||||
</DisableSpecificWarnings>
|
</DisableSpecificWarnings>
|
||||||
|
<ExceptionHandling Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Async</ExceptionHandling>
|
||||||
|
<ExceptionHandling Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Async</ExceptionHandling>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
</ItemDefinitionGroup>
|
</ItemDefinitionGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="duktape.h" />
|
<ClInclude Include="duktape.h" />
|
||||||
|
<ClInclude Include="duk_module_duktape.h" />
|
||||||
<ClInclude Include="duk_config.h" />
|
<ClInclude Include="duk_config.h" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="duktape.c" />
|
<ClCompile Include="duktape.cpp" />
|
||||||
|
<ClCompile Include="duk_module_duktape.cpp" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
|
@ -17,9 +17,15 @@
|
||||||
<ClInclude Include="duktape.h">
|
<ClInclude Include="duktape.h">
|
||||||
<Filter>Header Files</Filter>
|
<Filter>Header Files</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="duk_module_duktape.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="duktape.c">
|
<ClCompile Include="duktape.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="duk_module_duktape.cpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
|
@ -40,7 +40,8 @@ Source: "{#BaseDir}\Plugin\Input\PJ64_NRage.dll"; DestDir: "{app}\Plugin\Input"
|
||||||
Source: "{#BaseDir}\Plugin\Input\Project64-Input.dll"; DestDir: "{app}\Plugin\Input"
|
Source: "{#BaseDir}\Plugin\Input\Project64-Input.dll"; DestDir: "{app}\Plugin\Input"
|
||||||
Source: "{#BaseDir}\Plugin\RSP\RSP 1.7.dll"; DestDir: "{app}\Plugin\RSP"
|
Source: "{#BaseDir}\Plugin\RSP\RSP 1.7.dll"; DestDir: "{app}\Plugin\RSP"
|
||||||
Source: "{#BaseDir}\Scripts\example.js"; DestDir: "{app}\Scripts"
|
Source: "{#BaseDir}\Scripts\example.js"; DestDir: "{app}\Scripts"
|
||||||
Source: "{#BaseDir}\apidoc.htm"; DestDir: "{app}"
|
Source: "{#BaseDir}\Scripts\api_documentation.js"; DestDir: "{app}\Scripts"
|
||||||
|
Source: "{#BaseDir}\JS-API-Documentation.html"; DestDir: "{app}"
|
||||||
|
|
||||||
[Dirs]
|
[Dirs]
|
||||||
Name: "{app}\Config"; Permissions: everyone-full
|
Name: "{app}\Config"; Permissions: everyone-full
|
||||||
|
|
|
@ -30,4 +30,10 @@ __interface CDebugger
|
||||||
virtual void CPUStepStarted(void) = 0;
|
virtual void CPUStepStarted(void) = 0;
|
||||||
virtual void CPUStep(void) = 0;
|
virtual void CPUStep(void) = 0;
|
||||||
virtual void CPUStepEnded(void) = 0;
|
virtual void CPUStepEnded(void) = 0;
|
||||||
|
virtual void PIFReadStarted(void) = 0;
|
||||||
|
virtual void RSPReceivedTask(void) = 0;
|
||||||
|
virtual void PIDMAReadStarted(void) = 0;
|
||||||
|
virtual void PIDMAWriteStarted(void) = 0;
|
||||||
|
virtual void EmulationStarted(void) = 0;
|
||||||
|
virtual void EmulationStopped(void) = 0;
|
||||||
};
|
};
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include <Project64-core/N64System/Mips/Disk.h>
|
#include <Project64-core/N64System/Mips/Disk.h>
|
||||||
#include <Project64-core/N64System/N64Disk.h>
|
#include <Project64-core/N64System/N64Disk.h>
|
||||||
#include <Project64-core/N64System/N64System.h>
|
#include <Project64-core/N64System/N64System.h>
|
||||||
|
#include <Project64-core/Debugger.h>
|
||||||
|
|
||||||
CDMA::CDMA(CFlashram & FlashRam, CSram & Sram) :
|
CDMA::CDMA(CFlashram & FlashRam, CSram & Sram) :
|
||||||
m_FlashRam(FlashRam),
|
m_FlashRam(FlashRam),
|
||||||
|
@ -43,6 +44,11 @@ void CDMA::OnFirstDMA()
|
||||||
|
|
||||||
void CDMA::PI_DMA_READ()
|
void CDMA::PI_DMA_READ()
|
||||||
{
|
{
|
||||||
|
if (g_Debugger != NULL && HaveDebugger())
|
||||||
|
{
|
||||||
|
g_Debugger->PIDMAReadStarted();
|
||||||
|
}
|
||||||
|
|
||||||
// PI_STATUS_REG |= PI_STATUS_DMA_BUSY;
|
// PI_STATUS_REG |= PI_STATUS_DMA_BUSY;
|
||||||
uint32_t PI_RD_LEN_REG = ((g_Reg->PI_RD_LEN_REG) & 0x00FFFFFFul) + 1;
|
uint32_t PI_RD_LEN_REG = ((g_Reg->PI_RD_LEN_REG) & 0x00FFFFFFul) + 1;
|
||||||
|
|
||||||
|
@ -190,6 +196,11 @@ void CDMA::PI_DMA_READ()
|
||||||
|
|
||||||
void CDMA::PI_DMA_WRITE()
|
void CDMA::PI_DMA_WRITE()
|
||||||
{
|
{
|
||||||
|
if (g_Debugger != NULL && HaveDebugger())
|
||||||
|
{
|
||||||
|
g_Debugger->PIDMAWriteStarted();
|
||||||
|
}
|
||||||
|
|
||||||
// Rounding PI_WR_LEN_REG up to the nearest even number fixes AI Shougi 3, Doraemon 3, etc.
|
// Rounding PI_WR_LEN_REG up to the nearest even number fixes AI Shougi 3, Doraemon 3, etc.
|
||||||
uint32_t PI_WR_LEN_REG = ((g_Reg->PI_WR_LEN_REG) & 0x00FFFFFEul) + 2;
|
uint32_t PI_WR_LEN_REG = ((g_Reg->PI_WR_LEN_REG) & 0x00FFFFFEul) + 2;
|
||||||
uint32_t PI_CART_ADDR_REG = !g_Settings->LoadBool(Game_UnalignedDMA) ? g_Reg->PI_CART_ADDR_REG & ~1 : g_Reg->PI_CART_ADDR_REG;
|
uint32_t PI_CART_ADDR_REG = !g_Settings->LoadBool(Game_UnalignedDMA) ? g_Reg->PI_CART_ADDR_REG & ~1 : g_Reg->PI_CART_ADDR_REG;
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
#include <Project64-core/N64System/Mips/Rumblepak.h>
|
#include <Project64-core/N64System/Mips/Rumblepak.h>
|
||||||
#include <Project64-core/N64System/Mips/Mempak.h>
|
#include <Project64-core/N64System/Mips/Mempak.h>
|
||||||
#include <Project64-core/Logging.h>
|
#include <Project64-core/Logging.h>
|
||||||
|
#include <Project64-core/Debugger.h>
|
||||||
|
|
||||||
CPifRam::CPifRam(bool SavesReadOnly) :
|
CPifRam::CPifRam(bool SavesReadOnly) :
|
||||||
CEeprom(SavesReadOnly)
|
CEeprom(SavesReadOnly)
|
||||||
|
@ -260,6 +261,12 @@ void CPifRam::SI_DMA_READ()
|
||||||
}
|
}
|
||||||
|
|
||||||
PifRamRead();
|
PifRamRead();
|
||||||
|
|
||||||
|
if (CDebugSettings::HaveDebugger())
|
||||||
|
{
|
||||||
|
g_Debugger->PIFReadStarted();
|
||||||
|
}
|
||||||
|
|
||||||
SI_DRAM_ADDR_REG &= 0xFFFFFFF8;
|
SI_DRAM_ADDR_REG &= 0xFFFFFFF8;
|
||||||
if ((int32_t)SI_DRAM_ADDR_REG < 0)
|
if ((int32_t)SI_DRAM_ADDR_REG < 0)
|
||||||
{
|
{
|
||||||
|
|
|
@ -505,7 +505,6 @@ bool CN64System::RunFileImage(const char * FileLoc)
|
||||||
else if (g_Rom->CicChipID() == CIC_NUS_DDTL)
|
else if (g_Rom->CicChipID() == CIC_NUS_DDTL)
|
||||||
g_Settings->SaveString(File_DiskIPLTOOLPath, FileLoc);
|
g_Settings->SaveString(File_DiskIPLTOOLPath, FileLoc);
|
||||||
}
|
}
|
||||||
|
|
||||||
RunLoadedImage();
|
RunLoadedImage();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -1182,6 +1181,11 @@ void CN64System::ExecuteCPU()
|
||||||
{
|
{
|
||||||
m_SyncCPU->m_Plugins->RomOpened();
|
m_SyncCPU->m_Plugins->RomOpened();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (g_Debugger != nullptr && HaveDebugger())
|
||||||
|
{
|
||||||
|
g_Debugger->EmulationStarted();
|
||||||
|
}
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
_controlfp(_PC_53, _MCW_PC);
|
_controlfp(_PC_53, _MCW_PC);
|
||||||
#endif
|
#endif
|
||||||
|
@ -1211,6 +1215,12 @@ void CN64System::ExecuteCPU()
|
||||||
{
|
{
|
||||||
m_SyncCPU->m_Plugins->RomClosed();
|
m_SyncCPU->m_Plugins->RomClosed();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (g_Debugger != nullptr && HaveDebugger())
|
||||||
|
{
|
||||||
|
g_Debugger->EmulationStopped();
|
||||||
|
}
|
||||||
|
|
||||||
WriteTrace(TraceN64System, TraceDebug, "Done");
|
WriteTrace(TraceN64System, TraceDebug, "Done");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2280,6 +2290,11 @@ void CN64System::RunRSP()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (g_Debugger != NULL && HaveDebugger())
|
||||||
|
{
|
||||||
|
g_Debugger->RSPReceivedTask();
|
||||||
|
}
|
||||||
|
|
||||||
switch (Task)
|
switch (Task)
|
||||||
{
|
{
|
||||||
case 1:
|
case 1:
|
||||||
|
|
|
@ -331,6 +331,7 @@ void CSettings::AddHowToHandleSetting(const char * BaseDirectory)
|
||||||
AddHandler(Debugger_ShowDListAListCount, new CSettingTypeApplication("Debugger", "Show Dlist Alist Count", false));
|
AddHandler(Debugger_ShowDListAListCount, new CSettingTypeApplication("Debugger", "Show Dlist Alist Count", false));
|
||||||
AddHandler(Debugger_ShowRecompMemSize, new CSettingTypeApplication("Debugger", "Show Recompiler Memory size", false));
|
AddHandler(Debugger_ShowRecompMemSize, new CSettingTypeApplication("Debugger", "Show Recompiler Memory size", false));
|
||||||
AddHandler(Debugger_RecordExecutionTimes, new CSettingTypeApplication("Debugger", "Record Execution Times", false));
|
AddHandler(Debugger_RecordExecutionTimes, new CSettingTypeApplication("Debugger", "Record Execution Times", false));
|
||||||
|
AddHandler(Debugger_SilentBreak, new CSettingTypeTempBool(false));
|
||||||
AddHandler(Debugger_SteppingOps, new CSettingTypeTempBool(false));
|
AddHandler(Debugger_SteppingOps, new CSettingTypeTempBool(false));
|
||||||
AddHandler(Debugger_SkipOp, new CSettingTypeTempBool(false));
|
AddHandler(Debugger_SkipOp, new CSettingTypeTempBool(false));
|
||||||
AddHandler(Debugger_HaveExecutionBP, new CSettingTypeTempBool(false));
|
AddHandler(Debugger_HaveExecutionBP, new CSettingTypeTempBool(false));
|
||||||
|
@ -347,6 +348,7 @@ void CSettings::AddHowToHandleSetting(const char * BaseDirectory)
|
||||||
AddHandler(Debugger_ShowDivByZero, new CSettingTypeApplication("Debugger", "Show Div by zero", false));
|
AddHandler(Debugger_ShowDivByZero, new CSettingTypeApplication("Debugger", "Show Div by zero", false));
|
||||||
AddHandler(Debugger_AppLogFlush, new CSettingTypeApplication("Logging", "Log Auto Flush", (uint32_t)false));
|
AddHandler(Debugger_AppLogFlush, new CSettingTypeApplication("Logging", "Log Auto Flush", (uint32_t)false));
|
||||||
AddHandler(Debugger_RecordRecompilerAsm, new CSettingTypeApplication("Debugger", "Record Recompiler Asm", false));
|
AddHandler(Debugger_RecordRecompilerAsm, new CSettingTypeApplication("Debugger", "Record Recompiler Asm", false));
|
||||||
|
AddHandler(Debugger_AutorunScripts, new CSettingTypeApplication("Debugger", "Autorun Scripts", ""));
|
||||||
|
|
||||||
// Logging
|
// Logging
|
||||||
AddHandler(Debugger_TraceMD5, new CSettingTypeApplication("Logging", "MD5", (uint32_t)g_ModuleLogLevel[TraceMD5]));
|
AddHandler(Debugger_TraceMD5, new CSettingTypeApplication("Logging", "MD5", (uint32_t)g_ModuleLogLevel[TraceMD5]));
|
||||||
|
|
|
@ -253,6 +253,7 @@ enum SettingID
|
||||||
Debugger_ShowRecompMemSize,
|
Debugger_ShowRecompMemSize,
|
||||||
Debugger_DebugLanguage,
|
Debugger_DebugLanguage,
|
||||||
Debugger_RecordExecutionTimes,
|
Debugger_RecordExecutionTimes,
|
||||||
|
Debugger_SilentBreak,
|
||||||
Debugger_SteppingOps,
|
Debugger_SteppingOps,
|
||||||
Debugger_SkipOp,
|
Debugger_SkipOp,
|
||||||
Debugger_HaveExecutionBP,
|
Debugger_HaveExecutionBP,
|
||||||
|
@ -265,6 +266,7 @@ enum SettingID
|
||||||
Debugger_FpExceptionBreakpoints,
|
Debugger_FpExceptionBreakpoints,
|
||||||
Debugger_IntrBreakpoints,
|
Debugger_IntrBreakpoints,
|
||||||
Debugger_RcpIntrBreakpoints,
|
Debugger_RcpIntrBreakpoints,
|
||||||
|
Debugger_AutorunScripts,
|
||||||
|
|
||||||
// Trace
|
// Trace
|
||||||
Debugger_TraceMD5,
|
Debugger_TraceMD5,
|
||||||
|
|
|
@ -73,6 +73,7 @@
|
||||||
<ClCompile Include="UserInterface\Debugger\Debugger-MemoryDump.cpp" />
|
<ClCompile Include="UserInterface\Debugger\Debugger-MemoryDump.cpp" />
|
||||||
<ClCompile Include="UserInterface\Debugger\Debugger-MemorySearch.cpp" />
|
<ClCompile Include="UserInterface\Debugger\Debugger-MemorySearch.cpp" />
|
||||||
<ClCompile Include="UserInterface\Debugger\Debugger-RegisterTabs.cpp" />
|
<ClCompile Include="UserInterface\Debugger\Debugger-RegisterTabs.cpp" />
|
||||||
|
<ClCompile Include="UserInterface\Debugger\Debugger-ScriptsAutorun.cpp" />
|
||||||
<ClCompile Include="UserInterface\Debugger\Debugger-Scripts.cpp" />
|
<ClCompile Include="UserInterface\Debugger\Debugger-Scripts.cpp" />
|
||||||
<ClCompile Include="UserInterface\Debugger\Debugger-StackTrace.cpp" />
|
<ClCompile Include="UserInterface\Debugger\Debugger-StackTrace.cpp" />
|
||||||
<ClCompile Include="UserInterface\Debugger\Debugger-StackView.cpp" />
|
<ClCompile Include="UserInterface\Debugger\Debugger-StackView.cpp" />
|
||||||
|
@ -81,11 +82,34 @@
|
||||||
<ClCompile Include="UserInterface\Debugger\Debugger-ViewMemory.cpp" />
|
<ClCompile Include="UserInterface\Debugger\Debugger-ViewMemory.cpp" />
|
||||||
<ClCompile Include="UserInterface\Debugger\Debugger.cpp" />
|
<ClCompile Include="UserInterface\Debugger\Debugger.cpp" />
|
||||||
<ClCompile Include="UserInterface\Debugger\DebugMMU.cpp" />
|
<ClCompile Include="UserInterface\Debugger\DebugMMU.cpp" />
|
||||||
|
<ClCompile Include="UserInterface\Debugger\ScriptAPI\JSIntervalWorker.cpp" />
|
||||||
|
<ClCompile Include="UserInterface\Debugger\ScriptAPI\JSServerWorker.cpp" />
|
||||||
|
<ClCompile Include="UserInterface\Debugger\ScriptAPI\JSSocketWorker.cpp" />
|
||||||
|
<ClCompile Include="UserInterface\Debugger\ScriptAPI\N64Image.cpp" />
|
||||||
|
<ClCompile Include="UserInterface\Debugger\ScriptAPI\N64Image_PNG.cpp" />
|
||||||
|
<ClCompile Include="UserInterface\Debugger\ScriptAPI\ScriptAPI_interval.cpp" />
|
||||||
|
<ClCompile Include="UserInterface\Debugger\ScriptAPI\ScriptAPI_N64Image.cpp" />
|
||||||
|
<ClCompile Include="UserInterface\Debugger\ScriptAPI\ScriptAPI_Socket.cpp" />
|
||||||
<ClCompile Include="UserInterface\Debugger\DMALog.cpp" />
|
<ClCompile Include="UserInterface\Debugger\DMALog.cpp" />
|
||||||
<ClCompile Include="UserInterface\Debugger\MemoryScanner.cpp" />
|
<ClCompile Include="UserInterface\Debugger\MemoryScanner.cpp" />
|
||||||
<ClCompile Include="UserInterface\Debugger\ScriptHook.cpp" />
|
<ClCompile Include="UserInterface\Debugger\ScriptAPI\ScriptAPI.cpp" />
|
||||||
|
<ClCompile Include="UserInterface\Debugger\ScriptAPI\ScriptAPI_AddressRange.cpp" />
|
||||||
|
<ClCompile Include="UserInterface\Debugger\ScriptAPI\ScriptAPI_alert.cpp" />
|
||||||
|
<ClCompile Include="UserInterface\Debugger\ScriptAPI\ScriptAPI_asm.cpp" />
|
||||||
|
<ClCompile Include="UserInterface\Debugger\ScriptAPI\ScriptAPI_console.cpp" />
|
||||||
|
<ClCompile Include="UserInterface\Debugger\ScriptAPI\ScriptAPI_debug.cpp" />
|
||||||
|
<ClCompile Include="UserInterface\Debugger\ScriptAPI\ScriptAPI_events.cpp" />
|
||||||
|
<ClCompile Include="UserInterface\Debugger\ScriptAPI\ScriptAPI_exec.cpp" />
|
||||||
|
<ClCompile Include="UserInterface\Debugger\ScriptAPI\ScriptAPI_fs.cpp" />
|
||||||
|
<ClCompile Include="UserInterface\Debugger\ScriptAPI\ScriptAPI_mem.cpp" />
|
||||||
|
<ClCompile Include="UserInterface\Debugger\ScriptAPI\ScriptAPI_Number_hex.cpp" />
|
||||||
|
<ClCompile Include="UserInterface\Debugger\ScriptAPI\ScriptAPI_cpu.cpp" />
|
||||||
|
<ClCompile Include="UserInterface\Debugger\ScriptAPI\ScriptAPI_pj64.cpp" />
|
||||||
|
<ClCompile Include="UserInterface\Debugger\ScriptAPI\ScriptAPI_script.cpp" />
|
||||||
|
<ClCompile Include="UserInterface\Debugger\ScriptAPI\ScriptAPI_Server.cpp" />
|
||||||
<ClCompile Include="UserInterface\Debugger\ScriptInstance.cpp" />
|
<ClCompile Include="UserInterface\Debugger\ScriptInstance.cpp" />
|
||||||
<ClCompile Include="UserInterface\Debugger\ScriptSystem.cpp" />
|
<ClCompile Include="UserInterface\Debugger\ScriptSystem.cpp" />
|
||||||
|
<ClCompile Include="UserInterface\Debugger\ScriptWorker.cpp" />
|
||||||
<ClCompile Include="UserInterface\Debugger\Symbols.cpp" />
|
<ClCompile Include="UserInterface\Debugger\Symbols.cpp" />
|
||||||
<ClCompile Include="UserInterface\DiscordRPC.cpp" />
|
<ClCompile Include="UserInterface\DiscordRPC.cpp" />
|
||||||
<ClCompile Include="UserInterface\EnhancementUI.cpp" />
|
<ClCompile Include="UserInterface\EnhancementUI.cpp" />
|
||||||
|
@ -146,6 +170,7 @@
|
||||||
<ClInclude Include="UserInterface\Debugger\Debugger-MemoryDump.h" />
|
<ClInclude Include="UserInterface\Debugger\Debugger-MemoryDump.h" />
|
||||||
<ClInclude Include="UserInterface\Debugger\Debugger-MemorySearch.h" />
|
<ClInclude Include="UserInterface\Debugger\Debugger-MemorySearch.h" />
|
||||||
<ClInclude Include="UserInterface\Debugger\Debugger-RegisterTabs.h" />
|
<ClInclude Include="UserInterface\Debugger\Debugger-RegisterTabs.h" />
|
||||||
|
<ClInclude Include="UserInterface\Debugger\Debugger-ScriptsAutorun.h" />
|
||||||
<ClInclude Include="UserInterface\Debugger\Debugger-Scripts.h" />
|
<ClInclude Include="UserInterface\Debugger\Debugger-Scripts.h" />
|
||||||
<ClInclude Include="UserInterface\Debugger\Debugger-StackTrace.h" />
|
<ClInclude Include="UserInterface\Debugger\Debugger-StackTrace.h" />
|
||||||
<ClInclude Include="UserInterface\Debugger\Debugger-StackView.h" />
|
<ClInclude Include="UserInterface\Debugger\Debugger-StackView.h" />
|
||||||
|
@ -156,12 +181,18 @@
|
||||||
<ClInclude Include="UserInterface\Debugger\debugger.h" />
|
<ClInclude Include="UserInterface\Debugger\debugger.h" />
|
||||||
<ClInclude Include="UserInterface\Debugger\DebuggerUI.h" />
|
<ClInclude Include="UserInterface\Debugger\DebuggerUI.h" />
|
||||||
<ClInclude Include="UserInterface\Debugger\DebugMMU.h" />
|
<ClInclude Include="UserInterface\Debugger\DebugMMU.h" />
|
||||||
|
<ClInclude Include="UserInterface\Debugger\ScriptAPI\JSIntervalWorker.h" />
|
||||||
|
<ClInclude Include="UserInterface\Debugger\ScriptAPI\JSServerWorker.h" />
|
||||||
|
<ClInclude Include="UserInterface\Debugger\ScriptAPI\JSSocketWorker.h" />
|
||||||
|
<ClInclude Include="UserInterface\Debugger\ScriptAPI\N64Image.h" />
|
||||||
<ClInclude Include="UserInterface\Debugger\DMALog.h" />
|
<ClInclude Include="UserInterface\Debugger\DMALog.h" />
|
||||||
<ClInclude Include="UserInterface\Debugger\MemoryScanner.h" />
|
<ClInclude Include="UserInterface\Debugger\MemoryScanner.h" />
|
||||||
<ClInclude Include="UserInterface\Debugger\OpInfo.h" />
|
<ClInclude Include="UserInterface\Debugger\OpInfo.h" />
|
||||||
<ClInclude Include="UserInterface\Debugger\ScriptHook.h" />
|
<ClInclude Include="UserInterface\Debugger\ScriptAPI\ScriptAPI.h" />
|
||||||
<ClInclude Include="UserInterface\Debugger\ScriptInstance.h" />
|
<ClInclude Include="UserInterface\Debugger\ScriptInstance.h" />
|
||||||
<ClInclude Include="UserInterface\Debugger\ScriptSystem.h" />
|
<ClInclude Include="UserInterface\Debugger\ScriptSystem.h" />
|
||||||
|
<ClInclude Include="UserInterface\Debugger\ScriptTypes.h" />
|
||||||
|
<ClInclude Include="UserInterface\Debugger\ScriptWorker.h" />
|
||||||
<ClInclude Include="UserInterface\Debugger\Symbols.h" />
|
<ClInclude Include="UserInterface\Debugger\Symbols.h" />
|
||||||
<ClInclude Include="UserInterface\EnhancementUI.h" />
|
<ClInclude Include="UserInterface\EnhancementUI.h" />
|
||||||
<ClInclude Include="UserInterface\MainWindow.h" />
|
<ClInclude Include="UserInterface\MainWindow.h" />
|
||||||
|
@ -193,6 +224,7 @@
|
||||||
<ClInclude Include="UserInterface\SupportWindow.h" />
|
<ClInclude Include="UserInterface\SupportWindow.h" />
|
||||||
<ClInclude Include="UserInterface\WelcomeScreen.h" />
|
<ClInclude Include="UserInterface\WelcomeScreen.h" />
|
||||||
<ClInclude Include="UserInterface\WTLControls\DisplayMode.h" />
|
<ClInclude Include="UserInterface\WTLControls\DisplayMode.h" />
|
||||||
|
<ClInclude Include="UserInterface\WTLControls\EditConInput.h" />
|
||||||
<ClInclude Include="UserInterface\WTLControls\EditNumber32.h" />
|
<ClInclude Include="UserInterface\WTLControls\EditNumber32.h" />
|
||||||
<ClInclude Include="UserInterface\WTLControls\GetCWindowText.h" />
|
<ClInclude Include="UserInterface\WTLControls\GetCWindowText.h" />
|
||||||
<ClInclude Include="UserInterface\WTLControls\HexEditCtrl.h" />
|
<ClInclude Include="UserInterface\WTLControls\HexEditCtrl.h" />
|
||||||
|
@ -215,6 +247,9 @@
|
||||||
<ProjectReference Include="..\3rdParty\duktape\duktape.vcxproj">
|
<ProjectReference Include="..\3rdParty\duktape\duktape.vcxproj">
|
||||||
<Project>{e8d9a652-a354-4374-b6c5-a51ee62749fd}</Project>
|
<Project>{e8d9a652-a354-4374-b6c5-a51ee62749fd}</Project>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="..\3rdParty\png\png.vcxproj">
|
||||||
|
<Project>{17836496-31b0-46f2-b1b1-366d7df6f04c}</Project>
|
||||||
|
</ProjectReference>
|
||||||
<ProjectReference Include="..\3rdParty\zlib\zlib.vcxproj">
|
<ProjectReference Include="..\3rdParty\zlib\zlib.vcxproj">
|
||||||
<Project>{731bd205-2826-4631-b7af-117658e88dbc}</Project>
|
<Project>{731bd205-2826-4631-b7af-117658e88dbc}</Project>
|
||||||
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
|
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
|
||||||
|
@ -245,7 +280,6 @@
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="res\divider.cur" />
|
<None Include="res\divider.cur" />
|
||||||
<None Include="res\hand.cur" />
|
<None Include="res\hand.cur" />
|
||||||
<None Include="UserInterface\API.js" />
|
|
||||||
<None Include="UserInterface\icons\bin00001.bin" />
|
<None Include="UserInterface\icons\bin00001.bin" />
|
||||||
<None Include="UserInterface\Icons\divider.cur" />
|
<None Include="UserInterface\Icons\divider.cur" />
|
||||||
<None Include="UserInterface\Icons\hand.cur" />
|
<None Include="UserInterface\Icons\hand.cur" />
|
||||||
|
|
|
@ -46,6 +46,12 @@
|
||||||
<Filter Include="Header Files\User Interface Headers\Debugger Headers">
|
<Filter Include="Header Files\User Interface Headers\Debugger Headers">
|
||||||
<UniqueIdentifier>{c4249d55-df70-4453-b017-b548514ad094}</UniqueIdentifier>
|
<UniqueIdentifier>{c4249d55-df70-4453-b017-b548514ad094}</UniqueIdentifier>
|
||||||
</Filter>
|
</Filter>
|
||||||
|
<Filter Include="Source Files\User Interface Source\Debugger Source\ScriptAPI">
|
||||||
|
<UniqueIdentifier>{b31e3a28-aae6-4b12-9b59-c95551bc38bf}</UniqueIdentifier>
|
||||||
|
</Filter>
|
||||||
|
<Filter Include="Header Files\User Interface Headers\Debugger Headers\ScriptAPI">
|
||||||
|
<UniqueIdentifier>{f95fae8a-638b-4a4f-9df2-4a702f2bcb0b}</UniqueIdentifier>
|
||||||
|
</Filter>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="stdafx.cpp">
|
<ClCompile Include="stdafx.cpp">
|
||||||
|
@ -171,9 +177,6 @@
|
||||||
<ClCompile Include="UserInterface\Debugger\DMALog.cpp">
|
<ClCompile Include="UserInterface\Debugger\DMALog.cpp">
|
||||||
<Filter>Source Files\User Interface Source\Debugger Source</Filter>
|
<Filter>Source Files\User Interface Source\Debugger Source</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="UserInterface\Debugger\ScriptHook.cpp">
|
|
||||||
<Filter>Source Files\User Interface Source\Debugger Source</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="UserInterface\Debugger\ScriptInstance.cpp">
|
<ClCompile Include="UserInterface\Debugger\ScriptInstance.cpp">
|
||||||
<Filter>Source Files\User Interface Source\Debugger Source</Filter>
|
<Filter>Source Files\User Interface Source\Debugger Source</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
@ -255,6 +258,81 @@
|
||||||
<ClCompile Include="UserInterface\RomBrowser.cpp">
|
<ClCompile Include="UserInterface\RomBrowser.cpp">
|
||||||
<Filter>Source Files\User Interface Source</Filter>
|
<Filter>Source Files\User Interface Source</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="UserInterface\Debugger\ScriptAPI\N64Image.cpp">
|
||||||
|
<Filter>Source Files\User Interface Source\Debugger Source\ScriptAPI</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="UserInterface\Debugger\ScriptAPI\N64Image_PNG.cpp">
|
||||||
|
<Filter>Source Files\User Interface Source\Debugger Source\ScriptAPI</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="UserInterface\Debugger\ScriptAPI\ScriptAPI.cpp">
|
||||||
|
<Filter>Source Files\User Interface Source\Debugger Source\ScriptAPI</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="UserInterface\Debugger\ScriptAPI\ScriptAPI_AddressRange.cpp">
|
||||||
|
<Filter>Source Files\User Interface Source\Debugger Source\ScriptAPI</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="UserInterface\Debugger\ScriptAPI\ScriptAPI_alert.cpp">
|
||||||
|
<Filter>Source Files\User Interface Source\Debugger Source\ScriptAPI</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="UserInterface\Debugger\ScriptAPI\ScriptAPI_asm.cpp">
|
||||||
|
<Filter>Source Files\User Interface Source\Debugger Source\ScriptAPI</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="UserInterface\Debugger\ScriptAPI\ScriptAPI_console.cpp">
|
||||||
|
<Filter>Source Files\User Interface Source\Debugger Source\ScriptAPI</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="UserInterface\Debugger\ScriptAPI\ScriptAPI_cpu.cpp">
|
||||||
|
<Filter>Source Files\User Interface Source\Debugger Source\ScriptAPI</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="UserInterface\Debugger\ScriptAPI\ScriptAPI_debug.cpp">
|
||||||
|
<Filter>Source Files\User Interface Source\Debugger Source\ScriptAPI</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="UserInterface\Debugger\ScriptAPI\ScriptAPI_events.cpp">
|
||||||
|
<Filter>Source Files\User Interface Source\Debugger Source\ScriptAPI</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="UserInterface\Debugger\ScriptAPI\ScriptAPI_exec.cpp">
|
||||||
|
<Filter>Source Files\User Interface Source\Debugger Source\ScriptAPI</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="UserInterface\Debugger\ScriptAPI\ScriptAPI_fs.cpp">
|
||||||
|
<Filter>Source Files\User Interface Source\Debugger Source\ScriptAPI</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="UserInterface\Debugger\ScriptAPI\ScriptAPI_interval.cpp">
|
||||||
|
<Filter>Source Files\User Interface Source\Debugger Source\ScriptAPI</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="UserInterface\Debugger\ScriptAPI\ScriptAPI_mem.cpp">
|
||||||
|
<Filter>Source Files\User Interface Source\Debugger Source\ScriptAPI</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="UserInterface\Debugger\ScriptAPI\ScriptAPI_N64Image.cpp">
|
||||||
|
<Filter>Source Files\User Interface Source\Debugger Source\ScriptAPI</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="UserInterface\Debugger\ScriptAPI\ScriptAPI_Number_hex.cpp">
|
||||||
|
<Filter>Source Files\User Interface Source\Debugger Source\ScriptAPI</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="UserInterface\Debugger\ScriptAPI\ScriptAPI_pj64.cpp">
|
||||||
|
<Filter>Source Files\User Interface Source\Debugger Source\ScriptAPI</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="UserInterface\Debugger\ScriptAPI\ScriptAPI_script.cpp">
|
||||||
|
<Filter>Source Files\User Interface Source\Debugger Source\ScriptAPI</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="UserInterface\Debugger\ScriptAPI\ScriptAPI_Server.cpp">
|
||||||
|
<Filter>Source Files\User Interface Source\Debugger Source\ScriptAPI</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="UserInterface\Debugger\ScriptAPI\ScriptAPI_Socket.cpp">
|
||||||
|
<Filter>Source Files\User Interface Source\Debugger Source\ScriptAPI</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="UserInterface\Debugger\ScriptAPI\JSIntervalWorker.cpp">
|
||||||
|
<Filter>Source Files\User Interface Source\Debugger Source\ScriptAPI</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="UserInterface\Debugger\ScriptAPI\JSServerWorker.cpp">
|
||||||
|
<Filter>Source Files\User Interface Source\Debugger Source\ScriptAPI</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="UserInterface\Debugger\ScriptAPI\JSSocketWorker.cpp">
|
||||||
|
<Filter>Source Files\User Interface Source\Debugger Source\ScriptAPI</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="UserInterface\Debugger\Debugger-ScriptsAutorun.cpp">
|
||||||
|
<Filter>Source Files\User Interface Source\Debugger Source</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="UserInterface\Debugger\ScriptWorker.cpp">
|
||||||
|
<Filter>Source Files\User Interface Source\Debugger Source</Filter>
|
||||||
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="UserInterface\resource.h">
|
<ClInclude Include="UserInterface\resource.h">
|
||||||
|
@ -416,9 +494,6 @@
|
||||||
<ClInclude Include="UserInterface\Debugger\OpInfo.h">
|
<ClInclude Include="UserInterface\Debugger\OpInfo.h">
|
||||||
<Filter>Header Files\User Interface Headers\Debugger Headers</Filter>
|
<Filter>Header Files\User Interface Headers\Debugger Headers</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="UserInterface\Debugger\ScriptHook.h">
|
|
||||||
<Filter>Header Files\User Interface Headers\Debugger Headers</Filter>
|
|
||||||
</ClInclude>
|
|
||||||
<ClInclude Include="UserInterface\Debugger\ScriptInstance.h">
|
<ClInclude Include="UserInterface\Debugger\ScriptInstance.h">
|
||||||
<Filter>Header Files\User Interface Headers\Debugger Headers</Filter>
|
<Filter>Header Files\User Interface Headers\Debugger Headers</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
@ -494,6 +569,33 @@
|
||||||
<ClInclude Include="UserInterface\CheatUI.h">
|
<ClInclude Include="UserInterface\CheatUI.h">
|
||||||
<Filter>Header Files\User Interface Headers</Filter>
|
<Filter>Header Files\User Interface Headers</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="UserInterface\WTLControls\EditConInput.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="UserInterface\Debugger\Debugger-ScriptsAutorun.h">
|
||||||
|
<Filter>Header Files\User Interface Headers\Debugger Headers</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="UserInterface\Debugger\ScriptTypes.h">
|
||||||
|
<Filter>Header Files\User Interface Headers\Debugger Headers</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="UserInterface\Debugger\ScriptWorker.h">
|
||||||
|
<Filter>Header Files\User Interface Headers\Debugger Headers</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="UserInterface\Debugger\ScriptAPI\ScriptAPI.h">
|
||||||
|
<Filter>Header Files\User Interface Headers\Debugger Headers\ScriptAPI</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="UserInterface\Debugger\ScriptAPI\JSIntervalWorker.h">
|
||||||
|
<Filter>Header Files\User Interface Headers\Debugger Headers\ScriptAPI</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="UserInterface\Debugger\ScriptAPI\JSServerWorker.h">
|
||||||
|
<Filter>Header Files\User Interface Headers\Debugger Headers\ScriptAPI</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="UserInterface\Debugger\ScriptAPI\JSSocketWorker.h">
|
||||||
|
<Filter>Header Files\User Interface Headers\Debugger Headers\ScriptAPI</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="UserInterface\Debugger\ScriptAPI\N64Image.h">
|
||||||
|
<Filter>Header Files\User Interface Headers\Debugger Headers\ScriptAPI</Filter>
|
||||||
|
</ClInclude>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="res\divider.cur">
|
<None Include="res\divider.cur">
|
||||||
|
@ -514,7 +616,6 @@
|
||||||
<None Include="UserInterface\icons\javascri.bin">
|
<None Include="UserInterface\icons\javascri.bin">
|
||||||
<Filter>Resource Files</Filter>
|
<Filter>Resource Files</Filter>
|
||||||
</None>
|
</None>
|
||||||
<None Include="UserInterface\API.js" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Image Include="res\ListItems.bmp">
|
<Image Include="res\ListItems.bmp">
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,17 +1,24 @@
|
||||||
#include "stdafx.h"
|
#include "stdafx.h"
|
||||||
|
|
||||||
#include "DebuggerUI.h"
|
#include "DebuggerUI.h"
|
||||||
|
|
||||||
CDebugScripts::CDebugScripts(CDebuggerUI* debugger) :
|
CDebugScripts::CDebugScripts(CDebuggerUI* debugger) :
|
||||||
CDebugDialog<CDebugScripts>(debugger),
|
CDebugDialog<CDebugScripts>(debugger),
|
||||||
CToolTipDialog<CDebugScripts>(),
|
CToolTipDialog<CDebugScripts>(),
|
||||||
m_hQuitScriptDirWatchEvent(nullptr),
|
m_hQuitScriptDirWatchEvent(nullptr),
|
||||||
m_hScriptDirWatchThread(nullptr)
|
m_hScriptDirWatchThread(nullptr),
|
||||||
|
m_InputHistoryIndex(0),
|
||||||
|
m_MonoFont(nullptr),
|
||||||
|
m_MonoBoldFont(nullptr)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
CDebugScripts::~CDebugScripts(void)
|
CDebugScripts::~CDebugScripts(void)
|
||||||
{
|
{
|
||||||
|
for (size_t i = 0; i < m_InputHistory.size(); i++)
|
||||||
|
{
|
||||||
|
delete[] m_InputHistory[i];
|
||||||
|
}
|
||||||
|
m_InputHistory.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
LRESULT CDebugScripts::OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
|
LRESULT CDebugScripts::OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
|
||||||
|
@ -20,11 +27,15 @@ LRESULT CDebugScripts::OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*l
|
||||||
DlgSavePos_Init(DebuggerUI_ScriptsPos);
|
DlgSavePos_Init(DebuggerUI_ScriptsPos);
|
||||||
DlgToolTip_Init();
|
DlgToolTip_Init();
|
||||||
|
|
||||||
HFONT monoFont = CreateFont(-11, 0, 0, 0,
|
m_MonoFont = CreateFont(-12, 0, 0, 0,
|
||||||
FW_DONTCARE, FALSE, FALSE, FALSE, DEFAULT_CHARSET,
|
FW_DONTCARE, FALSE, FALSE, FALSE, DEFAULT_CHARSET,
|
||||||
OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
|
OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
|
||||||
CLEARTYPE_QUALITY, FF_DONTCARE, L"Consolas"
|
CLEARTYPE_QUALITY, FF_DONTCARE, L"Consolas");
|
||||||
);
|
|
||||||
|
m_MonoBoldFont = CreateFont(-13, 0, 0, 0,
|
||||||
|
FW_BOLD, FALSE, FALSE, FALSE, DEFAULT_CHARSET,
|
||||||
|
OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
|
||||||
|
CLEARTYPE_QUALITY, FF_DONTCARE, L"Consolas");
|
||||||
|
|
||||||
m_ScriptList.Attach(GetDlgItem(IDC_SCRIPT_LIST));
|
m_ScriptList.Attach(GetDlgItem(IDC_SCRIPT_LIST));
|
||||||
m_ScriptList.AddColumn(L"Status", 0);
|
m_ScriptList.AddColumn(L"Status", 0);
|
||||||
|
@ -34,32 +45,34 @@ LRESULT CDebugScripts::OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*l
|
||||||
m_ScriptList.SetExtendedListViewStyle(LVS_EX_FULLROWSELECT | LVS_EX_DOUBLEBUFFER);
|
m_ScriptList.SetExtendedListViewStyle(LVS_EX_FULLROWSELECT | LVS_EX_DOUBLEBUFFER);
|
||||||
m_ScriptList.ModifyStyle(LVS_OWNERDRAWFIXED, 0, 0);
|
m_ScriptList.ModifyStyle(LVS_OWNERDRAWFIXED, 0, 0);
|
||||||
|
|
||||||
m_EvalEdit.Attach(GetDlgItem(IDC_EVAL_EDIT));
|
m_ConInputEdit.Attach(GetDlgItem(IDC_EVAL_EDIT));
|
||||||
m_EvalEdit.SetScriptWindow(this);
|
m_ConInputEdit.SetFont(m_MonoFont);
|
||||||
m_EvalEdit.SetFont(monoFont);
|
m_ConInputEdit.EnableWindow(FALSE);
|
||||||
m_EvalEdit.EnableWindow(FALSE);
|
|
||||||
|
|
||||||
m_ConsoleEdit.Attach(GetDlgItem(IDC_CONSOLE_EDIT));
|
m_ConOutputEdit.Attach(GetDlgItem(IDC_CONSOLE_EDIT));
|
||||||
m_ConsoleEdit.SetLimitText(0);
|
m_ConOutputEdit.SetLimitText(0);
|
||||||
m_ConsoleEdit.SetFont(monoFont);
|
m_ConOutputEdit.SetFont(m_MonoFont);
|
||||||
|
|
||||||
|
::SendMessage(GetDlgItem(IDC_EVAL_LBL), WM_SETFONT, (WPARAM)m_MonoBoldFont, 0);
|
||||||
|
|
||||||
int statusPaneWidths[] = { -1 };
|
int statusPaneWidths[] = { -1 };
|
||||||
m_StatusBar.Attach(GetDlgItem(IDC_STATUSBAR));
|
m_StatusBar.Attach(GetDlgItem(IDC_STATUSBAR));
|
||||||
m_StatusBar.SetParts(1, statusPaneWidths);
|
m_StatusBar.SetParts(1, statusPaneWidths);
|
||||||
|
|
||||||
|
m_Debugger->ScriptSystem()->LoadAutorunList();
|
||||||
|
|
||||||
RefreshList();
|
RefreshList();
|
||||||
|
|
||||||
m_InstallDir = (std::string)CPath(CPath::MODULE_DIRECTORY);
|
m_InstallDir = m_Debugger->ScriptSystem()->InstallDirPath();
|
||||||
m_ScriptsDir = m_InstallDir + "Scripts\\";
|
m_ScriptsDir = m_Debugger->ScriptSystem()->ScriptsDirPath();
|
||||||
|
|
||||||
if (!PathFileExistsA(m_ScriptsDir.c_str()))
|
SetTimer(CONFLUSH_TIMER_ID, CONFLUSH_TIMER_INTERVAL, nullptr);
|
||||||
{
|
|
||||||
CreateDirectoryA(m_ScriptsDir.c_str(), nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
m_hQuitScriptDirWatchEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
|
m_hQuitScriptDirWatchEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
|
||||||
m_hScriptDirWatchThread = CreateThread(nullptr, 0, ScriptDirWatchProc, (void*)this, 0, nullptr);
|
m_hScriptDirWatchThread = CreateThread(nullptr, 0, ScriptDirWatchProc, (void*)this, 0, nullptr);
|
||||||
|
|
||||||
|
m_ConOutputEdit.SetWindowText(m_Debugger->ScriptSystem()->GetConsoleBuffer().ToUTF16().c_str());
|
||||||
|
|
||||||
LoadWindowPos();
|
LoadWindowPos();
|
||||||
WindowCreated();
|
WindowCreated();
|
||||||
|
|
||||||
|
@ -68,10 +81,15 @@ LRESULT CDebugScripts::OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*l
|
||||||
|
|
||||||
LRESULT CDebugScripts::OnDestroy(void)
|
LRESULT CDebugScripts::OnDestroy(void)
|
||||||
{
|
{
|
||||||
|
KillTimer(CONFLUSH_TIMER_ID);
|
||||||
|
|
||||||
SetEvent(m_hQuitScriptDirWatchEvent);
|
SetEvent(m_hQuitScriptDirWatchEvent);
|
||||||
WaitForSingleObject(m_hScriptDirWatchThread, INFINITE);
|
WaitForSingleObject(m_hScriptDirWatchThread, INFINITE);
|
||||||
CloseHandle(m_hQuitScriptDirWatchEvent);
|
CloseHandle(m_hQuitScriptDirWatchEvent);
|
||||||
CloseHandle(m_hScriptDirWatchThread);
|
CloseHandle(m_hScriptDirWatchThread);
|
||||||
|
|
||||||
|
DeleteObject(m_MonoFont);
|
||||||
|
DeleteObject(m_MonoBoldFont);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,8 +101,26 @@ LRESULT CDebugScripts::OnCtlColorStatic(UINT /*uMsg*/, WPARAM wParam, LPARAM lPa
|
||||||
|
|
||||||
if (ctrlId == IDC_CONSOLE_EDIT)
|
if (ctrlId == IDC_CONSOLE_EDIT)
|
||||||
{
|
{
|
||||||
SetBkColor(hDC, RGB(255, 255, 255));
|
SetTextColor(hDC, RGB(0xEE, 0xEE, 0xEE));
|
||||||
SetDCBrushColor(hDC, RGB(255, 255, 255));
|
SetBkColor(hDC, RGB(0x22, 0x22, 0x22));
|
||||||
|
SetDCBrushColor(hDC, RGB(0x22, 0x22, 0x22));
|
||||||
|
return (LRESULT)GetStockObject(DC_BRUSH);
|
||||||
|
}
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
LRESULT CDebugScripts::OnCtlColorEdit(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
|
||||||
|
{
|
||||||
|
HDC hDC = (HDC)wParam;
|
||||||
|
HWND hCtrl = (HWND)lParam;
|
||||||
|
WORD ctrlId = (WORD) ::GetWindowLong(hCtrl, GWL_ID);
|
||||||
|
|
||||||
|
if (ctrlId == IDC_EVAL_EDIT)
|
||||||
|
{
|
||||||
|
SetTextColor(hDC, RGB(0xEE, 0xEE, 0xEE));
|
||||||
|
SetBkColor(hDC, RGB(0x22, 0x22, 0x22));
|
||||||
|
SetDCBrushColor(hDC, RGB(0x22, 0x22, 0x22));
|
||||||
return (LRESULT)GetStockObject(DC_BRUSH);
|
return (LRESULT)GetStockObject(DC_BRUSH);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,23 +144,26 @@ DWORD WINAPI CDebugScripts::ScriptDirWatchProc(void* ctx)
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
DWORD status = WaitForMultipleObjects(2, hEvents, FALSE, INFINITE);
|
DWORD nHandle = WaitForMultipleObjects(2, hEvents, FALSE, INFINITE);
|
||||||
|
|
||||||
switch (status)
|
switch (nHandle)
|
||||||
{
|
{
|
||||||
case WAIT_OBJECT_0:
|
case WAIT_OBJECT_0:
|
||||||
if (FindNextChangeNotification(hEvents[0]) == FALSE)
|
if (FindNextChangeNotification(hEvents[0]) == FALSE)
|
||||||
{
|
{
|
||||||
return 0;
|
goto done;
|
||||||
}
|
}
|
||||||
_this->PostMessage(WM_REFRESH_LIST, 0, 0);
|
_this->PostMessage(WM_REFRESH_LIST, 0, 0);
|
||||||
break;
|
break;
|
||||||
case WAIT_OBJECT_0 + 1:
|
|
||||||
return 0;
|
|
||||||
default:
|
default:
|
||||||
return 0;
|
case WAIT_OBJECT_0 + 1:
|
||||||
|
goto done;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
done:
|
||||||
|
FindCloseChangeNotification(hEvents[0]);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CDebugScripts::OnExitSizeMove(void)
|
void CDebugScripts::OnExitSizeMove(void)
|
||||||
|
@ -141,12 +180,25 @@ void CDebugScripts::ConsoleCopy()
|
||||||
|
|
||||||
EmptyClipboard();
|
EmptyClipboard();
|
||||||
|
|
||||||
size_t nChars = m_ConsoleEdit.GetWindowTextLength() + 1;
|
size_t nChars = m_ConOutputEdit.GetWindowTextLength() + 1;
|
||||||
|
|
||||||
HGLOBAL hMem = GlobalAlloc(GMEM_MOVEABLE, nChars * sizeof(wchar_t));
|
HGLOBAL hMem = GlobalAlloc(GMEM_MOVEABLE, nChars * sizeof(wchar_t));
|
||||||
|
|
||||||
|
if (hMem == nullptr)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
wchar_t* memBuf = (wchar_t*)GlobalLock(hMem);
|
wchar_t* memBuf = (wchar_t*)GlobalLock(hMem);
|
||||||
m_ConsoleEdit.GetWindowText(memBuf, nChars);
|
|
||||||
|
if (memBuf == nullptr)
|
||||||
|
{
|
||||||
|
GlobalUnlock(hMem);
|
||||||
|
GlobalFree(hMem);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_ConOutputEdit.GetWindowText(memBuf, nChars);
|
||||||
|
|
||||||
GlobalUnlock(hMem);
|
GlobalUnlock(hMem);
|
||||||
SetClipboardData(CF_UNICODETEXT, hMem);
|
SetClipboardData(CF_UNICODETEXT, hMem);
|
||||||
|
@ -159,7 +211,9 @@ void CDebugScripts::ConsolePrint(const char* text)
|
||||||
{
|
{
|
||||||
if (m_hWnd != nullptr)
|
if (m_hWnd != nullptr)
|
||||||
{
|
{
|
||||||
SendMessage(WM_CONSOLE_PRINT, (WPARAM)text);
|
// OnConsolePrint will free this
|
||||||
|
char* textCopy = _strdup(text);
|
||||||
|
PostMessage(WM_CONSOLE_PRINT, (WPARAM)textCopy);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -167,7 +221,7 @@ void CDebugScripts::ConsoleClear()
|
||||||
{
|
{
|
||||||
if (m_hWnd != nullptr)
|
if (m_hWnd != nullptr)
|
||||||
{
|
{
|
||||||
SendMessage(WM_CONSOLE_CLEAR);
|
PostMessage(WM_CONSOLE_CLEAR);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -187,18 +241,23 @@ LRESULT CDebugScripts::OnClicked(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*
|
||||||
EndDialog(0);
|
EndDialog(0);
|
||||||
break;
|
break;
|
||||||
case ID_POPUP_RUN:
|
case ID_POPUP_RUN:
|
||||||
case IDC_RUN_BTN:
|
|
||||||
RunSelected();
|
RunSelected();
|
||||||
break;
|
break;
|
||||||
|
case IDC_RUN_BTN:
|
||||||
|
ToggleSelected();
|
||||||
|
break;
|
||||||
case ID_POPUP_STOP:
|
case ID_POPUP_STOP:
|
||||||
case IDC_STOP_BTN:
|
|
||||||
StopSelected();
|
StopSelected();
|
||||||
break;
|
break;
|
||||||
case ID_POPUP_SCRIPT_EDIT:
|
case ID_POPUP_SCRIPT_EDIT:
|
||||||
EditSelected();
|
EditSelected();
|
||||||
break;
|
break;
|
||||||
|
case ID_POPUP_AUTORUN:
|
||||||
|
m_AutorunDlg.DoModal(m_Debugger, m_SelectedScriptName);
|
||||||
|
m_ScriptList.RedrawWindow();
|
||||||
|
break;
|
||||||
case IDC_CLEAR_BTN:
|
case IDC_CLEAR_BTN:
|
||||||
ConsoleClear();
|
m_Debugger->ScriptSystem()->ConsoleClear();
|
||||||
break;
|
break;
|
||||||
case IDC_COPY_BTN:
|
case IDC_COPY_BTN:
|
||||||
ConsoleCopy();
|
ConsoleCopy();
|
||||||
|
@ -212,7 +271,6 @@ LRESULT CDebugScripts::OnClicked(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*
|
||||||
|
|
||||||
LRESULT CDebugScripts::OnScriptListDblClicked(NMHDR* pNMHDR)
|
LRESULT CDebugScripts::OnScriptListDblClicked(NMHDR* pNMHDR)
|
||||||
{
|
{
|
||||||
// Run script on double click
|
|
||||||
NMITEMACTIVATE* pIA = reinterpret_cast<NMITEMACTIVATE*>(pNMHDR);
|
NMITEMACTIVATE* pIA = reinterpret_cast<NMITEMACTIVATE*>(pNMHDR);
|
||||||
int nItem = pIA->iItem;
|
int nItem = pIA->iItem;
|
||||||
|
|
||||||
|
@ -228,24 +286,25 @@ LRESULT CDebugScripts::OnScriptListDblClicked(NMHDR* pNMHDR)
|
||||||
|
|
||||||
void CDebugScripts::RefreshStatus()
|
void CDebugScripts::RefreshStatus()
|
||||||
{
|
{
|
||||||
INSTANCE_STATE state = m_Debugger->ScriptSystem()->GetInstanceState(m_SelectedScriptName.c_str());
|
JSInstanceStatus status = m_Debugger->ScriptSystem()->GetStatus(m_SelectedScriptName.c_str());
|
||||||
|
|
||||||
stdstr statusText = m_ScriptsDir + m_SelectedScriptName;
|
stdstr statusText = m_ScriptsDir + m_SelectedScriptName;
|
||||||
|
|
||||||
if (state == STATE_RUNNING)
|
if (status == JS_STATUS_STARTED)
|
||||||
{
|
{
|
||||||
statusText += " (Running)";
|
statusText += " (Started)";
|
||||||
m_EvalEdit.EnableWindow(TRUE);
|
m_ConInputEdit.EnableWindow(TRUE);
|
||||||
|
m_ConInputEdit.SetFocus();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (state == STATE_STARTED)
|
if (status == JS_STATUS_STARTING)
|
||||||
{
|
{
|
||||||
statusText += " (Started)";
|
statusText += " (Starting)";
|
||||||
}
|
}
|
||||||
m_EvalEdit.EnableWindow(FALSE);
|
m_ConInputEdit.EnableWindow(FALSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_StatusBar.SetText(0, statusText.ToUTF16().c_str());
|
m_StatusBar.SetText(0, statusText.ToUTF16().c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -259,12 +318,12 @@ LRESULT CDebugScripts::OnScriptListRClicked(NMHDR* pNMHDR)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
INSTANCE_STATE state = m_Debugger->ScriptSystem()->GetInstanceState(m_SelectedScriptName.c_str());
|
JSInstanceStatus status = m_Debugger->ScriptSystem()->GetStatus(m_SelectedScriptName.c_str());
|
||||||
|
|
||||||
HMENU hMenu = LoadMenu(GetModuleHandle(nullptr), MAKEINTRESOURCE(IDR_SCRIPT_POPUP));
|
HMENU hMenu = LoadMenu(GetModuleHandle(nullptr), MAKEINTRESOURCE(IDR_SCRIPT_POPUP));
|
||||||
HMENU hPopupMenu = GetSubMenu(hMenu, 0);
|
HMENU hPopupMenu = GetSubMenu(hMenu, 0);
|
||||||
|
|
||||||
if (state == STATE_STARTED || state == STATE_RUNNING)
|
if (status == JS_STATUS_STARTING || status == JS_STATUS_STARTED)
|
||||||
{
|
{
|
||||||
EnableMenuItem(hPopupMenu, ID_POPUP_RUN, MF_DISABLED | MF_GRAYED);
|
EnableMenuItem(hPopupMenu, ID_POPUP_RUN, MF_DISABLED | MF_GRAYED);
|
||||||
}
|
}
|
||||||
|
@ -303,13 +362,18 @@ LRESULT CDebugScripts::OnScriptListCustomDraw(NMHDR* pNMHDR)
|
||||||
wchar_t scriptName[MAX_PATH];
|
wchar_t scriptName[MAX_PATH];
|
||||||
m_ScriptList.GetItemText(nItem, 1, scriptName, MAX_PATH);
|
m_ScriptList.GetItemText(nItem, 1, scriptName, MAX_PATH);
|
||||||
|
|
||||||
INSTANCE_STATE state = m_Debugger->ScriptSystem()->GetInstanceState(stdstr("").FromUTF16(scriptName).c_str());
|
if (m_Debugger->ScriptSystem()->AutorunList().count(stdstr().FromUTF16(scriptName)) > 0 && pLVCD->iSubItem == 1)
|
||||||
|
{
|
||||||
|
pLVCD->clrText = RGB(0x00, 0x80, 0x00);
|
||||||
|
}
|
||||||
|
|
||||||
if (state == STATE_STARTED)
|
JSInstanceStatus status = m_Debugger->ScriptSystem()->GetStatus(stdstr("").FromUTF16(scriptName).c_str());
|
||||||
|
|
||||||
|
if (status == JS_STATUS_STARTING)
|
||||||
{
|
{
|
||||||
pLVCD->clrTextBk = RGB(0xFF, 0xFF, 0xAA);
|
pLVCD->clrTextBk = RGB(0xFF, 0xFF, 0xAA);
|
||||||
}
|
}
|
||||||
else if (state == STATE_RUNNING)
|
else if (status == JS_STATUS_STARTED)
|
||||||
{
|
{
|
||||||
pLVCD->clrTextBk = RGB(0xAA, 0xFF, 0xAA);
|
pLVCD->clrTextBk = RGB(0xAA, 0xFF, 0xAA);
|
||||||
}
|
}
|
||||||
|
@ -332,41 +396,27 @@ LRESULT CDebugScripts::OnScriptListItemChanged(NMHDR* pNMHDR)
|
||||||
m_ScriptList.GetItemText(lpStateChange->iItem, 1, ScriptName, MAX_PATH);
|
m_ScriptList.GetItemText(lpStateChange->iItem, 1, ScriptName, MAX_PATH);
|
||||||
m_SelectedScriptName = stdstr().FromUTF16(ScriptName).c_str();
|
m_SelectedScriptName = stdstr().FromUTF16(ScriptName).c_str();
|
||||||
|
|
||||||
INSTANCE_STATE state = m_Debugger->ScriptSystem()->GetInstanceState(m_SelectedScriptName.c_str());
|
JSInstanceStatus status = m_Debugger->ScriptSystem()->GetStatus(m_SelectedScriptName.c_str());
|
||||||
|
|
||||||
::EnableWindow(GetDlgItem(IDC_STOP_BTN), state == STATE_RUNNING || state == STATE_STARTED);
|
::SetWindowText(GetDlgItem(IDC_RUN_BTN), status == JS_STATUS_STOPPED ? L"Run" : L"Stop");
|
||||||
::EnableWindow(GetDlgItem(IDC_RUN_BTN), state == STATE_STOPPED || state == STATE_INVALID);
|
|
||||||
|
|
||||||
RefreshStatus();
|
RefreshStatus();
|
||||||
}
|
}
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
LRESULT CDebugScripts::OnConsoleLog(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/)
|
LRESULT CDebugScripts::OnConsolePrint(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/)
|
||||||
{
|
{
|
||||||
const char *text = (const char*)wParam;
|
char *text = (char*)wParam;
|
||||||
|
m_ConOutputBuffer += text;
|
||||||
::ShowWindow(*this, SW_SHOWNOACTIVATE);
|
free(text);
|
||||||
|
|
||||||
SCROLLINFO scroll;
|
|
||||||
scroll.cbSize = sizeof(SCROLLINFO);
|
|
||||||
scroll.fMask = SIF_ALL;
|
|
||||||
m_ConsoleEdit.GetScrollInfo(SB_VERT, &scroll);
|
|
||||||
|
|
||||||
m_ConsoleEdit.SetRedraw(FALSE);
|
|
||||||
m_ConsoleEdit.AppendText(stdstr(text).ToUTF16().c_str());
|
|
||||||
m_ConsoleEdit.SetRedraw(TRUE);
|
|
||||||
|
|
||||||
if ((scroll.nPage + scroll.nPos) - 1 == (uint32_t)scroll.nMax)
|
|
||||||
{
|
|
||||||
m_ConsoleEdit.ScrollCaret();
|
|
||||||
}
|
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
LRESULT CDebugScripts::OnConsoleClear(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
|
LRESULT CDebugScripts::OnConsoleClear(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
|
||||||
{
|
{
|
||||||
m_ConsoleEdit.SetWindowText(L"");
|
m_ConOutputBuffer = "";
|
||||||
|
m_ConOutputEdit.SetWindowText(L"");
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -374,10 +424,11 @@ LRESULT CDebugScripts::OnRefreshList(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*
|
||||||
{
|
{
|
||||||
int nIndex = m_ScriptList.GetSelectedIndex();
|
int nIndex = m_ScriptList.GetSelectedIndex();
|
||||||
|
|
||||||
CPath SearchPath(m_ScriptsDir, "*");
|
CPath searchPath(m_ScriptsDir.c_str(), "*");
|
||||||
|
|
||||||
if (!SearchPath.FindFirst(CPath::FIND_ATTRIBUTE_ALLFILES))
|
if (!searchPath.FindFirst(CPath::FIND_ATTRIBUTE_FILES))
|
||||||
{
|
{
|
||||||
|
m_ScriptList.DeleteAllItems();
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -388,27 +439,32 @@ LRESULT CDebugScripts::OnRefreshList(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*
|
||||||
|
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
stdstr scriptFileName = SearchPath.GetNameExtension();
|
if (searchPath.GetExtension() != "js")
|
||||||
INSTANCE_STATE state = m_Debugger->ScriptSystem()->GetInstanceState(scriptFileName.c_str());
|
|
||||||
const wchar_t *statusIcon = L"";
|
|
||||||
|
|
||||||
switch (state)
|
|
||||||
{
|
{
|
||||||
case STATE_STARTED:
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
stdstr scriptFileName = searchPath.GetNameExtension();
|
||||||
|
JSInstanceStatus status = m_Debugger->ScriptSystem()->GetStatus(scriptFileName.c_str());
|
||||||
|
const wchar_t *statusIcon = L"";
|
||||||
|
|
||||||
|
switch (status)
|
||||||
|
{
|
||||||
|
case JS_STATUS_STARTING:
|
||||||
statusIcon = L"*";
|
statusIcon = L"*";
|
||||||
break;
|
break;
|
||||||
case STATE_RUNNING:
|
case JS_STATUS_STARTED:
|
||||||
statusIcon = L">";
|
statusIcon = L">";
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
statusIcon = L"-";
|
statusIcon = L"-";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_ScriptList.AddItem(nItem, 0, statusIcon);
|
m_ScriptList.AddItem(nItem, 0, statusIcon);
|
||||||
m_ScriptList.SetItemText(nItem, 1, scriptFileName.ToUTF16().c_str());
|
m_ScriptList.SetItemText(nItem, 1, scriptFileName.ToUTF16().c_str());
|
||||||
nItem++;
|
nItem++;
|
||||||
} while (SearchPath.FindNext());
|
} while (searchPath.FindNext());
|
||||||
|
|
||||||
m_ScriptList.SetRedraw(true);
|
m_ScriptList.SetRedraw(true);
|
||||||
m_ScriptList.Invalidate();
|
m_ScriptList.Invalidate();
|
||||||
|
@ -421,15 +477,9 @@ LRESULT CDebugScripts::OnRefreshList(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CDebugScripts::EvaluateInSelectedInstance(const char* code)
|
void CDebugScripts::SendInput(const char* name, const char* code)
|
||||||
{
|
{
|
||||||
INSTANCE_STATE state = m_Debugger->ScriptSystem()->GetInstanceState(m_SelectedScriptName.c_str());
|
m_Debugger->ScriptSystem()->Input(name, code);
|
||||||
|
|
||||||
if (state == STATE_RUNNING || state == STATE_STARTED)
|
|
||||||
{
|
|
||||||
CScriptInstance* instance = m_Debugger->ScriptSystem()->GetInstance(m_SelectedScriptName.c_str());
|
|
||||||
instance->Eval(code);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CDebugScripts::RunSelected()
|
void CDebugScripts::RunSelected()
|
||||||
|
@ -439,16 +489,9 @@ void CDebugScripts::RunSelected()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
INSTANCE_STATE state = m_Debugger->ScriptSystem()->GetInstanceState(m_SelectedScriptName.c_str());
|
stdstr path = m_ScriptsDir + m_SelectedScriptName;
|
||||||
|
|
||||||
if (state == STATE_INVALID || state == STATE_STOPPED)
|
m_Debugger->ScriptSystem()->StartScript(m_SelectedScriptName.c_str(), path.c_str());
|
||||||
{
|
|
||||||
m_Debugger->ScriptSystem()->RunScript(m_SelectedScriptName.c_str());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_Debugger->Debug_LogScriptsWindow("[Error: script is already running]\n");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CDebugScripts::StopSelected()
|
void CDebugScripts::StopSelected()
|
||||||
|
@ -458,9 +501,9 @@ void CDebugScripts::StopSelected()
|
||||||
|
|
||||||
void CDebugScripts::ToggleSelected()
|
void CDebugScripts::ToggleSelected()
|
||||||
{
|
{
|
||||||
INSTANCE_STATE state = m_Debugger->ScriptSystem()->GetInstanceState(m_SelectedScriptName.c_str());
|
JSInstanceStatus status = m_Debugger->ScriptSystem()->GetStatus(m_SelectedScriptName.c_str());
|
||||||
|
|
||||||
if (state == STATE_INVALID || state == STATE_STOPPED)
|
if (status == JS_STATUS_STOPPED)
|
||||||
{
|
{
|
||||||
RunSelected();
|
RunSelected();
|
||||||
}
|
}
|
||||||
|
@ -472,71 +515,111 @@ void CDebugScripts::ToggleSelected()
|
||||||
|
|
||||||
void CDebugScripts::EditSelected()
|
void CDebugScripts::EditSelected()
|
||||||
{
|
{
|
||||||
ShellExecuteA(nullptr, "edit", m_SelectedScriptName.c_str(), nullptr, m_ScriptsDir.c_str(), SW_SHOWNORMAL);
|
stdstr scriptPath = m_ScriptsDir + m_SelectedScriptName;
|
||||||
|
ShellExecuteA(nullptr, "edit", scriptPath.c_str(), nullptr, m_InstallDir.c_str(), SW_SHOWNORMAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Console input
|
LRESULT CDebugScripts::OnInputSpecialKey(NMHDR* pNMHDR)
|
||||||
LRESULT CEditEval::OnKeyDown(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled)
|
|
||||||
{
|
{
|
||||||
if (wParam == VK_UP)
|
NMCISPECIALKEY* pnmsk = (NMCISPECIALKEY*)pNMHDR;
|
||||||
|
|
||||||
|
if (pnmsk->vkey == VK_UP)
|
||||||
{
|
{
|
||||||
if (m_HistoryIdx > 0)
|
if (m_InputHistoryIndex > 0)
|
||||||
{
|
{
|
||||||
const std::string & Code = m_History[--m_HistoryIdx];
|
wchar_t* code = m_InputHistory[--m_InputHistoryIndex];
|
||||||
SetWindowText(stdstr(Code).ToUTF16().c_str());
|
m_ConInputEdit.SetWindowText(code);
|
||||||
SetSel((int)Code.length(), (int)Code.length());
|
int selEnd = wcslen(code);
|
||||||
|
m_ConInputEdit.SetSel(selEnd, selEnd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
else if (wParam == VK_DOWN)
|
|
||||||
|
if (pnmsk->vkey == VK_DOWN)
|
||||||
{
|
{
|
||||||
int size = m_History.size();
|
size_t size = m_InputHistory.size();
|
||||||
if (m_HistoryIdx < size - 1)
|
|
||||||
|
if (m_InputHistoryIndex < size)
|
||||||
{
|
{
|
||||||
const std::string & Code = m_History[++m_HistoryIdx];
|
m_InputHistoryIndex++;
|
||||||
SetWindowText(stdstr(Code).ToUTF16().c_str());
|
|
||||||
SetSel((int)Code.length(), (int)Code.length());
|
|
||||||
}
|
}
|
||||||
else if (m_HistoryIdx < size)
|
|
||||||
|
if (m_InputHistoryIndex < size)
|
||||||
{
|
{
|
||||||
SetWindowText(L"");
|
wchar_t* code = m_InputHistory[m_InputHistoryIndex];
|
||||||
m_HistoryIdx++;
|
m_ConInputEdit.SetWindowText(code);
|
||||||
|
int selEnd = wcslen(code);
|
||||||
|
m_ConInputEdit.SetSel(selEnd, selEnd);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_ConInputEdit.SetWindowText(L"");
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
else if (wParam == VK_RETURN)
|
|
||||||
|
if (pnmsk->vkey == VK_RETURN)
|
||||||
{
|
{
|
||||||
if (m_ScriptWindow == nullptr)
|
size_t codeLength = m_ConInputEdit.GetWindowTextLength();
|
||||||
|
|
||||||
|
if (codeLength == 0)
|
||||||
{
|
{
|
||||||
bHandled = FALSE;
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Code = GetCWindowText(*this);
|
wchar_t* code = new wchar_t[codeLength + 1];
|
||||||
m_ScriptWindow->EvaluateInSelectedInstance(Code.c_str());
|
m_ConInputEdit.GetWindowText(code, codeLength + 1);
|
||||||
|
m_ConInputEdit.SetWindowText(L"");
|
||||||
|
|
||||||
SetWindowText(L"");
|
SendInput(m_SelectedScriptName.c_str(), stdstr().FromUTF16(code).c_str());
|
||||||
int historySize = m_History.size();
|
|
||||||
|
|
||||||
// Remove duplicate
|
// If there is a duplicate entry move it to the bottom
|
||||||
for (int i = 0; i < historySize; i++)
|
for (size_t i = 0; i < m_InputHistory.size(); i++)
|
||||||
{
|
{
|
||||||
if (strcmp(Code.c_str(), m_History[i].c_str()) == 0)
|
if (wcscmp(code, m_InputHistory[i]) == 0)
|
||||||
{
|
{
|
||||||
m_History.erase(m_History.begin() + i);
|
wchar_t* str = m_InputHistory[i];
|
||||||
historySize--;
|
m_InputHistory.erase(m_InputHistory.begin() + i);
|
||||||
break;
|
m_InputHistory.push_back(str);
|
||||||
|
m_InputHistoryIndex = m_InputHistory.size();
|
||||||
|
delete[] code;
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove oldest if maxed
|
m_InputHistory.push_back(code);
|
||||||
if (historySize >= HISTORY_MAX_ENTRIES)
|
m_InputHistoryIndex = m_InputHistory.size();
|
||||||
{
|
return 0;
|
||||||
m_History.erase(m_History.begin() + 0);
|
|
||||||
historySize--;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_History.push_back(Code);
|
|
||||||
m_HistoryIdx = historySize++;
|
|
||||||
}
|
}
|
||||||
bHandled = FALSE;
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CDebugScripts::OnTimer(UINT_PTR nIDEvent)
|
||||||
|
{
|
||||||
|
if (nIDEvent == CONFLUSH_TIMER_ID)
|
||||||
|
{
|
||||||
|
if (m_ConOutputBuffer == "")
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SCROLLINFO scroll;
|
||||||
|
scroll.cbSize = sizeof(SCROLLINFO);
|
||||||
|
scroll.fMask = SIF_ALL;
|
||||||
|
m_ConOutputEdit.GetScrollInfo(SB_VERT, &scroll);
|
||||||
|
|
||||||
|
m_ConOutputEdit.SetRedraw(FALSE);
|
||||||
|
m_ConOutputEdit.AppendText(m_ConOutputBuffer.ToUTF16().c_str());
|
||||||
|
m_ConOutputEdit.SetRedraw(TRUE);
|
||||||
|
|
||||||
|
if ((scroll.nPage + scroll.nPos) - 1 == (uint32_t)scroll.nMax)
|
||||||
|
{
|
||||||
|
m_ConOutputEdit.ScrollCaret();
|
||||||
|
}
|
||||||
|
|
||||||
|
m_ConOutputBuffer = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,73 +1,12 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "DebuggerUI.h"
|
#include "DebuggerUI.h"
|
||||||
#include "ScriptSystem.h"
|
#include "ScriptSystem.h"
|
||||||
|
#include <Project64/UserInterface/WTLControls/EditConInput.h>
|
||||||
#include <Project64/UserInterface/WTLControls/TooltipDialog.h>
|
#include <Project64/UserInterface/WTLControls/TooltipDialog.h>
|
||||||
|
#include "Debugger-ScriptsAutorun.h"
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
class CScriptList : public CListViewCtrl
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
BEGIN_MSG_MAP_EX(CScriptList)
|
|
||||||
END_MSG_MAP()
|
|
||||||
};
|
|
||||||
|
|
||||||
class CEditEval : public CWindowImpl<CEditEval, CEdit>
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
static const int HISTORY_MAX_ENTRIES = 20;
|
|
||||||
std::vector<std::string> m_History;
|
|
||||||
int m_HistoryIdx;
|
|
||||||
CDebugScripts* m_ScriptWindow;
|
|
||||||
|
|
||||||
public:
|
|
||||||
CEditEval()
|
|
||||||
{
|
|
||||||
m_HistoryIdx = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetScriptWindow(CDebugScripts* scriptWindow)
|
|
||||||
{
|
|
||||||
m_ScriptWindow = scriptWindow;
|
|
||||||
}
|
|
||||||
|
|
||||||
LRESULT OnKeyDown(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
|
|
||||||
|
|
||||||
BOOL Attach(HWND hWndNew)
|
|
||||||
{
|
|
||||||
return SubclassWindow(hWndNew);
|
|
||||||
}
|
|
||||||
|
|
||||||
BEGIN_MSG_MAP_EX(CEditEval)
|
|
||||||
MESSAGE_HANDLER(WM_KEYDOWN, OnKeyDown)
|
|
||||||
END_MSG_MAP()
|
|
||||||
};
|
|
||||||
|
|
||||||
class CEditConsole : public CWindowImpl<CEditEval, CEdit>
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
LRESULT OnKeyDown(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/)
|
|
||||||
{
|
|
||||||
if (GetKeyState(VK_CONTROL) < 0)
|
|
||||||
{
|
|
||||||
if (wParam == 'A')
|
|
||||||
{
|
|
||||||
this->SetSelAll();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
public:
|
|
||||||
BOOL Attach(HWND hWndNew)
|
|
||||||
{
|
|
||||||
return SubclassWindow(hWndNew);
|
|
||||||
}
|
|
||||||
|
|
||||||
BEGIN_MSG_MAP_EX(CEditEval)
|
|
||||||
MESSAGE_HANDLER(WM_KEYDOWN, OnKeyDown)
|
|
||||||
END_MSG_MAP()
|
|
||||||
};
|
|
||||||
|
|
||||||
class CDebugScripts :
|
class CDebugScripts :
|
||||||
public CDebugDialog<CDebugScripts>,
|
public CDebugDialog<CDebugScripts>,
|
||||||
public CDialogResize<CDebugScripts>,
|
public CDialogResize<CDebugScripts>,
|
||||||
|
@ -76,18 +15,34 @@ class CDebugScripts :
|
||||||
private:
|
private:
|
||||||
enum {
|
enum {
|
||||||
WM_REFRESH_LIST = WM_USER + 1,
|
WM_REFRESH_LIST = WM_USER + 1,
|
||||||
WM_CONSOLE_PRINT = WM_USER + 2,
|
WM_SCRIPT_STATUS = WM_USER + 2,
|
||||||
WM_CONSOLE_CLEAR = WM_USER + 3
|
WM_CONSOLE_PRINT = WM_USER + 3,
|
||||||
|
WM_CONSOLE_CLEAR = WM_USER + 4
|
||||||
};
|
};
|
||||||
|
|
||||||
CEditEval m_EvalEdit;
|
enum {
|
||||||
CEditConsole m_ConsoleEdit;
|
CONFLUSH_TIMER_ID = 0,
|
||||||
CScriptList m_ScriptList;
|
CONFLUSH_TIMER_INTERVAL = 50
|
||||||
|
};
|
||||||
|
|
||||||
|
CScriptsAutorunDlg m_AutorunDlg;
|
||||||
|
|
||||||
|
CEditConInput m_ConInputEdit;
|
||||||
|
CEditConOutput m_ConOutputEdit;
|
||||||
|
CListViewCtrl m_ScriptList;
|
||||||
CStatusBarCtrl m_StatusBar;
|
CStatusBarCtrl m_StatusBar;
|
||||||
stdstr m_SelectedScriptName;
|
|
||||||
|
HFONT m_MonoFont, m_MonoBoldFont;
|
||||||
|
|
||||||
stdstr m_InstallDir;
|
stdstr m_InstallDir;
|
||||||
stdstr m_ScriptsDir;
|
stdstr m_ScriptsDir;
|
||||||
|
|
||||||
|
stdstr m_SelectedScriptName;
|
||||||
|
std::vector<wchar_t*> m_InputHistory;
|
||||||
|
size_t m_InputHistoryIndex;
|
||||||
|
|
||||||
|
stdstr m_ConOutputBuffer;
|
||||||
|
|
||||||
HANDLE m_hQuitScriptDirWatchEvent;
|
HANDLE m_hQuitScriptDirWatchEvent;
|
||||||
HANDLE m_hScriptDirWatchThread;
|
HANDLE m_hScriptDirWatchThread;
|
||||||
static DWORD WINAPI ScriptDirWatchProc(void *ctx);
|
static DWORD WINAPI ScriptDirWatchProc(void *ctx);
|
||||||
|
@ -99,28 +54,32 @@ private:
|
||||||
void RefreshStatus();
|
void RefreshStatus();
|
||||||
void ConsoleCopy();
|
void ConsoleCopy();
|
||||||
|
|
||||||
|
void SendInput(const char* name, const char* code);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
enum { IDD = IDD_Debugger_Scripts };
|
enum { IDD = IDD_Debugger_Scripts };
|
||||||
|
|
||||||
CDebugScripts(CDebuggerUI * debugger);
|
CDebugScripts(CDebuggerUI * debugger);
|
||||||
virtual ~CDebugScripts(void);
|
virtual ~CDebugScripts(void);
|
||||||
|
|
||||||
void EvaluateInSelectedInstance(const char* code);
|
|
||||||
void ConsolePrint(const char* text);
|
void ConsolePrint(const char* text);
|
||||||
void ConsoleClear();
|
void ConsoleClear();
|
||||||
void RefreshList();
|
void RefreshList();
|
||||||
|
|
||||||
LRESULT OnInitDialog(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
|
LRESULT OnInitDialog(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
|
||||||
LRESULT OnCtlColorStatic(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
|
LRESULT OnCtlColorStatic(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
|
||||||
|
LRESULT OnCtlColorEdit(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
|
||||||
LRESULT OnDestroy(void);
|
LRESULT OnDestroy(void);
|
||||||
LRESULT OnClicked(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/);
|
LRESULT OnClicked(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/);
|
||||||
LRESULT OnScriptListDblClicked(NMHDR* pNMHDR);
|
LRESULT OnScriptListDblClicked(NMHDR* pNMHDR);
|
||||||
LRESULT OnScriptListRClicked(NMHDR* pNMHDR);
|
LRESULT OnScriptListRClicked(NMHDR* pNMHDR);
|
||||||
LRESULT OnScriptListCustomDraw(NMHDR* pNMHDR);
|
LRESULT OnScriptListCustomDraw(NMHDR* pNMHDR);
|
||||||
LRESULT OnScriptListItemChanged(NMHDR* pNMHDR);
|
LRESULT OnScriptListItemChanged(NMHDR* pNMHDR);
|
||||||
|
LRESULT OnInputSpecialKey(NMHDR* pNMHDR);
|
||||||
void OnExitSizeMove(void);
|
void OnExitSizeMove(void);
|
||||||
|
void OnTimer(UINT_PTR nIDEvent);
|
||||||
|
|
||||||
LRESULT OnConsoleLog(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
|
LRESULT OnConsolePrint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
|
||||||
LRESULT OnConsoleClear(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
|
LRESULT OnConsoleClear(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
|
||||||
LRESULT OnRefreshList(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
|
LRESULT OnRefreshList(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
|
||||||
|
|
||||||
|
@ -128,17 +87,19 @@ public:
|
||||||
COMMAND_CODE_HANDLER(BN_CLICKED, OnClicked)
|
COMMAND_CODE_HANDLER(BN_CLICKED, OnClicked)
|
||||||
MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)
|
MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)
|
||||||
MESSAGE_HANDLER(WM_CTLCOLORSTATIC, OnCtlColorStatic)
|
MESSAGE_HANDLER(WM_CTLCOLORSTATIC, OnCtlColorStatic)
|
||||||
|
MESSAGE_HANDLER(WM_CTLCOLOREDIT, OnCtlColorEdit)
|
||||||
NOTIFY_HANDLER_EX(IDC_SCRIPT_LIST, NM_DBLCLK, OnScriptListDblClicked)
|
NOTIFY_HANDLER_EX(IDC_SCRIPT_LIST, NM_DBLCLK, OnScriptListDblClicked)
|
||||||
NOTIFY_HANDLER_EX(IDC_SCRIPT_LIST, NM_RCLICK, OnScriptListRClicked)
|
NOTIFY_HANDLER_EX(IDC_SCRIPT_LIST, NM_RCLICK, OnScriptListRClicked)
|
||||||
NOTIFY_HANDLER_EX(IDC_SCRIPT_LIST, NM_CUSTOMDRAW, OnScriptListCustomDraw)
|
NOTIFY_HANDLER_EX(IDC_SCRIPT_LIST, NM_CUSTOMDRAW, OnScriptListCustomDraw)
|
||||||
NOTIFY_HANDLER_EX(IDC_SCRIPT_LIST, LVN_ITEMCHANGED, OnScriptListItemChanged)
|
NOTIFY_HANDLER_EX(IDC_SCRIPT_LIST, LVN_ITEMCHANGED, OnScriptListItemChanged)
|
||||||
|
NOTIFY_HANDLER_EX(IDC_EVAL_EDIT, CIN_SPECIALKEY, OnInputSpecialKey)
|
||||||
|
MSG_WM_TIMER(OnTimer)
|
||||||
MSG_WM_DESTROY(OnDestroy)
|
MSG_WM_DESTROY(OnDestroy)
|
||||||
MSG_WM_EXITSIZEMOVE(OnExitSizeMove);
|
MSG_WM_EXITSIZEMOVE(OnExitSizeMove);
|
||||||
MESSAGE_HANDLER(WM_CONSOLE_PRINT, OnConsoleLog)
|
MESSAGE_HANDLER(WM_CONSOLE_PRINT, OnConsolePrint)
|
||||||
MESSAGE_HANDLER(WM_CONSOLE_CLEAR, OnConsoleClear)
|
MESSAGE_HANDLER(WM_CONSOLE_CLEAR, OnConsoleClear)
|
||||||
MESSAGE_HANDLER(WM_REFRESH_LIST, OnRefreshList)
|
MESSAGE_HANDLER(WM_REFRESH_LIST, OnRefreshList)
|
||||||
CHAIN_MSG_MAP(CDialogResize<CDebugScripts>)
|
CHAIN_MSG_MAP(CDialogResize<CDebugScripts>)
|
||||||
CHAIN_MSG_MAP_MEMBER(m_ScriptList)
|
|
||||||
END_MSG_MAP()
|
END_MSG_MAP()
|
||||||
|
|
||||||
BEGIN_DLGRESIZE_MAP(CDebugScripts)
|
BEGIN_DLGRESIZE_MAP(CDebugScripts)
|
||||||
|
@ -151,7 +112,6 @@ public:
|
||||||
DLGRESIZE_CONTROL(IDC_EVAL_LBL, DLSZ_MOVE_Y)
|
DLGRESIZE_CONTROL(IDC_EVAL_LBL, DLSZ_MOVE_Y)
|
||||||
DLGRESIZE_CONTROL(IDC_EVAL_EDIT, DLSZ_SIZE_X | DLSZ_MOVE_Y)
|
DLGRESIZE_CONTROL(IDC_EVAL_EDIT, DLSZ_SIZE_X | DLSZ_MOVE_Y)
|
||||||
DLGRESIZE_CONTROL(IDC_RUN_BTN, DLSZ_MOVE_Y)
|
DLGRESIZE_CONTROL(IDC_RUN_BTN, DLSZ_MOVE_Y)
|
||||||
DLGRESIZE_CONTROL(IDC_STOP_BTN, DLSZ_MOVE_Y)
|
|
||||||
DLGRESIZE_CONTROL(IDC_SCRIPTDIR_BTN, DLSZ_MOVE_Y)
|
DLGRESIZE_CONTROL(IDC_SCRIPTDIR_BTN, DLSZ_MOVE_Y)
|
||||||
DLGRESIZE_CONTROL(IDC_STATUSBAR, DLSZ_SIZE_X | DLSZ_MOVE_Y)
|
DLGRESIZE_CONTROL(IDC_STATUSBAR, DLSZ_SIZE_X | DLSZ_MOVE_Y)
|
||||||
END_DLGRESIZE_MAP()
|
END_DLGRESIZE_MAP()
|
||||||
|
@ -159,8 +119,7 @@ public:
|
||||||
BEGIN_TOOLTIP_MAP()
|
BEGIN_TOOLTIP_MAP()
|
||||||
TOOLTIP(IDC_CLEAR_BTN, "Clear console output")
|
TOOLTIP(IDC_CLEAR_BTN, "Clear console output")
|
||||||
TOOLTIP(IDC_COPY_BTN, "Copy console output to the clipboard")
|
TOOLTIP(IDC_COPY_BTN, "Copy console output to the clipboard")
|
||||||
TOOLTIP(IDC_RUN_BTN, "Run selected script")
|
TOOLTIP(IDC_RUN_BTN, "Toggle selected script")
|
||||||
TOOLTIP(IDC_STOP_BTN, "Stop selected script")
|
|
||||||
TOOLTIP(IDC_SCRIPTDIR_BTN, "Open scripts directory in file explorer")
|
TOOLTIP(IDC_SCRIPTDIR_BTN, "Open scripts directory in file explorer")
|
||||||
END_TOOLTIP_MAP()
|
END_TOOLTIP_MAP()
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,301 @@
|
||||||
|
#include <stdafx.h>
|
||||||
|
#include "DebuggerUI.h"
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
CScriptsAutorunDlg::CScriptsAutorunDlg() :
|
||||||
|
CDialogImpl<CScriptsAutorunDlg>(),
|
||||||
|
m_hQuitScriptDirWatchEvent(nullptr),
|
||||||
|
m_hScriptDirWatchThread(nullptr),
|
||||||
|
m_bScriptListNeedsRefocus(false),
|
||||||
|
m_bAutorunListNeedsRefocus(false),
|
||||||
|
m_Debugger(nullptr),
|
||||||
|
m_ScriptSystem(nullptr)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
CScriptsAutorunDlg::~CScriptsAutorunDlg()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
INT_PTR CScriptsAutorunDlg::DoModal(CDebuggerUI* debugger, stdstr selectedScriptName)
|
||||||
|
{
|
||||||
|
m_Debugger = debugger;
|
||||||
|
m_ScriptSystem = debugger->ScriptSystem();
|
||||||
|
m_InitSelectedScriptName = selectedScriptName;
|
||||||
|
return CDialogImpl<CScriptsAutorunDlg>::DoModal();
|
||||||
|
}
|
||||||
|
|
||||||
|
LRESULT CScriptsAutorunDlg::OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
|
||||||
|
{
|
||||||
|
CenterWindow();
|
||||||
|
|
||||||
|
m_ScriptListView.Attach(GetDlgItem(IDC_SCRIPT_LIST));
|
||||||
|
m_AutorunListView.Attach(GetDlgItem(IDC_AUTORUN_LIST));
|
||||||
|
|
||||||
|
m_ScriptListView.SetExtendedListViewStyle(LVS_EX_FULLROWSELECT | LVS_EX_DOUBLEBUFFER);
|
||||||
|
m_ScriptListView.AddColumn(L"Script", 0);
|
||||||
|
m_ScriptListView.SetColumnWidth(0, LVSCW_AUTOSIZE_USEHEADER);
|
||||||
|
|
||||||
|
m_AutorunListView.SetExtendedListViewStyle(LVS_EX_FULLROWSELECT | LVS_EX_DOUBLEBUFFER);
|
||||||
|
m_AutorunListView.AddColumn(L"Script", 0);
|
||||||
|
m_AutorunListView.SetColumnWidth(0, LVSCW_AUTOSIZE_USEHEADER);
|
||||||
|
|
||||||
|
m_hQuitScriptDirWatchEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
|
||||||
|
m_hScriptDirWatchThread = CreateThread(nullptr, 0, ScriptDirWatchProc, (void*)this, 0, nullptr);
|
||||||
|
|
||||||
|
m_ScriptSystem->LoadAutorunList();
|
||||||
|
|
||||||
|
RefreshAutorunList();
|
||||||
|
RefreshScriptList();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
LRESULT CScriptsAutorunDlg::OnDestroy(void)
|
||||||
|
{
|
||||||
|
SetEvent(m_hQuitScriptDirWatchEvent);
|
||||||
|
WaitForSingleObject(m_hScriptDirWatchThread, INFINITE);
|
||||||
|
CloseHandle(m_hQuitScriptDirWatchEvent);
|
||||||
|
CloseHandle(m_hScriptDirWatchThread);
|
||||||
|
|
||||||
|
m_ScriptListView.Detach();
|
||||||
|
m_AutorunListView.Detach();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
LRESULT CScriptsAutorunDlg::OnOKCancel(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hwnd*/, BOOL& /*bHandled*/)
|
||||||
|
{
|
||||||
|
EndDialog(0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
LRESULT CScriptsAutorunDlg::OnAdd(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hwnd*/, BOOL& /*bHandled*/)
|
||||||
|
{
|
||||||
|
m_bScriptListNeedsRefocus = true;
|
||||||
|
AddSelected();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
LRESULT CScriptsAutorunDlg::OnRemove(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hwnd*/, BOOL& /*bHandled*/)
|
||||||
|
{
|
||||||
|
m_bAutorunListNeedsRefocus = true;
|
||||||
|
RemoveSelected();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
LRESULT CScriptsAutorunDlg::OnScriptListDblClicked(NMHDR* /*pNMHDR*/)
|
||||||
|
{
|
||||||
|
AddSelected();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
LRESULT CScriptsAutorunDlg::OnCtrlSetFocus(NMHDR* pNMHDR)
|
||||||
|
{
|
||||||
|
bool bEnableScriptButtons = false;
|
||||||
|
bool bEnableAutorunButtons = false;
|
||||||
|
|
||||||
|
switch (pNMHDR->idFrom)
|
||||||
|
{
|
||||||
|
case IDC_SCRIPT_LIST:
|
||||||
|
case IDC_ADD_BTN:
|
||||||
|
bEnableScriptButtons = true;
|
||||||
|
bEnableAutorunButtons = false;
|
||||||
|
break;
|
||||||
|
case IDC_AUTORUN_LIST:
|
||||||
|
case IDC_REMOVE_BTN:
|
||||||
|
bEnableAutorunButtons = true;
|
||||||
|
bEnableScriptButtons = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
::EnableWindow(GetDlgItem(IDC_ADD_BTN), bEnableScriptButtons);
|
||||||
|
::EnableWindow(GetDlgItem(IDC_REMOVE_BTN), bEnableAutorunButtons);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
LRESULT CScriptsAutorunDlg::OnAutorunListDblClicked(NMHDR* /*pNMHDR*/)
|
||||||
|
{
|
||||||
|
RemoveSelected();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
LRESULT CScriptsAutorunDlg::OnRefreshScriptList(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
|
||||||
|
{
|
||||||
|
int nSelectedItem = m_ScriptListView.GetSelectedIndex();
|
||||||
|
|
||||||
|
CPath searchPath(m_ScriptSystem->ScriptsDirPath(), "*");
|
||||||
|
|
||||||
|
if (!searchPath.FindFirst(CPath::FIND_ATTRIBUTE_FILES))
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_ScriptListView.SetRedraw(false);
|
||||||
|
m_ScriptListView.DeleteAllItems();
|
||||||
|
|
||||||
|
size_t nItem = 0;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
stdstr scriptFileName = searchPath.GetNameExtension();
|
||||||
|
if (searchPath.GetExtension() == "js" &&
|
||||||
|
m_ScriptSystem->AutorunList().count(scriptFileName) == 0)
|
||||||
|
{
|
||||||
|
m_ScriptListView.AddItem(nItem, 0, scriptFileName.ToUTF16().c_str());
|
||||||
|
if (scriptFileName == m_InitSelectedScriptName)
|
||||||
|
{
|
||||||
|
nSelectedItem = nItem;
|
||||||
|
m_bScriptListNeedsRefocus = true;
|
||||||
|
m_InitSelectedScriptName = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
nItem++;
|
||||||
|
}
|
||||||
|
} while (searchPath.FindNext());
|
||||||
|
|
||||||
|
m_ScriptListView.SetColumnWidth(0, LVSCW_AUTOSIZE_USEHEADER);
|
||||||
|
|
||||||
|
int itemCount = m_ScriptListView.GetItemCount();
|
||||||
|
if (itemCount != 0 && nSelectedItem != -1)
|
||||||
|
{
|
||||||
|
m_ScriptListView.SelectItem(nSelectedItem >= itemCount ? itemCount - 1 : nSelectedItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_bScriptListNeedsRefocus)
|
||||||
|
{
|
||||||
|
m_ScriptListView.SetFocus();
|
||||||
|
m_bScriptListNeedsRefocus = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_ScriptListView.SetRedraw(true);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
LRESULT CScriptsAutorunDlg::OnRefreshAutorunList(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
|
||||||
|
{
|
||||||
|
int nSelectedItem = m_AutorunListView.GetSelectedIndex();
|
||||||
|
|
||||||
|
m_AutorunListView.SetRedraw(FALSE);
|
||||||
|
m_AutorunListView.DeleteAllItems();
|
||||||
|
|
||||||
|
int nItem = 0;
|
||||||
|
std::set<std::string>& scripts = m_ScriptSystem->AutorunList();
|
||||||
|
std::set<std::string>::iterator it;
|
||||||
|
for (it = scripts.begin(); it != scripts.end(); it++)
|
||||||
|
{
|
||||||
|
m_AutorunListView.AddItem(nItem, 0, stdstr(*it).ToUTF16().c_str());
|
||||||
|
if (*it == m_InitSelectedScriptName)
|
||||||
|
{
|
||||||
|
nSelectedItem = nItem;
|
||||||
|
m_bAutorunListNeedsRefocus = true;
|
||||||
|
m_InitSelectedScriptName = "";
|
||||||
|
}
|
||||||
|
nItem++;
|
||||||
|
}
|
||||||
|
|
||||||
|
int itemCount = m_AutorunListView.GetItemCount();
|
||||||
|
if (itemCount != 0 && nSelectedItem != -1)
|
||||||
|
{
|
||||||
|
m_AutorunListView.SelectItem(nSelectedItem >= itemCount ? itemCount - 1 : nSelectedItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_bAutorunListNeedsRefocus)
|
||||||
|
{
|
||||||
|
m_AutorunListView.SetFocus();
|
||||||
|
m_bAutorunListNeedsRefocus = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_AutorunListView.SetRedraw(TRUE);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
DWORD WINAPI CScriptsAutorunDlg::ScriptDirWatchProc(void* ctx)
|
||||||
|
{
|
||||||
|
CScriptsAutorunDlg* _this = (CScriptsAutorunDlg*)ctx;
|
||||||
|
|
||||||
|
stdstr scriptsDir = _this->m_ScriptSystem->ScriptsDirPath();
|
||||||
|
|
||||||
|
HANDLE hEvents[2];
|
||||||
|
hEvents[0] = FindFirstChangeNotification(scriptsDir.ToUTF16().c_str(), FALSE, FILE_NOTIFY_CHANGE_FILE_NAME);
|
||||||
|
|
||||||
|
if (hEvents[0] == INVALID_HANDLE_VALUE)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
hEvents[1] = _this->m_hQuitScriptDirWatchEvent;
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
DWORD status = WaitForMultipleObjects(2, hEvents, FALSE, INFINITE);
|
||||||
|
|
||||||
|
switch (status)
|
||||||
|
{
|
||||||
|
case WAIT_OBJECT_0:
|
||||||
|
if (FindNextChangeNotification(hEvents[0]) == FALSE)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
_this->RefreshScriptList();
|
||||||
|
break;
|
||||||
|
case WAIT_OBJECT_0 + 1:
|
||||||
|
return 0;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CScriptsAutorunDlg::AddSelected()
|
||||||
|
{
|
||||||
|
int nItem = m_ScriptListView.GetSelectedIndex();
|
||||||
|
if (nItem == -1)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
wchar_t scriptName[MAX_PATH];
|
||||||
|
m_ScriptListView.GetItemText(nItem, 0, scriptName, MAX_PATH);
|
||||||
|
m_ScriptSystem->AutorunList().insert(stdstr().FromUTF16(scriptName));
|
||||||
|
m_ScriptSystem->SaveAutorunList();
|
||||||
|
|
||||||
|
RefreshAutorunList();
|
||||||
|
RefreshScriptList();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CScriptsAutorunDlg::RemoveSelected()
|
||||||
|
{
|
||||||
|
int nItem = m_AutorunListView.GetSelectedIndex();
|
||||||
|
if (nItem == -1)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
wchar_t scriptName[MAX_PATH];
|
||||||
|
m_AutorunListView.GetItemText(nItem, 0, scriptName, MAX_PATH);
|
||||||
|
m_ScriptSystem->AutorunList().erase(stdstr().FromUTF16(scriptName));
|
||||||
|
m_ScriptSystem->SaveAutorunList();
|
||||||
|
|
||||||
|
RefreshAutorunList();
|
||||||
|
RefreshScriptList();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CScriptsAutorunDlg::RefreshScriptList()
|
||||||
|
{
|
||||||
|
if (m_hWnd != nullptr)
|
||||||
|
{
|
||||||
|
PostMessage(WM_REFRESH_LIST);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CScriptsAutorunDlg::RefreshAutorunList()
|
||||||
|
{
|
||||||
|
if (m_hWnd != nullptr)
|
||||||
|
{
|
||||||
|
PostMessage(WM_REFRESH_AUTORUN_LIST);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,69 @@
|
||||||
|
#pragma once
|
||||||
|
#include <unordered_set>
|
||||||
|
|
||||||
|
class CScriptsAutorunDlg :
|
||||||
|
public CDialogImpl<CScriptsAutorunDlg>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum { IDD = IDD_Debugger_ScriptsAutorun };
|
||||||
|
|
||||||
|
CScriptsAutorunDlg();
|
||||||
|
virtual ~CScriptsAutorunDlg();
|
||||||
|
|
||||||
|
INT_PTR DoModal(CDebuggerUI* debugger, stdstr selectedScriptName);
|
||||||
|
|
||||||
|
LRESULT OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/);
|
||||||
|
LRESULT OnDestroy(void);
|
||||||
|
LRESULT OnOKCancel(WORD wNotifyCode, WORD wID, HWND hwnd, BOOL& bHandled);
|
||||||
|
LRESULT OnAdd(WORD wNotifyCode, WORD wID, HWND hwnd, BOOL& bHandled);
|
||||||
|
LRESULT OnRemove(WORD wNotifyCode, WORD wID, HWND hwnd, BOOL& bHandled);
|
||||||
|
LRESULT OnScriptListDblClicked(NMHDR* pNMHDR);
|
||||||
|
LRESULT OnAutorunListDblClicked(NMHDR* pNMHDR);
|
||||||
|
LRESULT OnCtrlSetFocus(NMHDR* pNMHDR);
|
||||||
|
LRESULT OnRefreshScriptList(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
|
||||||
|
LRESULT OnRefreshAutorunList(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
|
||||||
|
|
||||||
|
BEGIN_MSG_MAP_EX(CAddBreakpointDlg)
|
||||||
|
MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)
|
||||||
|
MSG_WM_DESTROY(OnDestroy)
|
||||||
|
COMMAND_HANDLER(IDOK, BN_CLICKED, OnOKCancel)
|
||||||
|
COMMAND_HANDLER(IDCANCEL, BN_CLICKED, OnOKCancel)
|
||||||
|
COMMAND_HANDLER(IDC_ADD_BTN, BN_CLICKED, OnAdd)
|
||||||
|
COMMAND_HANDLER(IDC_REMOVE_BTN, BN_CLICKED, OnRemove)
|
||||||
|
NOTIFY_HANDLER_EX(IDC_SCRIPT_LIST, NM_DBLCLK, OnScriptListDblClicked)
|
||||||
|
NOTIFY_HANDLER_EX(IDC_AUTORUN_LIST, NM_DBLCLK, OnAutorunListDblClicked)
|
||||||
|
NOTIFY_CODE_HANDLER_EX(NM_SETFOCUS, OnCtrlSetFocus)
|
||||||
|
MESSAGE_HANDLER(WM_REFRESH_LIST, OnRefreshScriptList)
|
||||||
|
MESSAGE_HANDLER(WM_REFRESH_AUTORUN_LIST, OnRefreshAutorunList)
|
||||||
|
END_MSG_MAP()
|
||||||
|
|
||||||
|
private:
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
WM_REFRESH_LIST = WM_USER + 1,
|
||||||
|
WM_REFRESH_AUTORUN_LIST = WM_USER + 2
|
||||||
|
};
|
||||||
|
|
||||||
|
CDebuggerUI* m_Debugger;
|
||||||
|
CScriptSystem* m_ScriptSystem;
|
||||||
|
|
||||||
|
stdstr m_InitSelectedScriptName;
|
||||||
|
//std::set<std::string> m_AutorunSet;
|
||||||
|
|
||||||
|
bool m_bScriptListNeedsRefocus;
|
||||||
|
bool m_bAutorunListNeedsRefocus;
|
||||||
|
|
||||||
|
CListViewCtrl m_ScriptListView;
|
||||||
|
CListViewCtrl m_AutorunListView;
|
||||||
|
|
||||||
|
HANDLE m_hQuitScriptDirWatchEvent;
|
||||||
|
HANDLE m_hScriptDirWatchThread;
|
||||||
|
static DWORD WINAPI ScriptDirWatchProc(void* ctx);
|
||||||
|
|
||||||
|
void AddSelected();
|
||||||
|
void RemoveSelected();
|
||||||
|
void RefreshScriptList();
|
||||||
|
void RefreshAutorunList();
|
||||||
|
//void LoadAutorunSet();
|
||||||
|
//void SaveAutorunSet();
|
||||||
|
};
|
|
@ -1,10 +1,10 @@
|
||||||
#include "stdafx.h"
|
#include "stdafx.h"
|
||||||
#include "DebuggerUI.h"
|
#include "DebuggerUI.h"
|
||||||
#include "ScriptHook.h"
|
|
||||||
|
|
||||||
#include "CPULog.h"
|
#include "CPULog.h"
|
||||||
#include "DMALog.h"
|
#include "DMALog.h"
|
||||||
#include "Symbols.h"
|
#include "Symbols.h"
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
CPj64Module _Module;
|
CPj64Module _Module;
|
||||||
|
|
||||||
|
@ -41,6 +41,8 @@ CDebuggerUI::CDebuggerUI() :
|
||||||
g_Settings->RegisterChangeCB(Debugger_SteppingOps, this, (CSettings::SettingChangedFunc)SteppingOpsChanged);
|
g_Settings->RegisterChangeCB(Debugger_SteppingOps, this, (CSettings::SettingChangedFunc)SteppingOpsChanged);
|
||||||
g_Settings->RegisterChangeCB(GameRunning_CPU_Running, this, (CSettings::SettingChangedFunc)GameCpuRunningChanged);
|
g_Settings->RegisterChangeCB(GameRunning_CPU_Running, this, (CSettings::SettingChangedFunc)GameCpuRunningChanged);
|
||||||
g_Settings->RegisterChangeCB(Game_GameName, this, (CSettings::SettingChangedFunc)GameNameChanged);
|
g_Settings->RegisterChangeCB(Game_GameName, this, (CSettings::SettingChangedFunc)GameNameChanged);
|
||||||
|
g_Settings->RegisterChangeCB(GameRunning_CPU_Paused, this, (CSettings::SettingChangedFunc)GamePausedChanged);
|
||||||
|
g_Settings->RegisterChangeCB(Debugger_WaitingForStep, this, (CSettings::SettingChangedFunc)WaitingForStepChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
CDebuggerUI::~CDebuggerUI(void)
|
CDebuggerUI::~CDebuggerUI(void)
|
||||||
|
@ -49,6 +51,8 @@ CDebuggerUI::~CDebuggerUI(void)
|
||||||
g_Settings->UnregisterChangeCB(GameRunning_InReset, this, (CSettings::SettingChangedFunc)GameReset);
|
g_Settings->UnregisterChangeCB(GameRunning_InReset, this, (CSettings::SettingChangedFunc)GameReset);
|
||||||
g_Settings->RegisterChangeCB(GameRunning_CPU_Running, this, (CSettings::SettingChangedFunc)GameCpuRunningChanged);
|
g_Settings->RegisterChangeCB(GameRunning_CPU_Running, this, (CSettings::SettingChangedFunc)GameCpuRunningChanged);
|
||||||
g_Settings->UnregisterChangeCB(Game_GameName, this, (CSettings::SettingChangedFunc)GameNameChanged);
|
g_Settings->UnregisterChangeCB(Game_GameName, this, (CSettings::SettingChangedFunc)GameNameChanged);
|
||||||
|
g_Settings->UnregisterChangeCB(GameRunning_CPU_Paused, this, (CSettings::SettingChangedFunc)GamePausedChanged);
|
||||||
|
g_Settings->UnregisterChangeCB(Debugger_WaitingForStep, this, (CSettings::SettingChangedFunc)WaitingForStepChanged);
|
||||||
Debug_Reset();
|
Debug_Reset();
|
||||||
delete m_MemoryView;
|
delete m_MemoryView;
|
||||||
delete m_CommandsView;
|
delete m_CommandsView;
|
||||||
|
@ -70,7 +74,11 @@ void CDebuggerUI::SteppingOpsChanged(CDebuggerUI * _this)
|
||||||
{
|
{
|
||||||
if (g_Settings->LoadBool(Debugger_SteppingOps))
|
if (g_Settings->LoadBool(Debugger_SteppingOps))
|
||||||
{
|
{
|
||||||
_this->OpenCommandWindow();
|
if (!g_Settings->LoadBool(Debugger_SilentBreak))
|
||||||
|
{
|
||||||
|
_this->OpenCommandWindow();
|
||||||
|
}
|
||||||
|
g_Settings->SaveBool(Debugger_SilentBreak, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,15 +99,49 @@ void CDebuggerUI::GameNameChanged(CDebuggerUI * _this)
|
||||||
{
|
{
|
||||||
_this->m_MemorySearch->GameReset();
|
_this->m_MemorySearch->GameReset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JSHookEmuStateChangeEnv env;
|
||||||
|
env.state = JS_EMU_LOADED_ROM;
|
||||||
|
_this->m_ScriptSystem->InvokeAppCallbacks(JS_HOOK_EMUSTATECHANGE, &env);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CDebuggerUI::GamePausedChanged(CDebuggerUI * _this)
|
||||||
|
{
|
||||||
|
JSHookEmuStateChangeEnv env;
|
||||||
|
env.state = g_Settings->LoadBool(GameRunning_CPU_Paused) ? JS_EMU_PAUSED : JS_EMU_RESUMED;
|
||||||
|
_this->m_ScriptSystem->InvokeAppCallbacks(JS_HOOK_EMUSTATECHANGE, &env);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CDebuggerUI::WaitingForStepChanged(CDebuggerUI* _this)
|
||||||
|
{
|
||||||
|
if (g_Settings->LoadBool(Debugger_WaitingForStep))
|
||||||
|
{
|
||||||
|
JSHookEmuStateChangeEnv env;
|
||||||
|
env.state = JS_EMU_DEBUG_PAUSED;
|
||||||
|
_this->ScriptSystem()->InvokeAppCallbacks(JS_HOOK_EMUSTATECHANGE, &env);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
JSHookEmuStateChangeEnv env;
|
||||||
|
env.state = JS_EMU_DEBUG_RESUMED;
|
||||||
|
_this->ScriptSystem()->InvokeAppCallbacks(JS_HOOK_EMUSTATECHANGE, &env);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CDebuggerUI::GameReset(CDebuggerUI * _this)
|
void CDebuggerUI::GameReset(CDebuggerUI * _this)
|
||||||
{
|
{
|
||||||
if (!g_Settings->LoadBool(GameRunning_InReset))
|
if (!g_Settings->LoadBool(GameRunning_InReset))
|
||||||
{
|
{
|
||||||
|
JSHookEmuStateChangeEnv env;
|
||||||
|
env.state = JS_EMU_RESET;
|
||||||
|
_this->m_ScriptSystem->InvokeAppCallbacks(JS_HOOK_EMUSTATECHANGE, &env);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JSHookEmuStateChangeEnv env;
|
||||||
|
env.state = JS_EMU_RESETTING;
|
||||||
|
_this->m_ScriptSystem->InvokeAppCallbacks(JS_HOOK_EMUSTATECHANGE, &env);
|
||||||
|
|
||||||
if (_this->m_CommandsView)
|
if (_this->m_CommandsView)
|
||||||
{
|
{
|
||||||
_this->m_CommandsView->Reset();
|
_this->m_CommandsView->Reset();
|
||||||
|
@ -450,6 +492,11 @@ CSymbolTable* CDebuggerUI::SymbolTable()
|
||||||
return m_SymbolTable;
|
return m_SymbolTable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SyncEvent& CDebuggerUI::StepEvent()
|
||||||
|
{
|
||||||
|
return m_StepEvent;
|
||||||
|
}
|
||||||
|
|
||||||
// CDebugger implementation
|
// CDebugger implementation
|
||||||
|
|
||||||
void CDebuggerUI::TLBChanged()
|
void CDebuggerUI::TLBChanged()
|
||||||
|
@ -552,34 +599,11 @@ void CDebuggerUI::CPUStepStarted()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_ScriptSystem->HaveCallbacks())
|
JSHookCpuStepEnv hookEnv = { 0 };
|
||||||
{
|
hookEnv.pc = pc;
|
||||||
m_ScriptSystem->HookCPUExec()->InvokeByAddressInRange(pc);
|
hookEnv.opInfo = opInfo;
|
||||||
if (SkipOp()) { return; }
|
|
||||||
|
m_ScriptSystem->InvokeAppCallbacks(JS_HOOK_CPUSTEP, (void*)&hookEnv);
|
||||||
m_ScriptSystem->HookCPUExecOpcode()->InvokeByAddressInRange_MaskedOpcode(pc, R4300iOp::m_Opcode.Hex);
|
|
||||||
if (SkipOp()) { return; }
|
|
||||||
|
|
||||||
m_ScriptSystem->HookCPUGPRValue()->InvokeByAddressInRange_GPRValue(pc);
|
|
||||||
if (SkipOp()) { return; }
|
|
||||||
|
|
||||||
if (bStoreOp)
|
|
||||||
{
|
|
||||||
m_ScriptSystem->HookCPUWrite()->InvokeByAddressInRange(storeAddress);
|
|
||||||
if (SkipOp()) { return; }
|
|
||||||
}
|
|
||||||
|
|
||||||
if (opInfo.IsLoadCommand())
|
|
||||||
{
|
|
||||||
m_ScriptSystem->HookCPURead()->InvokeByAddressInRange(opInfo.GetLoadStoreAddress());
|
|
||||||
if (SkipOp()) { return; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bStoreOp && storeAddress == 0xA460000C) // PI_WR_LEN_REG
|
|
||||||
{
|
|
||||||
HandleCartToRamDMA();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (CDebugSettings::ExceptionBreakpoints() != 0)
|
if (CDebugSettings::ExceptionBreakpoints() != 0)
|
||||||
{
|
{
|
||||||
|
@ -665,41 +689,78 @@ void CDebuggerUI::CPUStepEnded()
|
||||||
|
|
||||||
void CDebuggerUI::FrameDrawn()
|
void CDebuggerUI::FrameDrawn()
|
||||||
{
|
{
|
||||||
static HWND hMainWnd = nullptr;
|
//RenderWindow* mainWindow = g_Plugins->MainWindow();
|
||||||
|
//HWND hMainWnd = (HWND)mainWindow->GetWindowHandle();
|
||||||
|
// todo: m_ScriptSystem->InvokeAppCallbacks(JS_HOOK_GFXUPDATE, ...);
|
||||||
|
}
|
||||||
|
|
||||||
static HFONT monoFont = CreateFont(-11, 0, 0, 0,
|
void CDebuggerUI::PIFReadStarted(void)
|
||||||
FW_DONTCARE, FALSE, FALSE, FALSE, DEFAULT_CHARSET,
|
{
|
||||||
OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS,
|
m_ScriptSystem->InvokeAppCallbacks(JS_HOOK_PIFREAD);
|
||||||
PROOF_QUALITY, FF_DONTCARE, L"Consolas"
|
}
|
||||||
);
|
|
||||||
|
|
||||||
if (hMainWnd == nullptr)
|
void CDebuggerUI::RSPReceivedTask(void)
|
||||||
{
|
{
|
||||||
RenderWindow* mainWindow = g_Plugins->MainWindow();
|
JSHookSpTaskEnv env;
|
||||||
|
|
||||||
if (mainWindow == nullptr)
|
DebugLoad_VAddr(0xA4000FC0, env.taskType);
|
||||||
{
|
DebugLoad_VAddr(0xA4000FC4, env.taskFlags);
|
||||||
return;
|
DebugLoad_VAddr(0xA4000FC8, env.ucodeBootAddress);
|
||||||
}
|
DebugLoad_VAddr(0xA4000FCC, env.ucodeBootSize);
|
||||||
|
DebugLoad_VAddr(0xA4000FD0, env.ucodeAddress);
|
||||||
|
DebugLoad_VAddr(0xA4000FD4, env.ucodeSize);
|
||||||
|
DebugLoad_VAddr(0xA4000FD8, env.ucodeDataAddress);
|
||||||
|
DebugLoad_VAddr(0xA4000FDC, env.ucodeDataSize);
|
||||||
|
DebugLoad_VAddr(0xA4000FE0, env.dramStackAddress);
|
||||||
|
DebugLoad_VAddr(0xA4000FE4, env.dramStackSize);
|
||||||
|
DebugLoad_VAddr(0xA4000FE8, env.outputBuffAddress);
|
||||||
|
DebugLoad_VAddr(0xA4000FEC, env.outputBuffSize);
|
||||||
|
DebugLoad_VAddr(0xA4000FF0, env.dataAddress);
|
||||||
|
DebugLoad_VAddr(0xA4000FF4, env.dataSize);
|
||||||
|
DebugLoad_VAddr(0xA4000FF8, env.yieldDataAddress);
|
||||||
|
DebugLoad_VAddr(0xA4000FFC, env.yieldDataSize);
|
||||||
|
|
||||||
hMainWnd = (HWND)mainWindow->GetWindowHandle();
|
m_ScriptSystem->InvokeAppCallbacks(JS_HOOK_RSPTASK, &env);
|
||||||
}
|
}
|
||||||
|
|
||||||
HDC hdc = GetDC(hMainWnd);
|
void CDebuggerUI::PIDMAReadStarted(void)
|
||||||
|
{
|
||||||
|
JSHookPiDmaEnv env;
|
||||||
|
|
||||||
CRect rt;
|
env.direction = 1;
|
||||||
|
DebugLoad_VAddr(0xA4600000, env.dramAddress);
|
||||||
|
DebugLoad_VAddr(0xA4600004, env.cartAddress);
|
||||||
|
DebugLoad_VAddr(0xA4600008, env.length);
|
||||||
|
|
||||||
GetClientRect(hMainWnd, &rt);
|
m_ScriptSystem->InvokeAppCallbacks(JS_HOOK_PIDMA, &env);
|
||||||
SetBkColor(hdc, RGB(0, 0, 0));
|
}
|
||||||
|
|
||||||
SelectObject(hdc, monoFont);
|
void CDebuggerUI::PIDMAWriteStarted(void)
|
||||||
SetTextColor(hdc, RGB(255, 255, 255));
|
{
|
||||||
SetBkColor(hdc, RGB(0, 0, 0));
|
JSHookPiDmaEnv env;
|
||||||
|
|
||||||
m_ScriptSystem->SetScreenDC(hdc);
|
env.direction = 0;
|
||||||
m_ScriptSystem->HookFrameDrawn()->InvokeAll();
|
DebugLoad_VAddr(0xA4600000, env.dramAddress);
|
||||||
|
DebugLoad_VAddr(0xA4600004, env.cartAddress);
|
||||||
|
DebugLoad_VAddr(0xA460000C, env.length);
|
||||||
|
|
||||||
ReleaseDC(hMainWnd, hdc);
|
m_ScriptSystem->InvokeAppCallbacks(JS_HOOK_PIDMA, &env);
|
||||||
|
|
||||||
|
HandleCartToRamDMA();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CDebuggerUI::EmulationStarted(void)
|
||||||
|
{
|
||||||
|
JSHookEmuStateChangeEnv env;
|
||||||
|
env.state = JS_EMU_STARTED;
|
||||||
|
m_ScriptSystem->InvokeAppCallbacks(JS_HOOK_EMUSTATECHANGE, &env);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CDebuggerUI::EmulationStopped(void)
|
||||||
|
{
|
||||||
|
JSHookEmuStateChangeEnv env;
|
||||||
|
env.state = JS_EMU_STOPPED;
|
||||||
|
m_ScriptSystem->InvokeAppCallbacks(JS_HOOK_EMUSTATECHANGE, &env);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CDebuggerUI::WaitForStep(void)
|
void CDebuggerUI::WaitForStep(void)
|
||||||
|
@ -709,6 +770,16 @@ void CDebuggerUI::WaitForStep(void)
|
||||||
g_Settings->SaveBool(Debugger_WaitingForStep, false);
|
g_Settings->SaveBool(Debugger_WaitingForStep, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CDebuggerUI::StartAutorunScripts(void)
|
||||||
|
{
|
||||||
|
if (m_ScriptSystem == nullptr)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_ScriptSystem->ExecAutorunList();
|
||||||
|
}
|
||||||
|
|
||||||
bool CDebuggerUI::ExecutionBP(uint32_t address)
|
bool CDebuggerUI::ExecutionBP(uint32_t address)
|
||||||
{
|
{
|
||||||
return m_Breakpoints != nullptr && m_Breakpoints->ExecutionBPExists(address, true) != CBreakpoints::BP_NOT_SET;
|
return m_Breakpoints != nullptr && m_Breakpoints->ExecutionBPExists(address, true) != CBreakpoints::BP_NOT_SET;
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include "Debugger-TLB.h"
|
#include "Debugger-TLB.h"
|
||||||
#include "Debugger-Commands.h"
|
#include "Debugger-Commands.h"
|
||||||
#include "Debugger-Scripts.h"
|
#include "Debugger-Scripts.h"
|
||||||
|
#include "Debugger-ScriptsAutorun.h"
|
||||||
#include "Debugger-Symbols.h"
|
#include "Debugger-Symbols.h"
|
||||||
#include "Debugger-AddBreakpoint.h"
|
#include "Debugger-AddBreakpoint.h"
|
||||||
#include "Debugger-AddSymbol.h"
|
#include "Debugger-AddSymbol.h"
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
#include <stdafx.h>
|
||||||
|
#include "JSIntervalWorker.h"
|
||||||
|
#include "../ScriptInstance.h"
|
||||||
|
#include "ScriptAPI.h"
|
||||||
|
|
||||||
|
CJSIntervalWorker::CJSIntervalWorker(CScriptInstance* inst, void* dukObjectHeapPtr, int delayMS, bool bOnce) :
|
||||||
|
CScriptWorker(inst, dukObjectHeapPtr),
|
||||||
|
m_DelayMS(delayMS),
|
||||||
|
m_bOnce(bOnce)
|
||||||
|
{
|
||||||
|
m_hTimerQuitEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
CJSIntervalWorker::~CJSIntervalWorker()
|
||||||
|
{
|
||||||
|
StopWorkerProc();
|
||||||
|
CloseHandle(m_hTimerQuitEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CJSIntervalWorker::WorkerProc()
|
||||||
|
{
|
||||||
|
HANDLE hTimer = CreateWaitableTimer(nullptr, false, nullptr);
|
||||||
|
|
||||||
|
LARGE_INTEGER liTime;
|
||||||
|
liTime.QuadPart = -m_DelayMS * 10000;
|
||||||
|
SetWaitableTimer(hTimer, &liTime, m_DelayMS, nullptr, nullptr, true);
|
||||||
|
|
||||||
|
HANDLE hWaitHandles[] = { hTimer, m_hTimerQuitEvent };
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
DWORD nHandle = WaitForMultipleObjects(2, hWaitHandles, FALSE, INFINITE);
|
||||||
|
|
||||||
|
if (nHandle == WAIT_OBJECT_0)
|
||||||
|
{
|
||||||
|
m_Instance->PostCMethodCall(m_DukObjectHeapPtr, ScriptAPI::js__IntervalContext_invokeFunc);
|
||||||
|
|
||||||
|
if (m_bOnce)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CloseHandle(hTimer);
|
||||||
|
m_Instance->PostCMethodCall(m_DukObjectHeapPtr, ScriptAPI::js__IntervalContext_remove);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CJSIntervalWorker::StopWorkerProc()
|
||||||
|
{
|
||||||
|
SetEvent(m_hTimerQuitEvent);
|
||||||
|
CScriptWorker::StopWorkerProc();
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
#include "../ScriptWorker.h"
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
class CJSIntervalWorker : public CScriptWorker {
|
||||||
|
private:
|
||||||
|
int m_DelayMS;
|
||||||
|
bool m_bOnce;
|
||||||
|
HANDLE m_hTimerQuitEvent;
|
||||||
|
public:
|
||||||
|
CJSIntervalWorker(CScriptInstance* inst, void* dukObjectHeapPtr, int delayMS, bool bOnce);
|
||||||
|
virtual ~CJSIntervalWorker();
|
||||||
|
|
||||||
|
virtual void WorkerProc();
|
||||||
|
virtual void StopWorkerProc();
|
||||||
|
};
|
|
@ -0,0 +1,244 @@
|
||||||
|
#include <stdafx.h>
|
||||||
|
#include "JSServerWorker.h"
|
||||||
|
#include "JSSocketWorker.h"
|
||||||
|
|
||||||
|
CJSServerWorker::CJSServerWorker(CScriptInstance* instance, void* dukObjectHeapPtr) :
|
||||||
|
CScriptWorker(instance, dukObjectHeapPtr),
|
||||||
|
m_bWinsockOK(false),
|
||||||
|
m_ServerSocket(INVALID_SOCKET)
|
||||||
|
{
|
||||||
|
WSADATA wsaData;
|
||||||
|
m_bWinsockOK = (WSAStartup(MAKEWORD(2, 2), &wsaData) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
CJSServerWorker::~CJSServerWorker()
|
||||||
|
{
|
||||||
|
StopWorkerProc();
|
||||||
|
|
||||||
|
if (m_bWinsockOK)
|
||||||
|
{
|
||||||
|
WSACleanup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CJSServerWorker::Init(const char* address, unsigned short port)
|
||||||
|
{
|
||||||
|
m_Queue.listenAddress = address;
|
||||||
|
m_Queue.listenPort = port;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CJSServerWorker::WorkerProc()
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
union {
|
||||||
|
SOCKADDR service;
|
||||||
|
SOCKADDR_IN service_ipv4;
|
||||||
|
SOCKADDR_IN6 service_ipv6;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (m_ServerSocket != INVALID_SOCKET)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inet_pton(AF_INET, m_Queue.listenAddress.c_str(), &service_ipv4.sin_addr) == 1)
|
||||||
|
{
|
||||||
|
service_ipv4.sin_family = AF_INET;
|
||||||
|
service_ipv4.sin_port = htons(m_Queue.listenPort);
|
||||||
|
}
|
||||||
|
else if (inet_pton(AF_INET6, m_Queue.listenAddress.c_str(), &service_ipv6.sin6_addr) == 1)
|
||||||
|
{
|
||||||
|
service_ipv6.sin6_family = AF_INET6;
|
||||||
|
service_ipv6.sin6_port = htons(m_Queue.listenPort);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
JSEmitError("invalid address");
|
||||||
|
goto stop_cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_ServerSocket = socket(service.sa_family, SOCK_STREAM, IPPROTO_TCP);
|
||||||
|
if (m_ServerSocket == INVALID_SOCKET)
|
||||||
|
{
|
||||||
|
JSEmitError("failed to initialize server socket");
|
||||||
|
goto stop_cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
ULONG nonBlock = 1;
|
||||||
|
rc = ioctlsocket(m_ServerSocket, FIONBIO, &nonBlock);
|
||||||
|
if (rc == SOCKET_ERROR)
|
||||||
|
{
|
||||||
|
JSEmitError("ioctlsocket() FIONBIO error");
|
||||||
|
goto stop_cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = ::bind(m_ServerSocket, (const SOCKADDR*)&service,
|
||||||
|
service.sa_family == AF_INET ? sizeof(service_ipv4) : sizeof(service_ipv6));
|
||||||
|
|
||||||
|
if (rc == SOCKET_ERROR)
|
||||||
|
{
|
||||||
|
JSEmitError(stdstr_f("bind() error (%u)", WSAGetLastError()).c_str());
|
||||||
|
goto stop_cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = listen(m_ServerSocket, SOMAXCONN);
|
||||||
|
|
||||||
|
if (rc == SOCKET_ERROR)
|
||||||
|
{
|
||||||
|
JSEmitError("listen() error");
|
||||||
|
goto stop_cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
CGuard guard(m_CS);
|
||||||
|
strncpy(m_Address.address, m_Queue.listenAddress.c_str(), sizeof(m_Address.address));
|
||||||
|
m_Address.port = m_Queue.listenPort;
|
||||||
|
m_Address.family = service.sa_family == AF_INET ? "IPv4" : "IPv6";
|
||||||
|
}
|
||||||
|
|
||||||
|
JSEmitListening();
|
||||||
|
|
||||||
|
TIMEVAL timeout;
|
||||||
|
timeout.tv_sec = 0;
|
||||||
|
timeout.tv_usec = TIMEOUT_MS * 1000;
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
if (StopRequested())
|
||||||
|
{
|
||||||
|
goto stop_cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
CGuard guard(m_Queue.cs);
|
||||||
|
if (m_Queue.bClosePending)
|
||||||
|
{
|
||||||
|
goto stop_cleanup;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fd_set readFds;
|
||||||
|
FD_ZERO(&readFds);
|
||||||
|
FD_SET(m_ServerSocket, &readFds);
|
||||||
|
|
||||||
|
int numFds = select(0, &readFds, nullptr, nullptr, &timeout);
|
||||||
|
|
||||||
|
if (numFds == 0)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (numFds == SOCKET_ERROR)
|
||||||
|
{
|
||||||
|
goto stop_cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (numFds > 0 && FD_ISSET(m_ServerSocket, &readFds))
|
||||||
|
{
|
||||||
|
SOCKET clientSocket = accept(m_ServerSocket, nullptr, nullptr);
|
||||||
|
JSEmitConnection(clientSocket);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stop_cleanup:
|
||||||
|
{
|
||||||
|
CGuard guard(m_CS);
|
||||||
|
strncpy(m_Address.address, "", sizeof(m_Address.address));
|
||||||
|
m_Address.port = 0;
|
||||||
|
m_Address.family = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_ServerSocket != INVALID_SOCKET)
|
||||||
|
{
|
||||||
|
closesocket(m_ServerSocket);
|
||||||
|
m_ServerSocket = INVALID_SOCKET;
|
||||||
|
JSEmitClose();
|
||||||
|
}
|
||||||
|
|
||||||
|
m_Instance->PostCMethodCall(m_DukObjectHeapPtr, ScriptAPI::js__UnrefObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string CJSServerWorker::GetAddress()
|
||||||
|
{
|
||||||
|
CGuard guard(m_CS);
|
||||||
|
return m_Address.address;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned short CJSServerWorker::GetPort()
|
||||||
|
{
|
||||||
|
CGuard guard(m_CS);
|
||||||
|
return m_Address.port;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* CJSServerWorker::GetFamily()
|
||||||
|
{
|
||||||
|
CGuard guard(m_CS);
|
||||||
|
return m_Address.family;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CJSServerWorker::JSEmitConnection(SOCKET c)
|
||||||
|
{
|
||||||
|
m_Instance->PostCMethodCall(m_DukObjectHeapPtr, ScriptAPI::js__Emitter_emit,
|
||||||
|
CbArgs_EmitConnection, (void*)&c, sizeof(c));
|
||||||
|
}
|
||||||
|
|
||||||
|
void CJSServerWorker::JSEmitClose()
|
||||||
|
{
|
||||||
|
m_Instance->PostCMethodCall(m_DukObjectHeapPtr, ScriptAPI::js__Emitter_emit,
|
||||||
|
CbArgs_EmitClose);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CJSServerWorker::JSEmitListening()
|
||||||
|
{
|
||||||
|
m_Instance->PostCMethodCall(m_DukObjectHeapPtr, ScriptAPI::js__Emitter_emit,
|
||||||
|
CbArgs_EmitListening);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CJSServerWorker::JSEmitError(const char* errMessage)
|
||||||
|
{
|
||||||
|
m_Instance->PostCMethodCall(m_DukObjectHeapPtr, ScriptAPI::js__Emitter_emit,
|
||||||
|
CbArgs_EmitError, (void*)errMessage, strlen(errMessage) + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_idx_t CJSServerWorker::CbArgs_EmitConnection(duk_context* ctx, void* _env)
|
||||||
|
{
|
||||||
|
duk_push_string(ctx, "connection");
|
||||||
|
|
||||||
|
SOCKET client = *(SOCKET*)_env;
|
||||||
|
duk_push_global_object(ctx);
|
||||||
|
duk_get_prop_string(ctx, -1, "Socket");
|
||||||
|
duk_remove(ctx, -2);
|
||||||
|
duk_pnew(ctx, 0);
|
||||||
|
|
||||||
|
ScriptAPI::RefObject(ctx, -1);
|
||||||
|
|
||||||
|
duk_get_prop_string(ctx, -1, HS_socketWorkerPtr);
|
||||||
|
CJSSocketWorker* socketWorker = (CJSSocketWorker*)duk_get_pointer(ctx, -1);
|
||||||
|
duk_pop(ctx);
|
||||||
|
|
||||||
|
socketWorker->Init(client);
|
||||||
|
socketWorker->StartWorkerProc();
|
||||||
|
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_idx_t CJSServerWorker::CbArgs_EmitClose(duk_context* ctx, void* /*_env*/)
|
||||||
|
{
|
||||||
|
duk_push_string(ctx, "close");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_idx_t CJSServerWorker::CbArgs_EmitListening(duk_context* ctx, void* /*_env*/)
|
||||||
|
{
|
||||||
|
duk_push_string(ctx, "listening");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_idx_t CJSServerWorker::CbArgs_EmitError(duk_context* ctx, void* _env)
|
||||||
|
{
|
||||||
|
const char* errMessage = (const char*)_env;
|
||||||
|
duk_push_string(ctx, "error");
|
||||||
|
duk_push_error_object(ctx, DUK_ERR_ERROR, errMessage);
|
||||||
|
return 2;
|
||||||
|
}
|
|
@ -0,0 +1,60 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <winsock2.h>
|
||||||
|
#include <ws2tcpip.h>
|
||||||
|
#include <windows.h>
|
||||||
|
#include "ScriptAPI.h"
|
||||||
|
|
||||||
|
class CJSServerWorker : public CScriptWorker
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
enum { TIMEOUT_MS = 1 };
|
||||||
|
|
||||||
|
struct ServerQueue {
|
||||||
|
CriticalSection cs;
|
||||||
|
std::string listenAddress;
|
||||||
|
unsigned short listenPort;
|
||||||
|
bool bClosePending;
|
||||||
|
ServerQueue() : listenAddress(""), listenPort(0), bClosePending(false) {}
|
||||||
|
} m_Queue;
|
||||||
|
|
||||||
|
struct JSServerAddrInfo
|
||||||
|
{
|
||||||
|
char address[INET6_ADDRSTRLEN];
|
||||||
|
const char* family;
|
||||||
|
unsigned short port;
|
||||||
|
|
||||||
|
JSServerAddrInfo() :
|
||||||
|
address(""),
|
||||||
|
family(""),
|
||||||
|
port(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
bool m_bWinsockOK;
|
||||||
|
SOCKET m_ServerSocket;
|
||||||
|
JSServerAddrInfo m_Address;
|
||||||
|
|
||||||
|
public:
|
||||||
|
CJSServerWorker(CScriptInstance* instance, void* dukObjectHeapPtr);
|
||||||
|
virtual ~CJSServerWorker();
|
||||||
|
|
||||||
|
void Init(const char* address, unsigned short port);
|
||||||
|
void WorkerProc();
|
||||||
|
|
||||||
|
std::string GetAddress();
|
||||||
|
unsigned short GetPort();
|
||||||
|
const char* GetFamily();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void JSEmitConnection(SOCKET c);
|
||||||
|
void JSEmitClose();
|
||||||
|
void JSEmitListening();
|
||||||
|
void JSEmitError(const char* errMessage);
|
||||||
|
|
||||||
|
static duk_idx_t CbArgs_EmitConnection(duk_context* ctx, void* _env);
|
||||||
|
static duk_idx_t CbArgs_EmitClose(duk_context* ctx, void* _env);
|
||||||
|
static duk_idx_t CbArgs_EmitListening(duk_context* ctx, void* _env);
|
||||||
|
static duk_idx_t CbArgs_EmitError(duk_context* ctx, void* _env);
|
||||||
|
};
|
|
@ -0,0 +1,597 @@
|
||||||
|
#include <stdafx.h>
|
||||||
|
#include "JSSocketWorker.h"
|
||||||
|
#include "ScriptAPI.h"
|
||||||
|
|
||||||
|
CJSSocketWorker::CJSSocketWorker(CScriptInstance* inst, void* objectHeapPtr, bool bAllowHalfOpen) :
|
||||||
|
CScriptWorker(inst, objectHeapPtr),
|
||||||
|
m_Socket(INVALID_SOCKET),
|
||||||
|
m_bAllowHalfOpen(bAllowHalfOpen),
|
||||||
|
m_bWinsockOK(false)
|
||||||
|
{
|
||||||
|
WSADATA wsaData;
|
||||||
|
m_bWinsockOK = (WSAStartup(MAKEWORD(2, 2), &wsaData) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
CJSSocketWorker::~CJSSocketWorker()
|
||||||
|
{
|
||||||
|
StopWorkerProc();
|
||||||
|
|
||||||
|
if (m_bWinsockOK)
|
||||||
|
{
|
||||||
|
WSACleanup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CJSSocketWorker::Init(SOCKET sock)
|
||||||
|
{
|
||||||
|
if (!m_bWinsockOK)
|
||||||
|
{
|
||||||
|
JSEmitError("WSAStartup() error");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_hThread != nullptr)
|
||||||
|
{
|
||||||
|
JSEmitError("invalid state - socket is already active");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_Socket = sock;
|
||||||
|
|
||||||
|
UpdateAddresses();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CJSSocketWorker::Init(const char* host, unsigned short port)
|
||||||
|
{
|
||||||
|
if (!m_bWinsockOK)
|
||||||
|
{
|
||||||
|
JSEmitError("WSAStartup() error");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_hThread != nullptr)
|
||||||
|
{
|
||||||
|
JSEmitError("invalid state - socket is already active");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_Queue.connectHost = host;
|
||||||
|
m_Queue.connectPort = port;
|
||||||
|
m_Queue.bConnectPending = true;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CJSSocketWorker::Write(const char* data, size_t length, duk_int_t callbackId, bool bEnd)
|
||||||
|
{
|
||||||
|
CGuard guard(m_Queue.cs);
|
||||||
|
|
||||||
|
if (m_Queue.bFullClosePending ||
|
||||||
|
m_Queue.bSendClosePending ||
|
||||||
|
m_Queue.bSendClosed)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(bEnd)
|
||||||
|
{
|
||||||
|
m_Queue.bSendClosePending = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
BufferedWrite bufferedWrite;
|
||||||
|
bufferedWrite.offset = 0;
|
||||||
|
bufferedWrite.callbackId = callbackId;
|
||||||
|
|
||||||
|
if (length != 0)
|
||||||
|
{
|
||||||
|
bufferedWrite.data.resize(length);
|
||||||
|
memcpy(&bufferedWrite.data[0], data, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_Queue.writes.push_back(bufferedWrite);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CJSSocketWorker::WorkerProc()
|
||||||
|
{
|
||||||
|
TIMEVAL timeout;
|
||||||
|
timeout.tv_sec = 0;
|
||||||
|
timeout.tv_usec = TIMEOUT_MS * 1000;
|
||||||
|
|
||||||
|
bool bWritable = false;
|
||||||
|
bool bConnectPending = false;
|
||||||
|
bool bWritesPending = false;
|
||||||
|
bool bRecvClosed = false;
|
||||||
|
|
||||||
|
{
|
||||||
|
CGuard guard(m_Queue.cs);
|
||||||
|
bConnectPending = m_Queue.bConnectPending;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!bConnectPending)
|
||||||
|
{
|
||||||
|
// assume it's already writable
|
||||||
|
bWritable = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bConnectPending && ProcConnect())
|
||||||
|
{
|
||||||
|
bConnectPending = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
if (StopRequested())
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
CGuard guard(m_Queue.cs);
|
||||||
|
bWritesPending = m_Queue.writes.size() > 0;
|
||||||
|
bRecvClosed = m_Queue.bRecvClosed;
|
||||||
|
|
||||||
|
if (m_Queue.bFullClosePending && !bWritesPending)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bRecvClosed && !bWritesPending)
|
||||||
|
{
|
||||||
|
// nothing to do
|
||||||
|
Sleep(TIMEOUT_MS);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int numFds;
|
||||||
|
fd_set readFds, writeFds;
|
||||||
|
fd_set* pWriteFds = nullptr;
|
||||||
|
fd_set* pReadFds = nullptr;
|
||||||
|
|
||||||
|
if (!bRecvClosed)
|
||||||
|
{
|
||||||
|
pReadFds = &readFds;
|
||||||
|
FD_ZERO(pReadFds);
|
||||||
|
FD_SET(m_Socket, pReadFds);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bWritesPending || !bWritable)
|
||||||
|
{
|
||||||
|
pWriteFds = &writeFds;
|
||||||
|
FD_ZERO(pWriteFds);
|
||||||
|
FD_SET(m_Socket, pWriteFds);
|
||||||
|
}
|
||||||
|
|
||||||
|
numFds = select(0, pReadFds, pWriteFds, nullptr, &timeout);
|
||||||
|
if (numFds == SOCKET_ERROR)
|
||||||
|
{
|
||||||
|
JSEmitError("select() error");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (numFds == 0)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pWriteFds && FD_ISSET(m_Socket, pWriteFds))
|
||||||
|
{
|
||||||
|
if (!bWritable)
|
||||||
|
{
|
||||||
|
bWritable = true;
|
||||||
|
JSEmitConnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bWritesPending)
|
||||||
|
{
|
||||||
|
ProcSendData();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pReadFds && FD_ISSET(m_Socket, pReadFds))
|
||||||
|
{
|
||||||
|
ProcRecvData();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
closesocket(m_Socket);
|
||||||
|
JSEmitClose();
|
||||||
|
ClearAddress();
|
||||||
|
ClearQueue();
|
||||||
|
m_Socket = INVALID_SOCKET;
|
||||||
|
|
||||||
|
m_Instance->PostCMethodCall(m_DukObjectHeapPtr, ScriptAPI::js__UnrefObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CJSSocketWorker::ProcConnect()
|
||||||
|
{
|
||||||
|
stdstr strPort, strHost;
|
||||||
|
|
||||||
|
{
|
||||||
|
CGuard guard(m_Queue.cs);
|
||||||
|
strPort = stdstr_f("%d", m_Queue.connectPort);
|
||||||
|
strHost = m_Queue.connectHost;
|
||||||
|
m_Queue.bConnectPending = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct addrinfo hints, *result, *ptr;
|
||||||
|
ZeroMemory(&hints, sizeof(hints));
|
||||||
|
hints.ai_family = AF_UNSPEC;
|
||||||
|
hints.ai_socktype = SOCK_STREAM;
|
||||||
|
hints.ai_protocol = IPPROTO_TCP;
|
||||||
|
|
||||||
|
int rc;
|
||||||
|
rc = getaddrinfo(m_Queue.connectHost.c_str(), strPort.c_str(), &hints, &result);
|
||||||
|
|
||||||
|
if (rc != 0)
|
||||||
|
{
|
||||||
|
JSEmitError("getaddrinfo() error");
|
||||||
|
JSEmitLookup(m_RemoteAddress); // port=0, err
|
||||||
|
freeaddrinfo(result);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (ptr = result; ptr != nullptr; ptr = ptr->ai_next)
|
||||||
|
{
|
||||||
|
m_Socket = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol);
|
||||||
|
if (m_Socket == INVALID_SOCKET)
|
||||||
|
{
|
||||||
|
JSEmitError("socket() error");
|
||||||
|
freeaddrinfo(result);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = connect(m_Socket, ptr->ai_addr, (int)ptr->ai_addrlen);
|
||||||
|
|
||||||
|
if (rc != SOCKET_ERROR)
|
||||||
|
{
|
||||||
|
ULONG nonBlock = 1;
|
||||||
|
rc = ioctlsocket(m_Socket, FIONBIO, &nonBlock);
|
||||||
|
if (rc == SOCKET_ERROR)
|
||||||
|
{
|
||||||
|
JSEmitError("ioctlsocket() FIONBIO error");
|
||||||
|
closesocket(m_Socket);
|
||||||
|
freeaddrinfo(result);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateAddresses();
|
||||||
|
JSEmitLookup(m_RemoteAddress);
|
||||||
|
freeaddrinfo(result);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
closesocket(m_Socket);
|
||||||
|
m_Socket = INVALID_SOCKET;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CJSSocketWorker::ProcSendData()
|
||||||
|
{
|
||||||
|
CGuard guard(m_Queue.cs);
|
||||||
|
BufferedWrite& bufferedWrite = m_Queue.writes.front();
|
||||||
|
|
||||||
|
int avail = bufferedWrite.data.size() - bufferedWrite.offset;
|
||||||
|
int numBytesSent = send(m_Socket, &bufferedWrite.data[bufferedWrite.offset], avail, 0);
|
||||||
|
|
||||||
|
if (numBytesSent >= 0)
|
||||||
|
{
|
||||||
|
bufferedWrite.offset += numBytesSent;
|
||||||
|
if (bufferedWrite.offset == bufferedWrite.data.size())
|
||||||
|
{
|
||||||
|
if (bufferedWrite.callbackId != -1)
|
||||||
|
{
|
||||||
|
m_Instance->PostCMethodCall(m_DukObjectHeapPtr, ScriptAPI::js_Socket__invokeWriteCallback,
|
||||||
|
CbArgs_Write, &bufferedWrite.callbackId, sizeof(bufferedWrite.callbackId));
|
||||||
|
}
|
||||||
|
m_Queue.writes.erase(m_Queue.writes.begin());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_Queue.writes.size() == 0)
|
||||||
|
{
|
||||||
|
JSEmitDrain();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
JSEmitError("send() error");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_Queue.writes.size() == 0 &&
|
||||||
|
m_Queue.bSendClosePending)
|
||||||
|
{
|
||||||
|
shutdown(m_Socket, SD_SEND);
|
||||||
|
m_Queue.bSendClosePending = false;
|
||||||
|
m_Queue.bSendClosed = true;
|
||||||
|
|
||||||
|
m_Instance->PostCMethodCall(m_DukObjectHeapPtr, ScriptAPI::js_Socket__invokeWriteEndCallbacks);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CJSSocketWorker::ProcRecvData()
|
||||||
|
{
|
||||||
|
char recvBuffer[8192];
|
||||||
|
int numBytesReceived = recv(m_Socket, recvBuffer, sizeof(recvBuffer), 0);
|
||||||
|
|
||||||
|
if (numBytesReceived > 0)
|
||||||
|
{
|
||||||
|
JSEmitData(recvBuffer, numBytesReceived);
|
||||||
|
}
|
||||||
|
else if (numBytesReceived == 0)
|
||||||
|
{
|
||||||
|
JSEmitEnd();
|
||||||
|
|
||||||
|
CGuard guard(m_Queue.cs);
|
||||||
|
if (!m_bAllowHalfOpen)
|
||||||
|
{
|
||||||
|
m_Queue.bFullClosePending = true;
|
||||||
|
}
|
||||||
|
m_Queue.bRecvClosed = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
JSEmitError("recv() error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CJSSocketWorker::UpdateAddresses()
|
||||||
|
{
|
||||||
|
CGuard guard(m_CS);
|
||||||
|
|
||||||
|
WSAPROTOCOL_INFO protocolInfo;
|
||||||
|
int protocolInfoSize = sizeof(protocolInfo);
|
||||||
|
|
||||||
|
if (getsockopt(m_Socket, SOL_SOCKET, SO_PROTOCOL_INFO, (char*)&protocolInfo, &protocolInfoSize) != 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int& family = protocolInfo.iAddressFamily;
|
||||||
|
sockaddr* pLocalAddr = nullptr;
|
||||||
|
sockaddr* pRemoteAddr = nullptr;
|
||||||
|
|
||||||
|
union {
|
||||||
|
sockaddr_in ipv4;
|
||||||
|
sockaddr_in6 ipv6;
|
||||||
|
} localAddr;
|
||||||
|
|
||||||
|
union {
|
||||||
|
sockaddr_in ipv4;
|
||||||
|
sockaddr_in6 ipv6;
|
||||||
|
} remoteAddr;
|
||||||
|
|
||||||
|
int addrSize = 0;
|
||||||
|
|
||||||
|
if (family == AF_INET)
|
||||||
|
{
|
||||||
|
pLocalAddr = (sockaddr*)&localAddr.ipv4;
|
||||||
|
pRemoteAddr = (sockaddr*)&remoteAddr.ipv4;
|
||||||
|
addrSize = sizeof(sockaddr_in);
|
||||||
|
}
|
||||||
|
else if (family == AF_INET6)
|
||||||
|
{
|
||||||
|
pLocalAddr = (sockaddr*)&localAddr.ipv6;
|
||||||
|
pRemoteAddr = (sockaddr*)&remoteAddr.ipv6;
|
||||||
|
addrSize = sizeof(sockaddr_in6);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
g_Notify->BreakPoint(__FILE__, __LINE__);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
getsockname(m_Socket, pLocalAddr, &addrSize);
|
||||||
|
getpeername(m_Socket, pRemoteAddr, &addrSize);
|
||||||
|
|
||||||
|
getnameinfo(pLocalAddr, addrSize,
|
||||||
|
m_LocalAddress.address, sizeof(m_LocalAddress.address),
|
||||||
|
0, 0, NI_NUMERICHOST);
|
||||||
|
|
||||||
|
getnameinfo(pRemoteAddr, addrSize,
|
||||||
|
m_RemoteAddress.address, sizeof(m_RemoteAddress.address),
|
||||||
|
0, 0, NI_NUMERICHOST);
|
||||||
|
|
||||||
|
if (family == AF_INET)
|
||||||
|
{
|
||||||
|
m_LocalAddress.port = ntohs(localAddr.ipv4.sin_port);
|
||||||
|
m_LocalAddress.family = "IPv4";
|
||||||
|
|
||||||
|
m_RemoteAddress.port = ntohs(remoteAddr.ipv4.sin_port);
|
||||||
|
m_RemoteAddress.family = "IPv4";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_LocalAddress.port = ntohs(localAddr.ipv6.sin6_port);
|
||||||
|
m_LocalAddress.family = "IPv6";
|
||||||
|
|
||||||
|
m_RemoteAddress.port = ntohs(remoteAddr.ipv6.sin6_port);
|
||||||
|
m_RemoteAddress.family = "IPv6";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CJSSocketWorker::JSEmitConnect()
|
||||||
|
{
|
||||||
|
m_Instance->PostCMethodCall(m_DukObjectHeapPtr,
|
||||||
|
ScriptAPI::js__Emitter_emit, CbArgs_EmitConnect);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CJSSocketWorker::JSEmitData(const char* data, size_t size)
|
||||||
|
{
|
||||||
|
JSEmitDataEnv env;
|
||||||
|
env.data = new char[size];
|
||||||
|
env.size = size;
|
||||||
|
|
||||||
|
memcpy(env.data, data, size);
|
||||||
|
|
||||||
|
m_Instance->PostCMethodCall(m_DukObjectHeapPtr,
|
||||||
|
ScriptAPI::js__Emitter_emit, CbArgs_EmitData, (void*)&env, sizeof(env));
|
||||||
|
}
|
||||||
|
|
||||||
|
void CJSSocketWorker::JSEmitEnd()
|
||||||
|
{
|
||||||
|
m_Instance->PostCMethodCall(m_DukObjectHeapPtr,
|
||||||
|
ScriptAPI::js__Emitter_emit, CbArgs_EmitEnd);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CJSSocketWorker::JSEmitClose()
|
||||||
|
{
|
||||||
|
m_Instance->PostCMethodCall(m_DukObjectHeapPtr,
|
||||||
|
ScriptAPI::js__Emitter_emit, CbArgs_EmitClose);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CJSSocketWorker::JSEmitDrain()
|
||||||
|
{
|
||||||
|
m_Instance->PostCMethodCall(m_DukObjectHeapPtr,
|
||||||
|
ScriptAPI::js__Emitter_emit, CbArgs_EmitDrain);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CJSSocketWorker::JSEmitLookup(JSSocketAddrInfo& addr)
|
||||||
|
{
|
||||||
|
m_Instance->PostCMethodCall(m_DukObjectHeapPtr,
|
||||||
|
ScriptAPI::js__Emitter_emit, CbArgs_EmitLookup, (void*)&addr, sizeof(addr));
|
||||||
|
}
|
||||||
|
|
||||||
|
void CJSSocketWorker::JSEmitError(const char* errMessage)
|
||||||
|
{
|
||||||
|
m_Instance->PostCMethodCall(m_DukObjectHeapPtr,
|
||||||
|
ScriptAPI::js__Emitter_emit, CbArgs_EmitError, (void*)errMessage, strlen(errMessage) + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_idx_t CJSSocketWorker::CbArgs_EmitConnect(duk_context* ctx, void* /*_env*/)
|
||||||
|
{
|
||||||
|
duk_push_string(ctx, "connect");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_idx_t CJSSocketWorker::CbArgs_EmitData(duk_context* ctx, void* _env)
|
||||||
|
{
|
||||||
|
JSEmitDataEnv* env = (JSEmitDataEnv*)_env;
|
||||||
|
duk_push_string(ctx, "data");
|
||||||
|
char* buffer = (char*)duk_push_fixed_buffer(ctx, env->size);
|
||||||
|
memcpy(buffer, env->data, env->size);
|
||||||
|
|
||||||
|
delete[] env->data;
|
||||||
|
|
||||||
|
duk_push_buffer_object(ctx, -1, 0, env->size, DUK_BUFOBJ_NODEJS_BUFFER);
|
||||||
|
duk_remove(ctx, -2);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_idx_t CJSSocketWorker::CbArgs_EmitEnd(duk_context* ctx, void* /*_env*/)
|
||||||
|
{
|
||||||
|
duk_push_string(ctx, "end");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_idx_t CJSSocketWorker::CbArgs_EmitClose(duk_context* ctx, void* /*_env*/)
|
||||||
|
{
|
||||||
|
duk_push_string(ctx, "close");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_idx_t CJSSocketWorker::CbArgs_EmitDrain(duk_context* ctx, void* /*_env*/)
|
||||||
|
{
|
||||||
|
duk_push_string(ctx, "drain");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_idx_t CJSSocketWorker::CbArgs_EmitLookup(duk_context* ctx, void* _env)
|
||||||
|
{
|
||||||
|
JSSocketAddrInfo* addr = (JSSocketAddrInfo*)_env;
|
||||||
|
|
||||||
|
duk_push_string(ctx, "lookup");
|
||||||
|
duk_push_object(ctx);
|
||||||
|
|
||||||
|
if (addr->port != 0)
|
||||||
|
{
|
||||||
|
duk_push_null(ctx);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
duk_push_error_object(ctx, DUK_ERR_ERROR, "dns lookup error");
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_put_prop_string(ctx, -2, "err");
|
||||||
|
duk_push_string(ctx, addr->address);
|
||||||
|
duk_put_prop_string(ctx, -2, "address");
|
||||||
|
duk_push_uint(ctx, addr->port);
|
||||||
|
duk_put_prop_string(ctx, -2, "port");
|
||||||
|
duk_push_string(ctx, addr->family);
|
||||||
|
duk_put_prop_string(ctx, -2, "family");
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_idx_t CJSSocketWorker::CbArgs_EmitError(duk_context* ctx, void* _env)
|
||||||
|
{
|
||||||
|
const char* errMessage = (const char*)_env;
|
||||||
|
duk_push_string(ctx, "error");
|
||||||
|
duk_push_error_object(ctx, DUK_ERR_ERROR, errMessage);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_idx_t CJSSocketWorker::CbArgs_Write(duk_context* ctx, void* _env)
|
||||||
|
{
|
||||||
|
duk_int_t callbackId = *(duk_int_t*)_env;
|
||||||
|
duk_push_int(ctx, callbackId);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string CJSSocketWorker::GetLocalAddress()
|
||||||
|
{
|
||||||
|
CGuard guard(m_CS);
|
||||||
|
return m_LocalAddress.address;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned short CJSSocketWorker::GetLocalPort()
|
||||||
|
{
|
||||||
|
CGuard guard(m_CS);
|
||||||
|
return m_LocalAddress.port;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string CJSSocketWorker::GetRemoteAddress()
|
||||||
|
{
|
||||||
|
CGuard guard(m_CS);
|
||||||
|
return m_RemoteAddress.address;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned short CJSSocketWorker::GetRemotePort()
|
||||||
|
{
|
||||||
|
CGuard guard(m_CS);
|
||||||
|
return m_RemoteAddress.port;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* CJSSocketWorker::GetFamily()
|
||||||
|
{
|
||||||
|
CGuard guard(m_CS);
|
||||||
|
return m_LocalAddress.family;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CJSSocketWorker::ClearAddress()
|
||||||
|
{
|
||||||
|
CGuard guard(m_CS);
|
||||||
|
|
||||||
|
m_LocalAddress.port = 0;
|
||||||
|
memset(m_LocalAddress.address, 0, sizeof(m_LocalAddress.address));
|
||||||
|
m_LocalAddress.family = "";
|
||||||
|
|
||||||
|
m_RemoteAddress.port = 0;
|
||||||
|
memset(m_RemoteAddress.address, 0, sizeof(m_RemoteAddress.address));
|
||||||
|
m_RemoteAddress.family = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
void CJSSocketWorker::ClearQueue()
|
||||||
|
{
|
||||||
|
CGuard guard(m_Queue.cs);
|
||||||
|
|
||||||
|
m_Queue.bSendClosePending = false;
|
||||||
|
m_Queue.bSendClosed = false;
|
||||||
|
m_Queue.bFullClosePending = false;
|
||||||
|
m_Queue.bConnectPending = false;
|
||||||
|
m_Queue.connectHost = "";
|
||||||
|
m_Queue.connectPort = 0;
|
||||||
|
m_Queue.writes.clear();
|
||||||
|
}
|
|
@ -0,0 +1,115 @@
|
||||||
|
#include <winsock2.h>
|
||||||
|
#include <ws2tcpip.h>
|
||||||
|
#include <windows.h>
|
||||||
|
#include "ScriptAPI.h"
|
||||||
|
#include "../ScriptWorker.h"
|
||||||
|
#pragma comment (lib, "Ws2_32.lib")
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
class CJSSocketWorker : public CScriptWorker
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
enum { TIMEOUT_MS = 1 };
|
||||||
|
|
||||||
|
struct JSSocketAddrInfo
|
||||||
|
{
|
||||||
|
char address[INET6_ADDRSTRLEN];
|
||||||
|
const char* family;
|
||||||
|
unsigned short port;
|
||||||
|
|
||||||
|
JSSocketAddrInfo() :
|
||||||
|
address(""),
|
||||||
|
family(""),
|
||||||
|
port(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BufferedWrite
|
||||||
|
{
|
||||||
|
size_t offset;
|
||||||
|
std::vector<char> data;
|
||||||
|
duk_int_t callbackId;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct JSEmitDataEnv {
|
||||||
|
char* data;
|
||||||
|
size_t size;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct JSSocketQueue
|
||||||
|
{
|
||||||
|
CriticalSection cs;
|
||||||
|
|
||||||
|
bool bConnectPending;
|
||||||
|
bool bFullClosePending;
|
||||||
|
bool bSendClosePending;
|
||||||
|
bool bSendClosed;
|
||||||
|
bool bRecvClosed;
|
||||||
|
|
||||||
|
std::string connectHost;
|
||||||
|
unsigned short connectPort;
|
||||||
|
std::vector<BufferedWrite> writes;
|
||||||
|
|
||||||
|
JSSocketQueue() :
|
||||||
|
bConnectPending(false),
|
||||||
|
bFullClosePending(false),
|
||||||
|
bSendClosePending(false),
|
||||||
|
bSendClosed(false),
|
||||||
|
bRecvClosed(false),
|
||||||
|
connectHost(""),
|
||||||
|
connectPort(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
SOCKET m_Socket;
|
||||||
|
bool m_bWinsockOK;
|
||||||
|
bool m_bAllowHalfOpen;
|
||||||
|
JSSocketAddrInfo m_LocalAddress;
|
||||||
|
JSSocketAddrInfo m_RemoteAddress;
|
||||||
|
JSSocketQueue m_Queue;
|
||||||
|
|
||||||
|
public:
|
||||||
|
CJSSocketWorker(CScriptInstance* inst, void* objectHeapPtr, bool bAllowHalfOpen);
|
||||||
|
virtual ~CJSSocketWorker();
|
||||||
|
|
||||||
|
bool Init(SOCKET sock);
|
||||||
|
bool Init(const char* host, unsigned short port);
|
||||||
|
|
||||||
|
void WorkerProc();
|
||||||
|
|
||||||
|
bool Write(const char* data, size_t length, duk_int_t callbackId, bool bEnd = false);
|
||||||
|
//bool GetAddress(JSSocketAddrInfo& address);
|
||||||
|
std::string GetLocalAddress();
|
||||||
|
unsigned short GetLocalPort();
|
||||||
|
std::string GetRemoteAddress();
|
||||||
|
unsigned short GetRemotePort();
|
||||||
|
const char* GetFamily();
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool ProcConnect();
|
||||||
|
void ProcSendData();
|
||||||
|
void ProcRecvData();
|
||||||
|
|
||||||
|
void UpdateAddresses();
|
||||||
|
void ClearAddress();
|
||||||
|
void ClearQueue();
|
||||||
|
|
||||||
|
void JSEmitConnect();
|
||||||
|
void JSEmitData(const char* data, size_t size);
|
||||||
|
void JSEmitEnd();
|
||||||
|
void JSEmitClose();
|
||||||
|
void JSEmitDrain();
|
||||||
|
void JSEmitLookup(JSSocketAddrInfo& addr);
|
||||||
|
void JSEmitError(const char* errMessage);
|
||||||
|
|
||||||
|
static duk_idx_t CbArgs_EmitConnect(duk_context* ctx, void* _env);
|
||||||
|
static duk_idx_t CbArgs_EmitData(duk_context* ctx, void* _env);
|
||||||
|
static duk_idx_t CbArgs_EmitEnd(duk_context* ctx, void* _env);
|
||||||
|
static duk_idx_t CbArgs_EmitClose(duk_context* ctx, void* _env);
|
||||||
|
static duk_idx_t CbArgs_EmitDrain(duk_context* ctx, void* _env);
|
||||||
|
static duk_idx_t CbArgs_EmitLookup(duk_context* ctx, void* _env);
|
||||||
|
static duk_idx_t CbArgs_EmitError(duk_context* ctx, void* _env);
|
||||||
|
static duk_idx_t CbArgs_Write(duk_context* ctx, void* _env);
|
||||||
|
};
|
|
@ -0,0 +1,520 @@
|
||||||
|
#include <stdafx.h>
|
||||||
|
#include "N64Image.h"
|
||||||
|
|
||||||
|
struct ImgFormatInfo {
|
||||||
|
int bitsPerPixel;
|
||||||
|
int paletteColorCount;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const std::map<int, ImgFormatInfo> FormatInfo = {
|
||||||
|
{ IMG_I4, { 4, 0 } },
|
||||||
|
{ IMG_IA4, { 4, 0 } },
|
||||||
|
{ IMG_I8, { 8, 0 } },
|
||||||
|
{ IMG_IA8, { 8, 0 } },
|
||||||
|
{ IMG_IA16, { 16, 0 } },
|
||||||
|
{ IMG_RGBA16, { 16, 0 } },
|
||||||
|
{ IMG_RGBA32, { 32, 0 } },
|
||||||
|
{ IMG_CI4_RGBA16, { 4, 16 } },
|
||||||
|
{ IMG_CI4_IA16, { 4, 16 } },
|
||||||
|
{ IMG_CI8_RGBA16, { 8, 256 } },
|
||||||
|
{ IMG_CI8_IA16, { 8, 256 } },
|
||||||
|
};
|
||||||
|
|
||||||
|
CN64Image::CN64Image() :
|
||||||
|
m_PixelSize(0),
|
||||||
|
m_Format(IMG_RGBA32),
|
||||||
|
m_Width(0),
|
||||||
|
m_Height(0),
|
||||||
|
m_NumPixels(0),
|
||||||
|
m_bUsePalette(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
int CN64Image::Init(int format, size_t width, size_t height,
|
||||||
|
void* pixelData, size_t pixelDataSize,
|
||||||
|
void* paletteData, size_t paletteDataSize)
|
||||||
|
{
|
||||||
|
m_Format = format;
|
||||||
|
m_PixelSize = BitsPerPixel(format);
|
||||||
|
m_bUsePalette = UsesPalette(format);
|
||||||
|
m_Width = width;
|
||||||
|
m_Height = height;
|
||||||
|
m_NumPixels = width * height;
|
||||||
|
|
||||||
|
size_t requiredPixelDataSize = (m_NumPixels * m_PixelSize) / 8;
|
||||||
|
|
||||||
|
if (pixelData != nullptr && pixelDataSize != requiredPixelDataSize)
|
||||||
|
{
|
||||||
|
return N64IMG_DATA_SIZE_INCORRECT;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pixelDataSize = requiredPixelDataSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_PixelData.resize(pixelDataSize);
|
||||||
|
|
||||||
|
if (pixelData != nullptr)
|
||||||
|
{
|
||||||
|
memcpy(m_PixelData.data(), pixelData, m_PixelData.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_bUsePalette)
|
||||||
|
{
|
||||||
|
size_t maxPaletteSize = (1 << m_PixelSize) * 2;
|
||||||
|
|
||||||
|
if (paletteData == nullptr)
|
||||||
|
{
|
||||||
|
m_PaletteData.resize(maxPaletteSize);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_PaletteData.resize(min(paletteDataSize, maxPaletteSize));
|
||||||
|
memcpy(m_PaletteData.data(), paletteData, m_PaletteData.size());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int result = UpdateBitmap();
|
||||||
|
if (result != N64IMG_OK)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return N64IMG_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
int CN64Image::Init(int format, uint8_t* pngData, size_t pngSize)
|
||||||
|
{
|
||||||
|
m_Format = format;
|
||||||
|
m_PixelSize = BitsPerPixel(format);
|
||||||
|
m_bUsePalette = UsesPalette(format);
|
||||||
|
|
||||||
|
int result = ReadPNG(pngData, pngSize, &m_Width, &m_Height, m_BitmapRgba32);
|
||||||
|
if (result != N64IMG_OK)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_NumPixels = m_Width * m_Height;
|
||||||
|
|
||||||
|
size_t requiredPixelDataSize = (m_NumPixels * m_PixelSize) / 8;
|
||||||
|
m_PixelData.resize(requiredPixelDataSize);
|
||||||
|
|
||||||
|
result = UpdatePixelsAndPaletteFromBitmap();
|
||||||
|
if (result != N64IMG_OK)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = UpdateBitmap();
|
||||||
|
if (result != N64IMG_OK)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return N64IMG_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CN64Image::ToPNG(std::vector<uint8_t>& outPngImage)
|
||||||
|
{
|
||||||
|
WritePNG(m_BitmapRgba32.data(), m_Width, m_Height, outPngImage);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t* CN64Image::PalettePtr(size_t index)
|
||||||
|
{
|
||||||
|
size_t offset = index * sizeof(uint16_t);
|
||||||
|
if (offset + sizeof(uint16_t) > m_PaletteData.size())
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return (uint16_t*)&m_PaletteData[offset];
|
||||||
|
}
|
||||||
|
|
||||||
|
void* CN64Image::TexelPtr(size_t index)
|
||||||
|
{
|
||||||
|
size_t offset = (index * m_PixelSize) / 8;
|
||||||
|
if (offset + max(1, (m_PixelSize / 8)) > m_PixelData.size())
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return (void*)&m_PixelData[offset];
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t* CN64Image::BitmapPtr(size_t index)
|
||||||
|
{
|
||||||
|
size_t offset = index * sizeof(uint32_t);
|
||||||
|
if (offset + sizeof(uint32_t) > m_BitmapRgba32.size())
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return (uint32_t*)&m_BitmapRgba32[offset];
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int CN64Image::GetTexel(size_t index)
|
||||||
|
{
|
||||||
|
void* pTexel = TexelPtr(index);
|
||||||
|
|
||||||
|
if (pTexel == nullptr)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (m_PixelSize)
|
||||||
|
{
|
||||||
|
case 4:
|
||||||
|
if ((index % 2) == 0)
|
||||||
|
{
|
||||||
|
return (*(uint8_t*)pTexel & 0xF0) >> 4;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return (*(uint8_t*)pTexel & 0x0F);
|
||||||
|
}
|
||||||
|
case 8:
|
||||||
|
return *(uint8_t*)pTexel;
|
||||||
|
case 16:
|
||||||
|
return _byteswap_ushort(*(uint16_t*)pTexel);
|
||||||
|
case 32:
|
||||||
|
return _byteswap_ulong(*(uint32_t*)pTexel);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CN64Image::SetTexel(size_t index, unsigned int value)
|
||||||
|
{
|
||||||
|
size_t offset = (index * m_PixelSize) / 8;
|
||||||
|
|
||||||
|
if (offset + (m_PixelSize / 8) > m_PixelData.size())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (m_PixelSize)
|
||||||
|
{
|
||||||
|
case 4:
|
||||||
|
if ((index % 2) == 0)
|
||||||
|
{
|
||||||
|
m_PixelData[offset] = (uint8_t)((m_PixelData[offset] & 0x0F) | (value << 4));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_PixelData[offset] = (uint8_t)((m_PixelData[offset] & 0xF0) | (value & 0x0F));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 8:
|
||||||
|
*(uint8_t*)&m_PixelData[offset] = (uint8_t)value;
|
||||||
|
break;
|
||||||
|
case 16:
|
||||||
|
*(uint16_t*)&m_PixelData[offset] = _byteswap_ushort((uint16_t)value);
|
||||||
|
break;
|
||||||
|
case 32:
|
||||||
|
*(uint32_t*)&m_PixelData[offset] = _byteswap_ulong(value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CN64Image::GetPaletteColor(size_t index, unsigned int* color)
|
||||||
|
{
|
||||||
|
uint16_t* pColor = PalettePtr(index);
|
||||||
|
|
||||||
|
if (pColor == nullptr)
|
||||||
|
{
|
||||||
|
*color = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
*color = _byteswap_ushort(*pColor);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CN64Image::SetPaletteColor(size_t index, unsigned int color)
|
||||||
|
{
|
||||||
|
uint16_t* pColor = PalettePtr(index);
|
||||||
|
|
||||||
|
if (pColor == nullptr)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
*pColor = _byteswap_ushort(color & 0xFFFF);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CN64Image::GetBitmapColor(size_t index, uint32_t* color)
|
||||||
|
{
|
||||||
|
uint32_t* pColor = BitmapPtr(index);
|
||||||
|
if (pColor == nullptr)
|
||||||
|
{
|
||||||
|
*color = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
*color = _byteswap_ulong(*pColor);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CN64Image::SetBitmapColor(size_t index, unsigned int color)
|
||||||
|
{
|
||||||
|
uint32_t* pColor = BitmapPtr(index);
|
||||||
|
if (pColor == nullptr)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
*pColor = _byteswap_ulong(color);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int CN64Image::UpdateBitmap()
|
||||||
|
{
|
||||||
|
m_BitmapRgba32.resize(m_NumPixels * sizeof(uint32_t));
|
||||||
|
|
||||||
|
for (size_t nPixel = 0; nPixel < m_NumPixels; nPixel++)
|
||||||
|
{
|
||||||
|
unsigned int color = 0;
|
||||||
|
unsigned int texel = GetTexel(nPixel);
|
||||||
|
|
||||||
|
if (m_bUsePalette)
|
||||||
|
{
|
||||||
|
if (!GetPaletteColor(texel, &color))
|
||||||
|
{
|
||||||
|
return N64IMG_INVALID_COLOR_INDEX;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
color = texel;
|
||||||
|
}
|
||||||
|
|
||||||
|
SetBitmapColor(nPixel, ColorToRgba32(m_Format, color));
|
||||||
|
}
|
||||||
|
|
||||||
|
return N64IMG_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
int CN64Image::UpdatePixelsAndPaletteFromBitmap()
|
||||||
|
{
|
||||||
|
if (m_bUsePalette)
|
||||||
|
{
|
||||||
|
m_PaletteData.resize(1 << m_PixelSize);
|
||||||
|
|
||||||
|
std::vector<uint16_t> newPalette;
|
||||||
|
std::map<uint16_t, size_t> colorIndexMap;
|
||||||
|
std::vector<size_t> indices;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < m_NumPixels; i++)
|
||||||
|
{
|
||||||
|
uint32_t colorRgba32;
|
||||||
|
GetBitmapColor(i, &colorRgba32);
|
||||||
|
|
||||||
|
uint16_t color16 = (uint16_t)ColorFromRgba32(m_Format, colorRgba32);
|
||||||
|
|
||||||
|
if (colorIndexMap.count(color16) > 0)
|
||||||
|
{
|
||||||
|
indices.push_back(colorIndexMap[color16]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (newPalette.size() > (size_t)(1 << m_PixelSize))
|
||||||
|
{
|
||||||
|
return N64IMG_TOO_MANY_COLORS;
|
||||||
|
}
|
||||||
|
|
||||||
|
colorIndexMap[color16] = newPalette.size();
|
||||||
|
indices.push_back(newPalette.size());
|
||||||
|
newPalette.push_back(color16);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t nPixel = 0; nPixel < indices.size(); nPixel++)
|
||||||
|
{
|
||||||
|
SetTexel(nPixel, indices[nPixel]);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_PaletteData.resize(newPalette.size());
|
||||||
|
|
||||||
|
for (size_t nColor = 0; nColor < newPalette.size(); nColor++)
|
||||||
|
{
|
||||||
|
SetPaletteColor(nColor, newPalette[nColor]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (size_t nPixel = 0; nPixel < m_NumPixels; nPixel++)
|
||||||
|
{
|
||||||
|
uint32_t colorRgba32;
|
||||||
|
GetBitmapColor(nPixel, &colorRgba32);
|
||||||
|
SetTexel(nPixel, ColorFromRgba32(m_Format, colorRgba32));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return N64IMG_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t>& CN64Image::PaletteData()
|
||||||
|
{
|
||||||
|
return m_PaletteData;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t>& CN64Image::PixelData()
|
||||||
|
{
|
||||||
|
return m_PixelData;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t>& CN64Image::Bitmap()
|
||||||
|
{
|
||||||
|
return m_BitmapRgba32;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t CN64Image::Width()
|
||||||
|
{
|
||||||
|
return m_Width;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t CN64Image::Height()
|
||||||
|
{
|
||||||
|
return m_Height;
|
||||||
|
}
|
||||||
|
|
||||||
|
int CN64Image::Format()
|
||||||
|
{
|
||||||
|
return m_Format;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CN64Image::UsesPalette()
|
||||||
|
{
|
||||||
|
return UsesPalette(m_Format);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int CN64Image::ColorFromRgba32(int dstFormat, uint32_t rgba32)
|
||||||
|
{
|
||||||
|
if (dstFormat == IMG_RGBA32)
|
||||||
|
{
|
||||||
|
return rgba32;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t r = ((rgba32 >> 24) & 0xFF);
|
||||||
|
uint8_t g = ((rgba32 >> 16) & 0xFF);
|
||||||
|
uint8_t b = ((rgba32 >> 8) & 0xFF);
|
||||||
|
uint8_t a = ((rgba32 >> 0) & 0xFF);
|
||||||
|
|
||||||
|
uint8_t i;
|
||||||
|
|
||||||
|
switch (dstFormat)
|
||||||
|
{
|
||||||
|
case IMG_RGBA16:
|
||||||
|
case IMG_CI8_RGBA16:
|
||||||
|
case IMG_CI4_RGBA16:
|
||||||
|
return ((r / 8) << 11) | ((g / 8) << 6) | ((b / 8) << 1) | (a / 128);
|
||||||
|
case IMG_IA16:
|
||||||
|
case IMG_CI8_IA16:
|
||||||
|
case IMG_CI4_IA16:
|
||||||
|
i = (r + g + b) / 3;
|
||||||
|
return (i << 8) | a;
|
||||||
|
case IMG_I4:
|
||||||
|
i = (r + g + b) / 3;
|
||||||
|
return (i / 16);
|
||||||
|
case IMG_IA4:
|
||||||
|
i = (r + g + b) / 3;
|
||||||
|
return ((i / 32) << 1) | (a / 128);
|
||||||
|
case IMG_I8:
|
||||||
|
i = (r + g + b) / 3;
|
||||||
|
return i;
|
||||||
|
case IMG_IA8:
|
||||||
|
i = (r + g + b) / 3;
|
||||||
|
return ((i / 16) << 4) | (a / 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t CN64Image::ColorToRgba32(int srcFormat, unsigned int color)
|
||||||
|
{
|
||||||
|
uint8_t r = 0, g = 0, b = 0, a = 0;
|
||||||
|
|
||||||
|
switch (srcFormat)
|
||||||
|
{
|
||||||
|
case IMG_RGBA32:
|
||||||
|
return color;
|
||||||
|
case IMG_RGBA16:
|
||||||
|
case IMG_CI8_RGBA16:
|
||||||
|
case IMG_CI4_RGBA16:
|
||||||
|
r = (((color >> 11) & 0x1F) * 255) / 31;
|
||||||
|
g = (((color >> 6) & 0x1F) * 255) / 31;
|
||||||
|
b = (((color >> 1) & 0x1F) * 255) / 31;
|
||||||
|
a = (color & 1) * 255;
|
||||||
|
break;
|
||||||
|
case IMG_IA16:
|
||||||
|
case IMG_CI8_IA16:
|
||||||
|
case IMG_CI4_IA16:
|
||||||
|
r = g = b = (uint8_t)(color >> 8);
|
||||||
|
a = (color & 0xFF);
|
||||||
|
break;
|
||||||
|
case IMG_I4:
|
||||||
|
r = g = b = (uint8_t)(color * 17);
|
||||||
|
a = 255;
|
||||||
|
break;
|
||||||
|
case IMG_IA4:
|
||||||
|
r = g = b = (uint8_t)(((color >> 1) * 255) / 7);
|
||||||
|
a = (color & 1) * 255;
|
||||||
|
break;
|
||||||
|
case IMG_I8:
|
||||||
|
r = g = b = (uint8_t)color;
|
||||||
|
a = 255;
|
||||||
|
break;
|
||||||
|
case IMG_IA8:
|
||||||
|
r = g = b = (uint8_t)((color >> 4) * 17);
|
||||||
|
a = (color & 0x0F) * 17;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (r << 24) | (g << 16) | (b << 8) | a;
|
||||||
|
}
|
||||||
|
|
||||||
|
int CN64Image::BitsPerPixel(int format)
|
||||||
|
{
|
||||||
|
if (FormatInfo.count(format))
|
||||||
|
{
|
||||||
|
return FormatInfo.at(format).bitsPerPixel;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int CN64Image::PaletteColorCount(int format)
|
||||||
|
{
|
||||||
|
if (FormatInfo.count(format))
|
||||||
|
{
|
||||||
|
return FormatInfo.at(format).paletteColorCount;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CN64Image::UsesPalette(int format)
|
||||||
|
{
|
||||||
|
if (FormatInfo.count(format))
|
||||||
|
{
|
||||||
|
return FormatInfo.at(format).paletteColorCount > 0;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* CN64Image::ResultCodeName(int resultCode)
|
||||||
|
{
|
||||||
|
static const std::map<int, const char*> names = {
|
||||||
|
{ N64IMG_OK, "OK" },
|
||||||
|
{ N64IMG_DATA_SIZE_INCORRECT, "ERR_DATA_SIZE_INCORRECT" },
|
||||||
|
{ N64IMG_INVALID_COLOR_INDEX, "ERR_INVALID_COLOR_INDEX" },
|
||||||
|
{ N64IMG_INCOMPATIBLE_COLOR, "ERR_INCOMPATIBLE_COLOR" },
|
||||||
|
{ N64IMG_TOO_MANY_COLORS, "ERR_TOO_MANY_COLORS" },
|
||||||
|
{ N64IMG_PNG_HEADER_MISSING, "ERR_PNG_HEADER_MISSING" },
|
||||||
|
{ N64IMG_PNG_OUT_OF_MEMORY, "ERR_PNG_OUT_OF_MEMORY" },
|
||||||
|
{ N64IMG_PNG_EXCEPTION, "ERR_PNG_EXCEPTION" },
|
||||||
|
{ N64IMG_PNG_PARSER_FAILED, "ERR_PNG_PARSER_FAILED" }
|
||||||
|
};
|
||||||
|
|
||||||
|
if (names.count(resultCode) != 0)
|
||||||
|
{
|
||||||
|
return names.at(resultCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
return "ERR_UNKNOWN";
|
||||||
|
}
|
|
@ -0,0 +1,107 @@
|
||||||
|
#pragma once
|
||||||
|
#include <stdafx.h>
|
||||||
|
#include "ScriptAPI.h"
|
||||||
|
|
||||||
|
enum {
|
||||||
|
G_IM_FMT_RGBA,
|
||||||
|
G_IM_FMT_YUV,
|
||||||
|
G_IM_FMT_CI,
|
||||||
|
G_IM_FMT_IA,
|
||||||
|
G_IM_FMT_I
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
G_IM_SIZ_4b,
|
||||||
|
G_IM_SIZ_8b,
|
||||||
|
G_IM_SIZ_16b,
|
||||||
|
G_IM_SIZ_32b
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
G_TT_NONE = 0x0000,
|
||||||
|
G_TT_RGBA16 = 0x8000,
|
||||||
|
G_TT_IA16 = 0xC000,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
IMG_RGBA16 = (G_IM_FMT_RGBA << 3 | G_IM_SIZ_16b),
|
||||||
|
IMG_RGBA32 = (G_IM_FMT_RGBA << 3 | G_IM_SIZ_32b),
|
||||||
|
IMG_CI4_RGBA16 = (G_IM_FMT_CI << 3 | G_IM_SIZ_4b) | G_TT_RGBA16,
|
||||||
|
IMG_CI4_IA16 = (G_IM_FMT_CI << 3 | G_IM_SIZ_4b) | G_TT_IA16,
|
||||||
|
IMG_CI8_RGBA16 = (G_IM_FMT_CI << 3 | G_IM_SIZ_8b) | G_TT_RGBA16,
|
||||||
|
IMG_CI8_IA16 = (G_IM_FMT_CI << 3 | G_IM_SIZ_8b) | G_TT_IA16,
|
||||||
|
IMG_IA4 = (G_IM_FMT_IA << 3 | G_IM_SIZ_4b),
|
||||||
|
IMG_IA8 = (G_IM_FMT_IA << 3 | G_IM_SIZ_8b),
|
||||||
|
IMG_IA16 = (G_IM_FMT_IA << 3 | G_IM_SIZ_16b),
|
||||||
|
IMG_I4 = (G_IM_FMT_I << 3 | G_IM_SIZ_4b),
|
||||||
|
IMG_I8 = (G_IM_FMT_I << 3 | G_IM_SIZ_8b),
|
||||||
|
};
|
||||||
|
|
||||||
|
enum N64ImageResult {
|
||||||
|
N64IMG_OK,
|
||||||
|
N64IMG_DATA_SIZE_INCORRECT,
|
||||||
|
N64IMG_INVALID_COLOR_INDEX,
|
||||||
|
N64IMG_INCOMPATIBLE_COLOR,
|
||||||
|
N64IMG_TOO_MANY_COLORS,
|
||||||
|
N64IMG_PNG_HEADER_MISSING,
|
||||||
|
N64IMG_PNG_OUT_OF_MEMORY,
|
||||||
|
N64IMG_PNG_EXCEPTION,
|
||||||
|
N64IMG_PNG_PARSER_FAILED
|
||||||
|
};
|
||||||
|
|
||||||
|
class CN64Image
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
int m_PixelSize;
|
||||||
|
int m_Format;
|
||||||
|
bool m_bUsePalette;
|
||||||
|
|
||||||
|
size_t m_Width;
|
||||||
|
size_t m_Height;
|
||||||
|
size_t m_NumPixels;
|
||||||
|
|
||||||
|
std::vector<uint8_t> m_PixelData;
|
||||||
|
std::vector<uint8_t> m_PaletteData;
|
||||||
|
std::vector<uint8_t> m_BitmapRgba32;
|
||||||
|
|
||||||
|
public:
|
||||||
|
CN64Image();
|
||||||
|
|
||||||
|
int Init(int format, size_t width, size_t height,
|
||||||
|
void* pixelData = nullptr, size_t pixelDataSize = 0,
|
||||||
|
void* paletteData = nullptr, size_t paletteDataSize = 0);
|
||||||
|
|
||||||
|
int Init(int format, uint8_t* pngData, size_t pngSize);
|
||||||
|
void ToPNG(std::vector<uint8_t>& outPngImage);
|
||||||
|
|
||||||
|
int UpdateBitmap();
|
||||||
|
std::vector<uint8_t>& PaletteData();
|
||||||
|
std::vector<uint8_t>& PixelData();
|
||||||
|
std::vector<uint8_t>& Bitmap();
|
||||||
|
size_t Width();
|
||||||
|
size_t Height();
|
||||||
|
int Format();
|
||||||
|
bool UsesPalette();
|
||||||
|
|
||||||
|
static int ReadPNG(uint8_t* pngData, size_t pngSize, size_t* width, size_t* height, std::vector<uint8_t>& outRGBA32);
|
||||||
|
static void WritePNG(uint8_t* rgba32, size_t width, size_t height, std::vector<uint8_t>& buffer);
|
||||||
|
|
||||||
|
static unsigned int ColorFromRgba32(int dstFormat, uint32_t rgba32);
|
||||||
|
static uint32_t ColorToRgba32(int srcFormat, unsigned int color);
|
||||||
|
static int BitsPerPixel(int format);
|
||||||
|
static int PaletteColorCount(int format);
|
||||||
|
static bool UsesPalette(int format);
|
||||||
|
static const char* ResultCodeName(int resultCode);
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint16_t* PalettePtr(size_t index);
|
||||||
|
void* TexelPtr(size_t index);
|
||||||
|
uint32_t* BitmapPtr(size_t index);
|
||||||
|
unsigned int GetTexel(size_t index);
|
||||||
|
void SetTexel(size_t index, unsigned int value);
|
||||||
|
bool GetPaletteColor(size_t index, unsigned int* color);
|
||||||
|
bool SetPaletteColor(size_t index, unsigned int color);
|
||||||
|
bool GetBitmapColor(size_t index, uint32_t* color);
|
||||||
|
bool SetBitmapColor(size_t index, unsigned int color);
|
||||||
|
int UpdatePixelsAndPaletteFromBitmap();
|
||||||
|
};
|
|
@ -0,0 +1,201 @@
|
||||||
|
#include <stdafx.h>
|
||||||
|
#include <3rdParty/png/png.h>
|
||||||
|
#include "N64Image.h"
|
||||||
|
|
||||||
|
#pragma warning (disable:4611) // disable setjmp/c++ deconstruction warning
|
||||||
|
|
||||||
|
struct PNGReadState {
|
||||||
|
uint8_t* pngData;
|
||||||
|
size_t pngSize;
|
||||||
|
png_size_t offset;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void PNGReadCallback(png_structp png_ptr, png_bytep data, png_size_t length);
|
||||||
|
static void PNGWriteCallback(png_structp png_ptr, png_bytep data, png_size_t length);
|
||||||
|
static void PNGFlushCallback(png_structp png_ptr);
|
||||||
|
static bool ParsePNGRow(png_byte* row, png_size_t rowSize, int bitDepth, int colorType, std::vector<uint8_t>& outRGBA32);
|
||||||
|
|
||||||
|
int CN64Image::ReadPNG(uint8_t* pngData, size_t pngSize, size_t* outWidth, size_t* outHeight, std::vector<uint8_t>& outRGBA32)
|
||||||
|
{
|
||||||
|
if (!png_check_sig(pngData, 8))
|
||||||
|
{
|
||||||
|
return N64IMG_PNG_HEADER_MISSING;
|
||||||
|
}
|
||||||
|
|
||||||
|
png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
|
||||||
|
|
||||||
|
if (!png_ptr)
|
||||||
|
{
|
||||||
|
return N64IMG_PNG_OUT_OF_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
png_infop info_ptr = png_create_info_struct(png_ptr);
|
||||||
|
|
||||||
|
if (!info_ptr)
|
||||||
|
{
|
||||||
|
png_destroy_read_struct(&png_ptr, nullptr, nullptr);
|
||||||
|
return N64IMG_PNG_OUT_OF_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (setjmp(png_jmpbuf(png_ptr)))
|
||||||
|
{
|
||||||
|
g_Notify->BreakPoint(__FILE__, __LINE__);
|
||||||
|
|
||||||
|
png_destroy_read_struct(&png_ptr, &info_ptr, nullptr);
|
||||||
|
return N64IMG_PNG_EXCEPTION;
|
||||||
|
}
|
||||||
|
|
||||||
|
PNGReadState readState;
|
||||||
|
readState.pngData = pngData;
|
||||||
|
readState.pngSize = pngSize;
|
||||||
|
readState.offset = 8;
|
||||||
|
|
||||||
|
png_set_read_fn(png_ptr, &readState, PNGReadCallback);
|
||||||
|
png_set_sig_bytes(png_ptr, 8);
|
||||||
|
png_read_info(png_ptr, info_ptr);
|
||||||
|
|
||||||
|
png_uint_32 width, height;
|
||||||
|
int bitDepth, colorType;
|
||||||
|
png_get_IHDR(png_ptr, info_ptr, &width, &height, &bitDepth,
|
||||||
|
&colorType, nullptr, nullptr, nullptr);
|
||||||
|
|
||||||
|
png_size_t rowSize = png_get_rowbytes(png_ptr, info_ptr);
|
||||||
|
std::vector<png_bytep> rowPointers(height);
|
||||||
|
std::vector<png_byte> imageData(height * rowSize);
|
||||||
|
|
||||||
|
for (size_t nRow = 0; nRow < height; nRow++)
|
||||||
|
{
|
||||||
|
rowPointers[nRow] = &imageData[nRow * rowSize];
|
||||||
|
}
|
||||||
|
|
||||||
|
png_read_image(png_ptr, &rowPointers[0]);
|
||||||
|
|
||||||
|
for (size_t nRow = 0; nRow < height; nRow++)
|
||||||
|
{
|
||||||
|
if (!ParsePNGRow(rowPointers[nRow], rowSize, bitDepth, colorType, outRGBA32))
|
||||||
|
{
|
||||||
|
png_destroy_read_struct(&png_ptr, &info_ptr, nullptr);
|
||||||
|
return N64IMG_PNG_PARSER_FAILED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
png_destroy_read_struct(&png_ptr, &info_ptr, nullptr);
|
||||||
|
|
||||||
|
*outWidth = width;
|
||||||
|
*outHeight = height;
|
||||||
|
return N64IMG_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CN64Image::WritePNG(uint8_t* rgba32, size_t width, size_t height, std::vector<uint8_t>& buffer)
|
||||||
|
{
|
||||||
|
png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
|
||||||
|
|
||||||
|
if (!png_ptr)
|
||||||
|
{
|
||||||
|
png_destroy_write_struct(&png_ptr, nullptr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
png_infop info_ptr = png_create_info_struct(png_ptr);
|
||||||
|
|
||||||
|
if (!info_ptr)
|
||||||
|
{
|
||||||
|
png_destroy_write_struct(&png_ptr, nullptr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (setjmp(png_jmpbuf(png_ptr)))
|
||||||
|
{
|
||||||
|
png_destroy_write_struct(&png_ptr, &info_ptr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
png_set_IHDR(png_ptr, info_ptr, width, height,
|
||||||
|
8, PNG_COLOR_TYPE_RGBA, PNG_INTERLACE_NONE,
|
||||||
|
PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
|
||||||
|
|
||||||
|
png_set_write_fn(png_ptr, &buffer, PNGWriteCallback, PNGFlushCallback);
|
||||||
|
png_write_info(png_ptr, info_ptr);
|
||||||
|
|
||||||
|
size_t rowSize = width * 4;
|
||||||
|
|
||||||
|
std::vector<png_bytep> rowPointers(height);
|
||||||
|
for (size_t nRow = 0; nRow < height; nRow++)
|
||||||
|
{
|
||||||
|
rowPointers[nRow] = &rgba32[nRow * rowSize];
|
||||||
|
}
|
||||||
|
|
||||||
|
png_write_image(png_ptr, &rowPointers[0]);
|
||||||
|
png_write_end(png_ptr, info_ptr);
|
||||||
|
png_destroy_write_struct(&png_ptr, &info_ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void PNGReadCallback(png_structp png_ptr, png_bytep data, png_size_t length)
|
||||||
|
{
|
||||||
|
PNGReadState* state = (PNGReadState*)png_get_io_ptr(png_ptr);
|
||||||
|
if (state->offset + length > state->pngSize)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
memcpy(data, &state->pngData[state->offset], length);
|
||||||
|
state->offset += length;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void PNGWriteCallback(png_structp png_ptr, png_bytep data, png_size_t length)
|
||||||
|
{
|
||||||
|
std::vector<uint8_t>* buffer = (std::vector<uint8_t>*)png_get_io_ptr(png_ptr);
|
||||||
|
buffer->insert(buffer->end(), &data[0], &data[length]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void PNGFlushCallback(png_structp /*png_ptr*/)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool ParsePNGRow(png_byte* row, png_size_t rowSize, int bitDepth, int colorType, std::vector<uint8_t>& outRGBA32)
|
||||||
|
{
|
||||||
|
if (colorType == PNG_COLOR_TYPE_RGBA)
|
||||||
|
{
|
||||||
|
if (bitDepth == 8)
|
||||||
|
{
|
||||||
|
outRGBA32.insert(outRGBA32.end(), &row[0], &row[rowSize]);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (bitDepth == 16)
|
||||||
|
{
|
||||||
|
for (png_size_t i = 0; i < rowSize; i += 8)
|
||||||
|
{
|
||||||
|
outRGBA32.push_back(png_get_uint_16(&row[i + 0]) >> 8);
|
||||||
|
outRGBA32.push_back(png_get_uint_16(&row[i + 2]) >> 8);
|
||||||
|
outRGBA32.push_back(png_get_uint_16(&row[i + 4]) >> 8);
|
||||||
|
outRGBA32.push_back(png_get_uint_16(&row[i + 6]) >> 8);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (colorType == PNG_COLOR_TYPE_RGB)
|
||||||
|
{
|
||||||
|
if (bitDepth == 8)
|
||||||
|
{
|
||||||
|
for (png_size_t i = 0; i < rowSize; i += 3)
|
||||||
|
{
|
||||||
|
outRGBA32.insert(outRGBA32.end(), &row[i], &row[i + 3]);
|
||||||
|
outRGBA32.push_back(255);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (bitDepth == 16)
|
||||||
|
{
|
||||||
|
for (png_size_t i = 0; i < rowSize; i += 6)
|
||||||
|
{
|
||||||
|
outRGBA32.push_back(png_get_uint_16(&row[i + 0]) >> 8);
|
||||||
|
outRGBA32.push_back(png_get_uint_16(&row[i + 2]) >> 8);
|
||||||
|
outRGBA32.push_back(png_get_uint_16(&row[i + 4]) >> 8);
|
||||||
|
outRGBA32.push_back(255);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,455 @@
|
||||||
|
#include "../ScriptTypes.h"
|
||||||
|
#include "../ScriptSystem.h"
|
||||||
|
#include "../ScriptInstance.h"
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#define HS_gAppCallbacks DUK_HIDDEN_SYMBOL("gAppCallbacks")
|
||||||
|
#define HS_gInstancePtr DUK_HIDDEN_SYMBOL("gInstancePtr")
|
||||||
|
#define HS_gInputListener DUK_HIDDEN_SYMBOL("gInputListener")
|
||||||
|
#define HS_gOpenFileDescriptors DUK_HIDDEN_SYMBOL("gOpenFileDescriptors")
|
||||||
|
#define HS_gKeepAlive DUK_HIDDEN_SYMBOL("gKeepAlive")
|
||||||
|
#define HS_gPrivateCallEnabled DUK_HIDDEN_SYMBOL("gPrivateCallEnabled")
|
||||||
|
#define HS_gNativeModules DUK_HIDDEN_SYMBOL("gNativeModules")
|
||||||
|
#define HS_gObjectRefs DUK_HIDDEN_SYMBOL("gObjectRefs")
|
||||||
|
#define HS_gNextObjectRefId DUK_HIDDEN_SYMBOL("gNextObjectRefId")
|
||||||
|
#define HS_gIntervals DUK_HIDDEN_SYMBOL("gIntervals")
|
||||||
|
#define HS_gNextInvervalId DUK_HIDDEN_SYMBOL("gNextIntervalId")
|
||||||
|
|
||||||
|
#define HS_objectRefId DUK_HIDDEN_SYMBOL("objectRefId")
|
||||||
|
#define HS_emitterListeners DUK_HIDDEN_SYMBOL("emitterListeners")
|
||||||
|
#define HS_emitterNextListenerId DUK_HIDDEN_SYMBOL("emitterNextListenerId")
|
||||||
|
#define HS_socketWorkerPtr DUK_HIDDEN_SYMBOL("socketWorkerPtr")
|
||||||
|
#define HS_socketNextWriteCallbackId DUK_HIDDEN_SYMBOL("nextWriteCallbackId")
|
||||||
|
#define HS_socketWriteCallbacks DUK_HIDDEN_SYMBOL("writeCallbacks")
|
||||||
|
#define HS_socketWriteEndCallbacks DUK_HIDDEN_SYMBOL("endCallbacks")
|
||||||
|
#define HS_serverWorkerPtr DUK_HIDDEN_SYMBOL("serverWorkerPtr")
|
||||||
|
#define HS_renderWindowPtr DUK_HIDDEN_SYMBOL("renderWindowPtr")
|
||||||
|
#define HS_n64ImagePtr DUK_HIDDEN_SYMBOL("n64ImagePtr")
|
||||||
|
|
||||||
|
namespace ScriptAPI
|
||||||
|
{
|
||||||
|
struct DukPropListEntry;
|
||||||
|
|
||||||
|
enum MemType {
|
||||||
|
U8, U16, U32, S8, S16, S32, F32, F64,
|
||||||
|
U64, S64
|
||||||
|
};
|
||||||
|
|
||||||
|
enum ArgType {
|
||||||
|
Arg_Any,
|
||||||
|
Arg_Number,
|
||||||
|
Arg_BufferData,
|
||||||
|
Arg_String,
|
||||||
|
Arg_Function,
|
||||||
|
Arg_Object,
|
||||||
|
Arg_Array,
|
||||||
|
Arg_Boolean,
|
||||||
|
|
||||||
|
ArgAttr_Optional = (1 << 31),
|
||||||
|
Arg_OptAny = Arg_Any | ArgAttr_Optional,
|
||||||
|
Arg_OptNumber = Arg_Number | ArgAttr_Optional,
|
||||||
|
Arg_OptBufferData = Arg_BufferData | ArgAttr_Optional,
|
||||||
|
Arg_OptString = Arg_String | ArgAttr_Optional,
|
||||||
|
Arg_OptFunction = Arg_Function | ArgAttr_Optional,
|
||||||
|
Arg_OptObject = Arg_Object | ArgAttr_Optional,
|
||||||
|
Arg_OptArray = Arg_Array | ArgAttr_Optional,
|
||||||
|
Arg_OptBoolean = Arg_Boolean | ArgAttr_Optional,
|
||||||
|
|
||||||
|
ArgAttrs = ArgAttr_Optional
|
||||||
|
};
|
||||||
|
|
||||||
|
// ScriptAPI
|
||||||
|
void InitEnvironment(duk_context* ctx, CScriptInstance* inst);
|
||||||
|
void DefineGlobalConstants(duk_context* ctx);
|
||||||
|
|
||||||
|
void DefineGlobalClass(duk_context* ctx, const char* className,
|
||||||
|
duk_c_function constructorFunc,
|
||||||
|
const DukPropListEntry* prototypeProps = nullptr,
|
||||||
|
const DukPropListEntry* staticProps = nullptr);
|
||||||
|
|
||||||
|
void DefineGlobalInterface(duk_context* ctx, const char* name, const DukPropListEntry* props);
|
||||||
|
|
||||||
|
void DefineGlobalFunction(duk_context* ctx, const char* name, duk_c_function func);
|
||||||
|
|
||||||
|
CScriptInstance* GetInstance(duk_context* ctx);
|
||||||
|
|
||||||
|
JSAppCallbackID AddAppCallback(duk_context* ctx, duk_idx_t callbackFuncIdx,
|
||||||
|
JSAppHookID hookId,
|
||||||
|
JSDukArgSetupFunc argSetupFunc = nullptr,
|
||||||
|
JSAppCallbackCondFunc conditionFunc = nullptr,
|
||||||
|
JSAppCallbackCleanupFunc cleanupFunc = nullptr);
|
||||||
|
|
||||||
|
JSAppCallbackID AddAppCallback(duk_context* ctx, JSAppHookID hookId, JSAppCallback& callback);
|
||||||
|
|
||||||
|
bool RemoveAppCallback(duk_context* ctx, JSAppCallbackID callbackId);
|
||||||
|
duk_ret_t js__AppCallbackFinalizer(duk_context* ctx);
|
||||||
|
|
||||||
|
void RefObject(duk_context* ctx, duk_idx_t idx);
|
||||||
|
void UnrefObject(duk_context* ctx, duk_idx_t idx);
|
||||||
|
duk_ret_t js__UnrefObject(duk_context* ctx);
|
||||||
|
|
||||||
|
void InitEmitter(duk_context* ctx, duk_idx_t obj_idx, const std::vector<std::string>& eventNames);
|
||||||
|
duk_ret_t js__Emitter_emit(duk_context* ctx);
|
||||||
|
duk_ret_t js__Emitter_on(duk_context* ctx);
|
||||||
|
duk_ret_t js__Emitter_off(duk_context* ctx);
|
||||||
|
|
||||||
|
duk_ret_t js_Duktape_modSearch(duk_context* ctx); // require()
|
||||||
|
void RegisterNativeModule(duk_context* ctx, HMODULE hModule);
|
||||||
|
duk_ret_t js__NativeModuleFinalizer(duk_context* ctx);
|
||||||
|
|
||||||
|
void AllowPrivateCall(duk_context* ctx, bool bAllow);
|
||||||
|
bool PrivateCallAllowed(duk_context* ctx);
|
||||||
|
|
||||||
|
void PushNewDummyConstructor(duk_context* ctx, bool bFrozen = true);
|
||||||
|
void DefineGlobalDummyConstructors(duk_context* ctx, const char* constructorNames[], bool bFreeze = true);
|
||||||
|
void SetDummyConstructor(duk_context* ctx, duk_idx_t obj_idx, const char* globalConstructorName);
|
||||||
|
duk_ret_t js_DummyConstructor(duk_context* ctx);
|
||||||
|
|
||||||
|
const char* ArgTypeName(ArgType argType);
|
||||||
|
duk_bool_t ArgTypeMatches(duk_context* ctx, duk_idx_t idx, ArgType wantType);
|
||||||
|
duk_ret_t CheckArgs(duk_context* ctx, const std::vector<ArgType>& argTypes);
|
||||||
|
duk_ret_t CheckSetterAssignment(duk_context* ctx, ArgType wantType);
|
||||||
|
|
||||||
|
duk_ret_t ThrowInvalidArgsError(duk_context* ctx);
|
||||||
|
duk_ret_t ThrowInvalidArgError(duk_context* ctx, duk_idx_t idx, ArgType wantType);
|
||||||
|
duk_ret_t ThrowTooManyArgsError(duk_context* ctx);
|
||||||
|
duk_ret_t ThrowInvalidAssignmentError(duk_context* ctx, ArgType wantType);
|
||||||
|
duk_ret_t ThrowNotCallableError(duk_context* ctx);
|
||||||
|
|
||||||
|
void DebugStack(duk_context* ctx, const char* file, int line);
|
||||||
|
|
||||||
|
// ScriptAPI_events
|
||||||
|
void Define_events(duk_context* ctx);
|
||||||
|
duk_ret_t js_events_onstatechange(duk_context* ctx);
|
||||||
|
duk_ret_t js_events_onexec(duk_context* ctx);
|
||||||
|
duk_ret_t js_events_onread(duk_context* ctx);
|
||||||
|
duk_ret_t js_events_onwrite(duk_context* ctx);
|
||||||
|
duk_ret_t js_events_onopcode(duk_context* ctx);
|
||||||
|
duk_ret_t js_events_ongprvalue(duk_context* ctx);
|
||||||
|
duk_ret_t js_events_ondraw(duk_context* ctx);
|
||||||
|
duk_ret_t js_events_onpifread(duk_context* ctx);
|
||||||
|
duk_ret_t js_events_onsptask(duk_context* ctx);
|
||||||
|
duk_ret_t js_events_onpidma(duk_context* ctx);
|
||||||
|
duk_ret_t js_events_onmouseup(duk_context* ctx);
|
||||||
|
duk_ret_t js_events_onmousedown(duk_context* ctx);
|
||||||
|
duk_ret_t js_events_onmousemove(duk_context* ctx);
|
||||||
|
duk_ret_t js_events_remove(duk_context* ctx);
|
||||||
|
|
||||||
|
// ScriptAPI_console
|
||||||
|
void Define_console(duk_context* ctx);
|
||||||
|
duk_ret_t js_console_print(duk_context* ctx);
|
||||||
|
duk_ret_t js_console_log(duk_context* ctx);
|
||||||
|
duk_ret_t js_console_error(duk_context* ctx);
|
||||||
|
duk_ret_t js_console_clear(duk_context* ctx);
|
||||||
|
duk_ret_t js_console_listen(duk_context* ctx);
|
||||||
|
|
||||||
|
// ScriptAPI_mem
|
||||||
|
void Define_mem(duk_context* ctx);
|
||||||
|
template <class T> duk_ret_t js_mem__get(duk_context* ctx);
|
||||||
|
template <class T> duk_ret_t js_mem__set(duk_context* ctx);
|
||||||
|
duk_ret_t js_mem__boundget(duk_context* ctx);
|
||||||
|
duk_ret_t js_mem__boundset(duk_context* ctx);
|
||||||
|
duk_ret_t js_mem__type_constructor(duk_context* ctx);
|
||||||
|
duk_ret_t js_mem__get_ramsize(duk_context* ctx);
|
||||||
|
duk_ret_t js_mem__get_romsize(duk_context* ctx);
|
||||||
|
duk_ret_t js_mem__get_ptr(duk_context* ctx);
|
||||||
|
duk_ret_t js_mem_getblock(duk_context* ctx);
|
||||||
|
duk_ret_t js_mem_getstring(duk_context* ctx);
|
||||||
|
duk_ret_t js_mem_setblock(duk_context* ctx);
|
||||||
|
duk_ret_t js_mem_bindvar(duk_context* ctx);
|
||||||
|
duk_ret_t js_mem_bindvars(duk_context* ctx);
|
||||||
|
duk_ret_t js_mem_bindstruct(duk_context* ctx);
|
||||||
|
duk_ret_t js_mem_typedef(duk_context* ctx);
|
||||||
|
|
||||||
|
// ScriptAPI_Server
|
||||||
|
void Define_Server(duk_context* ctx);
|
||||||
|
duk_ret_t js_Server__constructor(duk_context* ctx);
|
||||||
|
duk_ret_t js_Server__finalizer(duk_context* ctx);
|
||||||
|
duk_ret_t js_Server__get_port(duk_context* ctx);
|
||||||
|
duk_ret_t js_Server__get_address(duk_context* ctx);
|
||||||
|
duk_ret_t js_Server__get_addressFamily(duk_context* ctx);
|
||||||
|
duk_ret_t js_Server_listen(duk_context* ctx);
|
||||||
|
duk_ret_t js_Server_close(duk_context* ctx);
|
||||||
|
|
||||||
|
// ScriptAPI_Socket
|
||||||
|
void Define_Socket(duk_context* ctx);
|
||||||
|
duk_ret_t js_Socket__constructor(duk_context* ctx);
|
||||||
|
duk_ret_t js_Socket__finalizer(duk_context* ctx);
|
||||||
|
duk_ret_t js_Socket__invokeWriteCallback(duk_context* ctx);
|
||||||
|
duk_ret_t js_Socket__invokeWriteEndCallbacks(duk_context* ctx);
|
||||||
|
duk_ret_t js_Socket_connect(duk_context* ctx);
|
||||||
|
duk_ret_t js_Socket_write(duk_context* ctx);
|
||||||
|
duk_ret_t js_Socket_end(duk_context* ctx);
|
||||||
|
duk_ret_t js_Socket_close(duk_context* ctx);
|
||||||
|
duk_ret_t js_Socket__get_localAddress(duk_context* ctx);
|
||||||
|
duk_ret_t js_Socket__get_localPort(duk_context* ctx);
|
||||||
|
duk_ret_t js_Socket__get_remoteAddress(duk_context* ctx);
|
||||||
|
duk_ret_t js_Socket__get_remotePort(duk_context* ctx);
|
||||||
|
duk_ret_t js_Socket__get_addressFamily(duk_context* ctx);
|
||||||
|
|
||||||
|
// ScriptAPI_script
|
||||||
|
void Define_script(duk_context* ctx);
|
||||||
|
duk_ret_t js_script_keepalive(duk_context* ctx);
|
||||||
|
duk_ret_t js_script_timeout(duk_context* ctx);
|
||||||
|
|
||||||
|
// ScriptAPI_fs
|
||||||
|
void Define_fs(duk_context* ctx);
|
||||||
|
duk_ret_t js_fs_open(duk_context* ctx);
|
||||||
|
duk_ret_t js_fs_close(duk_context* ctx);
|
||||||
|
duk_ret_t js_fs_write(duk_context* ctx);
|
||||||
|
duk_ret_t js_fs_writefile(duk_context* ctx);
|
||||||
|
duk_ret_t js_fs_read(duk_context* ctx);
|
||||||
|
duk_ret_t js_fs_readfile(duk_context* ctx);
|
||||||
|
duk_ret_t js_fs_fstat(duk_context* ctx);
|
||||||
|
duk_ret_t js_fs_stat(duk_context* ctx);
|
||||||
|
duk_ret_t js_fs_unlink(duk_context* ctx);
|
||||||
|
duk_ret_t js_fs_mkdir(duk_context* ctx);
|
||||||
|
duk_ret_t js_fs_rmdir(duk_context* ctx);
|
||||||
|
duk_ret_t js_fs_readdir(duk_context* ctx);
|
||||||
|
duk_ret_t js_fs_Stats__constructor(duk_context* ctx);
|
||||||
|
duk_ret_t js_fs_Stats_isDirectory(duk_context* ctx);
|
||||||
|
duk_ret_t js_fs_Stats_isFile(duk_context* ctx);
|
||||||
|
|
||||||
|
// ScriptAPI_debug
|
||||||
|
void Define_debug(duk_context* ctx);
|
||||||
|
duk_ret_t js_debug__get_paused(duk_context* ctx);
|
||||||
|
duk_ret_t js_debug_breakhere(duk_context* ctx);
|
||||||
|
duk_ret_t js_debug_step(duk_context* ctx);
|
||||||
|
duk_ret_t js_debug_skip(duk_context* ctx);
|
||||||
|
duk_ret_t js_debug_resume(duk_context* ctx);
|
||||||
|
duk_ret_t js_debug_showmemory(duk_context* ctx);
|
||||||
|
duk_ret_t js_debug_showcommands(duk_context* ctx);
|
||||||
|
|
||||||
|
// ScriptAPI_asm
|
||||||
|
void Define_asm(duk_context* ctx);
|
||||||
|
duk_ret_t js_asm_gprname(duk_context* ctx);
|
||||||
|
duk_ret_t js_asm_encode(duk_context* ctx);
|
||||||
|
duk_ret_t js_asm_decode(duk_context* ctx);
|
||||||
|
|
||||||
|
// ScriptAPI_cpu
|
||||||
|
void Define_cpu(duk_context* ctx);
|
||||||
|
duk_ret_t js_cpu_get(duk_context* ctx);
|
||||||
|
duk_ret_t js_cpu_set(duk_context* ctx);
|
||||||
|
duk_ret_t js_cpu_gpr_get(duk_context* ctx);
|
||||||
|
duk_ret_t js_cpu_gpr_set(duk_context* ctx);
|
||||||
|
duk_ret_t js_cpu_ugpr_get(duk_context* ctx);
|
||||||
|
duk_ret_t js_cpu_ugpr_set(duk_context* ctx);
|
||||||
|
duk_ret_t js_cpu_fpr_get(duk_context* ctx);
|
||||||
|
duk_ret_t js_cpu_fpr_set(duk_context* ctx);
|
||||||
|
duk_ret_t js_cpu_dfpr_get(duk_context* ctx);
|
||||||
|
duk_ret_t js_cpu_dfpr_set(duk_context* ctx);
|
||||||
|
duk_ret_t js_cpu_cop0_get(duk_context* ctx);
|
||||||
|
duk_ret_t js_cpu_cop0_set(duk_context* ctx);
|
||||||
|
|
||||||
|
// ScriptAPI_pj64
|
||||||
|
void Define_pj64(duk_context* ctx);
|
||||||
|
duk_ret_t js_pj64_open(duk_context* ctx);
|
||||||
|
duk_ret_t js_pj64_close(duk_context* ctx);
|
||||||
|
duk_ret_t js_pj64_reset(duk_context* ctx);
|
||||||
|
duk_ret_t js_pj64_pause(duk_context* ctx);
|
||||||
|
duk_ret_t js_pj64_resume(duk_context* ctx);
|
||||||
|
duk_ret_t js_pj64_limitfps(duk_context* ctx);
|
||||||
|
duk_ret_t js_pj64__get_installDirectory(duk_context* ctx);
|
||||||
|
duk_ret_t js_pj64__get_scriptsDirectory(duk_context* ctx);
|
||||||
|
duk_ret_t js_pj64__get_modulesDirectory(duk_context* ctx);
|
||||||
|
duk_ret_t js_pj64__get_romDirectory(duk_context* ctx);
|
||||||
|
duk_ret_t js_pj64__get_romInfo(duk_context* ctx);
|
||||||
|
duk_ret_t js_pj64_romInfo__get_goodName(duk_context* ctx);
|
||||||
|
duk_ret_t js_pj64_romInfo__get_fileName(duk_context* ctx);
|
||||||
|
duk_ret_t js_pj64_romInfo__get_filePath(duk_context* ctx);
|
||||||
|
duk_ret_t js_pj64_romInfo__get_headerCrc1(duk_context* ctx);
|
||||||
|
duk_ret_t js_pj64_romInfo__get_headerCrc2(duk_context* ctx);
|
||||||
|
duk_ret_t js_pj64_romInfo__get_headerName(duk_context* ctx);
|
||||||
|
duk_ret_t js_pj64_romInfo__get_headerMediaFormat(duk_context* ctx);
|
||||||
|
duk_ret_t js_pj64_romInfo__get_headerId(duk_context* ctx);
|
||||||
|
duk_ret_t js_pj64_romInfo__get_headerCountryCode(duk_context* ctx);
|
||||||
|
duk_ret_t js_pj64_romInfo__get_headerVersion(duk_context* ctx);
|
||||||
|
|
||||||
|
// ScriptAPI_AddressRange
|
||||||
|
void Define_AddressRange(duk_context* ctx);
|
||||||
|
duk_ret_t js_AddressRange__constructor(duk_context* ctx);
|
||||||
|
|
||||||
|
// ScriptAPI_Number_hex
|
||||||
|
void Define_Number_prototype_hex(duk_context* ctx);
|
||||||
|
duk_ret_t js_Number_prototype_hex(duk_context* ctx);
|
||||||
|
|
||||||
|
// ScriptAPI_N64Image
|
||||||
|
void Define_N64Image(duk_context* ctx);
|
||||||
|
duk_ret_t js_N64Image__constructor(duk_context* ctx);
|
||||||
|
duk_ret_t js_N64Image__finalizer(duk_context* ctx);
|
||||||
|
duk_ret_t js_N64Image_static_fromPNG(duk_context* ctx);
|
||||||
|
duk_ret_t js_N64Image_static_format(duk_context* ctx);
|
||||||
|
duk_ret_t js_N64Image_static_bpp(duk_context* ctx);
|
||||||
|
duk_ret_t js_N64Image_toPNG(duk_context* ctx);
|
||||||
|
duk_ret_t js_N64Image_update(duk_context* ctx);
|
||||||
|
|
||||||
|
// ScriptAPI_exec
|
||||||
|
void Define_exec(duk_context* ctx);
|
||||||
|
duk_ret_t js_exec(duk_context* ctx);
|
||||||
|
|
||||||
|
// ScriptAPI_alert
|
||||||
|
void Define_alert(duk_context* ctx);
|
||||||
|
duk_ret_t js_alert(duk_context* ctx);
|
||||||
|
|
||||||
|
// ScriptAPI_interval
|
||||||
|
void Define_interval(duk_context* ctx);
|
||||||
|
duk_ret_t js_setInterval(duk_context* ctx);
|
||||||
|
duk_ret_t js_clearInterval(duk_context* ctx);
|
||||||
|
duk_ret_t js_setTimeout(duk_context* ctx);
|
||||||
|
duk_ret_t js_clearTimeout(duk_context* ctx);
|
||||||
|
duk_ret_t js__IntervalContext_invokeFunc(duk_context* ctx);
|
||||||
|
duk_ret_t js__IntervalContext_remove(duk_context* ctx);
|
||||||
|
duk_ret_t js__IntervalContext_finalizer(duk_context* ctx);
|
||||||
|
|
||||||
|
enum {
|
||||||
|
R0, AT, V0, V1, A0, A1, A2, A3,
|
||||||
|
T0, T1, T2, T3, T4, T5, T6, T7,
|
||||||
|
S0, S1, S2, S3, S4, S5, S6, S7,
|
||||||
|
T8, T9, K0, K1, GP, SP, FP, RA,
|
||||||
|
//S8 = FP
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
GPR_R0 = (1 << R0),
|
||||||
|
GPR_AT = (1 << AT),
|
||||||
|
GPR_V0 = (1 << V0),
|
||||||
|
GPR_V1 = (1 << V1),
|
||||||
|
GPR_A0 = (1 << A0),
|
||||||
|
GPR_A1 = (1 << A1),
|
||||||
|
GPR_A2 = (1 << A2),
|
||||||
|
GPR_A3 = (1 << A3),
|
||||||
|
GPR_T0 = (1 << T0),
|
||||||
|
GPR_T1 = (1 << T1),
|
||||||
|
GPR_T2 = (1 << T2),
|
||||||
|
GPR_T3 = (1 << T3),
|
||||||
|
GPR_T4 = (1 << T4),
|
||||||
|
GPR_T5 = (1 << T5),
|
||||||
|
GPR_T6 = (1 << T6),
|
||||||
|
GPR_T7 = (1 << T7),
|
||||||
|
GPR_S0 = (1 << S0),
|
||||||
|
GPR_S1 = (1 << S1),
|
||||||
|
GPR_S2 = (1 << S2),
|
||||||
|
GPR_S3 = (1 << S3),
|
||||||
|
GPR_S4 = (1 << S4),
|
||||||
|
GPR_S5 = (1 << S5),
|
||||||
|
GPR_S6 = (1 << S6),
|
||||||
|
GPR_S7 = (1 << S7),
|
||||||
|
GPR_T8 = (1 << T8),
|
||||||
|
GPR_T9 = (1 << T9),
|
||||||
|
GPR_K0 = (1 << K0),
|
||||||
|
GPR_K1 = (1 << K1),
|
||||||
|
GPR_GP = (1 << GP),
|
||||||
|
GPR_SP = (1 << SP),
|
||||||
|
GPR_FP = (1 << FP),
|
||||||
|
GPR_RA = (1 << RA),
|
||||||
|
//GPR_S8 = GPR_FP,
|
||||||
|
GPR_ANY = 0xFFFFFFFF
|
||||||
|
};
|
||||||
|
|
||||||
|
#define DUK_TYPE_ID(id) \
|
||||||
|
static const DukPropTypeID _TYPE = DukPropTypeID::Type_ ## id;
|
||||||
|
|
||||||
|
#define DUK_DECLVAL_MEMBER(structName, memberName) \
|
||||||
|
structName memberName; Value(structName v) : memberName(v) {}
|
||||||
|
|
||||||
|
#define DUK_SCALARTYPE_IMPL(structName, primitiveType) \
|
||||||
|
DUK_TYPE_ID(structName) primitiveType value; structName(primitiveType value) : value(value){}
|
||||||
|
|
||||||
|
enum DukPropTypeID
|
||||||
|
{
|
||||||
|
Type_DukInt,
|
||||||
|
Type_DukUInt,
|
||||||
|
Type_DukNumber,
|
||||||
|
Type_DukString,
|
||||||
|
Type_DukBoolean,
|
||||||
|
Type_DukPointer,
|
||||||
|
Type_DukCFunction,
|
||||||
|
Type_DukDupIndex,
|
||||||
|
Type_DukGetter,
|
||||||
|
Type_DukGetterSetter,
|
||||||
|
Type_DukObject,
|
||||||
|
Type_DukProxy
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DukInt { DUK_SCALARTYPE_IMPL(DukInt, duk_int_t) };
|
||||||
|
struct DukUInt { DUK_SCALARTYPE_IMPL(DukUInt, duk_uint_t) };
|
||||||
|
struct DukNumber { DUK_SCALARTYPE_IMPL(DukNumber, duk_double_t) };
|
||||||
|
struct DukBoolean { DUK_SCALARTYPE_IMPL(DukBoolean, duk_bool_t) };
|
||||||
|
struct DukString { DUK_SCALARTYPE_IMPL(DukString, const char*) };
|
||||||
|
struct DukPointer { DUK_SCALARTYPE_IMPL(DukPointer, void*) };
|
||||||
|
struct DukDupIndex { DUK_SCALARTYPE_IMPL(DukDupIndex, duk_idx_t) };
|
||||||
|
//struct DukObject { DUK_SCALARTYPE_IMPL(DukObject, duk_idx_t) };
|
||||||
|
|
||||||
|
struct DukCFunction {
|
||||||
|
DUK_TYPE_ID(DukCFunction)
|
||||||
|
duk_c_function func;
|
||||||
|
duk_int_t nargs;
|
||||||
|
DukCFunction(duk_c_function func, duk_int_t nargs = DUK_VARARGS) :
|
||||||
|
func(func), nargs(nargs) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DukObject {
|
||||||
|
DUK_TYPE_ID(DukObject)
|
||||||
|
const DukPropListEntry* props;
|
||||||
|
DukObject(const DukPropListEntry* props = nullptr) :
|
||||||
|
props(props) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DukGetter {
|
||||||
|
DUK_SCALARTYPE_IMPL(DukGetter, duk_c_function)
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DukGetterSetter {
|
||||||
|
DUK_TYPE_ID(DukGetterSetter)
|
||||||
|
duk_c_function getter, setter;
|
||||||
|
DukGetterSetter(duk_c_function getter, duk_c_function setter) :
|
||||||
|
getter(getter), setter(setter) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DukProxy {
|
||||||
|
DUK_TYPE_ID(DukProxy)
|
||||||
|
duk_c_function getter, setter;
|
||||||
|
DukProxy(duk_c_function getter, duk_c_function setter) :
|
||||||
|
getter(getter), setter(setter) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DukPropListEntry
|
||||||
|
{
|
||||||
|
const char* key;
|
||||||
|
DukPropTypeID typeId;
|
||||||
|
bool writable, enumerable;
|
||||||
|
|
||||||
|
union Value {
|
||||||
|
DUK_DECLVAL_MEMBER(DukInt, dukInt)
|
||||||
|
DUK_DECLVAL_MEMBER(DukUInt, dukUInt)
|
||||||
|
DUK_DECLVAL_MEMBER(DukNumber, dukNumber)
|
||||||
|
DUK_DECLVAL_MEMBER(DukBoolean, dukBoolean)
|
||||||
|
DUK_DECLVAL_MEMBER(DukString, dukString)
|
||||||
|
DUK_DECLVAL_MEMBER(DukPointer, dukPointer)
|
||||||
|
DUK_DECLVAL_MEMBER(DukCFunction, dukCFunction)
|
||||||
|
DUK_DECLVAL_MEMBER(DukGetter, dukGetter)
|
||||||
|
DUK_DECLVAL_MEMBER(DukGetterSetter, dukGetterSetter)
|
||||||
|
DUK_DECLVAL_MEMBER(DukDupIndex, dukDupIndex)
|
||||||
|
DUK_DECLVAL_MEMBER(DukObject, dukObject)
|
||||||
|
DUK_DECLVAL_MEMBER(DukProxy, dukProxy)
|
||||||
|
} value;
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
DukPropListEntry(const char* key, T value, bool writable = false, bool enumerable = true) :
|
||||||
|
key(key), typeId(T::_TYPE), writable(writable), enumerable(enumerable), value(value) {}
|
||||||
|
|
||||||
|
DukPropListEntry(nullptr_t) :
|
||||||
|
key(nullptr), value(DukInt(0)) {}
|
||||||
|
|
||||||
|
DukPropListEntry(const char* key) :
|
||||||
|
key(key), typeId(Type_DukInt), writable(false), enumerable(false), value(DukInt(0)) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// todo proxy object
|
||||||
|
// todo DukObjectConfig_DummyConstructor
|
||||||
|
// todo DukObjectConfig_Finalizer
|
||||||
|
// todo DukObjectConfig_Freeze
|
||||||
|
|
||||||
|
void DukPutPropList(duk_context* ctx, duk_idx_t obj_idx, const DukPropListEntry* props);
|
||||||
|
};
|
|
@ -0,0 +1,52 @@
|
||||||
|
#include <stdafx.h>
|
||||||
|
#include "ScriptAPI.h"
|
||||||
|
|
||||||
|
void ScriptAPI::Define_AddressRange(duk_context* ctx)
|
||||||
|
{
|
||||||
|
DefineGlobalClass(ctx, "AddressRange", js_AddressRange__constructor);
|
||||||
|
|
||||||
|
struct { const char* key; uint32_t start, end; } ranges[] = {
|
||||||
|
{ "ADDR_ANY", 0x00000000, 0xFFFFFFFF },
|
||||||
|
{ "ADDR_ANY_KUSEG", 0x00000000, 0x7FFFFFFF },
|
||||||
|
{ "ADDR_ANY_KSEG0", 0x80000000, 0x9FFFFFFF },
|
||||||
|
{ "ADDR_ANY_KSEG1", 0xA0000000, 0xBFFFFFFF },
|
||||||
|
{ "ADDR_ANY_KSEG2", 0xC0000000, 0xFFFFFFFF },
|
||||||
|
{ "ADDR_ANY_RDRAM", 0x80000000, 0x807FFFFF },
|
||||||
|
{ "ADDR_ANY_RDRAM_UNC", 0xA0000000, 0xA07FFFFF },
|
||||||
|
{ "ADDR_ANY_CART_ROM", 0x90000000, 0x95FFFFFF },
|
||||||
|
{ "ADDR_ANY_CART_ROM_UNC", 0xB0000000, 0xB5FFFFFF },
|
||||||
|
{ nullptr, 0, 0 }
|
||||||
|
};
|
||||||
|
|
||||||
|
duk_push_global_object(ctx);
|
||||||
|
duk_push_c_function(ctx, js_AddressRange__constructor, DUK_VARARGS);
|
||||||
|
for(int i = 0; ranges[i].key != nullptr; i++)
|
||||||
|
{
|
||||||
|
duk_push_string(ctx, ranges[i].key);
|
||||||
|
duk_dup(ctx, -2);
|
||||||
|
duk_push_uint(ctx, ranges[i].start);
|
||||||
|
duk_push_uint(ctx, ranges[i].end);
|
||||||
|
duk_new(ctx, 2);
|
||||||
|
duk_def_prop(ctx, -4, DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_SET_ENUMERABLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_pop_n(ctx, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_AddressRange__constructor(duk_context* ctx)
|
||||||
|
{
|
||||||
|
CheckArgs(ctx, { Arg_Number, Arg_Number });
|
||||||
|
|
||||||
|
duk_to_uint32(ctx, 0);
|
||||||
|
duk_to_uint32(ctx, 1);
|
||||||
|
|
||||||
|
duk_push_this(ctx);
|
||||||
|
duk_dup(ctx, 0);
|
||||||
|
duk_put_prop_string(ctx, -2, "start");
|
||||||
|
duk_dup(ctx, 1);
|
||||||
|
duk_put_prop_string(ctx, -2, "end");
|
||||||
|
duk_freeze(ctx, -1);
|
||||||
|
duk_pop(ctx);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,241 @@
|
||||||
|
#include <stdafx.h>
|
||||||
|
#include "ScriptAPI.h"
|
||||||
|
#include "N64Image.h"
|
||||||
|
|
||||||
|
#pragma warning(disable: 4702) // disable unreachable code warning
|
||||||
|
|
||||||
|
using namespace ScriptAPI;
|
||||||
|
|
||||||
|
static CN64Image* GetThisImage(duk_context* ctx)
|
||||||
|
{
|
||||||
|
duk_push_this(ctx);
|
||||||
|
duk_get_prop_string(ctx, -1, HS_n64ImagePtr);
|
||||||
|
CN64Image* image = (CN64Image*)duk_get_pointer(ctx, -1);
|
||||||
|
duk_pop_n(ctx, 2);
|
||||||
|
|
||||||
|
if (image == nullptr)
|
||||||
|
{
|
||||||
|
duk_push_error_object(ctx, DUK_ERR_ERROR, "internal image object is null");
|
||||||
|
return duk_throw(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
return image;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScriptAPI::Define_N64Image(duk_context* ctx)
|
||||||
|
{
|
||||||
|
const DukPropListEntry prototype[] = {
|
||||||
|
{ "toPNG", DukCFunction(js_N64Image_toPNG) },
|
||||||
|
{ "update", DukCFunction(js_N64Image_update) },
|
||||||
|
{ nullptr}
|
||||||
|
};
|
||||||
|
|
||||||
|
const DukPropListEntry staticProps[] = {
|
||||||
|
{ "fromPNG", DukCFunction(js_N64Image_static_fromPNG) },
|
||||||
|
{ "format", DukCFunction(js_N64Image_static_format) },
|
||||||
|
{ "bpp", DukCFunction(js_N64Image_static_bpp) },
|
||||||
|
{ nullptr }
|
||||||
|
};
|
||||||
|
|
||||||
|
DefineGlobalClass(ctx, "N64Image", js_N64Image__constructor, prototype, staticProps);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void InitImageObjectProps(duk_context* ctx, duk_idx_t idx, CN64Image* image)
|
||||||
|
{
|
||||||
|
idx = duk_normalize_index(ctx, idx);
|
||||||
|
|
||||||
|
duk_push_external_buffer(ctx);
|
||||||
|
duk_config_buffer(ctx, -1, image->PixelData().data(), image->PixelData().size());
|
||||||
|
duk_push_buffer_object(ctx, -1, 0, image->PixelData().size(), DUK_BUFOBJ_NODEJS_BUFFER);
|
||||||
|
duk_remove(ctx, -2);
|
||||||
|
|
||||||
|
duk_idx_t pixels_idx = duk_normalize_index(ctx, -1);
|
||||||
|
|
||||||
|
if (image->UsesPalette())
|
||||||
|
{
|
||||||
|
duk_push_external_buffer(ctx);
|
||||||
|
duk_config_buffer(ctx, -1, image->PaletteData().data(), image->PaletteData().size());
|
||||||
|
duk_push_buffer_object(ctx, -2, 0, image->PaletteData().size(), DUK_BUFOBJ_NODEJS_BUFFER);
|
||||||
|
duk_remove(ctx, -2);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
duk_push_null(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_idx_t palette_idx = duk_normalize_index(ctx, -1);
|
||||||
|
|
||||||
|
const DukPropListEntry props[] = {
|
||||||
|
{ HS_n64ImagePtr, DukPointer(image) },
|
||||||
|
{ "pixels", DukDupIndex(pixels_idx) },
|
||||||
|
{ "palette", DukDupIndex(palette_idx) },
|
||||||
|
{ "width", DukUInt(image->Width()) },
|
||||||
|
{ "height", DukUInt(image->Height()) },
|
||||||
|
{ nullptr }
|
||||||
|
};
|
||||||
|
|
||||||
|
DukPutPropList(ctx, idx, props);
|
||||||
|
|
||||||
|
duk_pop_n(ctx, 2);
|
||||||
|
|
||||||
|
duk_push_c_function(ctx, ScriptAPI::js_N64Image__finalizer, 1);
|
||||||
|
duk_set_finalizer(ctx, idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_N64Image__constructor(duk_context* ctx)
|
||||||
|
{
|
||||||
|
CheckArgs(ctx, { Arg_Number, Arg_Number, Arg_OptNumber, Arg_OptBufferData, Arg_OptBufferData });
|
||||||
|
|
||||||
|
if (!duk_is_constructor_call(ctx))
|
||||||
|
{
|
||||||
|
return DUK_RET_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t pixelDataSize = 0, paletteDataSize = 0;
|
||||||
|
|
||||||
|
size_t width = duk_get_uint(ctx, 0);
|
||||||
|
size_t height = duk_get_uint(ctx, 1);
|
||||||
|
int format = duk_get_int_default(ctx, 2, IMG_RGBA32);
|
||||||
|
void* pixelData = duk_get_buffer_data_default(ctx, 3, &pixelDataSize, nullptr, 0);
|
||||||
|
void* paletteData = duk_get_buffer_data_default(ctx, 4, &paletteDataSize, nullptr, 0);
|
||||||
|
|
||||||
|
CN64Image* image = new CN64Image();
|
||||||
|
int result = image->Init(format, width, height, pixelData, pixelDataSize, paletteData, paletteDataSize);
|
||||||
|
|
||||||
|
if (result != N64IMG_OK)
|
||||||
|
{
|
||||||
|
duk_push_error_object(ctx, DUK_ERR_ERROR, "failed to initialize image (%s)",
|
||||||
|
CN64Image::ResultCodeName(result));
|
||||||
|
return duk_throw(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_push_this(ctx);
|
||||||
|
InitImageObjectProps(ctx, -1, image);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_N64Image__finalizer(duk_context* ctx)
|
||||||
|
{
|
||||||
|
duk_get_prop_string(ctx, 0, HS_n64ImagePtr);
|
||||||
|
CN64Image* image = (CN64Image*)duk_get_pointer(ctx, -1);
|
||||||
|
if (image == nullptr)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
delete image;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_N64Image_static_fromPNG(duk_context* ctx)
|
||||||
|
{
|
||||||
|
CheckArgs(ctx, { Arg_BufferData, Arg_OptNumber });
|
||||||
|
|
||||||
|
int format = duk_get_int_default(ctx, 1, IMG_RGBA32);
|
||||||
|
|
||||||
|
if (CN64Image::BitsPerPixel(format) == 0)
|
||||||
|
{
|
||||||
|
duk_push_error_object(ctx, DUK_RET_TYPE_ERROR, "invalid format");
|
||||||
|
return duk_throw(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t pngSize;
|
||||||
|
uint8_t* pngData = (uint8_t*)duk_get_buffer_data(ctx, 0, &pngSize);
|
||||||
|
|
||||||
|
CN64Image* image = new CN64Image();
|
||||||
|
int result = image->Init(format, pngData, pngSize);
|
||||||
|
if (result != N64IMG_OK)
|
||||||
|
{
|
||||||
|
delete image;
|
||||||
|
duk_push_error_object(ctx, DUK_ERR_ERROR, "failed to initialize image (%s)",
|
||||||
|
CN64Image::ResultCodeName(result), result);
|
||||||
|
return duk_throw(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_push_object(ctx);
|
||||||
|
duk_get_global_string(ctx, "N64Image");
|
||||||
|
duk_get_prop_string(ctx, -1, "prototype");
|
||||||
|
duk_set_prototype(ctx, -3);
|
||||||
|
duk_pop(ctx);
|
||||||
|
|
||||||
|
InitImageObjectProps(ctx, -1, image);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_N64Image_static_format(duk_context* ctx)
|
||||||
|
{
|
||||||
|
CheckArgs(ctx, { Arg_Number, Arg_Number, Arg_OptNumber });
|
||||||
|
|
||||||
|
duk_uint_t gbiFmt = duk_get_uint(ctx, 0);
|
||||||
|
duk_uint_t gbiSiz = duk_get_uint(ctx, 1);
|
||||||
|
duk_uint_t gbiTlutFmt = duk_get_uint_default(ctx, 2, G_TT_NONE);
|
||||||
|
|
||||||
|
int format = (gbiFmt << 3) | gbiSiz | gbiTlutFmt;
|
||||||
|
|
||||||
|
switch (format)
|
||||||
|
{
|
||||||
|
case IMG_RGBA16:
|
||||||
|
case IMG_RGBA32:
|
||||||
|
case IMG_CI4_RGBA16:
|
||||||
|
case IMG_CI4_IA16:
|
||||||
|
case IMG_CI8_RGBA16:
|
||||||
|
case IMG_CI8_IA16:
|
||||||
|
case IMG_IA4:
|
||||||
|
case IMG_IA8:
|
||||||
|
case IMG_IA16:
|
||||||
|
case IMG_I4:
|
||||||
|
case IMG_I8:
|
||||||
|
duk_push_number(ctx, format);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
duk_push_number(ctx, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_N64Image_static_bpp(duk_context* ctx)
|
||||||
|
{
|
||||||
|
CheckArgs(ctx, { Arg_Number });
|
||||||
|
|
||||||
|
duk_uint_t format = duk_get_uint(ctx, 0);
|
||||||
|
|
||||||
|
int bpp = 0;
|
||||||
|
|
||||||
|
switch (format)
|
||||||
|
{
|
||||||
|
case G_IM_SIZ_4b: bpp = 4; break;
|
||||||
|
case G_IM_SIZ_8b: bpp = 8; break;
|
||||||
|
case G_IM_SIZ_16b: bpp = 16; break;
|
||||||
|
case G_IM_SIZ_32b: bpp = 32; break;
|
||||||
|
default: bpp = CN64Image::BitsPerPixel(format); break;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_push_number(ctx, bpp);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_N64Image_toPNG(duk_context* ctx)
|
||||||
|
{
|
||||||
|
CN64Image* image = GetThisImage(ctx);
|
||||||
|
|
||||||
|
std::vector<uint8_t> png;
|
||||||
|
image->ToPNG(png);
|
||||||
|
|
||||||
|
void* pngCopy = duk_push_buffer(ctx, png.size(), false);
|
||||||
|
duk_push_buffer_object(ctx, -1, 0, png.size(), DUK_BUFOBJ_NODEJS_BUFFER);
|
||||||
|
memcpy(pngCopy, png.data(), png.size());
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_N64Image_update(duk_context* ctx)
|
||||||
|
{
|
||||||
|
CN64Image* image = GetThisImage(ctx);
|
||||||
|
int result = image->UpdateBitmap();
|
||||||
|
if (result != N64IMG_OK)
|
||||||
|
{
|
||||||
|
duk_push_error_object(ctx, DUK_ERR_ERROR, "bitmap update failed (%s)",
|
||||||
|
CN64Image::ResultCodeName(result));
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
#include <stdafx.h>
|
||||||
|
#include "ScriptAPI.h"
|
||||||
|
|
||||||
|
void ScriptAPI::Define_Number_prototype_hex(duk_context *ctx)
|
||||||
|
{
|
||||||
|
duk_get_global_string(ctx, "Number");
|
||||||
|
duk_get_prop_string(ctx, -1, "prototype");
|
||||||
|
duk_push_c_function(ctx, js_Number_prototype_hex, DUK_VARARGS);
|
||||||
|
duk_put_prop_string(ctx, -2, "hex");
|
||||||
|
duk_pop_n(ctx, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_Number_prototype_hex(duk_context *ctx)
|
||||||
|
{
|
||||||
|
CheckArgs(ctx, { Arg_OptNumber });
|
||||||
|
|
||||||
|
duk_uint_t value;
|
||||||
|
duk_uint_t length = 8;
|
||||||
|
char hexString[64];
|
||||||
|
|
||||||
|
length = duk_get_uint_default(ctx, 0, 8);
|
||||||
|
|
||||||
|
duk_push_this(ctx);
|
||||||
|
value = (duk_uint_t)duk_get_number(ctx, -1);
|
||||||
|
duk_pop(ctx);
|
||||||
|
|
||||||
|
snprintf(hexString, sizeof(hexString), "%0*X", length, value);
|
||||||
|
|
||||||
|
duk_push_string(ctx, hexString);
|
||||||
|
return 1;
|
||||||
|
}
|
|
@ -0,0 +1,119 @@
|
||||||
|
#include <stdafx.h>
|
||||||
|
#include "ScriptAPI.h"
|
||||||
|
#include "JSServerWorker.h"
|
||||||
|
|
||||||
|
static CJSServerWorker* GetThisServer(duk_context* ctx);
|
||||||
|
|
||||||
|
void ScriptAPI::Define_Server(duk_context* ctx)
|
||||||
|
{
|
||||||
|
const DukPropListEntry prototype[] = {
|
||||||
|
{ "listen", DukCFunction(js_Server_listen) },
|
||||||
|
{ "close", DukCFunction(js_Server_close) },
|
||||||
|
{ "on", DukCFunction(js__Emitter_on) },
|
||||||
|
{ "off", DukCFunction(js__Emitter_off) },
|
||||||
|
{ "port", DukGetter(js_Server__get_port) },
|
||||||
|
{ "address", DukGetter(js_Server__get_address) },
|
||||||
|
{ "addressFamily", DukGetter(js_Server__get_addressFamily) },
|
||||||
|
{ nullptr }
|
||||||
|
};
|
||||||
|
|
||||||
|
DefineGlobalClass(ctx, "Server", js_Server__constructor, prototype);
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_Server__constructor(duk_context* ctx)
|
||||||
|
{
|
||||||
|
CheckArgs(ctx, {});
|
||||||
|
|
||||||
|
if (!duk_is_constructor_call(ctx))
|
||||||
|
{
|
||||||
|
return DUK_RET_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
CScriptInstance* inst = GetInstance(ctx);
|
||||||
|
|
||||||
|
duk_push_this(ctx);
|
||||||
|
void* objectHeapPtr = duk_get_heapptr(ctx, -1);
|
||||||
|
|
||||||
|
InitEmitter(ctx, -1, {
|
||||||
|
"close",
|
||||||
|
"connection",
|
||||||
|
"error",
|
||||||
|
"listening"
|
||||||
|
});
|
||||||
|
|
||||||
|
duk_push_c_function(ctx, js_Server__finalizer, 1);
|
||||||
|
duk_set_finalizer(ctx, -2);
|
||||||
|
|
||||||
|
CJSServerWorker* serverWorker = new CJSServerWorker(inst, objectHeapPtr);
|
||||||
|
duk_push_pointer(ctx, serverWorker);
|
||||||
|
duk_put_prop_string(ctx, -2, HS_serverWorkerPtr);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_Server__finalizer(duk_context* ctx)
|
||||||
|
{
|
||||||
|
UnrefObject(ctx, 0);
|
||||||
|
duk_get_prop_string(ctx, 0, HS_serverWorkerPtr);
|
||||||
|
CJSServerWorker* serverWorker = (CJSServerWorker*)duk_get_pointer(ctx, -1);
|
||||||
|
delete serverWorker;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_Server_listen(duk_context* ctx)
|
||||||
|
{
|
||||||
|
CheckArgs(ctx, { Arg_Number, Arg_String, Arg_OptFunction });
|
||||||
|
CJSServerWorker* serverWorker = GetThisServer(ctx);
|
||||||
|
|
||||||
|
unsigned short port = (unsigned short)duk_get_int(ctx, 0);
|
||||||
|
const char* address = duk_get_string(ctx, 1);
|
||||||
|
|
||||||
|
// todo callback
|
||||||
|
|
||||||
|
duk_push_this(ctx);
|
||||||
|
RefObject(ctx, -1);
|
||||||
|
|
||||||
|
serverWorker->Init(address, port);
|
||||||
|
serverWorker->StartWorkerProc();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_Server_close(duk_context* ctx)
|
||||||
|
{
|
||||||
|
CheckArgs(ctx, {});
|
||||||
|
|
||||||
|
CJSServerWorker* serverWorker = GetThisServer(ctx);
|
||||||
|
serverWorker->StopWorkerProc();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_Server__get_port(duk_context* ctx)
|
||||||
|
{
|
||||||
|
CJSServerWorker* serverWorker = GetThisServer(ctx);
|
||||||
|
duk_push_uint(ctx, serverWorker->GetPort());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_Server__get_address(duk_context* ctx)
|
||||||
|
{
|
||||||
|
CJSServerWorker* serverWorker = GetThisServer(ctx);
|
||||||
|
duk_push_string(ctx, serverWorker->GetAddress().c_str());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_Server__get_addressFamily(duk_context* ctx)
|
||||||
|
{
|
||||||
|
CJSServerWorker* serverWorker = GetThisServer(ctx);
|
||||||
|
duk_push_string(ctx, serverWorker->GetFamily());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
CJSServerWorker* GetThisServer(duk_context* ctx)
|
||||||
|
{
|
||||||
|
duk_push_this(ctx);
|
||||||
|
duk_get_prop_string(ctx, -1, HS_serverWorkerPtr);
|
||||||
|
CJSServerWorker* serverWorker = (CJSServerWorker*)duk_get_pointer(ctx, -1);
|
||||||
|
duk_pop_n(ctx, 2);
|
||||||
|
return serverWorker;
|
||||||
|
}
|
|
@ -0,0 +1,327 @@
|
||||||
|
#include <stdafx.h>
|
||||||
|
#include "ScriptAPI.h"
|
||||||
|
#include "JSSocketWorker.h"
|
||||||
|
|
||||||
|
#pragma warning(disable: 4702) // disable unreachable code warning
|
||||||
|
|
||||||
|
static CJSSocketWorker* GetThisSocket(duk_context* ctx);
|
||||||
|
static duk_ret_t RequireBufferDataOrString(duk_context* ctx, duk_idx_t idx, const char** data, duk_size_t* size);
|
||||||
|
static duk_int_t RegisterWriteCallback(duk_context* ctx, duk_idx_t idx);
|
||||||
|
static void RegisterWriteEndCallback(duk_context* ctx, duk_idx_t idx);
|
||||||
|
|
||||||
|
void ScriptAPI::Define_Socket(duk_context* ctx)
|
||||||
|
{
|
||||||
|
const DukPropListEntry prototype[] = {
|
||||||
|
{ "connect", DukCFunction(js_Socket_connect) },
|
||||||
|
{ "write", DukCFunction(js_Socket_write) },
|
||||||
|
{ "end", DukCFunction(js_Socket_end) },
|
||||||
|
{ "close", DukCFunction(js_Socket_close) },
|
||||||
|
{ "on", DukCFunction(js__Emitter_on) },
|
||||||
|
{ "off", DukCFunction(js__Emitter_off) },
|
||||||
|
{ "localAddress", DukGetter(js_Socket__get_localAddress) },
|
||||||
|
{ "localPort", DukGetter(js_Socket__get_localPort) },
|
||||||
|
{ "remoteAddress", DukGetter(js_Socket__get_remoteAddress) },
|
||||||
|
{ "remotePort", DukGetter(js_Socket__get_remotePort) },
|
||||||
|
{ "addressFamily", DukGetter(js_Socket__get_addressFamily) },
|
||||||
|
{ nullptr }
|
||||||
|
};
|
||||||
|
|
||||||
|
DefineGlobalClass(ctx, "Socket", js_Socket__constructor, prototype, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_Socket__constructor(duk_context* ctx)
|
||||||
|
{
|
||||||
|
CheckArgs(ctx, { Arg_OptObject });
|
||||||
|
|
||||||
|
if (!duk_is_constructor_call(ctx))
|
||||||
|
{
|
||||||
|
return DUK_RET_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool bAllowHalfOpen = false;
|
||||||
|
|
||||||
|
CScriptInstance* inst = GetInstance(ctx);
|
||||||
|
|
||||||
|
if (duk_is_object(ctx, 0))
|
||||||
|
{
|
||||||
|
if (duk_has_prop_string(ctx, 0, "allowHalfOpen"))
|
||||||
|
{
|
||||||
|
duk_get_prop_string(ctx, 0, "allowHalfOpen");
|
||||||
|
bAllowHalfOpen = (bool)duk_get_boolean_default(ctx, -1, 0);
|
||||||
|
duk_pop(ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_push_this(ctx);
|
||||||
|
void* objectHeapPtr = duk_get_heapptr(ctx, -1);
|
||||||
|
|
||||||
|
InitEmitter(ctx, -1, {
|
||||||
|
"data",
|
||||||
|
"end",
|
||||||
|
"connect",
|
||||||
|
"error",
|
||||||
|
"close",
|
||||||
|
"drain",
|
||||||
|
"lookup"
|
||||||
|
});
|
||||||
|
|
||||||
|
duk_push_uint(ctx, 0);
|
||||||
|
duk_put_prop_string(ctx, -2, HS_socketNextWriteCallbackId);
|
||||||
|
|
||||||
|
duk_push_object(ctx);
|
||||||
|
duk_put_prop_string(ctx, -2, HS_socketWriteCallbacks);
|
||||||
|
|
||||||
|
duk_push_array(ctx);
|
||||||
|
duk_put_prop_string(ctx, -2, HS_socketWriteEndCallbacks);
|
||||||
|
|
||||||
|
CJSSocketWorker* socketWorker = new CJSSocketWorker(inst, objectHeapPtr, bAllowHalfOpen);
|
||||||
|
|
||||||
|
duk_push_pointer(ctx, (void*)socketWorker);
|
||||||
|
duk_put_prop_string(ctx, -2, HS_socketWorkerPtr);
|
||||||
|
|
||||||
|
duk_push_c_function(ctx, js_Socket__finalizer, 1);
|
||||||
|
duk_set_finalizer(ctx, -2);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_Socket__finalizer(duk_context* ctx)
|
||||||
|
{
|
||||||
|
duk_get_prop_string(ctx, 0, HS_socketWorkerPtr);
|
||||||
|
CJSSocketWorker* socketWorker = (CJSSocketWorker*)duk_get_pointer(ctx, -1);
|
||||||
|
|
||||||
|
if (socketWorker == nullptr)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
UnrefObject(ctx, 0);
|
||||||
|
delete socketWorker;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_Socket_connect(duk_context* ctx)
|
||||||
|
{
|
||||||
|
CheckArgs(ctx, { Arg_Number, Arg_String, Arg_OptFunction });
|
||||||
|
|
||||||
|
CJSSocketWorker* socketWorker = GetThisSocket(ctx);
|
||||||
|
|
||||||
|
duk_idx_t nargs = duk_get_top(ctx);
|
||||||
|
unsigned short port = (unsigned short)duk_get_uint(ctx, 0);
|
||||||
|
const char* host = duk_get_string(ctx, 1);
|
||||||
|
|
||||||
|
if (nargs == 3)
|
||||||
|
{
|
||||||
|
// add 'connect' event listener
|
||||||
|
duk_push_c_function(ctx, js__Emitter_on, 2); // todo once
|
||||||
|
duk_push_this(ctx);
|
||||||
|
duk_push_string(ctx, "connect");
|
||||||
|
duk_pull(ctx, 2);
|
||||||
|
duk_pcall_method(ctx, 2);
|
||||||
|
duk_pop(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_push_this(ctx);
|
||||||
|
RefObject(ctx, -1);
|
||||||
|
|
||||||
|
socketWorker->Init(host, port);
|
||||||
|
socketWorker->StartWorkerProc();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_Socket_write(duk_context* ctx)
|
||||||
|
{
|
||||||
|
CJSSocketWorker* socketWorker = GetThisSocket(ctx);
|
||||||
|
|
||||||
|
const char* data;
|
||||||
|
duk_size_t size;
|
||||||
|
duk_int_t callbackId = -1;
|
||||||
|
|
||||||
|
duk_idx_t nargs = duk_get_top(ctx);
|
||||||
|
RequireBufferDataOrString(ctx, 0, &data, &size);
|
||||||
|
|
||||||
|
if (nargs == 2 && duk_is_function(ctx, 1))
|
||||||
|
{
|
||||||
|
callbackId = RegisterWriteCallback(ctx, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
socketWorker->Write((char*)data, size, callbackId, false);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_Socket_end(duk_context* ctx)
|
||||||
|
{
|
||||||
|
CJSSocketWorker* socketWorker = GetThisSocket(ctx);
|
||||||
|
|
||||||
|
const char* data;
|
||||||
|
duk_size_t size;
|
||||||
|
duk_int_t callbackId = -1;
|
||||||
|
|
||||||
|
duk_idx_t nargs = duk_get_top(ctx);
|
||||||
|
RequireBufferDataOrString(ctx, 0, &data, &size);
|
||||||
|
|
||||||
|
if (nargs == 2 && duk_is_function(ctx, 1))
|
||||||
|
{
|
||||||
|
RegisterWriteEndCallback(ctx, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
socketWorker->Write((char*)data, size, callbackId, true);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_Socket__invokeWriteCallback(duk_context* ctx)
|
||||||
|
{
|
||||||
|
duk_int_t callbackId = duk_get_int(ctx, 0);
|
||||||
|
|
||||||
|
duk_push_this(ctx);
|
||||||
|
duk_get_prop_string(ctx, -1, HS_socketWriteCallbacks);
|
||||||
|
duk_get_prop_index(ctx, -1, callbackId);
|
||||||
|
duk_dup(ctx, -3);
|
||||||
|
|
||||||
|
if (duk_pcall_method(ctx, 0) != 0)
|
||||||
|
{
|
||||||
|
duk_throw(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_pop(ctx);
|
||||||
|
duk_del_prop_index(ctx, -1, callbackId);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_Socket__invokeWriteEndCallbacks(duk_context* ctx)
|
||||||
|
{
|
||||||
|
duk_push_this(ctx);
|
||||||
|
duk_get_prop_string(ctx, -1, HS_socketWriteEndCallbacks);
|
||||||
|
duk_size_t numCallbacks = duk_get_length(ctx, -1);
|
||||||
|
for (duk_size_t i = 0; i < numCallbacks; i++)
|
||||||
|
{
|
||||||
|
duk_get_prop_index(ctx, -1, i);
|
||||||
|
duk_dup(ctx, -3);
|
||||||
|
|
||||||
|
if (duk_pcall_method(ctx, 0) != 0)
|
||||||
|
{
|
||||||
|
duk_throw(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_pop(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_pop(ctx);
|
||||||
|
|
||||||
|
// reset array
|
||||||
|
duk_push_array(ctx);
|
||||||
|
duk_put_prop_string(ctx, -2, HS_socketWriteEndCallbacks);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_Socket_close(duk_context* ctx)
|
||||||
|
{
|
||||||
|
CJSSocketWorker* socketWorker = GetThisSocket(ctx);
|
||||||
|
socketWorker->StopWorkerProc();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_Socket__get_localAddress(duk_context* ctx)
|
||||||
|
{
|
||||||
|
CJSSocketWorker* socketWorker = GetThisSocket(ctx);
|
||||||
|
duk_push_string(ctx, socketWorker->GetLocalAddress().c_str());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_Socket__get_localPort(duk_context* ctx)
|
||||||
|
{
|
||||||
|
CJSSocketWorker* socketWorker = GetThisSocket(ctx);
|
||||||
|
duk_push_uint(ctx, socketWorker->GetLocalPort());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_Socket__get_remoteAddress(duk_context* ctx)
|
||||||
|
{
|
||||||
|
CJSSocketWorker* socketWorker = GetThisSocket(ctx);
|
||||||
|
duk_push_string(ctx, socketWorker->GetRemoteAddress().c_str());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_Socket__get_remotePort(duk_context* ctx)
|
||||||
|
{
|
||||||
|
CJSSocketWorker* socketWorker = GetThisSocket(ctx);
|
||||||
|
duk_push_uint(ctx, socketWorker->GetRemotePort());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_Socket__get_addressFamily(duk_context* ctx)
|
||||||
|
{
|
||||||
|
CJSSocketWorker* socketWorker = GetThisSocket(ctx);
|
||||||
|
duk_push_string(ctx, socketWorker->GetFamily());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
CJSSocketWorker* GetThisSocket(duk_context* ctx)
|
||||||
|
{
|
||||||
|
duk_push_this(ctx);
|
||||||
|
duk_get_prop_string(ctx, -1, HS_socketWorkerPtr);
|
||||||
|
CJSSocketWorker* socketWorker = (CJSSocketWorker*)duk_get_pointer(ctx, -1);
|
||||||
|
duk_pop_n(ctx, 2);
|
||||||
|
|
||||||
|
if (socketWorker == nullptr)
|
||||||
|
{
|
||||||
|
duk_push_error_object(ctx, DUK_ERR_ERROR, "internal socket object is null");
|
||||||
|
return duk_throw(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
return socketWorker;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t RequireBufferDataOrString(duk_context* ctx, duk_idx_t idx, const char** data, duk_size_t* size)
|
||||||
|
{
|
||||||
|
if (duk_is_buffer_data(ctx, idx))
|
||||||
|
{
|
||||||
|
*data = (const char*)duk_get_buffer_data(ctx, idx, size);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else if (duk_is_string(ctx, idx))
|
||||||
|
{
|
||||||
|
*data = duk_get_lstring(ctx, idx, size);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_push_error_object(ctx, DUK_ERR_TYPE_ERROR, "arg %d must be buffer-like or string", idx);
|
||||||
|
return duk_throw(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_int_t RegisterWriteCallback(duk_context* ctx, duk_idx_t idx)
|
||||||
|
{
|
||||||
|
idx = duk_normalize_index(ctx, idx);
|
||||||
|
|
||||||
|
duk_push_this(ctx);
|
||||||
|
duk_get_prop_string(ctx, -1, HS_socketNextWriteCallbackId);
|
||||||
|
duk_int_t callbackId = duk_get_int(ctx, -1);
|
||||||
|
duk_pop(ctx);
|
||||||
|
|
||||||
|
duk_get_prop_string(ctx, -1, HS_socketWriteCallbacks);
|
||||||
|
duk_dup(ctx, idx);
|
||||||
|
duk_put_prop_index(ctx, -2, callbackId);
|
||||||
|
duk_pop(ctx);
|
||||||
|
|
||||||
|
duk_push_int(ctx, callbackId + 1);
|
||||||
|
duk_put_prop_string(ctx, -2, HS_socketNextWriteCallbackId);
|
||||||
|
|
||||||
|
return callbackId;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RegisterWriteEndCallback(duk_context* ctx, duk_idx_t idx)
|
||||||
|
{
|
||||||
|
idx = duk_normalize_index(ctx, idx);
|
||||||
|
|
||||||
|
duk_push_this(ctx);
|
||||||
|
|
||||||
|
duk_get_prop_string(ctx, -1, HS_socketWriteEndCallbacks);
|
||||||
|
duk_size_t callbackIdx = duk_get_length(ctx, -1);
|
||||||
|
duk_dup(ctx, idx);
|
||||||
|
duk_put_prop_index(ctx, -2, callbackIdx);
|
||||||
|
duk_pop(ctx);
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
#include <stdafx.h>
|
||||||
|
#include <windows.h>
|
||||||
|
#include "ScriptAPI.h"
|
||||||
|
|
||||||
|
void ScriptAPI::Define_alert(duk_context* ctx)
|
||||||
|
{
|
||||||
|
DefineGlobalFunction(ctx, "alert", js_alert);
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_alert(duk_context* ctx)
|
||||||
|
{
|
||||||
|
CheckArgs(ctx, { Arg_Any, Arg_OptAny });
|
||||||
|
duk_idx_t nargs = duk_get_top(ctx);
|
||||||
|
|
||||||
|
const char* message = duk_safe_to_string(ctx, 0);
|
||||||
|
const char* caption = (nargs == 2) ? duk_safe_to_string(ctx, 1) : "";
|
||||||
|
|
||||||
|
HWND mainWindow = (HWND)g_Plugins->MainWindow()->GetWindowHandle();
|
||||||
|
MessageBoxA(mainWindow, message, caption, MB_OK);
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,82 @@
|
||||||
|
#include <stdafx.h>
|
||||||
|
#include "ScriptAPI.h"
|
||||||
|
#include "../Assembler.h"
|
||||||
|
#include <Project64-core/N64System/Mips/OpCodeName.h>
|
||||||
|
|
||||||
|
void ScriptAPI::Define_asm(duk_context *ctx)
|
||||||
|
{
|
||||||
|
const DukPropListEntry props[] = {
|
||||||
|
{ "gprname", DukCFunction(js_asm_gprname) },
|
||||||
|
{ "encode", DukCFunction(js_asm_encode) },
|
||||||
|
{ "decode", DukCFunction(js_asm_decode) },
|
||||||
|
{ nullptr }
|
||||||
|
};
|
||||||
|
|
||||||
|
DefineGlobalInterface(ctx, "asm", props);
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_asm_gprname(duk_context* ctx)
|
||||||
|
{
|
||||||
|
const char* names[32] = {
|
||||||
|
"r0", "at", "v0", "v1", "a0", "a1", "a2", "a3",
|
||||||
|
"t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7",
|
||||||
|
"s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7",
|
||||||
|
"t8", "t9", "k0", "k1", "gp", "sp", "fp", "ra"
|
||||||
|
};
|
||||||
|
|
||||||
|
CheckArgs(ctx, { Arg_Number });
|
||||||
|
|
||||||
|
duk_uint_t idx = duk_get_uint(ctx, 0);
|
||||||
|
|
||||||
|
if (idx < 32)
|
||||||
|
{
|
||||||
|
duk_push_string(ctx, names[idx]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return DUK_RET_RANGE_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_asm_encode(duk_context* ctx)
|
||||||
|
{
|
||||||
|
CheckArgs(ctx, { Arg_String, Arg_OptNumber });
|
||||||
|
|
||||||
|
const char *code = duk_get_string(ctx, 0);
|
||||||
|
uint32_t address = duk_get_uint_default(ctx, 1, 0);
|
||||||
|
|
||||||
|
// TODO: CAssembler's state is not thread safe.
|
||||||
|
// CAssembler should be object-oriented
|
||||||
|
|
||||||
|
uint32_t opcode;
|
||||||
|
if (!CAssembler::AssembleLine(code, &opcode, address))
|
||||||
|
{
|
||||||
|
duk_push_error_object(ctx, DUK_ERR_ERROR, "ASM syntax error");
|
||||||
|
duk_throw(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_push_uint(ctx, opcode);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_asm_decode(duk_context* ctx)
|
||||||
|
{
|
||||||
|
CheckArgs(ctx, { Arg_Number, Arg_OptNumber });
|
||||||
|
|
||||||
|
uint32_t opcode = duk_get_uint(ctx, 0);
|
||||||
|
uint32_t address = duk_get_uint_default(ctx, 1, 0);
|
||||||
|
|
||||||
|
// TODO: Thread safe version of R4300iOpcodeName
|
||||||
|
|
||||||
|
char* code = (char*)R4300iOpcodeName(opcode, address);
|
||||||
|
char* ptab = strchr(code, '\t');
|
||||||
|
if (ptab != nullptr)
|
||||||
|
{
|
||||||
|
*ptab = ' ';
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_push_string(ctx, code);
|
||||||
|
return 1;
|
||||||
|
}
|
|
@ -0,0 +1,127 @@
|
||||||
|
#include <stdafx.h>
|
||||||
|
#include <windows.h>
|
||||||
|
#include "ScriptAPI.h"
|
||||||
|
|
||||||
|
static void ConcatArgs(duk_context* ctx, stdstr& out);
|
||||||
|
|
||||||
|
void ScriptAPI::Define_console(duk_context* ctx)
|
||||||
|
{
|
||||||
|
const DukPropListEntry props[] = {
|
||||||
|
{ "print", DukCFunction(js_console_print) },
|
||||||
|
{ "log", DukCFunction(js_console_log) },
|
||||||
|
{ "error", DukCFunction(js_console_error) },
|
||||||
|
{ "clear", DukCFunction(js_console_clear) },
|
||||||
|
{ "listen", DukCFunction(js_console_listen) },
|
||||||
|
{ nullptr }
|
||||||
|
};
|
||||||
|
|
||||||
|
DefineGlobalInterface(ctx, "console", props);
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_console_print(duk_context* ctx)
|
||||||
|
{
|
||||||
|
stdstr out;
|
||||||
|
ConcatArgs(ctx, out);
|
||||||
|
GetInstance(ctx)->System()->ConsolePrint("%s", out.c_str());
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_console_log(duk_context* ctx)
|
||||||
|
{
|
||||||
|
stdstr out;
|
||||||
|
ConcatArgs(ctx, out);
|
||||||
|
GetInstance(ctx)->System()->ConsoleLog("%s", out.c_str());
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_console_error(duk_context* ctx)
|
||||||
|
{
|
||||||
|
CheckArgs(ctx, { Arg_OptAny });
|
||||||
|
|
||||||
|
if (duk_is_error(ctx, 0))
|
||||||
|
{
|
||||||
|
duk_get_prop_string(ctx, 0, "stack");
|
||||||
|
const char* message = duk_get_string(ctx, -1);
|
||||||
|
GetInstance(ctx)->System()->ConsoleLog("%s", message);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
GetInstance(ctx)->System()->ConsoleLog("%s", duk_safe_to_string(ctx, 0));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_console_clear(duk_context* ctx)
|
||||||
|
{
|
||||||
|
CheckArgs(ctx, {});
|
||||||
|
GetInstance(ctx)->System()->ConsoleClear();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_console_listen(duk_context* ctx)
|
||||||
|
{
|
||||||
|
CScriptInstance* inst = GetInstance(ctx);
|
||||||
|
|
||||||
|
duk_push_global_object(ctx);
|
||||||
|
duk_bool_t haveListener = duk_has_prop_string(ctx, -1, HS_gInputListener);
|
||||||
|
|
||||||
|
if (duk_is_function(ctx, 0))
|
||||||
|
{
|
||||||
|
duk_pull(ctx, 0);
|
||||||
|
duk_put_prop_string(ctx, -2, HS_gInputListener);
|
||||||
|
if (!haveListener)
|
||||||
|
{
|
||||||
|
inst->IncRefCount();
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else if (duk_is_null(ctx, 0))
|
||||||
|
{
|
||||||
|
if (haveListener)
|
||||||
|
{
|
||||||
|
duk_del_prop_string(ctx, -1, HS_gInputListener);
|
||||||
|
inst->DecRefCount();
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ThrowInvalidArgsError(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConcatArgs(duk_context* ctx, stdstr& out)
|
||||||
|
{
|
||||||
|
out = "";
|
||||||
|
duk_idx_t nargs = duk_get_top(ctx);
|
||||||
|
|
||||||
|
// note: global JSON.stringify must be intact
|
||||||
|
|
||||||
|
duk_get_global_string(ctx, "JSON");
|
||||||
|
duk_get_prop_string(ctx, -1, "stringify");
|
||||||
|
duk_remove(ctx, -2);
|
||||||
|
|
||||||
|
for (duk_idx_t n = 0; n < nargs; n++)
|
||||||
|
{
|
||||||
|
if (n != 0)
|
||||||
|
{
|
||||||
|
out += " ";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (duk_is_object(ctx, n))
|
||||||
|
{
|
||||||
|
duk_dup(ctx, n);
|
||||||
|
out += duk_safe_to_string(ctx, -1);
|
||||||
|
out += " ";
|
||||||
|
duk_pop(ctx);
|
||||||
|
duk_dup(ctx, -1);
|
||||||
|
duk_dup(ctx, n);
|
||||||
|
duk_push_null(ctx);
|
||||||
|
duk_push_int(ctx, 2);
|
||||||
|
duk_pcall(ctx, 3);
|
||||||
|
out += duk_safe_to_string(ctx, -1);
|
||||||
|
duk_pop(ctx);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
out += duk_safe_to_string(ctx, n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,500 @@
|
||||||
|
#include "stdafx.h"
|
||||||
|
#include "ScriptAPI.h"
|
||||||
|
#include <Project64-core/N64System/Mips/Register.h>
|
||||||
|
|
||||||
|
#pragma warning(disable: 4702) // disable unreachable code warning
|
||||||
|
|
||||||
|
static duk_ret_t GPRGetImpl(duk_context* ctx, bool bUpper);
|
||||||
|
static duk_ret_t GPRSetImpl(duk_context* ctx, bool bUpper);
|
||||||
|
static duk_ret_t FPRGetImpl(duk_context* ctx, bool bDouble);
|
||||||
|
static duk_ret_t FPRSetImpl(duk_context* ctx, bool bDouble);
|
||||||
|
|
||||||
|
static int FPRIndex(const char* regName);
|
||||||
|
static int GPRIndex(const char* regName);
|
||||||
|
static uint32_t* COP0RegPtr(const char* regName);
|
||||||
|
static uint32_t* CPURegPtr(const char *regName);
|
||||||
|
|
||||||
|
static duk_ret_t ThrowRegInvalidError(duk_context* ctx);
|
||||||
|
static duk_ret_t ThrowRegContextUnavailableError(duk_context* ctx);
|
||||||
|
static duk_ret_t ThrowRegAssignmentTypeError(duk_context* ctx);
|
||||||
|
|
||||||
|
void ScriptAPI::Define_cpu(duk_context* ctx)
|
||||||
|
{
|
||||||
|
// todo cleanup
|
||||||
|
|
||||||
|
#define REG_PROXY_FUNCTIONS(getter, setter) { \
|
||||||
|
{ "get", getter, 2 }, \
|
||||||
|
{ "set", setter, 3 }, \
|
||||||
|
{ nullptr, nullptr, 0 } \
|
||||||
|
}
|
||||||
|
|
||||||
|
const struct {
|
||||||
|
const char *key;
|
||||||
|
const duk_function_list_entry functions[3];
|
||||||
|
} proxies[] = {
|
||||||
|
{ "gpr", REG_PROXY_FUNCTIONS(js_cpu_gpr_get, js_cpu_gpr_set) },
|
||||||
|
{ "ugpr", REG_PROXY_FUNCTIONS(js_cpu_ugpr_get, js_cpu_ugpr_set) },
|
||||||
|
{ "fpr", REG_PROXY_FUNCTIONS(js_cpu_fpr_get, js_cpu_fpr_set) },
|
||||||
|
{ "dfpr", REG_PROXY_FUNCTIONS(js_cpu_dfpr_get, js_cpu_dfpr_set) },
|
||||||
|
{ "cop0", REG_PROXY_FUNCTIONS(js_cpu_cop0_get, js_cpu_cop0_set) },
|
||||||
|
{ nullptr, nullptr }
|
||||||
|
};
|
||||||
|
|
||||||
|
const duk_function_list_entry cpufuncs[] = REG_PROXY_FUNCTIONS(js_cpu_get, js_cpu_set);
|
||||||
|
|
||||||
|
duk_push_global_object(ctx);
|
||||||
|
duk_push_object(ctx);
|
||||||
|
|
||||||
|
for (size_t i = 0; proxies[i].key != nullptr; i++)
|
||||||
|
{
|
||||||
|
duk_push_string(ctx, proxies[i].key);
|
||||||
|
duk_push_object(ctx);
|
||||||
|
duk_push_object(ctx);
|
||||||
|
duk_put_function_list(ctx, -1, proxies[i].functions);
|
||||||
|
duk_push_proxy(ctx, 0);
|
||||||
|
duk_freeze(ctx, -1);
|
||||||
|
duk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_SET_ENUMERABLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_push_object(ctx);
|
||||||
|
duk_put_function_list(ctx, -1, cpufuncs);
|
||||||
|
duk_push_proxy(ctx, 0);
|
||||||
|
duk_freeze(ctx, -1);
|
||||||
|
|
||||||
|
duk_put_prop_string(ctx, -2, "cpu");
|
||||||
|
duk_pop(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_cpu_get(duk_context* ctx)
|
||||||
|
{
|
||||||
|
if (g_Reg == nullptr)
|
||||||
|
{
|
||||||
|
return ThrowRegContextUnavailableError(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* key = duk_get_string(ctx, 1);
|
||||||
|
uint32_t* pReg = CPURegPtr(key);
|
||||||
|
|
||||||
|
if (pReg == nullptr)
|
||||||
|
{
|
||||||
|
duk_get_prop_string(ctx, 0, key);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_push_uint(ctx, *pReg);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_cpu_set(duk_context* ctx)
|
||||||
|
{
|
||||||
|
if (g_Reg == nullptr)
|
||||||
|
{
|
||||||
|
return ThrowRegContextUnavailableError(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t* pReg = CPURegPtr(duk_get_string(ctx, 1));
|
||||||
|
|
||||||
|
if (!duk_is_number(ctx, 2) || pReg == nullptr)
|
||||||
|
{
|
||||||
|
return ThrowRegAssignmentTypeError(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
*pReg = duk_get_uint(ctx, 2);
|
||||||
|
duk_push_true(ctx);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_cpu_gpr_get(duk_context* ctx)
|
||||||
|
{
|
||||||
|
return GPRGetImpl(ctx, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_cpu_gpr_set(duk_context* ctx)
|
||||||
|
{
|
||||||
|
return GPRSetImpl(ctx, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_cpu_ugpr_get(duk_context* ctx)
|
||||||
|
{
|
||||||
|
return GPRGetImpl(ctx, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_cpu_ugpr_set(duk_context* ctx)
|
||||||
|
{
|
||||||
|
return GPRSetImpl(ctx, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_cpu_fpr_get(duk_context* ctx)
|
||||||
|
{
|
||||||
|
return FPRGetImpl(ctx, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_cpu_fpr_set(duk_context* ctx)
|
||||||
|
{
|
||||||
|
return FPRSetImpl(ctx, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_cpu_dfpr_get(duk_context* ctx)
|
||||||
|
{
|
||||||
|
return FPRGetImpl(ctx, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_cpu_dfpr_set(duk_context* ctx)
|
||||||
|
{
|
||||||
|
return FPRSetImpl(ctx, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_cpu_cop0_get(duk_context* ctx)
|
||||||
|
{
|
||||||
|
if (g_Reg == nullptr)
|
||||||
|
{
|
||||||
|
return ThrowRegContextUnavailableError(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!duk_is_string(ctx, 1))
|
||||||
|
{
|
||||||
|
return ThrowRegInvalidError(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* name = duk_get_string(ctx, 1);
|
||||||
|
|
||||||
|
if (strcmp(name, "cause") == 0)
|
||||||
|
{
|
||||||
|
duk_push_uint(ctx, g_Reg->FAKE_CAUSE_REGISTER | g_Reg->CAUSE_REGISTER);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t* reg = COP0RegPtr(name);
|
||||||
|
|
||||||
|
if (reg == nullptr)
|
||||||
|
{
|
||||||
|
return ThrowRegInvalidError(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_push_uint(ctx, *reg);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_cpu_cop0_set(duk_context* ctx)
|
||||||
|
{
|
||||||
|
if (g_Reg == nullptr)
|
||||||
|
{
|
||||||
|
return ThrowRegContextUnavailableError(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!duk_is_string(ctx, 1))
|
||||||
|
{
|
||||||
|
return ThrowRegInvalidError(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* name = duk_get_string(ctx, 1);
|
||||||
|
|
||||||
|
if (!duk_is_number(ctx, 2))
|
||||||
|
{
|
||||||
|
return ThrowRegAssignmentTypeError(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(name, "cause") == 0)
|
||||||
|
{
|
||||||
|
uint32_t value = duk_get_uint(ctx, 2);
|
||||||
|
g_Reg->FAKE_CAUSE_REGISTER = value;
|
||||||
|
g_Reg->CAUSE_REGISTER = value;
|
||||||
|
g_Reg->CheckInterrupts();
|
||||||
|
|
||||||
|
duk_push_true(ctx);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t* reg = COP0RegPtr(name);
|
||||||
|
|
||||||
|
if (reg == nullptr)
|
||||||
|
{
|
||||||
|
return ThrowRegInvalidError(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
*reg = duk_get_uint(ctx, 2);
|
||||||
|
duk_push_true(ctx);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static duk_ret_t GPRGetImpl(duk_context* ctx, bool bUpper)
|
||||||
|
{
|
||||||
|
int regIndex = -1;
|
||||||
|
|
||||||
|
if (g_Reg == nullptr)
|
||||||
|
{
|
||||||
|
return ThrowRegContextUnavailableError(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (duk_is_number(ctx, 1))
|
||||||
|
{
|
||||||
|
regIndex = duk_get_int(ctx, 1);
|
||||||
|
}
|
||||||
|
else if (duk_is_string(ctx, 1))
|
||||||
|
{
|
||||||
|
regIndex = GPRIndex(duk_get_string(ctx, 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (regIndex < 0 || regIndex > 31)
|
||||||
|
{
|
||||||
|
return ThrowRegInvalidError(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_push_uint(ctx, g_Reg->m_GPR[regIndex].UW[bUpper ? 1: 0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static duk_ret_t GPRSetImpl(duk_context* ctx, bool bUpper)
|
||||||
|
{
|
||||||
|
int regIndex = -1;
|
||||||
|
|
||||||
|
if (g_Reg == nullptr)
|
||||||
|
{
|
||||||
|
return ThrowRegContextUnavailableError(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!duk_is_number(ctx, 2))
|
||||||
|
{
|
||||||
|
return ThrowRegAssignmentTypeError(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t value = duk_get_uint(ctx, 2);
|
||||||
|
|
||||||
|
if (duk_is_number(ctx, 1))
|
||||||
|
{
|
||||||
|
regIndex = duk_get_int(ctx, 1);
|
||||||
|
}
|
||||||
|
else if (duk_is_string(ctx, 1))
|
||||||
|
{
|
||||||
|
regIndex = GPRIndex(duk_get_string(ctx, 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (regIndex == 0)
|
||||||
|
{
|
||||||
|
duk_push_true(ctx);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (regIndex < 0 || regIndex > 31)
|
||||||
|
{
|
||||||
|
return ThrowRegInvalidError(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_Reg->m_GPR[regIndex].UW[bUpper ? 1 : 0] = value;
|
||||||
|
duk_push_true(ctx);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static duk_ret_t FPRGetImpl(duk_context* ctx, bool bDouble)
|
||||||
|
{
|
||||||
|
int regIndex = -1;
|
||||||
|
|
||||||
|
if (g_Reg == nullptr)
|
||||||
|
{
|
||||||
|
return ThrowRegContextUnavailableError(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (duk_is_number(ctx, 1))
|
||||||
|
{
|
||||||
|
regIndex = duk_get_int(ctx, 1);
|
||||||
|
}
|
||||||
|
else if (duk_is_string(ctx, 1))
|
||||||
|
{
|
||||||
|
regIndex = FPRIndex(duk_get_string(ctx, 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (regIndex < 0 || regIndex > 31)
|
||||||
|
{
|
||||||
|
return ThrowRegInvalidError(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bDouble)
|
||||||
|
{
|
||||||
|
duk_push_number(ctx, (duk_double_t)*g_Reg->m_FPR_D[regIndex & 0x1E]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
duk_push_number(ctx, (duk_double_t)*g_Reg->m_FPR_S[regIndex]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static duk_ret_t FPRSetImpl(duk_context* ctx, bool bDouble)
|
||||||
|
{
|
||||||
|
int regIndex = -1;
|
||||||
|
|
||||||
|
if (g_Reg == nullptr)
|
||||||
|
{
|
||||||
|
return ThrowRegContextUnavailableError(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!duk_is_number(ctx, 2))
|
||||||
|
{
|
||||||
|
return ThrowRegAssignmentTypeError(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (duk_is_number(ctx, 1))
|
||||||
|
{
|
||||||
|
regIndex = duk_get_int(ctx, 1);
|
||||||
|
}
|
||||||
|
else if (duk_is_string(ctx, 1))
|
||||||
|
{
|
||||||
|
regIndex = FPRIndex(duk_get_string(ctx, 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (regIndex < 0 || regIndex > 31)
|
||||||
|
{
|
||||||
|
return ThrowRegInvalidError(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_double_t value = duk_get_number(ctx, 2);
|
||||||
|
|
||||||
|
if (bDouble)
|
||||||
|
{
|
||||||
|
*g_Reg->m_FPR_D[regIndex & 0x1E] = value;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
*g_Reg->m_FPR_S[regIndex] = (float)value;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_push_true(ctx);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int GPRIndex(const char* regName)
|
||||||
|
{
|
||||||
|
const char* names[] = {
|
||||||
|
"r0", "at", "v0", "v1", "a0", "a1", "a2", "a3",
|
||||||
|
"t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7",
|
||||||
|
"s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7",
|
||||||
|
"t8", "t9", "k0", "k1", "gp", "sp", "fp", "ra"
|
||||||
|
};
|
||||||
|
|
||||||
|
for (int i = 0; i < 32; i++)
|
||||||
|
{
|
||||||
|
if (strcmp(names[i], regName) == 0)
|
||||||
|
{
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int FPRIndex(const char* regName)
|
||||||
|
{
|
||||||
|
const char* names[32] = {
|
||||||
|
"f0", "f1", "f2", "f3", "f4","f5", "f6", "f7", "f8",
|
||||||
|
"f9", "f10", "f11", "f12", "f13", "f14", "f15", "f16",
|
||||||
|
"f17", "f18", "f19", "f20", "f21", "f22", "f23", "f24",
|
||||||
|
"f25", "f26", "f27", "f28", "f29", "f30", "f31"
|
||||||
|
};
|
||||||
|
|
||||||
|
for (int i = 0; i < 32; i++)
|
||||||
|
{
|
||||||
|
if (strcmp(names[i], regName) == 0)
|
||||||
|
{
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t* COP0RegPtr(const char *regName)
|
||||||
|
{
|
||||||
|
if (g_Reg == nullptr)
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct {
|
||||||
|
const char* name;
|
||||||
|
uint32_t *ptr;
|
||||||
|
} names[] = {
|
||||||
|
{ "index", &g_Reg->INDEX_REGISTER },
|
||||||
|
{ "random", &g_Reg->RANDOM_REGISTER },
|
||||||
|
{ "entrylo0", &g_Reg->ENTRYLO0_REGISTER },
|
||||||
|
{ "entrylo1", &g_Reg->ENTRYLO1_REGISTER },
|
||||||
|
{ "context", &g_Reg->CONTEXT_REGISTER },
|
||||||
|
{ "pagemask", &g_Reg->PAGE_MASK_REGISTER },
|
||||||
|
{ "wired", &g_Reg->WIRED_REGISTER },
|
||||||
|
{ "badvaddr", &g_Reg->BAD_VADDR_REGISTER },
|
||||||
|
{ "count", &g_Reg->COUNT_REGISTER },
|
||||||
|
{ "entryhi", &g_Reg->ENTRYHI_REGISTER },
|
||||||
|
{ "compare", &g_Reg->COMPARE_REGISTER },
|
||||||
|
{ "status", &g_Reg->STATUS_REGISTER },
|
||||||
|
//{ "cause", &g_Reg->CAUSE_REGISTER },
|
||||||
|
{ "epc", &g_Reg->EPC_REGISTER },
|
||||||
|
{ "config", &g_Reg->CONFIG_REGISTER },
|
||||||
|
{ "taglo", &g_Reg->TAGLO_REGISTER },
|
||||||
|
{ "taghi", &g_Reg->TAGHI_REGISTER },
|
||||||
|
{ "errorepc", &g_Reg->ERROREPC_REGISTER },
|
||||||
|
{ nullptr, nullptr }
|
||||||
|
};
|
||||||
|
|
||||||
|
for (int i = 0; names[i].name != nullptr; i++)
|
||||||
|
{
|
||||||
|
if (strcmp(regName, names[i].name) == 0)
|
||||||
|
{
|
||||||
|
return names[i].ptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t* CPURegPtr(const char* key)
|
||||||
|
{
|
||||||
|
if (g_Reg == nullptr)
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(key, "pc") == 0)
|
||||||
|
{
|
||||||
|
return &g_Reg->m_PROGRAM_COUNTER;
|
||||||
|
}
|
||||||
|
else if (strcmp(key, "hi") == 0)
|
||||||
|
{
|
||||||
|
return &g_Reg->m_HI.UW[0];
|
||||||
|
}
|
||||||
|
else if (strcmp(key, "uhi") == 0)
|
||||||
|
{
|
||||||
|
return &g_Reg->m_HI.UW[1];
|
||||||
|
}
|
||||||
|
else if (strcmp(key, "lo") == 0)
|
||||||
|
{
|
||||||
|
return &g_Reg->m_LO.UW[0];
|
||||||
|
}
|
||||||
|
else if (strcmp(key, "ulo") == 0)
|
||||||
|
{
|
||||||
|
return &g_Reg->m_LO.UW[1];
|
||||||
|
}
|
||||||
|
else if (strcmp(key, "fcr31") == 0)
|
||||||
|
{
|
||||||
|
return &g_Reg->m_FPCR[31];
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static duk_ret_t ThrowRegInvalidError(duk_context* ctx)
|
||||||
|
{
|
||||||
|
duk_push_error_object(ctx, DUK_ERR_REFERENCE_ERROR, "invalid register name or number");
|
||||||
|
return duk_throw(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
static duk_ret_t ThrowRegContextUnavailableError(duk_context* ctx)
|
||||||
|
{
|
||||||
|
duk_push_error_object(ctx, DUK_ERR_ERROR, "CPU register context is unavailable");
|
||||||
|
return duk_throw(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
static duk_ret_t ThrowRegAssignmentTypeError(duk_context* ctx)
|
||||||
|
{
|
||||||
|
duk_push_error_object(ctx, DUK_ERR_TYPE_ERROR, "invalid register value assignment");
|
||||||
|
return duk_throw(ctx);
|
||||||
|
}
|
|
@ -0,0 +1,96 @@
|
||||||
|
#include <stdafx.h>
|
||||||
|
#include "ScriptAPI.h"
|
||||||
|
#include <Project64-core/Settings/DebugSettings.h>
|
||||||
|
|
||||||
|
void ScriptAPI::Define_debug(duk_context* ctx)
|
||||||
|
{
|
||||||
|
const DukPropListEntry props[] = {
|
||||||
|
{ "breakhere", DukCFunction(js_debug_breakhere) },
|
||||||
|
{ "step", DukCFunction(js_debug_step) },
|
||||||
|
{ "skip", DukCFunction(js_debug_skip) },
|
||||||
|
{ "resume", DukCFunction(js_debug_resume) },
|
||||||
|
{ "showmemory", DukCFunction(js_debug_showmemory) },
|
||||||
|
{ "showcommands", DukCFunction(js_debug_showcommands) },
|
||||||
|
{ "paused", DukGetter(js_debug__get_paused) },
|
||||||
|
{ nullptr }
|
||||||
|
};
|
||||||
|
|
||||||
|
DefineGlobalInterface(ctx, "debug", props);
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_debug_breakhere(duk_context* ctx)
|
||||||
|
{
|
||||||
|
CheckArgs(ctx, { Arg_OptBoolean });
|
||||||
|
|
||||||
|
if (duk_get_boolean_default(ctx, 0, (duk_bool_t)false) == 1)
|
||||||
|
{
|
||||||
|
g_Settings->SaveBool(Debugger_SilentBreak, true);
|
||||||
|
}
|
||||||
|
g_Settings->SaveBool(Debugger_SteppingOps, true);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_debug_step(duk_context* ctx)
|
||||||
|
{
|
||||||
|
CheckArgs(ctx, {});
|
||||||
|
|
||||||
|
if (g_Settings->LoadBool(Debugger_SteppingOps) &&
|
||||||
|
CDebugSettings::WaitingForStep())
|
||||||
|
{
|
||||||
|
g_Settings->SaveBool(Debugger_SilentBreak, true);
|
||||||
|
GetInstance(ctx)->Debugger()->StepEvent().Trigger();
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_debug_skip(duk_context* ctx)
|
||||||
|
{
|
||||||
|
CheckArgs(ctx, {});
|
||||||
|
|
||||||
|
g_Settings->SaveBool(Debugger_SkipOp, true);
|
||||||
|
|
||||||
|
if (g_Settings->LoadBool(Debugger_SteppingOps) &&
|
||||||
|
CDebugSettings::WaitingForStep())
|
||||||
|
{
|
||||||
|
GetInstance(ctx)->Debugger()->StepEvent().Trigger();
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_debug_showmemory(duk_context* ctx)
|
||||||
|
{
|
||||||
|
CheckArgs(ctx, { Arg_Number, Arg_OptBoolean });
|
||||||
|
|
||||||
|
uint32_t address = duk_get_uint(ctx, 0);
|
||||||
|
bool bPhysical = duk_get_boolean_default(ctx, 1, 0) ? true : false;
|
||||||
|
|
||||||
|
GetInstance(ctx)->Debugger()->Debug_ShowMemoryLocation(address, !bPhysical);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_debug_showcommands(duk_context* ctx)
|
||||||
|
{
|
||||||
|
CheckArgs(ctx, { Arg_Number });
|
||||||
|
uint32_t address = duk_get_uint(ctx, 0);
|
||||||
|
GetInstance(ctx)->Debugger()->Debug_ShowCommandsLocation(address, true);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_debug_resume(duk_context* ctx)
|
||||||
|
{
|
||||||
|
CheckArgs(ctx, {});
|
||||||
|
|
||||||
|
g_Settings->SaveBool(Debugger_SteppingOps, false);
|
||||||
|
|
||||||
|
if (CDebugSettings::WaitingForStep())
|
||||||
|
{
|
||||||
|
GetInstance(ctx)->Debugger()->StepEvent().Trigger();
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_debug__get_paused(duk_context* ctx)
|
||||||
|
{
|
||||||
|
duk_push_boolean(ctx, CDebugSettings::WaitingForStep() && g_Settings->LoadBool(Debugger_SteppingOps));
|
||||||
|
return 1;
|
||||||
|
}
|
|
@ -0,0 +1,829 @@
|
||||||
|
#include <stdafx.h>
|
||||||
|
#include "ScriptAPI.h"
|
||||||
|
#include "../OpInfo.h"
|
||||||
|
|
||||||
|
#pragma warning(disable: 4702) // disable unreachable code warning
|
||||||
|
|
||||||
|
using namespace ScriptAPI;
|
||||||
|
|
||||||
|
static bool CbCond_PcBetween(JSAppCallback* cb, void* env);
|
||||||
|
static bool CbCond_ReadAddrBetween(JSAppCallback* cb, void* env);
|
||||||
|
static bool CbCond_WriteAddrBetween(JSAppCallback* cb, void* env);
|
||||||
|
static bool CbCond_PcBetween_OpcodeEquals(JSAppCallback* cb, void* env);
|
||||||
|
static bool CbCond_PcBetween_GprValueEquals(JSAppCallback* cb, void* env);
|
||||||
|
|
||||||
|
static duk_idx_t CbArgs_GenericEventObject(duk_context* ctx, void* env);
|
||||||
|
static duk_idx_t CbArgs_EmuStateChangeEventObject(duk_context* ctx, void* env);
|
||||||
|
static duk_idx_t CbArgs_ExecEventObject(duk_context* ctx, void* env);
|
||||||
|
static duk_idx_t CbArgs_ReadEventObject(duk_context* ctx, void* env);
|
||||||
|
static duk_idx_t CbArgs_WriteEventObject(duk_context* ctx, void* env);
|
||||||
|
static duk_idx_t CbArgs_OpcodeEventObject(duk_context* ctx, void* env);
|
||||||
|
static duk_idx_t CbArgs_RegValueEventObject(duk_context* ctx, void* env);
|
||||||
|
static duk_idx_t CbArgs_MouseEventObject(duk_context* ctx, void* env);
|
||||||
|
static duk_idx_t CbArgs_SPTaskEventObject(duk_context* ctx, void* env);
|
||||||
|
static duk_idx_t CbArgs_PIEventObject(duk_context* ctx, void* env);
|
||||||
|
|
||||||
|
static duk_ret_t RequireAddressOrAddressRange(duk_context* ctx, duk_idx_t idx, uint32_t* addrStart, uint32_t *addrEnd);
|
||||||
|
static duk_ret_t RequireInterpreterCPU(duk_context* ctx);
|
||||||
|
|
||||||
|
void ScriptAPI::Define_events(duk_context* ctx)
|
||||||
|
{
|
||||||
|
const DukPropListEntry props[] = {
|
||||||
|
{ "onstatechange", DukCFunction(js_events_onstatechange) },
|
||||||
|
{ "onexec", DukCFunction(js_events_onexec) },
|
||||||
|
{ "onread", DukCFunction(js_events_onread) },
|
||||||
|
{ "onwrite", DukCFunction(js_events_onwrite) },
|
||||||
|
{ "ongprvalue", DukCFunction(js_events_ongprvalue) },
|
||||||
|
{ "onopcode", DukCFunction(js_events_onopcode) },
|
||||||
|
{ "onpifread", DukCFunction(js_events_onpifread) },
|
||||||
|
{ "onsptask", DukCFunction(js_events_onsptask) },
|
||||||
|
{ "onpidma", DukCFunction(js_events_onpidma) },
|
||||||
|
{ "onmouseup", DukCFunction(js_events_onmouseup) },
|
||||||
|
{ "onmousedown", DukCFunction(js_events_onmousedown) },
|
||||||
|
{ "onmousemove", DukCFunction(js_events_onmousemove) },
|
||||||
|
{ "remove", DukCFunction(js_events_remove) },
|
||||||
|
{ nullptr }
|
||||||
|
};
|
||||||
|
|
||||||
|
DefineGlobalInterface(ctx, "events", props);
|
||||||
|
|
||||||
|
DefineGlobalClass(ctx, "GenericEvent", js_DummyConstructor);
|
||||||
|
DefineGlobalClass(ctx, "EmuStateChangeEvent", js_DummyConstructor);
|
||||||
|
DefineGlobalClass(ctx, "CPUExecEvent", js_DummyConstructor);
|
||||||
|
DefineGlobalClass(ctx, "CPUReadWriteEvent", js_DummyConstructor);
|
||||||
|
DefineGlobalClass(ctx, "CPUOpcodeEvent", js_DummyConstructor);
|
||||||
|
DefineGlobalClass(ctx, "CPURegValueEvent", js_DummyConstructor);
|
||||||
|
DefineGlobalClass(ctx, "SPTaskEvent", js_DummyConstructor);
|
||||||
|
DefineGlobalClass(ctx, "PIEvent", js_DummyConstructor);
|
||||||
|
DefineGlobalClass(ctx, "DrawEvent", js_DummyConstructor);
|
||||||
|
|
||||||
|
const DukPropListEntry mouseEventStaticProps[] = {
|
||||||
|
{ "NONE", DukNumber(-1) },
|
||||||
|
{ "LEFT", DukNumber(0) },
|
||||||
|
{ "MIDDLE", DukNumber(1) },
|
||||||
|
{ "RIGHT", DukNumber(2) },
|
||||||
|
{nullptr}
|
||||||
|
};
|
||||||
|
|
||||||
|
DefineGlobalClass(ctx, "MouseEvent", js_DummyConstructor, nullptr, mouseEventStaticProps);
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_events_onstatechange(duk_context * ctx)
|
||||||
|
{
|
||||||
|
CheckArgs(ctx, { Arg_Function });
|
||||||
|
|
||||||
|
JSAppCallbackID callbackId = AddAppCallback(ctx, 0, JS_HOOK_EMUSTATECHANGE,
|
||||||
|
CbArgs_EmuStateChangeEventObject);
|
||||||
|
duk_push_uint(ctx, callbackId);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_events_onexec(duk_context* ctx)
|
||||||
|
{
|
||||||
|
CheckArgs(ctx, { Arg_Any, Arg_Function });
|
||||||
|
|
||||||
|
uint32_t addrStart, addrEnd;
|
||||||
|
RequireAddressOrAddressRange(ctx, 0, &addrStart, &addrEnd);
|
||||||
|
|
||||||
|
RequireInterpreterCPU(ctx);
|
||||||
|
|
||||||
|
JSAppCallback cb(GetInstance(ctx), duk_get_heapptr(ctx, 1),
|
||||||
|
CbCond_PcBetween, CbArgs_ExecEventObject);
|
||||||
|
cb.m_Params.addrStart = addrStart;
|
||||||
|
cb.m_Params.addrEnd = addrEnd;
|
||||||
|
|
||||||
|
JSAppCallbackID callbackId = AddAppCallback(ctx, JS_HOOK_CPUSTEP, cb);
|
||||||
|
|
||||||
|
duk_push_uint(ctx, callbackId);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_events_onread(duk_context* ctx)
|
||||||
|
{
|
||||||
|
CheckArgs(ctx, { Arg_Any, Arg_Function });
|
||||||
|
|
||||||
|
uint32_t addrStart, addrEnd;
|
||||||
|
RequireAddressOrAddressRange(ctx, 0, &addrStart, &addrEnd);
|
||||||
|
|
||||||
|
RequireInterpreterCPU(ctx);
|
||||||
|
|
||||||
|
JSAppCallback cb(GetInstance(ctx), duk_get_heapptr(ctx, 1),
|
||||||
|
CbCond_ReadAddrBetween, CbArgs_ReadEventObject);
|
||||||
|
cb.m_Params.addrStart = addrStart;
|
||||||
|
cb.m_Params.addrEnd = addrEnd;
|
||||||
|
|
||||||
|
JSAppCallbackID callbackId = AddAppCallback(ctx, JS_HOOK_CPUSTEP, cb);
|
||||||
|
|
||||||
|
duk_push_uint(ctx, callbackId);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_events_onwrite(duk_context* ctx)
|
||||||
|
{
|
||||||
|
CheckArgs(ctx, { Arg_Any, Arg_Function });
|
||||||
|
|
||||||
|
uint32_t addrStart, addrEnd;
|
||||||
|
RequireAddressOrAddressRange(ctx, 0, &addrStart, &addrEnd);
|
||||||
|
|
||||||
|
RequireInterpreterCPU(ctx);
|
||||||
|
|
||||||
|
JSAppCallback cb(GetInstance(ctx), duk_get_heapptr(ctx, 1),
|
||||||
|
CbCond_WriteAddrBetween, CbArgs_WriteEventObject);
|
||||||
|
cb.m_Params.addrStart = addrStart;
|
||||||
|
cb.m_Params.addrEnd = addrEnd;
|
||||||
|
|
||||||
|
JSAppCallbackID callbackId = AddAppCallback(ctx, JS_HOOK_CPUSTEP, cb);
|
||||||
|
|
||||||
|
duk_push_uint(ctx, callbackId);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_events_onopcode(duk_context* ctx)
|
||||||
|
{
|
||||||
|
CheckArgs(ctx, { Arg_Any, Arg_Number, Arg_Number, Arg_Function });
|
||||||
|
|
||||||
|
uint32_t addrStart, addrEnd;
|
||||||
|
RequireAddressOrAddressRange(ctx, 0, &addrStart, &addrEnd);
|
||||||
|
|
||||||
|
RequireInterpreterCPU(ctx);
|
||||||
|
|
||||||
|
uint32_t opcode = duk_get_uint(ctx, 1);
|
||||||
|
uint32_t mask = duk_get_uint(ctx, 2);
|
||||||
|
|
||||||
|
JSAppCallback cb(GetInstance(ctx), duk_get_heapptr(ctx, 3),
|
||||||
|
CbCond_PcBetween_OpcodeEquals, CbArgs_OpcodeEventObject);
|
||||||
|
cb.m_Params.addrStart = addrStart;
|
||||||
|
cb.m_Params.addrEnd = addrEnd;
|
||||||
|
cb.m_Params.opcode = opcode;
|
||||||
|
cb.m_Params.opcodeMask = mask;
|
||||||
|
|
||||||
|
JSAppCallbackID callbackId = AddAppCallback(ctx, JS_HOOK_CPUSTEP, cb);
|
||||||
|
|
||||||
|
duk_push_uint(ctx, callbackId);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_events_ongprvalue(duk_context* ctx)
|
||||||
|
{
|
||||||
|
CheckArgs(ctx, { Arg_Any, Arg_Number, Arg_Number, Arg_Function });
|
||||||
|
|
||||||
|
uint32_t addrStart, addrEnd;
|
||||||
|
RequireAddressOrAddressRange(ctx, 0, &addrStart, &addrEnd);
|
||||||
|
|
||||||
|
RequireInterpreterCPU(ctx);
|
||||||
|
|
||||||
|
JSAppCallback cb(GetInstance(ctx), duk_get_heapptr(ctx, 3),
|
||||||
|
CbCond_PcBetween_GprValueEquals, CbArgs_RegValueEventObject);
|
||||||
|
cb.m_Params.addrStart = addrStart;
|
||||||
|
cb.m_Params.addrEnd = addrEnd;
|
||||||
|
cb.m_Params.regIndices = duk_get_uint(ctx, 1);
|
||||||
|
cb.m_Params.regValue = duk_get_uint(ctx, 2);
|
||||||
|
|
||||||
|
JSAppCallbackID callbackId = AddAppCallback(ctx, JS_HOOK_CPUSTEP, cb);
|
||||||
|
|
||||||
|
duk_push_uint(ctx, callbackId);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_events_onpifread(duk_context* ctx)
|
||||||
|
{
|
||||||
|
CheckArgs(ctx, { Arg_Function });
|
||||||
|
|
||||||
|
JSAppCallbackID callbackId = AddAppCallback(ctx, 0, JS_HOOK_PIFREAD, CbArgs_GenericEventObject);
|
||||||
|
duk_push_uint(ctx, callbackId);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_events_onsptask(duk_context* ctx)
|
||||||
|
{
|
||||||
|
CheckArgs(ctx, { Arg_Function });
|
||||||
|
|
||||||
|
JSAppCallbackID callbackId = AddAppCallback(ctx, 0, JS_HOOK_RSPTASK, CbArgs_SPTaskEventObject);
|
||||||
|
duk_push_uint(ctx, callbackId);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_events_onpidma(duk_context* ctx)
|
||||||
|
{
|
||||||
|
CheckArgs(ctx, { Arg_Function });
|
||||||
|
|
||||||
|
JSAppCallbackID callbackId = AddAppCallback(ctx, 0, JS_HOOK_PIDMA, CbArgs_PIEventObject);
|
||||||
|
duk_push_uint(ctx, callbackId);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_events_onmouseup(duk_context* ctx)
|
||||||
|
{
|
||||||
|
CheckArgs(ctx, { Arg_Function });
|
||||||
|
|
||||||
|
JSAppCallbackID callbackId = AddAppCallback(ctx, 0, JS_HOOK_MOUSEUP, CbArgs_MouseEventObject);
|
||||||
|
duk_push_uint(ctx, callbackId);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_events_onmousedown(duk_context* ctx)
|
||||||
|
{
|
||||||
|
CheckArgs(ctx, { Arg_Function });
|
||||||
|
|
||||||
|
JSAppCallbackID callbackId = AddAppCallback(ctx, 0, JS_HOOK_MOUSEDOWN, CbArgs_MouseEventObject);
|
||||||
|
duk_push_uint(ctx, callbackId);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_events_onmousemove(duk_context* ctx)
|
||||||
|
{
|
||||||
|
CheckArgs(ctx, { Arg_Function });
|
||||||
|
|
||||||
|
JSAppCallbackID callbackId = AddAppCallback(ctx, 0, JS_HOOK_MOUSEMOVE, CbArgs_MouseEventObject);
|
||||||
|
duk_push_uint(ctx, callbackId);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_events_remove(duk_context* ctx)
|
||||||
|
{
|
||||||
|
CheckArgs(ctx, { Arg_Number });
|
||||||
|
|
||||||
|
JSAppCallbackID callbackId = (JSAppCallbackID)duk_get_uint(ctx, 0);
|
||||||
|
|
||||||
|
if (!RemoveAppCallback(ctx, callbackId))
|
||||||
|
{
|
||||||
|
duk_push_error_object(ctx, DUK_ERR_REFERENCE_ERROR, "invalid callback ID");
|
||||||
|
return duk_throw(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CbCond_ReadAddrBetween(JSAppCallback* cb, void* _env)
|
||||||
|
{
|
||||||
|
JSHookCpuStepEnv* env = (JSHookCpuStepEnv*)_env;
|
||||||
|
|
||||||
|
if(!env->opInfo.IsLoadCommand())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t addr = env->opInfo.GetLoadStoreAddress();
|
||||||
|
|
||||||
|
return (addr >= cb->m_Params.addrStart &&
|
||||||
|
addr <= cb->m_Params.addrEnd);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CbCond_WriteAddrBetween(JSAppCallback* cb, void* _env)
|
||||||
|
{
|
||||||
|
JSHookCpuStepEnv* env = (JSHookCpuStepEnv*)_env;
|
||||||
|
|
||||||
|
if(!env->opInfo.IsStoreCommand())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t addr = env->opInfo.GetLoadStoreAddress();
|
||||||
|
|
||||||
|
return (addr >= cb->m_Params.addrStart &&
|
||||||
|
addr <= cb->m_Params.addrEnd);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CbCond_PcBetween(JSAppCallback* cb, void* _env)
|
||||||
|
{
|
||||||
|
JSHookCpuStepEnv* env = (JSHookCpuStepEnv*)_env;
|
||||||
|
return (env->pc >= cb->m_Params.addrStart &&
|
||||||
|
env->pc <= cb->m_Params.addrEnd);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CbCond_PcBetween_OpcodeEquals(JSAppCallback* cb, void* _env)
|
||||||
|
{
|
||||||
|
if (!CbCond_PcBetween(cb, _env))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSHookCpuStepEnv* env = (JSHookCpuStepEnv*)_env;
|
||||||
|
return cb->m_Params.opcode == (env->opInfo.m_OpCode.Hex & cb->m_Params.opcodeMask);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool CbCond_PcBetween_GprValueEquals(JSAppCallback* cb, void* _env)
|
||||||
|
{
|
||||||
|
if (!CbCond_PcBetween(cb, _env))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSHookCpuStepEnv* env = (JSHookCpuStepEnv*)_env;
|
||||||
|
|
||||||
|
for(int i = 0; i < 32; i++)
|
||||||
|
{
|
||||||
|
if(cb->m_Params.regIndices & (1 << i))
|
||||||
|
{
|
||||||
|
if(g_Reg->m_GPR[i].UW[0] == cb->m_Params.regValue)
|
||||||
|
{
|
||||||
|
env->outAffectedRegIndex = i;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_idx_t CbArgs_EmuStateChangeEventObject(duk_context* ctx, void* _env)
|
||||||
|
{
|
||||||
|
CScriptInstance* inst = GetInstance(ctx);
|
||||||
|
JSHookEmuStateChangeEnv* env = (JSHookEmuStateChangeEnv*)_env;
|
||||||
|
duk_push_object(ctx);
|
||||||
|
SetDummyConstructor(ctx, -1, "EmuStateChangeEvent");
|
||||||
|
|
||||||
|
const DukPropListEntry props[] = {
|
||||||
|
{ "callbackId", DukUInt(inst->CallbackId()) },
|
||||||
|
{ "state", DukUInt(env->state) },
|
||||||
|
{ nullptr }
|
||||||
|
};
|
||||||
|
|
||||||
|
DukPutPropList(ctx, -1, props);
|
||||||
|
duk_freeze(ctx, -1);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_idx_t CbArgs_GenericEventObject(duk_context* ctx, void* /*_env*/)
|
||||||
|
{
|
||||||
|
CScriptInstance* inst = GetInstance(ctx);
|
||||||
|
duk_push_object(ctx);
|
||||||
|
SetDummyConstructor(ctx, -1, "GenericEvent");
|
||||||
|
|
||||||
|
const DukPropListEntry props[] = {
|
||||||
|
{ "callbackId", DukUInt(inst->CallbackId()) },
|
||||||
|
{ nullptr }
|
||||||
|
};
|
||||||
|
|
||||||
|
DukPutPropList(ctx, -1, props);
|
||||||
|
duk_freeze(ctx, -1);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_idx_t CbArgs_ExecEventObject(duk_context* ctx, void* _env)
|
||||||
|
{
|
||||||
|
CScriptInstance* inst = GetInstance(ctx);
|
||||||
|
JSHookCpuStepEnv* env = (JSHookCpuStepEnv*)_env;
|
||||||
|
duk_push_object(ctx);
|
||||||
|
SetDummyConstructor(ctx, -1, "CPUExecEvent");
|
||||||
|
|
||||||
|
const DukPropListEntry props[] = {
|
||||||
|
{ "callbackId", DukUInt(inst->CallbackId()) },
|
||||||
|
{ "pc", DukUInt(env->pc) },
|
||||||
|
{ nullptr }
|
||||||
|
};
|
||||||
|
|
||||||
|
DukPutPropList(ctx, -1, props);
|
||||||
|
duk_freeze(ctx, -1);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_idx_t CbArgs_ReadEventObject(duk_context* ctx, void* _env)
|
||||||
|
{
|
||||||
|
CScriptInstance* inst = GetInstance(ctx);
|
||||||
|
CDebuggerUI* debugger = inst->Debugger();
|
||||||
|
JSHookCpuStepEnv* env = (JSHookCpuStepEnv*)_env;
|
||||||
|
|
||||||
|
uint32_t address = env->opInfo.GetLoadStoreAddress();
|
||||||
|
|
||||||
|
uint8_t op = env->opInfo.m_OpCode.op;
|
||||||
|
uint8_t rt = env->opInfo.m_OpCode.rt;
|
||||||
|
bool bFPU = (op == R4300i_LWC1 || op == R4300i_LDC1);
|
||||||
|
|
||||||
|
duk_push_object(ctx);
|
||||||
|
SetDummyConstructor(ctx, -1, "CPUReadWriteEvent");
|
||||||
|
|
||||||
|
const DukPropListEntry props[] = {
|
||||||
|
{ "callbackId", DukUInt(inst->CallbackId()) },
|
||||||
|
{ "pc", DukUInt(env->pc) },
|
||||||
|
{ "address", DukUInt(address) },
|
||||||
|
{ "reg", DukUInt(rt) },
|
||||||
|
{ "fpu", DukBoolean(bFPU) },
|
||||||
|
{ nullptr }
|
||||||
|
};
|
||||||
|
|
||||||
|
DukPutPropList(ctx, -1, props);
|
||||||
|
|
||||||
|
union {
|
||||||
|
uint8_t u8;
|
||||||
|
int8_t s8;
|
||||||
|
uint16_t u16;
|
||||||
|
int16_t s16;
|
||||||
|
uint32_t u32;
|
||||||
|
int32_t s32;
|
||||||
|
float f32;
|
||||||
|
double f64;
|
||||||
|
uint64_t u64;
|
||||||
|
} value = {0};
|
||||||
|
|
||||||
|
bool bNeedUpper32 = false;
|
||||||
|
|
||||||
|
switch (env->opInfo.m_OpCode.op)
|
||||||
|
{
|
||||||
|
case R4300i_LB:
|
||||||
|
debugger->DebugLoad_VAddr(address, value.s8);
|
||||||
|
duk_push_int(ctx, value.s8);
|
||||||
|
duk_push_int(ctx, S8);
|
||||||
|
break;
|
||||||
|
case R4300i_LBU:
|
||||||
|
debugger->DebugLoad_VAddr(address, value.u8);
|
||||||
|
duk_push_uint(ctx, value.u8);
|
||||||
|
duk_push_int(ctx, U8);
|
||||||
|
break;
|
||||||
|
case R4300i_LH:
|
||||||
|
debugger->DebugLoad_VAddr(address, value.s16);
|
||||||
|
duk_push_int(ctx, value.s16);
|
||||||
|
duk_push_int(ctx, S16);
|
||||||
|
break;
|
||||||
|
case R4300i_LHU:
|
||||||
|
debugger->DebugLoad_VAddr(address, value.u16);
|
||||||
|
duk_push_uint(ctx, value.u16);
|
||||||
|
duk_push_int(ctx, U16);
|
||||||
|
break;
|
||||||
|
case R4300i_LL:
|
||||||
|
case R4300i_LW:
|
||||||
|
debugger->DebugLoad_VAddr(address, value.s32);
|
||||||
|
duk_push_int(ctx, value.s32);
|
||||||
|
duk_push_int(ctx, S32);
|
||||||
|
break;
|
||||||
|
case R4300i_LWU:
|
||||||
|
debugger->DebugLoad_VAddr(address, value.u32);
|
||||||
|
duk_push_uint(ctx, value.u32);
|
||||||
|
duk_push_int(ctx, U32);
|
||||||
|
break;
|
||||||
|
case R4300i_LWC1:
|
||||||
|
debugger->DebugLoad_VAddr(address, value.f32);
|
||||||
|
duk_push_number(ctx, value.f32);
|
||||||
|
duk_push_int(ctx, F32);
|
||||||
|
break;
|
||||||
|
case R4300i_LDC1:
|
||||||
|
debugger->DebugLoad_VAddr(address, value.f64);
|
||||||
|
duk_push_number(ctx, value.f64);
|
||||||
|
duk_push_int(ctx, F64);
|
||||||
|
break;
|
||||||
|
case R4300i_LD:
|
||||||
|
debugger->DebugLoad_VAddr(address, value.u64);
|
||||||
|
duk_push_number(ctx, (duk_double_t)(value.u64 & 0xFFFFFFFF));
|
||||||
|
duk_push_int(ctx, U64);
|
||||||
|
bNeedUpper32 = true;
|
||||||
|
break;
|
||||||
|
case R4300i_LDL:
|
||||||
|
{
|
||||||
|
int shift = (address & 7) * 8;
|
||||||
|
uint64_t mask = ~(((uint64_t)-1) << shift);
|
||||||
|
debugger->DebugLoad_VAddr(address & ~7, value.u64);
|
||||||
|
value.u64 = (g_Reg->m_GPR[rt].DW & mask) + (value.u64 << shift);
|
||||||
|
duk_push_number(ctx, (duk_double_t)(value.u64 & 0xFFFFFFFF));
|
||||||
|
duk_push_int(ctx, U64);
|
||||||
|
bNeedUpper32 = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case R4300i_LDR:
|
||||||
|
{
|
||||||
|
int shift = 56 - ((address & 7) * 8);
|
||||||
|
uint64_t mask = ~(((uint64_t)-1) >> shift);
|
||||||
|
debugger->DebugLoad_VAddr(address & ~7, value.u64);
|
||||||
|
value.u64 = (g_Reg->m_GPR[rt].DW & mask) + (value.u64 >> shift);
|
||||||
|
duk_push_number(ctx, (duk_double_t)(value.u64 & 0xFFFFFFFF));
|
||||||
|
duk_push_int(ctx, U64);
|
||||||
|
bNeedUpper32 = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case R4300i_LWL:
|
||||||
|
{
|
||||||
|
int shift = (address & 3) * 8;
|
||||||
|
uint32_t mask = ~(((uint32_t)-1) << shift);
|
||||||
|
debugger->DebugLoad_VAddr(address & ~3, value.s32);
|
||||||
|
value.s32 = (g_Reg->m_GPR[rt].W[0] & mask) + (value.s32 << shift);
|
||||||
|
duk_push_number(ctx, value.s32);
|
||||||
|
duk_push_int(ctx, S32);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case R4300i_LWR:
|
||||||
|
{
|
||||||
|
int shift = 24 - ((address & 3) * 8);
|
||||||
|
uint32_t mask = ~(((uint32_t)-1) >> shift);
|
||||||
|
debugger->DebugLoad_VAddr(address & ~3, value.s32);
|
||||||
|
value.s32 = (g_Reg->m_GPR[rt].W[0] & mask) + (value.s32 >> shift);
|
||||||
|
duk_push_number(ctx, value.s32);
|
||||||
|
duk_push_int(ctx, S32);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
duk_push_number(ctx, 0);
|
||||||
|
duk_push_number(ctx, 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_put_prop_string(ctx, -3, "valueType");
|
||||||
|
duk_put_prop_string(ctx, -2, "value");
|
||||||
|
|
||||||
|
if (bNeedUpper32)
|
||||||
|
{
|
||||||
|
duk_push_number(ctx, (duk_double_t)(value.u64 >> 32));
|
||||||
|
duk_put_prop_string(ctx, -2, "valueHi");
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_freeze(ctx, -1);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_idx_t CbArgs_WriteEventObject(duk_context* ctx, void* _env)
|
||||||
|
{
|
||||||
|
CScriptInstance* inst = GetInstance(ctx);
|
||||||
|
CDebuggerUI* debugger = inst->Debugger();
|
||||||
|
JSHookCpuStepEnv* env = (JSHookCpuStepEnv*)_env;
|
||||||
|
|
||||||
|
uint32_t address = env->opInfo.GetLoadStoreAddress();
|
||||||
|
|
||||||
|
uint8_t op = env->opInfo.m_OpCode.op;
|
||||||
|
uint8_t rt = env->opInfo.m_OpCode.rt;
|
||||||
|
bool bFPU = (op == R4300i_SWC1 || op == R4300i_SDC1);
|
||||||
|
|
||||||
|
duk_push_object(ctx);
|
||||||
|
SetDummyConstructor(ctx, -1, "CPUReadWriteEvent");
|
||||||
|
|
||||||
|
const DukPropListEntry props[] = {
|
||||||
|
{ "callbackId", DukUInt(inst->CallbackId()) },
|
||||||
|
{ "pc", DukUInt(env->pc) },
|
||||||
|
{ "address", DukUInt(address) },
|
||||||
|
{ "reg", DukUInt(rt) },
|
||||||
|
{ "fpu", DukBoolean(bFPU) },
|
||||||
|
{ nullptr }
|
||||||
|
};
|
||||||
|
|
||||||
|
DukPutPropList(ctx, -1, props);
|
||||||
|
|
||||||
|
bool bNeedUpper32 = false;
|
||||||
|
uint64_t value64 = 0;
|
||||||
|
|
||||||
|
switch (env->opInfo.m_OpCode.op)
|
||||||
|
{
|
||||||
|
case R4300i_SB:
|
||||||
|
duk_push_int(ctx, g_Reg->m_GPR[rt].B[0]);
|
||||||
|
duk_push_int(ctx, S8);
|
||||||
|
break;
|
||||||
|
case R4300i_SH:
|
||||||
|
duk_push_int(ctx, g_Reg->m_GPR[rt].HW[0]);
|
||||||
|
duk_push_int(ctx, S16);
|
||||||
|
break;
|
||||||
|
case R4300i_SW:
|
||||||
|
duk_push_int(ctx, g_Reg->m_GPR[rt].W[0]);
|
||||||
|
duk_push_int(ctx, S32);
|
||||||
|
break;
|
||||||
|
case R4300i_SWC1:
|
||||||
|
duk_push_number(ctx, *g_Reg->m_FPR_S[rt]);
|
||||||
|
duk_push_int(ctx, F32);
|
||||||
|
break;
|
||||||
|
case R4300i_SDC1:
|
||||||
|
duk_push_number(ctx, *g_Reg->m_FPR_D[rt]);
|
||||||
|
duk_push_int(ctx, F64);
|
||||||
|
break;
|
||||||
|
case R4300i_SD:
|
||||||
|
duk_push_number(ctx, g_Reg->m_GPR[rt].UW[0]);
|
||||||
|
duk_push_int(ctx, U64);
|
||||||
|
bNeedUpper32 = true;
|
||||||
|
break;
|
||||||
|
case R4300i_SWL:
|
||||||
|
{
|
||||||
|
int shift = (address & 3) * 8;
|
||||||
|
uint32_t mask = ~(((uint32_t)-1) >> shift);
|
||||||
|
uint32_t value;
|
||||||
|
debugger->DebugLoad_VAddr(address & ~3, value);
|
||||||
|
value = (value & mask) + (g_Reg->m_GPR[rt].UW[0] >> shift);
|
||||||
|
duk_push_number(ctx, value);
|
||||||
|
duk_push_int(ctx, S32);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case R4300i_SWR:
|
||||||
|
{
|
||||||
|
int shift = 24 - ((address & 3) * 8);
|
||||||
|
uint32_t mask = ~(((uint32_t)-1) << shift);
|
||||||
|
uint32_t value;
|
||||||
|
debugger->DebugLoad_VAddr(address & ~3, value);
|
||||||
|
value = (value & mask) + (g_Reg->m_GPR[rt].UW[0] >> shift);
|
||||||
|
duk_push_number(ctx, value);
|
||||||
|
duk_push_int(ctx, S32);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case R4300i_SDL:
|
||||||
|
{
|
||||||
|
int shift = (address & 7) * 8;
|
||||||
|
uint64_t mask = ~(((uint64_t)-1) >> shift);
|
||||||
|
debugger->DebugLoad_VAddr(address & ~7, value64);
|
||||||
|
value64 = (value64 & mask) + (g_Reg->m_GPR[rt].UDW >> shift);
|
||||||
|
duk_push_number(ctx, (duk_double_t)(value64 & 0xFFFFFFFF));
|
||||||
|
duk_push_int(ctx, U64);
|
||||||
|
}
|
||||||
|
case R4300i_SDR:
|
||||||
|
{
|
||||||
|
int shift = 56 - ((address & 7) * 8);
|
||||||
|
uint64_t mask = ~(((uint64_t)-1) << shift);
|
||||||
|
debugger->DebugLoad_VAddr(address & ~7, value64);
|
||||||
|
value64 = (value64 & mask) + (g_Reg->m_GPR[rt].UDW >> shift);
|
||||||
|
duk_push_number(ctx, (duk_double_t)(value64 & 0xFFFFFFFF));
|
||||||
|
duk_push_int(ctx, U64);
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
duk_push_number(ctx, 0);
|
||||||
|
duk_push_number(ctx, 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_put_prop_string(ctx, -3, "valueType");
|
||||||
|
duk_put_prop_string(ctx, -2, "value");
|
||||||
|
|
||||||
|
if (bNeedUpper32)
|
||||||
|
{
|
||||||
|
duk_push_number(ctx, (duk_double_t)(value64 >> 32));
|
||||||
|
duk_put_prop_string(ctx, -2, "valueHi");
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_freeze(ctx, -1);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_idx_t CbArgs_OpcodeEventObject(duk_context* ctx, void* _env)
|
||||||
|
{
|
||||||
|
CScriptInstance* inst = GetInstance(ctx);
|
||||||
|
JSHookCpuStepEnv* env = (JSHookCpuStepEnv*)_env;
|
||||||
|
duk_push_object(ctx);
|
||||||
|
SetDummyConstructor(ctx, -1, "CPUOpcodeEvent");
|
||||||
|
|
||||||
|
const DukPropListEntry props[] = {
|
||||||
|
{ "callbackId", DukUInt(inst->CallbackId()) },
|
||||||
|
{ "pc", DukUInt(env->pc) },
|
||||||
|
{ "opcode", DukUInt(env->opInfo.m_OpCode.Hex) },
|
||||||
|
{ nullptr }
|
||||||
|
};
|
||||||
|
|
||||||
|
DukPutPropList(ctx, -1, props);
|
||||||
|
duk_freeze(ctx, -1);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_idx_t CbArgs_RegValueEventObject(duk_context* ctx, void* _env)
|
||||||
|
{
|
||||||
|
CScriptInstance* inst = GetInstance(ctx);
|
||||||
|
JSHookCpuStepEnv* env = (JSHookCpuStepEnv*)_env;
|
||||||
|
duk_push_object(ctx);
|
||||||
|
SetDummyConstructor(ctx, -1, "CPURegValueEvent");
|
||||||
|
|
||||||
|
const DukPropListEntry props[] = {
|
||||||
|
{ "callbackId", DukUInt(inst->CallbackId()) },
|
||||||
|
{ "pc", DukUInt(env->pc) },
|
||||||
|
{ "value", DukUInt(g_Reg->m_GPR[env->outAffectedRegIndex].UW[0]) },
|
||||||
|
{ "reg", DukUInt(env->outAffectedRegIndex) },
|
||||||
|
{ nullptr }
|
||||||
|
};
|
||||||
|
|
||||||
|
DukPutPropList(ctx, -1, props);
|
||||||
|
duk_freeze(ctx, -1);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static duk_idx_t CbArgs_MouseEventObject(duk_context* ctx, void* _env)
|
||||||
|
{
|
||||||
|
CScriptInstance* inst = GetInstance(ctx);
|
||||||
|
JSHookMouseEnv* env = (JSHookMouseEnv*)_env;
|
||||||
|
duk_push_object(ctx);
|
||||||
|
SetDummyConstructor(ctx, -1, "MouseEvent");
|
||||||
|
|
||||||
|
const DukPropListEntry props[] = {
|
||||||
|
{ "callbackId", DukUInt(inst->CallbackId()) },
|
||||||
|
{ "button", DukInt(env->button) },
|
||||||
|
{ "x", DukInt(env->x) },
|
||||||
|
{ "y", DukInt(env->y) },
|
||||||
|
{ nullptr }
|
||||||
|
};
|
||||||
|
|
||||||
|
DukPutPropList(ctx, -1, props);
|
||||||
|
duk_freeze(ctx, -1);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static duk_idx_t CbArgs_SPTaskEventObject(duk_context* ctx, void* _env)
|
||||||
|
{
|
||||||
|
CScriptInstance* inst = GetInstance(ctx);
|
||||||
|
JSHookSpTaskEnv* env = (JSHookSpTaskEnv*)_env;
|
||||||
|
duk_push_object(ctx);
|
||||||
|
SetDummyConstructor(ctx, -1, "SPTaskEvent");
|
||||||
|
|
||||||
|
const DukPropListEntry props[] = {
|
||||||
|
{ "callbackId", DukUInt(inst->CallbackId()) },
|
||||||
|
{ "taskType", DukUInt(env->taskType) },
|
||||||
|
{ "taskFlags", DukUInt(env->taskFlags) },
|
||||||
|
{ "ucodeBootAddress", DukUInt(env->ucodeBootAddress | 0x80000000) },
|
||||||
|
{ "ucodeBootSize", DukUInt(env->ucodeBootSize) },
|
||||||
|
{ "ucodeAddress", DukUInt(env->ucodeAddress | 0x80000000) },
|
||||||
|
{ "ucodeSize", DukUInt(env->ucodeSize) },
|
||||||
|
{ "ucodeDataAddress", DukUInt(env->ucodeDataAddress | 0x80000000) },
|
||||||
|
{ "ucodeDataSize", DukUInt(env->ucodeDataSize) },
|
||||||
|
{ "dramStackAddress", DukUInt(env->dramStackAddress | 0x80000000) },
|
||||||
|
{ "dramStackSize", DukUInt(env->dramStackSize) },
|
||||||
|
{ "outputBuffAddress", DukUInt(env->outputBuffAddress | 0x80000000) },
|
||||||
|
{ "outputBuffSize", DukUInt(env->outputBuffSize) },
|
||||||
|
{ "dataAddress", DukUInt(env->dataAddress | 0x80000000) },
|
||||||
|
{ "dataSize", DukUInt(env->dataSize) },
|
||||||
|
{ "yieldDataAddress", DukUInt(env->yieldDataAddress | 0x80000000) },
|
||||||
|
{ "yieldDataSize", DukUInt(env->yieldDataSize) },
|
||||||
|
{ nullptr }
|
||||||
|
};
|
||||||
|
|
||||||
|
DukPutPropList(ctx, -1, props);
|
||||||
|
duk_freeze(ctx, -1);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static duk_idx_t CbArgs_PIEventObject(duk_context* ctx, void* _env)
|
||||||
|
{
|
||||||
|
CScriptInstance* inst = GetInstance(ctx);
|
||||||
|
JSHookPiDmaEnv* env = (JSHookPiDmaEnv*)_env;
|
||||||
|
duk_push_object(ctx);
|
||||||
|
SetDummyConstructor(ctx, -1, "PIEvent");
|
||||||
|
|
||||||
|
const DukPropListEntry props[] = {
|
||||||
|
{ "callbackId", DukUInt(inst->CallbackId()) },
|
||||||
|
{ "direction", DukUInt(env->direction) },
|
||||||
|
{ "dramAddress", DukUInt(env->dramAddress | 0x80000000) },
|
||||||
|
{ "cartAddress", DukUInt(env->cartAddress | 0xA0000000) },
|
||||||
|
{ "length", DukUInt(env->length + 1) },
|
||||||
|
{ nullptr }
|
||||||
|
};
|
||||||
|
|
||||||
|
DukPutPropList(ctx, -1, props);
|
||||||
|
duk_freeze(ctx, -1);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t RequireAddressOrAddressRange(duk_context* ctx, duk_idx_t idx, uint32_t* addrStart, uint32_t* addrEnd)
|
||||||
|
{
|
||||||
|
if(duk_is_number(ctx, idx))
|
||||||
|
{
|
||||||
|
if (abs(duk_get_number(ctx, idx)) > 0xFFFFFFFF)
|
||||||
|
{
|
||||||
|
duk_push_error_object(ctx, DUK_ERR_RANGE_ERROR,
|
||||||
|
"address is out of range");
|
||||||
|
return duk_throw(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t addr = duk_get_uint(ctx, idx);
|
||||||
|
*addrStart = addr;
|
||||||
|
*addrEnd = addr;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(duk_is_object(ctx, idx))
|
||||||
|
{
|
||||||
|
if(!duk_has_prop_string(ctx, idx, "start") ||
|
||||||
|
!duk_has_prop_string(ctx, idx, "end"))
|
||||||
|
{
|
||||||
|
duk_push_error_object(ctx, DUK_ERR_REFERENCE_ERROR,
|
||||||
|
"object is missing 'start' or 'end' property");
|
||||||
|
return duk_throw(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_get_prop_string(ctx, idx, "start");
|
||||||
|
duk_get_prop_string(ctx, idx, "end");
|
||||||
|
|
||||||
|
if(!duk_is_number(ctx, -2) ||
|
||||||
|
!duk_is_number(ctx, -1))
|
||||||
|
{
|
||||||
|
duk_pop_n(ctx, 2);
|
||||||
|
duk_push_error_object(ctx, DUK_ERR_REFERENCE_ERROR,
|
||||||
|
"'start' and 'end' properties must be numbers");
|
||||||
|
return duk_throw(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (abs(duk_get_number(ctx, -2)) > 0xFFFFFFFF ||
|
||||||
|
abs(duk_get_number(ctx, -1)) > 0xFFFFFFFF)
|
||||||
|
{
|
||||||
|
duk_push_error_object(ctx, DUK_ERR_RANGE_ERROR,
|
||||||
|
"'start' or 'end' property out of range");
|
||||||
|
return duk_throw(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
*addrStart = duk_get_uint(ctx, -2);
|
||||||
|
*addrEnd = duk_get_uint(ctx, -1);
|
||||||
|
duk_pop_n(ctx, 2);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_push_error_object(ctx, DUK_ERR_TYPE_ERROR,
|
||||||
|
"argument %d invalid; expected number or object", idx);
|
||||||
|
return duk_throw(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t RequireInterpreterCPU(duk_context* ctx)
|
||||||
|
{
|
||||||
|
if (!g_Settings->LoadBool(Setting_ForceInterpreterCPU) &&
|
||||||
|
(CPU_TYPE)g_Settings->LoadDword(Game_CpuType) != CPU_Interpreter)
|
||||||
|
{
|
||||||
|
duk_push_error_object(ctx, DUK_ERR_ERROR,
|
||||||
|
"this feature requires the interpreter core");
|
||||||
|
return duk_throw(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,150 @@
|
||||||
|
#include <stdafx.h>
|
||||||
|
#include <windows.h>
|
||||||
|
#include "ScriptAPI.h"
|
||||||
|
|
||||||
|
void ScriptAPI::Define_exec(duk_context* ctx)
|
||||||
|
{
|
||||||
|
DefineGlobalFunction(ctx, "exec", js_exec);
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_exec(duk_context* ctx)
|
||||||
|
{
|
||||||
|
CheckArgs(ctx, { Arg_String, Arg_OptObject });
|
||||||
|
|
||||||
|
CScriptInstance* inst = GetInstance(ctx);
|
||||||
|
CScriptSystem* sys = inst->System();
|
||||||
|
|
||||||
|
const char* command = duk_get_string(ctx, 0);
|
||||||
|
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
bool bShowWindow = false;
|
||||||
|
bool bVerbose = false;
|
||||||
|
stdstr cwd;
|
||||||
|
} options;
|
||||||
|
|
||||||
|
options.cwd = sys->InstallDirPath();
|
||||||
|
|
||||||
|
if (duk_is_object(ctx, 1))
|
||||||
|
{
|
||||||
|
duk_get_prop_string(ctx, 1, "hidden");
|
||||||
|
options.bShowWindow = duk_get_boolean_default(ctx, -1, false) != 0;
|
||||||
|
duk_pop(ctx);
|
||||||
|
|
||||||
|
duk_get_prop_string(ctx, 1, "verbose");
|
||||||
|
options.bVerbose = duk_get_boolean_default(ctx, -1, false) != 0;
|
||||||
|
duk_pop(ctx);
|
||||||
|
|
||||||
|
if (duk_has_prop_string(ctx, 1, "cwd"))
|
||||||
|
{
|
||||||
|
duk_get_prop_string(ctx, 1, "cwd");
|
||||||
|
options.cwd = duk_get_string(ctx, -1);
|
||||||
|
duk_pop(ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stdstr resultStdOut;
|
||||||
|
stdstr resultStdErr;
|
||||||
|
DWORD resultExitCode = EXIT_FAILURE;
|
||||||
|
|
||||||
|
SECURITY_ATTRIBUTES secAttr;
|
||||||
|
secAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
|
||||||
|
secAttr.bInheritHandle = TRUE;
|
||||||
|
secAttr.lpSecurityDescriptor = nullptr;
|
||||||
|
|
||||||
|
HANDLE hStdIn_r, hStdIn_w;
|
||||||
|
HANDLE hStdOut_r, hStdOut_w;
|
||||||
|
HANDLE hStdErr_r, hStdErr_w;
|
||||||
|
|
||||||
|
CreatePipe(&hStdIn_r, &hStdIn_w, &secAttr, 0);
|
||||||
|
CreatePipe(&hStdOut_r, &hStdOut_w, &secAttr, 0);
|
||||||
|
CreatePipe(&hStdErr_r, &hStdErr_w, &secAttr, 0);
|
||||||
|
|
||||||
|
STARTUPINFOA startupInfo;
|
||||||
|
ZeroMemory(&startupInfo, sizeof(startupInfo));
|
||||||
|
startupInfo.dwFlags = STARTF_USESTDHANDLES;
|
||||||
|
startupInfo.hStdInput = hStdIn_r;
|
||||||
|
startupInfo.hStdOutput = hStdOut_w;
|
||||||
|
startupInfo.hStdError = hStdErr_w;
|
||||||
|
|
||||||
|
if (!options.bShowWindow)
|
||||||
|
{
|
||||||
|
startupInfo.dwFlags |= STARTF_USESHOWWINDOW;
|
||||||
|
startupInfo.wShowWindow = SW_HIDE;
|
||||||
|
}
|
||||||
|
|
||||||
|
PROCESS_INFORMATION processInfo;
|
||||||
|
|
||||||
|
BOOL bSuccess = CreateProcessA(nullptr, (LPSTR)stdstr_f("cmd /c %s", command).c_str(), nullptr, nullptr, TRUE,
|
||||||
|
0, nullptr, options.cwd.c_str(), &startupInfo, &processInfo);
|
||||||
|
|
||||||
|
if (bSuccess)
|
||||||
|
{
|
||||||
|
CloseHandle(hStdIn_r);
|
||||||
|
CloseHandle(hStdIn_w);
|
||||||
|
CloseHandle(hStdOut_w);
|
||||||
|
CloseHandle(hStdErr_w);
|
||||||
|
|
||||||
|
char buffer[1024];
|
||||||
|
DWORD nBytesRead;
|
||||||
|
while (ReadFile(hStdOut_r, buffer, sizeof(buffer), &nBytesRead, nullptr) && nBytesRead != 0)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < nBytesRead; i++)
|
||||||
|
{
|
||||||
|
resultStdOut += buffer[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.bVerbose)
|
||||||
|
{
|
||||||
|
sys->ConsolePrint("%.*s", nBytesRead, buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while (ReadFile(hStdErr_r, buffer, sizeof(buffer), &nBytesRead, nullptr) && nBytesRead != 0)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < nBytesRead; i++)
|
||||||
|
{
|
||||||
|
resultStdErr += buffer[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CloseHandle(hStdErr_r);
|
||||||
|
CloseHandle(hStdOut_r);
|
||||||
|
CloseHandle(processInfo.hThread);
|
||||||
|
|
||||||
|
WaitForSingleObject(processInfo.hProcess, INFINITE);
|
||||||
|
GetExitCodeProcess(processInfo.hProcess, &resultExitCode);
|
||||||
|
|
||||||
|
CloseHandle(processInfo.hProcess);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CloseHandle(hStdIn_r);
|
||||||
|
CloseHandle(hStdIn_w);
|
||||||
|
CloseHandle(hStdOut_r);
|
||||||
|
CloseHandle(hStdOut_w);
|
||||||
|
CloseHandle(hStdErr_r);
|
||||||
|
CloseHandle(hStdErr_w);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!bSuccess || resultExitCode != 0)
|
||||||
|
{
|
||||||
|
const DukPropListEntry props[] = {
|
||||||
|
{ "status", DukInt(resultExitCode) },
|
||||||
|
{ "stdout", DukString(resultStdOut.c_str()) },
|
||||||
|
{ "stderr", DukString(resultStdErr.c_str()) },
|
||||||
|
{ "pid", DukUInt(processInfo.dwProcessId) },
|
||||||
|
{ nullptr }
|
||||||
|
};
|
||||||
|
|
||||||
|
duk_push_error_object(ctx, resultExitCode, "exec(): process returned %d", resultExitCode);
|
||||||
|
DukPutPropList(ctx, -1, props);
|
||||||
|
return duk_throw(ctx);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
duk_push_string(ctx, resultStdOut.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
|
@ -0,0 +1,560 @@
|
||||||
|
#include <stdafx.h>
|
||||||
|
#include "ScriptAPI.h"
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
#pragma warning(disable: 4702) // disable unreachable code warning
|
||||||
|
|
||||||
|
enum fsop { FS_READ, FS_WRITE };
|
||||||
|
static duk_ret_t ReadWriteImpl(duk_context *ctx, fsop op); // (fd, buffer, offset, length, position)
|
||||||
|
static duk_ret_t js__FileFinalizer(duk_context *ctx);
|
||||||
|
|
||||||
|
void ScriptAPI::Define_fs(duk_context *ctx)
|
||||||
|
{
|
||||||
|
// todo DukPutProps DukClass
|
||||||
|
|
||||||
|
const duk_function_list_entry funcs[] = {
|
||||||
|
{ "open", js_fs_open, DUK_VARARGS },
|
||||||
|
{ "close", js_fs_close, DUK_VARARGS },
|
||||||
|
{ "write", js_fs_write, DUK_VARARGS },
|
||||||
|
{ "writefile", js_fs_writefile, DUK_VARARGS },
|
||||||
|
{ "read", js_fs_read, DUK_VARARGS },
|
||||||
|
{ "readfile", js_fs_readfile, DUK_VARARGS },
|
||||||
|
{ "fstat", js_fs_fstat, DUK_VARARGS },
|
||||||
|
{ "stat", js_fs_stat, DUK_VARARGS },
|
||||||
|
{ "unlink", js_fs_unlink, DUK_VARARGS },
|
||||||
|
{ "mkdir", js_fs_mkdir, DUK_VARARGS },
|
||||||
|
{ "rmdir", js_fs_rmdir, DUK_VARARGS },
|
||||||
|
{ "readdir", js_fs_readdir, DUK_VARARGS },
|
||||||
|
{ "Stats", js_fs_Stats__constructor, DUK_VARARGS },
|
||||||
|
{ nullptr, nullptr, 0 }
|
||||||
|
};
|
||||||
|
|
||||||
|
const duk_function_list_entry Stats_funcs[] = {
|
||||||
|
{ "isFile", js_fs_Stats_isFile, DUK_VARARGS },
|
||||||
|
{ "isDirectory", js_fs_Stats_isDirectory, DUK_VARARGS },
|
||||||
|
{ nullptr, nullptr, 0 }
|
||||||
|
};
|
||||||
|
|
||||||
|
duk_push_global_object(ctx);
|
||||||
|
duk_push_string(ctx, "fs");
|
||||||
|
duk_push_object(ctx);
|
||||||
|
duk_put_function_list(ctx, -1, funcs);
|
||||||
|
|
||||||
|
duk_get_prop_string(ctx, -1, "Stats");
|
||||||
|
duk_push_object(ctx);
|
||||||
|
duk_put_function_list(ctx, -1, Stats_funcs);
|
||||||
|
duk_put_prop_string(ctx, -2, "prototype");
|
||||||
|
duk_pop(ctx);
|
||||||
|
|
||||||
|
duk_freeze(ctx, -1);
|
||||||
|
duk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_SET_ENUMERABLE);
|
||||||
|
duk_pop(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_fs_open(duk_context *ctx)
|
||||||
|
{
|
||||||
|
CheckArgs(ctx, { Arg_String, Arg_String });
|
||||||
|
|
||||||
|
const char* path = duk_get_string(ctx, 0);
|
||||||
|
const char* mode = duk_get_string(ctx, 1);
|
||||||
|
|
||||||
|
bool bModeValid = false;
|
||||||
|
|
||||||
|
const char* validModes[] = {
|
||||||
|
"r", "rb",
|
||||||
|
"w", "wb",
|
||||||
|
"a", "ab",
|
||||||
|
"r+", "rb+", "r+b",
|
||||||
|
"w+", "wb+", "w+b",
|
||||||
|
"a+", "ab+", "a+b",
|
||||||
|
nullptr
|
||||||
|
};
|
||||||
|
|
||||||
|
for (int i = 0; validModes[i] != nullptr; i++)
|
||||||
|
{
|
||||||
|
if (strcmp(mode, validModes[i]) == 0)
|
||||||
|
{
|
||||||
|
bModeValid = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!bModeValid)
|
||||||
|
{
|
||||||
|
duk_push_error_object(ctx, DUK_ERR_TYPE_ERROR, "mode '%s' is not valid", mode);
|
||||||
|
return duk_throw(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE *fp = fopen(path, mode);
|
||||||
|
|
||||||
|
if(fp == nullptr)
|
||||||
|
{
|
||||||
|
duk_push_error_object(ctx, DUK_ERR_ERROR, "could not open '%s' (mode: '%s')", path, mode);
|
||||||
|
return duk_throw(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
int fd = _fileno(fp);
|
||||||
|
|
||||||
|
// FILES[fd] = {fp: fp}
|
||||||
|
duk_get_global_string(ctx, HS_gOpenFileDescriptors);
|
||||||
|
|
||||||
|
duk_push_object(ctx);
|
||||||
|
duk_push_pointer(ctx, fp);
|
||||||
|
duk_put_prop_string(ctx, -2, "fp");
|
||||||
|
duk_push_c_function(ctx, js__FileFinalizer, 1);
|
||||||
|
duk_set_finalizer(ctx, -2);
|
||||||
|
|
||||||
|
duk_put_prop_index(ctx, -2, fd);
|
||||||
|
duk_pop_n(ctx, 2);
|
||||||
|
|
||||||
|
duk_push_number(ctx, fd);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_fs_close(duk_context *ctx)
|
||||||
|
{
|
||||||
|
CheckArgs(ctx, { Arg_Number });
|
||||||
|
|
||||||
|
int fd = duk_get_int(ctx, 0);
|
||||||
|
int rc = -1;
|
||||||
|
|
||||||
|
duk_get_global_string(ctx, HS_gOpenFileDescriptors);
|
||||||
|
|
||||||
|
if(duk_has_prop_index(ctx, -1, fd))
|
||||||
|
{
|
||||||
|
duk_get_prop_index(ctx, -1, fd);
|
||||||
|
duk_get_prop_string(ctx, -1, "fp");
|
||||||
|
|
||||||
|
FILE* fp = (FILE*)duk_get_pointer(ctx, -1);
|
||||||
|
rc = fclose(fp);
|
||||||
|
|
||||||
|
// unset finalizer before deleting
|
||||||
|
duk_push_undefined(ctx);
|
||||||
|
duk_set_finalizer(ctx, -3);
|
||||||
|
duk_del_prop_index(ctx, -3, fd);
|
||||||
|
|
||||||
|
duk_pop_n(ctx, 2);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
duk_push_error_object(ctx, DUK_ERR_ERROR, "invalid file descriptor");
|
||||||
|
return duk_throw(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_pop(ctx);
|
||||||
|
duk_push_number(ctx, rc);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_fs_write(duk_context *ctx)
|
||||||
|
{
|
||||||
|
CheckArgs(ctx, { Arg_Number, Arg_Any, Arg_OptNumber, Arg_OptNumber, Arg_OptNumber });
|
||||||
|
return ReadWriteImpl(ctx, FS_WRITE);
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_fs_writefile(duk_context *ctx)
|
||||||
|
{
|
||||||
|
CheckArgs(ctx, { Arg_String, Arg_Any });
|
||||||
|
|
||||||
|
const char* path = duk_get_string(ctx, 0);
|
||||||
|
|
||||||
|
void* buffer;
|
||||||
|
duk_size_t bufferSize;
|
||||||
|
|
||||||
|
if(duk_is_string(ctx, 1))
|
||||||
|
{
|
||||||
|
buffer = (void*)duk_get_lstring(ctx, 1, &bufferSize);
|
||||||
|
}
|
||||||
|
else if(duk_is_buffer_data(ctx, 1))
|
||||||
|
{
|
||||||
|
buffer = duk_get_buffer_data(ctx, 1, &bufferSize);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return ThrowInvalidArgsError(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE* fp = fopen(path, "wb");
|
||||||
|
|
||||||
|
if(fp == nullptr)
|
||||||
|
{
|
||||||
|
duk_push_error_object(ctx, DUK_ERR_ERROR, "could not open '%s' (mode: 'wb')", path);
|
||||||
|
return duk_throw(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(fwrite(buffer, 1, bufferSize, fp) != bufferSize)
|
||||||
|
{
|
||||||
|
fclose(fp);
|
||||||
|
return DUK_RET_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(fp);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_fs_read(duk_context *ctx)
|
||||||
|
{
|
||||||
|
CheckArgs(ctx, { Arg_Number, Arg_Any, Arg_Number, Arg_Number, Arg_Number });
|
||||||
|
return ReadWriteImpl(ctx, FS_READ);
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_fs_readfile(duk_context *ctx)
|
||||||
|
{
|
||||||
|
CheckArgs(ctx, { Arg_String });
|
||||||
|
|
||||||
|
const char* path = duk_get_string(ctx, 0);
|
||||||
|
|
||||||
|
FILE* fp = fopen(path, "rb");
|
||||||
|
|
||||||
|
if(fp == nullptr)
|
||||||
|
{
|
||||||
|
duk_push_error_object(ctx, DUK_ERR_ERROR, "could not open '%s' (mode: 'rb')", path);
|
||||||
|
return duk_throw(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct stat stats;
|
||||||
|
if(fstat(_fileno(fp), &stats) != 0)
|
||||||
|
{
|
||||||
|
fclose(fp);
|
||||||
|
return DUK_RET_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *data = duk_push_fixed_buffer(ctx, stats.st_size);
|
||||||
|
duk_push_buffer_object(ctx, -1, 0, stats.st_size, DUK_BUFOBJ_NODEJS_BUFFER);
|
||||||
|
|
||||||
|
if(fread(data, 1, stats.st_size, fp) != (size_t)stats.st_size)
|
||||||
|
{
|
||||||
|
duk_pop(ctx);
|
||||||
|
fclose(fp);
|
||||||
|
return DUK_RET_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(fp);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_fs_fstat(duk_context *ctx)
|
||||||
|
{
|
||||||
|
CheckArgs(ctx, { Arg_Number });
|
||||||
|
|
||||||
|
int fd = duk_get_int(ctx, 0);
|
||||||
|
|
||||||
|
duk_push_global_object(ctx);
|
||||||
|
duk_get_prop_string(ctx, -1, "fs");
|
||||||
|
duk_get_prop_string(ctx, -1, "Stats");
|
||||||
|
duk_push_number(ctx, fd);
|
||||||
|
duk_new(ctx, 1);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_fs_stat(duk_context *ctx)
|
||||||
|
{
|
||||||
|
CheckArgs(ctx, { Arg_String });
|
||||||
|
|
||||||
|
const char *path = duk_get_string(ctx, 0);
|
||||||
|
|
||||||
|
duk_push_global_object(ctx);
|
||||||
|
duk_get_prop_string(ctx, -1, "fs");
|
||||||
|
duk_get_prop_string(ctx, -1, "Stats");
|
||||||
|
duk_push_string(ctx, path);
|
||||||
|
duk_new(ctx, 1);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_fs_unlink(duk_context *ctx)
|
||||||
|
{
|
||||||
|
CheckArgs(ctx, { Arg_String });
|
||||||
|
|
||||||
|
const char *path = duk_get_string(ctx, 0);
|
||||||
|
duk_push_boolean(ctx, DeleteFileA(path) != 0);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_fs_mkdir(duk_context *ctx)
|
||||||
|
{
|
||||||
|
CheckArgs(ctx, { Arg_String });
|
||||||
|
|
||||||
|
const char *path = duk_get_string(ctx, 0);
|
||||||
|
duk_push_boolean(ctx, CreateDirectoryA(path, nullptr) != 0);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_fs_rmdir(duk_context *ctx)
|
||||||
|
{
|
||||||
|
CheckArgs(ctx, { Arg_String });
|
||||||
|
|
||||||
|
const char *path = duk_get_string(ctx, 0);
|
||||||
|
duk_push_boolean(ctx, RemoveDirectoryA(path) != 0);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// todo make sure failure behavior is similar to nodejs's fs.readdirSync
|
||||||
|
duk_ret_t ScriptAPI::js_fs_readdir(duk_context *ctx)
|
||||||
|
{
|
||||||
|
CheckArgs(ctx, { Arg_String });
|
||||||
|
|
||||||
|
const char* path = duk_get_string(ctx, 0);
|
||||||
|
|
||||||
|
char findFileName[MAX_PATH];
|
||||||
|
snprintf(findFileName, sizeof(findFileName), "%s%s", path, "\\*");
|
||||||
|
|
||||||
|
WIN32_FIND_DATAA ffd;
|
||||||
|
HANDLE hFind = FindFirstFileA(findFileName, &ffd);
|
||||||
|
|
||||||
|
if(hFind == INVALID_HANDLE_VALUE)
|
||||||
|
{
|
||||||
|
return DUK_RET_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_uarridx_t idx = 0;
|
||||||
|
duk_push_array(ctx);
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if(strcmp(ffd.cFileName, ".") == 0 ||
|
||||||
|
strcmp(ffd.cFileName, "..") == 0)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
duk_push_string(ctx, ffd.cFileName);
|
||||||
|
duk_put_prop_index(ctx, -2, idx++);
|
||||||
|
} while(FindNextFileA(hFind, &ffd));
|
||||||
|
|
||||||
|
FindClose(hFind);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_fs_Stats__constructor(duk_context *ctx)
|
||||||
|
{
|
||||||
|
if(!duk_is_constructor_call(ctx))
|
||||||
|
{
|
||||||
|
return DUK_RET_TYPE_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(duk_get_top(ctx) != 1)
|
||||||
|
{
|
||||||
|
return ThrowInvalidArgsError(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct stat stats;
|
||||||
|
|
||||||
|
if(duk_is_number(ctx, 0))
|
||||||
|
{
|
||||||
|
int fd = duk_get_int(ctx, 0);
|
||||||
|
if(fstat(fd, &stats) != 0)
|
||||||
|
{
|
||||||
|
return DUK_RET_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(duk_is_string(ctx, 0))
|
||||||
|
{
|
||||||
|
const char *path = duk_get_string(ctx, 0);
|
||||||
|
if(stat(path, &stats) != 0)
|
||||||
|
{
|
||||||
|
return DUK_RET_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return ThrowInvalidArgsError(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
const duk_number_list_entry numbers[] = {
|
||||||
|
{ "dev", (double)stats.st_dev },
|
||||||
|
{ "ino", (double)stats.st_ino },
|
||||||
|
{ "mode", (double)stats.st_mode },
|
||||||
|
{ "nlink", (double)stats.st_nlink },
|
||||||
|
{ "uid", (double)stats.st_uid },
|
||||||
|
{ "gid", (double)stats.st_gid },
|
||||||
|
{ "rdev", (double)stats.st_rdev },
|
||||||
|
{ "size", (double)stats.st_size },
|
||||||
|
{ "atimeMs", (double)stats.st_atime * 1000 },
|
||||||
|
{ "mtimeMs", (double)stats.st_mtime * 1000 },
|
||||||
|
{ "ctimeMs", (double)stats.st_ctime * 1000 },
|
||||||
|
{ nullptr, 0 }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct { const char *key; time_t time; } dates[3] = {
|
||||||
|
{ "atime", stats.st_atime * 1000 },
|
||||||
|
{ "mtime", stats.st_mtime * 1000 },
|
||||||
|
{ "ctime", stats.st_ctime * 1000 },
|
||||||
|
};
|
||||||
|
|
||||||
|
duk_push_global_object(ctx);
|
||||||
|
duk_get_prop_string(ctx, -1, "Date");
|
||||||
|
duk_remove(ctx, -2);
|
||||||
|
|
||||||
|
duk_push_this(ctx);
|
||||||
|
duk_put_number_list(ctx, -1, numbers);
|
||||||
|
|
||||||
|
for(int i = 0; i < 3; i++)
|
||||||
|
{
|
||||||
|
duk_dup(ctx, -2);
|
||||||
|
duk_push_number(ctx, (duk_double_t)dates[i].time);
|
||||||
|
duk_new(ctx, 1);
|
||||||
|
duk_put_prop_string(ctx, -2, dates[i].key);
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_remove(ctx, -2);
|
||||||
|
duk_freeze(ctx, -1);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_fs_Stats_isDirectory(duk_context *ctx)
|
||||||
|
{
|
||||||
|
CheckArgs(ctx, {});
|
||||||
|
|
||||||
|
duk_push_this(ctx);
|
||||||
|
duk_get_prop_string(ctx, -1, "mode");
|
||||||
|
duk_uint_t mode = duk_get_uint(ctx, -1);
|
||||||
|
duk_push_boolean(ctx, (mode & S_IFDIR) != 0);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_fs_Stats_isFile(duk_context *ctx)
|
||||||
|
{
|
||||||
|
CheckArgs(ctx, {});
|
||||||
|
|
||||||
|
duk_push_this(ctx);
|
||||||
|
duk_get_prop_string(ctx, -1, "mode");
|
||||||
|
duk_uint_t mode = duk_get_uint(ctx, -1);
|
||||||
|
duk_push_boolean(ctx, (mode & S_IFREG) != 0);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static duk_ret_t ReadWriteImpl(duk_context *ctx, fsop op)
|
||||||
|
{
|
||||||
|
int fd;
|
||||||
|
const char* buffer;
|
||||||
|
size_t offset = 0;
|
||||||
|
size_t length = 0;
|
||||||
|
size_t position = 0;
|
||||||
|
|
||||||
|
bool bHavePos = false;
|
||||||
|
duk_size_t bufferSize;
|
||||||
|
FILE* fp;
|
||||||
|
size_t rc;
|
||||||
|
|
||||||
|
duk_idx_t nargs = duk_get_top(ctx);
|
||||||
|
|
||||||
|
if(nargs < 2 || !duk_is_number(ctx, 0))
|
||||||
|
{
|
||||||
|
goto err_invalid_args;
|
||||||
|
}
|
||||||
|
|
||||||
|
fd = duk_get_int(ctx, 0);
|
||||||
|
|
||||||
|
if(duk_is_buffer_data(ctx, 1))
|
||||||
|
{
|
||||||
|
buffer = (const char*)duk_get_buffer_data(ctx, 1, &bufferSize);
|
||||||
|
}
|
||||||
|
else if(duk_is_string(ctx, 1) && op == FS_WRITE)
|
||||||
|
{
|
||||||
|
buffer = duk_get_lstring(ctx, 1, &bufferSize);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
goto err_invalid_args;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(nargs >= 3)
|
||||||
|
{
|
||||||
|
if(!duk_is_number(ctx, 2))
|
||||||
|
{
|
||||||
|
goto err_invalid_args;
|
||||||
|
}
|
||||||
|
|
||||||
|
offset = duk_get_uint(ctx, 2);
|
||||||
|
|
||||||
|
if(offset >= bufferSize)
|
||||||
|
{
|
||||||
|
goto err_invalid_range;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
length = bufferSize - offset;
|
||||||
|
|
||||||
|
if(nargs >= 4)
|
||||||
|
{
|
||||||
|
if(!duk_is_number(ctx, 3))
|
||||||
|
{
|
||||||
|
goto err_invalid_args;
|
||||||
|
}
|
||||||
|
length = duk_get_uint(ctx, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(nargs >= 5)
|
||||||
|
{
|
||||||
|
if(!duk_is_number(ctx, 4))
|
||||||
|
{
|
||||||
|
goto err_invalid_args;
|
||||||
|
}
|
||||||
|
position = duk_get_uint(ctx, 4);
|
||||||
|
bHavePos = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(offset + length > bufferSize)
|
||||||
|
{
|
||||||
|
goto err_invalid_range;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_get_global_string(ctx, HS_gOpenFileDescriptors);
|
||||||
|
|
||||||
|
if(!duk_has_prop_index(ctx, -1, fd))
|
||||||
|
{
|
||||||
|
goto err_invalid_fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_get_prop_index(ctx, -1, fd);
|
||||||
|
duk_get_prop_string(ctx, -1, "fp");
|
||||||
|
fp = (FILE*)duk_get_pointer(ctx, -1);
|
||||||
|
|
||||||
|
duk_pop_n(ctx, 3);
|
||||||
|
|
||||||
|
if(bHavePos)
|
||||||
|
{
|
||||||
|
fseek(fp, position, SEEK_SET);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(op)
|
||||||
|
{
|
||||||
|
case FS_READ:
|
||||||
|
rc = fread((void*)&buffer[offset], 1, length, fp);
|
||||||
|
break;
|
||||||
|
case FS_WRITE:
|
||||||
|
rc = fwrite((void*)&buffer[offset], 1, length, fp);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return DUK_RET_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_push_number(ctx, rc);
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
err_invalid_args:
|
||||||
|
return ScriptAPI::ThrowInvalidArgsError(ctx);
|
||||||
|
|
||||||
|
err_invalid_range:
|
||||||
|
duk_push_error_object(ctx, DUK_ERR_RANGE_ERROR, "invalid range");
|
||||||
|
return duk_throw(ctx);
|
||||||
|
|
||||||
|
err_invalid_fd:
|
||||||
|
duk_push_error_object(ctx, DUK_ERR_ERROR, "invalid file descriptor");
|
||||||
|
return duk_throw(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
static duk_ret_t js__FileFinalizer(duk_context *ctx)
|
||||||
|
{
|
||||||
|
CScriptInstance* inst = ScriptAPI::GetInstance(ctx);
|
||||||
|
|
||||||
|
duk_get_prop_string(ctx, 0, "fp");
|
||||||
|
FILE *fp = (FILE *)duk_get_pointer(ctx, -1);
|
||||||
|
duk_pop(ctx);
|
||||||
|
fclose(fp);
|
||||||
|
|
||||||
|
inst->System()->ConsoleLog("[SCRIPTSYS]: warning ('%s'): gc closed leftover file descriptor",
|
||||||
|
inst->Name().c_str());
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,150 @@
|
||||||
|
#include <stdafx.h>
|
||||||
|
#include "ScriptAPI.h"
|
||||||
|
#include "JSIntervalWorker.h"
|
||||||
|
|
||||||
|
using namespace ScriptAPI;
|
||||||
|
|
||||||
|
static int AddIntervalContext(duk_context* ctx, duk_idx_t func_idx, int delayMS, bool bOnce);
|
||||||
|
static void RemoveIntervalContext(duk_context* ctx, int intervalId);
|
||||||
|
|
||||||
|
void ScriptAPI::Define_interval(duk_context* ctx)
|
||||||
|
{
|
||||||
|
DefineGlobalFunction(ctx, "setInterval", js_setInterval);
|
||||||
|
DefineGlobalFunction(ctx, "clearInterval", js_clearInterval);
|
||||||
|
DefineGlobalFunction(ctx, "setTimeout", js_setTimeout);
|
||||||
|
DefineGlobalFunction(ctx, "clearTimeout", js_clearTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_setInterval(duk_context* ctx)
|
||||||
|
{
|
||||||
|
CheckArgs(ctx, { Arg_Function, Arg_Number });
|
||||||
|
int delayMS = duk_get_int(ctx, 1);
|
||||||
|
int intervalId = AddIntervalContext(ctx, 0, delayMS, false);
|
||||||
|
duk_push_int(ctx, intervalId);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_clearInterval(duk_context* ctx)
|
||||||
|
{
|
||||||
|
CheckArgs(ctx, { Arg_Number });
|
||||||
|
int intervalId = duk_get_int(ctx, 0);
|
||||||
|
RemoveIntervalContext(ctx, intervalId);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_setTimeout(duk_context* ctx)
|
||||||
|
{
|
||||||
|
CheckArgs(ctx, { Arg_Function, Arg_Number });
|
||||||
|
int delayMS = duk_get_int(ctx, 1);
|
||||||
|
int intervalId = AddIntervalContext(ctx, 0, delayMS, true);
|
||||||
|
duk_push_int(ctx, intervalId);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_clearTimeout(duk_context* ctx)
|
||||||
|
{
|
||||||
|
CheckArgs(ctx, { Arg_Number });
|
||||||
|
int intervalId = duk_get_int(ctx, 0);
|
||||||
|
RemoveIntervalContext(ctx, intervalId);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js__IntervalContext_invokeFunc(duk_context* ctx)
|
||||||
|
{
|
||||||
|
duk_push_this(ctx);
|
||||||
|
duk_get_prop_string(ctx, -1, "func");
|
||||||
|
duk_pcall(ctx, 0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js__IntervalContext_remove(duk_context* ctx)
|
||||||
|
{
|
||||||
|
duk_push_this(ctx);
|
||||||
|
duk_get_prop_string(ctx, -1, "id");
|
||||||
|
int intervalId = duk_get_int(ctx, -1);
|
||||||
|
RemoveIntervalContext(ctx, intervalId);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js__IntervalContext_finalizer(duk_context* ctx)
|
||||||
|
{
|
||||||
|
duk_push_this(ctx);
|
||||||
|
duk_get_prop_string(ctx, -1, "worker");
|
||||||
|
|
||||||
|
CJSIntervalWorker* intervalWorker = (CJSIntervalWorker*)duk_get_pointer(ctx, -1);
|
||||||
|
if (intervalWorker != nullptr)
|
||||||
|
{
|
||||||
|
delete intervalWorker;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int AddIntervalContext(duk_context* ctx, duk_idx_t func_idx, int delayMS, bool bOnce)
|
||||||
|
{
|
||||||
|
func_idx = duk_normalize_index(ctx, func_idx);
|
||||||
|
|
||||||
|
CScriptInstance* inst = GetInstance(ctx);
|
||||||
|
|
||||||
|
duk_push_global_object(ctx);
|
||||||
|
duk_get_prop_string(ctx, -1, HS_gNextInvervalId);
|
||||||
|
int intervalId = duk_get_int(ctx, -1);
|
||||||
|
duk_pop(ctx);
|
||||||
|
duk_push_int(ctx, intervalId + 1);
|
||||||
|
duk_put_prop_string(ctx, -2, HS_gNextInvervalId);
|
||||||
|
|
||||||
|
duk_get_prop_string(ctx, -1, HS_gIntervals);
|
||||||
|
|
||||||
|
duk_push_object(ctx);
|
||||||
|
RefObject(ctx, -1);
|
||||||
|
|
||||||
|
void* objectHeapPtr = duk_get_heapptr(ctx, -1);
|
||||||
|
CJSIntervalWorker* intervalWorker = new CJSIntervalWorker(inst, objectHeapPtr, delayMS, bOnce);
|
||||||
|
|
||||||
|
duk_dup(ctx, func_idx);
|
||||||
|
duk_put_prop_string(ctx, -2, "func");
|
||||||
|
|
||||||
|
duk_push_pointer(ctx, intervalWorker);
|
||||||
|
duk_put_prop_string(ctx, -2, "worker");
|
||||||
|
|
||||||
|
duk_push_int(ctx, intervalId);
|
||||||
|
duk_put_prop_string(ctx, -2, "id");
|
||||||
|
|
||||||
|
duk_push_c_function(ctx, js__IntervalContext_finalizer, 1);
|
||||||
|
duk_set_finalizer(ctx, -2);
|
||||||
|
|
||||||
|
duk_put_prop_index(ctx, -2, intervalId);
|
||||||
|
duk_pop_n(ctx, 2);
|
||||||
|
|
||||||
|
intervalWorker->StartWorkerProc();
|
||||||
|
|
||||||
|
return intervalId;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RemoveIntervalContext(duk_context* ctx, int intervalId)
|
||||||
|
{
|
||||||
|
duk_push_global_object(ctx);
|
||||||
|
duk_get_prop_string(ctx, -1, HS_gIntervals);
|
||||||
|
|
||||||
|
if (!duk_has_prop_index(ctx, -1, intervalId))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_get_prop_index(ctx, -1, intervalId);
|
||||||
|
UnrefObject(ctx, -1);
|
||||||
|
|
||||||
|
duk_get_prop_string(ctx, -1, "worker");
|
||||||
|
|
||||||
|
CJSIntervalWorker* intervalWorker = (CJSIntervalWorker*)duk_get_pointer(ctx, -1);
|
||||||
|
intervalWorker->StopWorkerProc();
|
||||||
|
delete intervalWorker;
|
||||||
|
|
||||||
|
duk_pop(ctx);
|
||||||
|
duk_push_pointer(ctx, nullptr);
|
||||||
|
duk_put_prop_string(ctx, -2, "worker");
|
||||||
|
duk_pop(ctx);
|
||||||
|
duk_del_prop_index(ctx, -1, intervalId);
|
||||||
|
|
||||||
|
duk_pop_n(ctx, 2);
|
||||||
|
}
|
|
@ -0,0 +1,533 @@
|
||||||
|
#include <stdafx.h>
|
||||||
|
#include "ScriptAPI.h"
|
||||||
|
#include <Project64/UserInterface/Debugger/debugger.h>
|
||||||
|
#include <Project64/UserInterface/Debugger/DebugMMU.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#pragma warning(disable: 4702) // disable unreachable code warning
|
||||||
|
|
||||||
|
using namespace ScriptAPI;
|
||||||
|
|
||||||
|
static size_t MemTypeSize(MemType t);
|
||||||
|
static duk_ret_t ThrowMemoryError(duk_context* ctx, uint32_t address);
|
||||||
|
|
||||||
|
void ScriptAPI::Define_mem(duk_context *ctx)
|
||||||
|
{
|
||||||
|
#define MEM_PROXY_FUNCS(T) js_mem__get<T>, js_mem__set<T>
|
||||||
|
|
||||||
|
const DukPropListEntry props[] = {
|
||||||
|
{ "getblock", DukCFunction(js_mem_getblock) },
|
||||||
|
{ "setblock", DukCFunction(js_mem_setblock) },
|
||||||
|
{ "getstring", DukCFunction(js_mem_getstring) },
|
||||||
|
{ "setstring", DukCFunction(js_mem_setblock) },
|
||||||
|
{ "bindvar", DukCFunction(js_mem_bindvar) },
|
||||||
|
{ "bindvars", DukCFunction(js_mem_bindvars) },
|
||||||
|
{ "bindstruct", DukCFunction(js_mem_bindstruct) },
|
||||||
|
{ "typedef", DukCFunction(js_mem_typedef) },
|
||||||
|
{ "ramSize", DukGetter(js_mem__get_ramsize) },
|
||||||
|
{ "romSize", DukGetter(js_mem__get_romsize) },
|
||||||
|
{ "ptr", DukGetter(js_mem__get_ptr) },
|
||||||
|
{ "u32", DukProxy(MEM_PROXY_FUNCS(uint32_t)) },
|
||||||
|
{ "u16", DukProxy(MEM_PROXY_FUNCS(uint16_t)) },
|
||||||
|
{ "u8", DukProxy(MEM_PROXY_FUNCS(uint8_t)) },
|
||||||
|
{ "s32", DukProxy(MEM_PROXY_FUNCS(int32_t)) },
|
||||||
|
{ "s16", DukProxy(MEM_PROXY_FUNCS(int16_t)) },
|
||||||
|
{ "s8", DukProxy(MEM_PROXY_FUNCS(int8_t)) },
|
||||||
|
{ "f64", DukProxy(MEM_PROXY_FUNCS(double)) },
|
||||||
|
{ "f32", DukProxy(MEM_PROXY_FUNCS(float)) },
|
||||||
|
{ nullptr }
|
||||||
|
};
|
||||||
|
|
||||||
|
DefineGlobalInterface(ctx, "mem", props);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
duk_ret_t ScriptAPI::js_mem__get(duk_context *ctx)
|
||||||
|
{
|
||||||
|
CScriptInstance *inst = GetInstance(ctx);
|
||||||
|
|
||||||
|
uint32_t addr = (uint32_t)duk_to_number(ctx, 1);
|
||||||
|
|
||||||
|
T value;
|
||||||
|
if(inst->Debugger()->DebugLoad_VAddr<T>(addr, value))
|
||||||
|
{
|
||||||
|
duk_push_number(ctx, value);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ThrowMemoryError(ctx, addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
duk_ret_t ScriptAPI::js_mem__set(duk_context *ctx)
|
||||||
|
{
|
||||||
|
CScriptInstance *inst = GetInstance(ctx);
|
||||||
|
|
||||||
|
uint32_t addr = (uint32_t)duk_to_number(ctx, 1);
|
||||||
|
T value = (T)duk_to_number(ctx, 2);
|
||||||
|
|
||||||
|
if(inst->Debugger()->DebugStore_VAddr<T>(addr, value))
|
||||||
|
{
|
||||||
|
duk_push_true(ctx);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ThrowMemoryError(ctx, addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_mem_getblock(duk_context *ctx)
|
||||||
|
{
|
||||||
|
CheckArgs(ctx, { Arg_Number, Arg_Number });
|
||||||
|
CScriptInstance *inst = GetInstance(ctx);
|
||||||
|
|
||||||
|
duk_uint_t addr = duk_to_uint(ctx, 0);
|
||||||
|
duk_uint_t length = duk_to_uint(ctx, 1);
|
||||||
|
|
||||||
|
uint8_t *data = (uint8_t*)duk_push_fixed_buffer(ctx, length);
|
||||||
|
duk_push_buffer_object(ctx, -1, 0, length, DUK_BUFOBJ_NODEJS_BUFFER);
|
||||||
|
|
||||||
|
uint32_t paddr;
|
||||||
|
uint8_t* memsrc = nullptr;
|
||||||
|
uint32_t offsetStart = 0;
|
||||||
|
|
||||||
|
if (addr < 0x80000000 || addr >= 0xC0000000)
|
||||||
|
{
|
||||||
|
if (!g_MMU || !g_MMU->TranslateVaddr(addr, paddr))
|
||||||
|
{
|
||||||
|
return ThrowMemoryError(ctx, addr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
paddr = addr & 0x1FFFFFFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (g_MMU && paddr >= 0x00000000 && (paddr + length) <= g_MMU->RdramSize())
|
||||||
|
{
|
||||||
|
memsrc = g_MMU->Rdram();
|
||||||
|
offsetStart = paddr;
|
||||||
|
}
|
||||||
|
else if (g_Rom && paddr >= 0x10000000 &&
|
||||||
|
((paddr - 0x10000000) + length) <= g_Rom->GetRomSize())
|
||||||
|
{
|
||||||
|
memsrc = g_Rom->GetRomAddress();
|
||||||
|
offsetStart = paddr - 0x10000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (memsrc != nullptr)
|
||||||
|
{
|
||||||
|
uint32_t offsetEnd = offsetStart + length;
|
||||||
|
uint32_t alignedOffsetStart = (offsetStart + 15) & ~15;
|
||||||
|
uint32_t alignedOffsetEnd = offsetEnd & ~15;
|
||||||
|
uint32_t prefixLen = alignedOffsetStart - offsetStart;
|
||||||
|
uint32_t middleLen = alignedOffsetEnd - alignedOffsetStart;
|
||||||
|
uint32_t suffixLen = offsetEnd - alignedOffsetEnd;
|
||||||
|
|
||||||
|
uint32_t* middleDst = (uint32_t*)&data[0 + prefixLen];
|
||||||
|
uint32_t* middleDstEnd = (uint32_t*)&data[0 + prefixLen + middleLen];
|
||||||
|
uint32_t* middleSrc = (uint32_t*)&memsrc[alignedOffsetStart];
|
||||||
|
|
||||||
|
for (size_t i = 0; i < prefixLen; i++)
|
||||||
|
{
|
||||||
|
data[i] = memsrc[(offsetStart + i) ^ 3];
|
||||||
|
}
|
||||||
|
|
||||||
|
while (middleDst < middleDstEnd)
|
||||||
|
{
|
||||||
|
*middleDst++ = _byteswap_ulong(*middleSrc++);
|
||||||
|
*middleDst++ = _byteswap_ulong(*middleSrc++);
|
||||||
|
*middleDst++ = _byteswap_ulong(*middleSrc++);
|
||||||
|
*middleDst++ = _byteswap_ulong(*middleSrc++);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < suffixLen; i++)
|
||||||
|
{
|
||||||
|
data[(length - suffixLen) + i] = memsrc[(alignedOffsetEnd + i) ^ 3];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < length; i++)
|
||||||
|
{
|
||||||
|
uint8_t byte;
|
||||||
|
if (inst->Debugger()->DebugLoad_VAddr<uint8_t>(addr + i, byte))
|
||||||
|
{
|
||||||
|
data[i] = byte;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return ThrowMemoryError(ctx, addr + i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_mem_getstring(duk_context *ctx)
|
||||||
|
{
|
||||||
|
CheckArgs(ctx, { Arg_Number, Arg_OptNumber });
|
||||||
|
CScriptInstance *inst = GetInstance(ctx);
|
||||||
|
|
||||||
|
duk_idx_t nargs = duk_get_top(ctx);
|
||||||
|
|
||||||
|
duk_uint_t addr = duk_to_uint(ctx, 0);
|
||||||
|
duk_uint_t maxLength = nargs > 1 ? duk_to_uint(ctx, 1) : 0xFFFFFFFF;
|
||||||
|
size_t length = 0;
|
||||||
|
|
||||||
|
for(size_t i = 0; i < maxLength; i++)
|
||||||
|
{
|
||||||
|
char c;
|
||||||
|
if(!inst->Debugger()->DebugLoad_VAddr<char>(addr + i, c))
|
||||||
|
{
|
||||||
|
return ThrowMemoryError(ctx, addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(c == 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
length++;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *str = new char[length + 1];
|
||||||
|
str[length] = '\0';
|
||||||
|
|
||||||
|
for(size_t i = 0; i < length; i++)
|
||||||
|
{
|
||||||
|
if(!inst->Debugger()->DebugLoad_VAddr<char>(addr + i, str[i]))
|
||||||
|
{
|
||||||
|
delete[] str;
|
||||||
|
return ThrowMemoryError(ctx, addr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_push_string(ctx, str);
|
||||||
|
delete[] str;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_mem_setblock(duk_context *ctx)
|
||||||
|
{
|
||||||
|
CheckArgs(ctx, { Arg_Number, Arg_OptNumber });
|
||||||
|
CScriptInstance *inst = GetInstance(ctx);
|
||||||
|
CDebuggerUI *debugger = inst->Debugger();
|
||||||
|
|
||||||
|
duk_idx_t nargs = duk_get_top(ctx);
|
||||||
|
|
||||||
|
char* data;
|
||||||
|
duk_size_t dataSize, length;
|
||||||
|
|
||||||
|
uint32_t address = duk_get_uint(ctx, 0);
|
||||||
|
|
||||||
|
if (duk_is_buffer_data(ctx, 1))
|
||||||
|
{
|
||||||
|
data = (char*)duk_get_buffer_data(ctx, 1, &dataSize);
|
||||||
|
}
|
||||||
|
else if(duk_is_string(ctx, 1))
|
||||||
|
{
|
||||||
|
data = (char*)duk_get_lstring(ctx, 1, &dataSize);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return ThrowInvalidArgsError(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nargs == 3)
|
||||||
|
{
|
||||||
|
duk_double_t l = duk_get_number(ctx, 2);
|
||||||
|
|
||||||
|
if (l < 0 || l > dataSize)
|
||||||
|
{
|
||||||
|
return DUK_RET_RANGE_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
length = (duk_size_t)l;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
length = dataSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < length; i++)
|
||||||
|
{
|
||||||
|
if (!debugger->DebugStore_VAddr(address + i, data[i]))
|
||||||
|
{
|
||||||
|
return ThrowMemoryError(ctx, address + i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_mem__boundget(duk_context *ctx)
|
||||||
|
{
|
||||||
|
CDebuggerUI *debugger = GetInstance(ctx)->Debugger();
|
||||||
|
|
||||||
|
uint32_t addr = duk_get_uint(ctx, 0);
|
||||||
|
duk_int_t type = duk_get_int(ctx, 1);
|
||||||
|
|
||||||
|
union {
|
||||||
|
uint8_t u8;
|
||||||
|
uint16_t u16;
|
||||||
|
uint32_t u32;
|
||||||
|
int8_t s8;
|
||||||
|
int16_t s16;
|
||||||
|
int32_t s32;
|
||||||
|
float f32;
|
||||||
|
double f64;
|
||||||
|
} retval;
|
||||||
|
|
||||||
|
#define MEM_BOUNDGET_TRY(addr, T, result, dukpush) \
|
||||||
|
if(debugger->DebugLoad_VAddr<T>(addr, result)) { dukpush(ctx, result); } \
|
||||||
|
else { goto memory_error; }
|
||||||
|
|
||||||
|
switch(type)
|
||||||
|
{
|
||||||
|
case U8:
|
||||||
|
MEM_BOUNDGET_TRY(addr, uint8_t, retval.u8, duk_push_uint);
|
||||||
|
return 1;
|
||||||
|
case U16:
|
||||||
|
MEM_BOUNDGET_TRY(addr, uint16_t, retval.u16, duk_push_uint);
|
||||||
|
return 1;
|
||||||
|
case U32:
|
||||||
|
MEM_BOUNDGET_TRY(addr, uint32_t, retval.u32, duk_push_uint);
|
||||||
|
return 1;
|
||||||
|
case S8:
|
||||||
|
MEM_BOUNDGET_TRY(addr, int8_t, retval.s8, duk_push_int);
|
||||||
|
return 1;
|
||||||
|
case S16:
|
||||||
|
MEM_BOUNDGET_TRY(addr, int16_t, retval.s16, duk_push_int);
|
||||||
|
return 1;
|
||||||
|
case S32:
|
||||||
|
MEM_BOUNDGET_TRY(addr, int32_t, retval.s32, duk_push_int);
|
||||||
|
return 1;
|
||||||
|
case F32:
|
||||||
|
MEM_BOUNDGET_TRY(addr, float, retval.f32, duk_push_number);
|
||||||
|
return 1;
|
||||||
|
case F64:
|
||||||
|
MEM_BOUNDGET_TRY(addr, double, retval.f64, duk_push_number);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
memory_error:
|
||||||
|
return ThrowMemoryError(ctx, addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_mem__boundset(duk_context *ctx)
|
||||||
|
{
|
||||||
|
CDebuggerUI *debugger = GetInstance(ctx)->Debugger();
|
||||||
|
|
||||||
|
uint32_t addr = duk_get_uint(ctx, 0);
|
||||||
|
duk_int_t type = duk_get_int(ctx, 1);
|
||||||
|
|
||||||
|
#define MEM_BOUNDSET_TRY(addr, T, value) \
|
||||||
|
if(debugger->DebugStore_VAddr<T>(addr, value)) { return 1; } \
|
||||||
|
else { goto memory_error; }
|
||||||
|
|
||||||
|
switch(type)
|
||||||
|
{
|
||||||
|
case U8:
|
||||||
|
MEM_BOUNDSET_TRY(addr, uint8_t, duk_get_uint(ctx, 2) & 0xFF);
|
||||||
|
break;
|
||||||
|
case U16:
|
||||||
|
MEM_BOUNDSET_TRY(addr, uint16_t, duk_get_uint(ctx, 2) & 0xFFFF);
|
||||||
|
break;
|
||||||
|
case U32:
|
||||||
|
MEM_BOUNDSET_TRY(addr, uint32_t, duk_get_uint(ctx, 2));
|
||||||
|
break;
|
||||||
|
case S8:
|
||||||
|
MEM_BOUNDSET_TRY(addr, int8_t, duk_get_int(ctx, 2) & 0xFF);
|
||||||
|
break;
|
||||||
|
case S16:
|
||||||
|
MEM_BOUNDSET_TRY(addr, int16_t, duk_get_int(ctx, 2) & 0xFFFF);
|
||||||
|
break;
|
||||||
|
case S32:
|
||||||
|
MEM_BOUNDSET_TRY(addr, int32_t, duk_get_int(ctx, 2));
|
||||||
|
break;
|
||||||
|
case F32:
|
||||||
|
MEM_BOUNDSET_TRY(addr, float, (float)duk_get_number(ctx, 2));
|
||||||
|
break;
|
||||||
|
case F64:
|
||||||
|
MEM_BOUNDSET_TRY(addr, double, duk_get_number(ctx, 2));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
memory_error:
|
||||||
|
return ThrowMemoryError(ctx, addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_mem_bindvar(duk_context *ctx)
|
||||||
|
{
|
||||||
|
CheckArgs(ctx, { Arg_Object, Arg_Number, Arg_String, Arg_Number });
|
||||||
|
|
||||||
|
duk_uint_t addr = duk_get_uint(ctx, 1);
|
||||||
|
const char* key = duk_get_string(ctx, 2);
|
||||||
|
duk_int_t type = duk_get_int(ctx, 3);
|
||||||
|
|
||||||
|
duk_push_string(ctx, key);
|
||||||
|
|
||||||
|
// [ ... js_mem__boundget ] -> [ ... js_mem__boundget.bind(obj, addr, type) ]
|
||||||
|
duk_push_c_function(ctx, js_mem__boundget, DUK_VARARGS);
|
||||||
|
duk_push_string(ctx, "bind");
|
||||||
|
duk_dup(ctx, 0);
|
||||||
|
duk_push_uint(ctx, addr);
|
||||||
|
duk_push_int(ctx, type);
|
||||||
|
duk_call_prop(ctx, -5, 3);
|
||||||
|
duk_remove(ctx, -2);
|
||||||
|
|
||||||
|
duk_push_c_function(ctx, js_mem__boundset, DUK_VARARGS);
|
||||||
|
duk_push_string(ctx, "bind");
|
||||||
|
duk_dup(ctx, 0);
|
||||||
|
duk_push_uint(ctx, addr);
|
||||||
|
duk_push_int(ctx, type);
|
||||||
|
duk_call_prop(ctx, -5, 3);
|
||||||
|
duk_remove(ctx, -2);
|
||||||
|
|
||||||
|
duk_def_prop(ctx, 0, DUK_DEFPROP_HAVE_GETTER | DUK_DEFPROP_HAVE_SETTER);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_mem_bindvars(duk_context *ctx)
|
||||||
|
{
|
||||||
|
CheckArgs(ctx, { Arg_Object, Arg_Array });
|
||||||
|
|
||||||
|
duk_size_t length = duk_get_length(ctx, 1);
|
||||||
|
|
||||||
|
for(duk_uarridx_t i = 0; i < length; i++)
|
||||||
|
{
|
||||||
|
duk_get_prop_index(ctx, 1, i);
|
||||||
|
if(!duk_is_array(ctx, -1) ||
|
||||||
|
duk_get_length(ctx, -1) != 3)
|
||||||
|
{
|
||||||
|
return DUK_RET_TYPE_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_push_c_function(ctx, js_mem_bindvar, 4);
|
||||||
|
duk_dup(ctx, 0);
|
||||||
|
duk_get_prop_index(ctx, -3, 0);
|
||||||
|
duk_get_prop_index(ctx, -4, 1);
|
||||||
|
duk_get_prop_index(ctx, -5, 2);
|
||||||
|
|
||||||
|
if(duk_pcall(ctx, 4) != DUK_EXEC_SUCCESS)
|
||||||
|
{
|
||||||
|
return duk_throw(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_pop_n(ctx, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_dup(ctx, 0);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_mem_bindstruct(duk_context *ctx)
|
||||||
|
{
|
||||||
|
CheckArgs(ctx, { Arg_Object, Arg_Number, Arg_Object });
|
||||||
|
|
||||||
|
uint32_t curAddr = duk_get_uint(ctx, 1);
|
||||||
|
|
||||||
|
duk_enum(ctx, 2, DUK_ENUM_OWN_PROPERTIES_ONLY);
|
||||||
|
|
||||||
|
while(duk_next(ctx, -1, 1))
|
||||||
|
{
|
||||||
|
MemType type = (MemType)duk_get_int(ctx, -1);
|
||||||
|
|
||||||
|
duk_push_c_function(ctx, js_mem_bindvar, 4);
|
||||||
|
duk_dup(ctx, 0);
|
||||||
|
duk_push_uint(ctx, curAddr);
|
||||||
|
duk_pull(ctx, -5);
|
||||||
|
duk_pull(ctx, -5);
|
||||||
|
|
||||||
|
if(duk_pcall(ctx, 4) != DUK_EXEC_SUCCESS)
|
||||||
|
{
|
||||||
|
return duk_throw(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_pop(ctx);
|
||||||
|
|
||||||
|
curAddr += MemTypeSize(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_dup(ctx, 0);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_mem__type_constructor(duk_context* ctx)
|
||||||
|
{
|
||||||
|
CheckArgs(ctx, { Arg_Object, Arg_Number });
|
||||||
|
|
||||||
|
duk_push_c_function(ctx, js_mem_bindstruct, 3);
|
||||||
|
duk_push_this(ctx);
|
||||||
|
duk_pull(ctx, 1);
|
||||||
|
duk_pull(ctx, 0);
|
||||||
|
|
||||||
|
if(duk_pcall(ctx, 3) != DUK_EXEC_SUCCESS)
|
||||||
|
{
|
||||||
|
return duk_throw(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_mem_typedef(duk_context* ctx)
|
||||||
|
{
|
||||||
|
CheckArgs(ctx, { Arg_Object });
|
||||||
|
|
||||||
|
duk_push_c_function(ctx, js_mem__type_constructor, DUK_VARARGS);
|
||||||
|
duk_push_string(ctx, "bind");
|
||||||
|
duk_push_null(ctx);
|
||||||
|
duk_dup(ctx, 0);
|
||||||
|
duk_call_prop(ctx, -4, 2);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_mem__get_ramsize(duk_context* ctx)
|
||||||
|
{
|
||||||
|
duk_push_number(ctx, g_MMU ? g_MMU->RdramSize() : 0);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_mem__get_romsize(duk_context* ctx)
|
||||||
|
{
|
||||||
|
duk_push_number(ctx, g_Rom ? g_Rom->GetRomSize() : 0);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_mem__get_ptr(duk_context* ctx)
|
||||||
|
{
|
||||||
|
duk_push_pointer(ctx, g_MMU ? g_MMU->Rdram() : nullptr);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t MemTypeSize(MemType t)
|
||||||
|
{
|
||||||
|
switch(t)
|
||||||
|
{
|
||||||
|
case U8:
|
||||||
|
case S8:
|
||||||
|
return 1;
|
||||||
|
case U16:
|
||||||
|
case S16:
|
||||||
|
return 2;
|
||||||
|
case U32:
|
||||||
|
case S32:
|
||||||
|
case F32:
|
||||||
|
return 4;
|
||||||
|
case F64:
|
||||||
|
return 8;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ThrowMemoryError(duk_context* ctx, uint32_t address)
|
||||||
|
{
|
||||||
|
duk_push_error_object(ctx, DUK_ERR_ERROR, "memory error (can't access 0x%08X)", address);
|
||||||
|
return duk_throw(ctx);
|
||||||
|
}
|
|
@ -0,0 +1,245 @@
|
||||||
|
#include <stdafx.h>
|
||||||
|
#include "ScriptAPI.h"
|
||||||
|
#include <Project64/UserInterface/DiscordRPC.h>
|
||||||
|
#include <Project64/UserInterface/MainWindow.h>
|
||||||
|
|
||||||
|
void ScriptAPI::Define_pj64(duk_context* ctx)
|
||||||
|
{
|
||||||
|
const DukPropListEntry romInfoProps[] = {
|
||||||
|
{ "goodName", DukGetter(js_pj64_romInfo__get_goodName) },
|
||||||
|
{ "fileName", DukGetter(js_pj64_romInfo__get_fileName) },
|
||||||
|
{ "filePath", DukGetter(js_pj64_romInfo__get_filePath) },
|
||||||
|
{ "crc1", DukGetter(js_pj64_romInfo__get_headerCrc1) },
|
||||||
|
{ "crc2", DukGetter(js_pj64_romInfo__get_headerCrc2) },
|
||||||
|
{ "name", DukGetter(js_pj64_romInfo__get_headerName) },
|
||||||
|
{ "mediaFormat", DukGetter(js_pj64_romInfo__get_headerMediaFormat) },
|
||||||
|
{ "id", DukGetter(js_pj64_romInfo__get_headerId) },
|
||||||
|
{ "countryCode", DukGetter(js_pj64_romInfo__get_headerCountryCode) },
|
||||||
|
{ "version", DukGetter(js_pj64_romInfo__get_headerVersion) },
|
||||||
|
{ nullptr }
|
||||||
|
};
|
||||||
|
|
||||||
|
const DukPropListEntry props[] = {
|
||||||
|
{ "open", DukCFunction(js_pj64_open) },
|
||||||
|
{ "close", DukCFunction(js_pj64_close) },
|
||||||
|
{ "reset", DukCFunction(js_pj64_reset) },
|
||||||
|
{ "pause", DukCFunction(js_pj64_pause) },
|
||||||
|
{ "resume", DukCFunction(js_pj64_resume) },
|
||||||
|
{ "limitfps", DukCFunction(js_pj64_limitfps) },
|
||||||
|
//{ "savestate", DukCFunction(js_pj64_savestate) },
|
||||||
|
//{ "loadstate", DukCFunction(js_pj64_loadstate) },
|
||||||
|
{ "installDirectory", DukGetter(js_pj64__get_installDirectory) },
|
||||||
|
{ "scriptsDirectory", DukGetter(js_pj64__get_scriptsDirectory) },
|
||||||
|
{ "modulesDirectory", DukGetter(js_pj64__get_modulesDirectory) },
|
||||||
|
{ "romDirectory", DukGetter(js_pj64__get_romDirectory) },
|
||||||
|
{ DUK_HIDDEN_SYMBOL("romInfo"), DukObject(romInfoProps) },
|
||||||
|
{ "romInfo", DukGetter(js_pj64__get_romInfo) },
|
||||||
|
{ nullptr }
|
||||||
|
};
|
||||||
|
|
||||||
|
DefineGlobalInterface(ctx, "pj64", props);
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_pj64_open(duk_context* ctx)
|
||||||
|
{
|
||||||
|
CheckArgs(ctx, { Arg_String });
|
||||||
|
|
||||||
|
const char* romPath = duk_get_string(ctx, 0);
|
||||||
|
|
||||||
|
HWND hMainWindow = (HWND)g_Plugins->MainWindow()->GetWindowHandle();
|
||||||
|
char* romPathCopy = new char[MAX_PATH]; // main window proc deletes
|
||||||
|
strncpy(romPathCopy, romPath, MAX_PATH);
|
||||||
|
PostMessage(hMainWindow, WM_JSAPI_ACTION, JSAPI_ACT_OPEN_ROM, (WPARAM)romPath);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_pj64_close(duk_context* /*ctx*/)
|
||||||
|
{
|
||||||
|
HWND hMainWindow = (HWND)g_Plugins->MainWindow()->GetWindowHandle();
|
||||||
|
PostMessage(hMainWindow, WM_JSAPI_ACTION, JSAPI_ACT_CLOSE_ROM, 0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_pj64_reset(duk_context* ctx)
|
||||||
|
{
|
||||||
|
CheckArgs(ctx, { Arg_OptBoolean });
|
||||||
|
bool bSoftReset = (bool)duk_get_boolean_default(ctx, 0, (duk_bool_t)false);
|
||||||
|
|
||||||
|
HWND hMainWindow = (HWND)g_Plugins->MainWindow()->GetWindowHandle();
|
||||||
|
PostMessage(hMainWindow, WM_JSAPI_ACTION, JSAPI_ACT_RESET, (WPARAM)bSoftReset);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_pj64_pause(duk_context* /*ctx*/)
|
||||||
|
{
|
||||||
|
HWND hMainWindow = (HWND)g_Plugins->MainWindow()->GetWindowHandle();
|
||||||
|
PostMessage(hMainWindow, WM_JSAPI_ACTION, JSAPI_ACT_PAUSE, 0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_pj64_resume(duk_context* /*ctx*/)
|
||||||
|
{
|
||||||
|
HWND hMainWindow = (HWND)g_Plugins->MainWindow()->GetWindowHandle();
|
||||||
|
PostMessage(hMainWindow, WM_JSAPI_ACTION, JSAPI_ACT_RESUME, 0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_pj64_limitfps(duk_context* ctx)
|
||||||
|
{
|
||||||
|
CheckArgs(ctx, { Arg_Boolean });
|
||||||
|
bool bLimitFps = duk_get_boolean(ctx, 0);
|
||||||
|
g_Settings->SaveBool(GameRunning_LimitFPS, bLimitFps);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
duk_ret_t ScriptAPI::js_pj64_savestate(duk_context* ctx)
|
||||||
|
{
|
||||||
|
// todo: adjust path
|
||||||
|
CheckArgs(ctx, { Arg_OptString });
|
||||||
|
const char* path = duk_get_string_default(ctx, 0, "");
|
||||||
|
|
||||||
|
g_Settings->SaveString(GameRunning_InstantSaveFile, path);
|
||||||
|
g_System->ExternalEvent(SysEvent_SaveMachineState);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_pj64_loadstate(duk_context* ctx)
|
||||||
|
{
|
||||||
|
// todo: adjust path
|
||||||
|
CheckArgs(ctx, { Arg_OptString });
|
||||||
|
const char* path = duk_get_string_default(ctx, 0, "");
|
||||||
|
|
||||||
|
g_Settings->SaveString(GameRunning_InstantSaveFile, path);
|
||||||
|
g_System->ExternalEvent(SysEvent_LoadMachineState);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_pj64__get_installDirectory(duk_context* ctx)
|
||||||
|
{
|
||||||
|
stdstr dirPath = GetInstance(ctx)->System()->InstallDirPath();
|
||||||
|
duk_push_string(ctx, dirPath.c_str());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_pj64__get_scriptsDirectory(duk_context* ctx)
|
||||||
|
{
|
||||||
|
stdstr dirPath = GetInstance(ctx)->System()->ScriptsDirPath();
|
||||||
|
duk_push_string(ctx, dirPath.c_str());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_pj64__get_modulesDirectory(duk_context* ctx)
|
||||||
|
{
|
||||||
|
stdstr dirPath = GetInstance(ctx)->System()->ModulesDirPath();
|
||||||
|
duk_push_string(ctx, dirPath.c_str());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_pj64__get_romDirectory(duk_context * ctx)
|
||||||
|
{
|
||||||
|
duk_push_string(ctx, g_Settings->LoadStringVal(RomList_GameDir).c_str());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_pj64__get_romInfo(duk_context* ctx)
|
||||||
|
{
|
||||||
|
if (g_Settings->LoadStringVal(Game_GameName) != "")
|
||||||
|
{
|
||||||
|
duk_push_this(ctx);
|
||||||
|
duk_get_prop_string(ctx, -1, DUK_HIDDEN_SYMBOL("romInfo"));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
duk_push_null(ctx);
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_pj64_romInfo__get_goodName(duk_context* ctx)
|
||||||
|
{
|
||||||
|
duk_push_string(ctx, g_Settings->LoadStringVal(Rdb_GoodName).c_str());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_pj64_romInfo__get_fileName(duk_context* ctx)
|
||||||
|
{
|
||||||
|
duk_push_string(ctx, CPath(g_Settings->LoadStringVal(Game_File)).GetNameExtension().c_str());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_pj64_romInfo__get_filePath(duk_context* ctx)
|
||||||
|
{
|
||||||
|
duk_push_string(ctx, g_Settings->LoadStringVal(Game_File).c_str());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_pj64_romInfo__get_headerCrc1(duk_context* ctx)
|
||||||
|
{
|
||||||
|
CDebuggerUI* debugger = GetInstance(ctx)->Debugger();
|
||||||
|
uint32_t crc1 = 0;
|
||||||
|
debugger->DebugLoad_VAddr<uint32_t>(0xB0000010, crc1);
|
||||||
|
duk_push_uint(ctx, crc1);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_pj64_romInfo__get_headerCrc2(duk_context* ctx)
|
||||||
|
{
|
||||||
|
CDebuggerUI* debugger = GetInstance(ctx)->Debugger();
|
||||||
|
uint32_t crc2 = 0;
|
||||||
|
debugger->DebugLoad_VAddr<uint32_t>(0xB0000014, crc2);
|
||||||
|
duk_push_uint(ctx, crc2);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_pj64_romInfo__get_headerName(duk_context* ctx)
|
||||||
|
{
|
||||||
|
CDebuggerUI* debugger = GetInstance(ctx)->Debugger();
|
||||||
|
char headerName[0x15] = "";
|
||||||
|
for (size_t i = 0; i < 0x14; i++)
|
||||||
|
{
|
||||||
|
debugger->DebugLoad_VAddr<char>(0xB0000020 + i, headerName[i]);
|
||||||
|
}
|
||||||
|
duk_push_string(ctx, headerName);
|
||||||
|
duk_trim(ctx, -1);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_pj64_romInfo__get_headerMediaFormat(duk_context* ctx)
|
||||||
|
{
|
||||||
|
CDebuggerUI* debugger = GetInstance(ctx)->Debugger();
|
||||||
|
uint32_t mediaFormat = 0;
|
||||||
|
debugger->DebugLoad_VAddr<uint32_t>(0xB0000038, mediaFormat);
|
||||||
|
duk_push_uint(ctx, mediaFormat);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_pj64_romInfo__get_headerId(duk_context* ctx)
|
||||||
|
{
|
||||||
|
CDebuggerUI* debugger = GetInstance(ctx)->Debugger();
|
||||||
|
char headerId[3] = "";
|
||||||
|
debugger->DebugLoad_VAddr<char>(0xB000003C, headerId[0]);
|
||||||
|
debugger->DebugLoad_VAddr<char>(0xB000003D, headerId[1]);
|
||||||
|
duk_push_string(ctx, headerId);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_pj64_romInfo__get_headerCountryCode(duk_context* ctx)
|
||||||
|
{
|
||||||
|
CDebuggerUI* debugger = GetInstance(ctx)->Debugger();
|
||||||
|
char countryCode[2] = "";
|
||||||
|
debugger->DebugLoad_VAddr<char>(0xB000003E, countryCode[0]);
|
||||||
|
duk_push_string(ctx, countryCode);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_pj64_romInfo__get_headerVersion(duk_context* ctx)
|
||||||
|
{
|
||||||
|
CDebuggerUI* debugger = GetInstance(ctx)->Debugger();
|
||||||
|
uint8_t headerVersion = 0;
|
||||||
|
debugger->DebugLoad_VAddr<uint8_t>(0xB000003F, headerVersion);
|
||||||
|
duk_push_uint(ctx, headerVersion);
|
||||||
|
return 1;
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
#include <stdafx.h>
|
||||||
|
#include "ScriptAPI.h"
|
||||||
|
|
||||||
|
void ScriptAPI::Define_script(duk_context *ctx)
|
||||||
|
{
|
||||||
|
const DukPropListEntry props[] = {
|
||||||
|
{ "timeout", DukCFunction(js_script_timeout) },
|
||||||
|
{ "keepalive", DukCFunction(js_script_keepalive) },
|
||||||
|
{ nullptr }
|
||||||
|
};
|
||||||
|
|
||||||
|
DefineGlobalInterface(ctx, "script", props);
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_script_timeout(duk_context *ctx)
|
||||||
|
{
|
||||||
|
CheckArgs(ctx, { Arg_Number });
|
||||||
|
CScriptInstance* inst = GetInstance(ctx);
|
||||||
|
inst->SetExecTimeout((uint64_t)duk_get_number(ctx, 0));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_ret_t ScriptAPI::js_script_keepalive(duk_context *ctx)
|
||||||
|
{
|
||||||
|
CheckArgs(ctx, { Arg_Boolean });
|
||||||
|
CScriptInstance* inst = GetInstance(ctx);
|
||||||
|
|
||||||
|
duk_bool_t bKeepAlive = duk_get_boolean(ctx, 0);
|
||||||
|
|
||||||
|
duk_push_global_object(ctx);
|
||||||
|
duk_bool_t bHaveProp = duk_has_prop_string(ctx, -1, HS_gKeepAlive);
|
||||||
|
|
||||||
|
if(bKeepAlive && !bHaveProp)
|
||||||
|
{
|
||||||
|
duk_push_boolean(ctx, 1);
|
||||||
|
duk_put_prop_string(ctx, -2, HS_gKeepAlive);
|
||||||
|
inst->IncRefCount();
|
||||||
|
}
|
||||||
|
else if(!bKeepAlive && bHaveProp)
|
||||||
|
{
|
||||||
|
duk_del_prop_string(ctx, -1, HS_gKeepAlive);
|
||||||
|
inst->DecRefCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
duk_pop(ctx);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -1,179 +0,0 @@
|
||||||
#include <stdafx.h>
|
|
||||||
|
|
||||||
#include "ScriptHook.h"
|
|
||||||
#include "ScriptInstance.h"
|
|
||||||
#include "ScriptSystem.h"
|
|
||||||
|
|
||||||
int CScriptHook::Add(CScriptInstance* scriptInstance, void* heapptr, uint32_t param, uint32_t param2,
|
|
||||||
uint32_t param3, uint32_t param4, bool bOnce)
|
|
||||||
{
|
|
||||||
JSCALLBACK jsCallback;
|
|
||||||
jsCallback.scriptInstance = scriptInstance;
|
|
||||||
jsCallback.heapptr = heapptr;
|
|
||||||
jsCallback.callbackId = m_ScriptSystem->GetNextCallbackId();
|
|
||||||
jsCallback.param = param;
|
|
||||||
jsCallback.param2 = param2;
|
|
||||||
jsCallback.param3 = param3;
|
|
||||||
jsCallback.param4 = param4;
|
|
||||||
jsCallback.bOnce = bOnce;
|
|
||||||
m_Callbacks.push_back(jsCallback);
|
|
||||||
m_ScriptSystem->CallbackAdded();
|
|
||||||
return jsCallback.callbackId;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CScriptHook::InvokeById(int callbackId)
|
|
||||||
{
|
|
||||||
int nCallbacks = m_Callbacks.size();
|
|
||||||
for (int i = 0; i < nCallbacks; i++)
|
|
||||||
{
|
|
||||||
if (m_Callbacks[i].callbackId == callbackId)
|
|
||||||
{
|
|
||||||
m_Callbacks[i].scriptInstance->Invoke(m_Callbacks[i].heapptr);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CScriptHook::InvokeByParam(uint32_t param)
|
|
||||||
{
|
|
||||||
int nCallbacks = m_Callbacks.size();
|
|
||||||
for (int i = 0; i < nCallbacks; i++)
|
|
||||||
{
|
|
||||||
if (m_Callbacks[i].param == param)
|
|
||||||
{
|
|
||||||
m_Callbacks[i].scriptInstance->Invoke(m_Callbacks[i].heapptr, param);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CScriptHook::InvokeByAddressInRange(uint32_t address)
|
|
||||||
{
|
|
||||||
int nCallbacks = m_Callbacks.size();
|
|
||||||
for (int i = 0; i < nCallbacks; i++)
|
|
||||||
{
|
|
||||||
if (address == m_Callbacks[i].param || (address >= m_Callbacks[i].param && address <= m_Callbacks[i].param2))
|
|
||||||
{
|
|
||||||
m_Callbacks[i].scriptInstance->Invoke(m_Callbacks[i].heapptr, address);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CScriptHook::InvokeByAddressInRange_MaskedOpcode(uint32_t pc, uint32_t opcode)
|
|
||||||
{
|
|
||||||
int nCallbacks = m_Callbacks.size();
|
|
||||||
for (int i = 0; i < nCallbacks; i++)
|
|
||||||
{
|
|
||||||
if (pc == m_Callbacks[i].param || (pc >= m_Callbacks[i].param && pc <= m_Callbacks[i].param2))
|
|
||||||
{
|
|
||||||
if ((m_Callbacks[i].param3 & m_Callbacks[i].param4) == (opcode & m_Callbacks[i].param4))
|
|
||||||
{
|
|
||||||
m_Callbacks[i].scriptInstance->Invoke(m_Callbacks[i].heapptr, pc);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CScriptHook::InvokeByAddressInRange_GPRValue(uint32_t pc)
|
|
||||||
{
|
|
||||||
int nCallbacks = m_Callbacks.size();
|
|
||||||
|
|
||||||
for (int i = 0; i < nCallbacks; i++)
|
|
||||||
{
|
|
||||||
uint32_t startAddress = m_Callbacks[i].param;
|
|
||||||
uint32_t endAddress = m_Callbacks[i].param2;
|
|
||||||
uint32_t registers = m_Callbacks[i].param3;
|
|
||||||
uint32_t value = m_Callbacks[i].param4;
|
|
||||||
|
|
||||||
if (!(pc == startAddress || (pc >= startAddress && pc <= endAddress)))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int nReg = 0; nReg < 32; nReg++)
|
|
||||||
{
|
|
||||||
if (registers & (1 << nReg))
|
|
||||||
{
|
|
||||||
if (value == g_Reg->m_GPR[nReg].UW[0])
|
|
||||||
{
|
|
||||||
m_Callbacks[i].scriptInstance->Invoke2(m_Callbacks[i].heapptr, pc, nReg);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CScriptHook::InvokeAll()
|
|
||||||
{
|
|
||||||
int nCallbacks = m_Callbacks.size();
|
|
||||||
for (int i = 0; i < nCallbacks; i++)
|
|
||||||
{
|
|
||||||
m_Callbacks[i].scriptInstance->Invoke(m_Callbacks[i].heapptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CScriptHook::RemoveById(int callbackId)
|
|
||||||
{
|
|
||||||
int nCallbacks = m_Callbacks.size();
|
|
||||||
for (int i = 0; i < nCallbacks; i++)
|
|
||||||
{
|
|
||||||
if (m_Callbacks[i].callbackId == callbackId)
|
|
||||||
{
|
|
||||||
m_Callbacks.erase(m_Callbacks.begin() + i);
|
|
||||||
m_ScriptSystem->CallbackRemoved();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CScriptHook::RemoveByParam(uint32_t param)
|
|
||||||
{
|
|
||||||
int nCallbacks = m_Callbacks.size();
|
|
||||||
for (int i = 0; i < nCallbacks; i++)
|
|
||||||
{
|
|
||||||
if (m_Callbacks[i].param == param)
|
|
||||||
{
|
|
||||||
m_Callbacks.erase(m_Callbacks.begin() + i);
|
|
||||||
m_ScriptSystem->CallbackRemoved();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CScriptHook::RemoveByInstance(CScriptInstance* scriptInstance)
|
|
||||||
{
|
|
||||||
int lastIndex = m_Callbacks.size() - 1;
|
|
||||||
for (int i = lastIndex; i >= 0; i--)
|
|
||||||
{
|
|
||||||
if (m_Callbacks[i].scriptInstance == scriptInstance)
|
|
||||||
{
|
|
||||||
m_Callbacks.erase(m_Callbacks.begin() + i);
|
|
||||||
m_ScriptSystem->CallbackRemoved();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CScriptHook::HasContext(CScriptInstance* scriptInstance)
|
|
||||||
{
|
|
||||||
for (size_t i = 0; i < m_Callbacks.size(); i++)
|
|
||||||
{
|
|
||||||
if (m_Callbacks[i].scriptInstance == scriptInstance)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
CScriptHook::CScriptHook(CScriptSystem* scriptSystem)
|
|
||||||
{
|
|
||||||
m_ScriptSystem = scriptSystem;
|
|
||||||
}
|
|
||||||
|
|
||||||
CScriptHook::~CScriptHook()
|
|
||||||
{
|
|
||||||
m_Callbacks.clear();
|
|
||||||
}
|
|
|
@ -1,45 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <stdafx.h>
|
|
||||||
|
|
||||||
class CScriptInstance;
|
|
||||||
class CScriptSystem;
|
|
||||||
|
|
||||||
class CScriptHook
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
typedef struct {
|
|
||||||
CScriptInstance* scriptInstance;
|
|
||||||
void* heapptr;
|
|
||||||
uint32_t param;
|
|
||||||
uint32_t param2;
|
|
||||||
uint32_t param3;
|
|
||||||
uint32_t param4;
|
|
||||||
int callbackId;
|
|
||||||
bool bOnce;
|
|
||||||
} JSCALLBACK;
|
|
||||||
|
|
||||||
CScriptSystem* m_ScriptSystem;
|
|
||||||
|
|
||||||
//int m_NextCallbackId;
|
|
||||||
vector<JSCALLBACK> m_Callbacks;
|
|
||||||
|
|
||||||
public:
|
|
||||||
CScriptHook(CScriptSystem* scriptSystem);
|
|
||||||
~CScriptHook();
|
|
||||||
int Add(CScriptInstance* scriptInstance, void* heapptr, uint32_t param = 0, uint32_t param2 = 0,
|
|
||||||
uint32_t param3 = 0, uint32_t param4 = 0, bool bOnce = false);
|
|
||||||
void InvokeAll();
|
|
||||||
void InvokeById(int callbackId);
|
|
||||||
void InvokeByParam(uint32_t param);
|
|
||||||
// Invoke if param >= cb.param && param < cb.param2
|
|
||||||
void InvokeByAddressInRange(uint32_t address);
|
|
||||||
// Invoke if param >= cb.param && param < cb.param2 && (value & cb.param4) == cb.param3
|
|
||||||
void InvokeByAddressInRange_MaskedOpcode(uint32_t pc, uint32_t value);
|
|
||||||
void InvokeByAddressInRange_GPRValue(uint32_t pc);
|
|
||||||
void RemoveById(int callbackId);
|
|
||||||
void RemoveByParam(uint32_t tag);
|
|
||||||
void RemoveByInstance(CScriptInstance* scriptInstance);
|
|
||||||
bool HasContext(CScriptInstance* scriptInstance);
|
|
||||||
//bool HasTag(uint32_t tag);
|
|
||||||
};
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,256 +1,62 @@
|
||||||
|
#include "ScriptTypes.h"
|
||||||
|
#include "ScriptSystem.h"
|
||||||
|
#include "ScriptWorker.h"
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "stdafx.h"
|
|
||||||
#include <3rdParty/duktape/duktape.h>
|
|
||||||
|
|
||||||
#include <winsock2.h>
|
|
||||||
#include <ws2tcpip.h>
|
|
||||||
#include <mswsock.h>
|
|
||||||
|
|
||||||
#pragma comment(lib, "Ws2_32.lib")
|
|
||||||
#pragma comment(lib, "Mswsock.lib")
|
|
||||||
|
|
||||||
class CScriptSystem;
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
STATE_STARTED, // Initial evaluation and execution
|
|
||||||
STATE_RUNNING, // Event loop running with pending events
|
|
||||||
STATE_STOPPED, // No pending events
|
|
||||||
STATE_INVALID
|
|
||||||
} INSTANCE_STATE;
|
|
||||||
|
|
||||||
class CScriptInstance
|
class CScriptInstance
|
||||||
{
|
{
|
||||||
typedef enum {
|
private:
|
||||||
EVENT_READ,
|
JSInstanceName m_InstanceName;
|
||||||
EVENT_WRITE,
|
CScriptSystem* m_System;
|
||||||
EVENT_ACCEPT,
|
duk_context* m_Ctx;
|
||||||
EVENT_CONNECT
|
size_t m_RefCount;
|
||||||
} IOEVENTTYPE;
|
uint64_t m_ExecTimeout;
|
||||||
|
uint64_t m_ExecStartTime;
|
||||||
|
std::ifstream m_SourceFile;
|
||||||
|
char* m_SourceCode;
|
||||||
|
JSAppCallbackID m_CurExecCallbackId;
|
||||||
|
std::vector<CScriptWorker*> m_Workers;
|
||||||
|
bool m_bStopping;
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
OVERLAPPED ovl;
|
|
||||||
IOEVENTTYPE eventType;
|
|
||||||
HANDLE fd;
|
|
||||||
HANDLE childFd; // Accepted socket
|
|
||||||
bool bSocket;
|
|
||||||
UINT id;
|
|
||||||
void* data;
|
|
||||||
DWORD dataLen; // Changed to bytes transferred after event is fired
|
|
||||||
void* callback;
|
|
||||||
} IOLISTENER;
|
|
||||||
|
|
||||||
// Wrapper for file/socket descriptor and completion port
|
|
||||||
typedef struct {
|
|
||||||
HANDLE fd;
|
|
||||||
HANDLE iocp;
|
|
||||||
bool bSocket;
|
|
||||||
} IOFD;
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
EVENT_STATUS_OK,
|
|
||||||
EVENT_STATUS_INTERRUPTED,
|
|
||||||
EVENT_STATUS_ERROR
|
|
||||||
} EVENT_STATUS;
|
|
||||||
|
|
||||||
// For synchronous file operations
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
FILE* fp;
|
|
||||||
int fd;
|
|
||||||
} FILE_FD;
|
|
||||||
|
|
||||||
typedef BOOL(__stdcall *Dynamic_CancelIoEx)(HANDLE, LPOVERLAPPED);
|
|
||||||
public:
|
public:
|
||||||
|
CScriptInstance(CScriptSystem* sys, const char* name);
|
||||||
CScriptInstance(CDebuggerUI* debugger);
|
|
||||||
~CScriptInstance();
|
~CScriptInstance();
|
||||||
|
|
||||||
void Start(char* path);
|
JSInstanceName& Name();
|
||||||
void ForceStop();
|
CScriptSystem* System();
|
||||||
void Invoke(void* heapptr, uint32_t param = 0);
|
CDebuggerUI* Debugger();
|
||||||
void Invoke2(void* heapptr, uint32_t param = 0, uint32_t param2 = 0);
|
JSAppCallbackID CallbackId();
|
||||||
INSTANCE_STATE GetState();
|
|
||||||
|
|
||||||
friend class PendingEval;
|
bool Run(const char* path);
|
||||||
const char* Eval(const char* jsCode);
|
void SetExecTimeout(uint64_t timeout);
|
||||||
|
bool IsTimedOut();
|
||||||
|
|
||||||
|
size_t GetRefCount();
|
||||||
|
void IncRefCount();
|
||||||
|
void DecRefCount();
|
||||||
|
void SetStopping(bool bStopping);
|
||||||
|
bool IsStopping();
|
||||||
|
|
||||||
|
bool RegisterWorker(CScriptWorker* worker);
|
||||||
|
void UnregisterWorker(CScriptWorker* worker);
|
||||||
|
void StopRegisteredWorkers();
|
||||||
|
|
||||||
|
void RawInvokeAppCallback(JSAppCallback& cb, void *_hookEnv);
|
||||||
|
|
||||||
|
void RawConsoleInput(const char* code);
|
||||||
|
|
||||||
|
void RawCall(void* dukFuncHeapPtr, JSDukArgSetupFunc argSetupFunc, void* param = nullptr);
|
||||||
|
|
||||||
|
void RawCMethodCall(void* dukThisHeapPtr, duk_c_function func,
|
||||||
|
JSDukArgSetupFunc argSetupFunc = nullptr,
|
||||||
|
void* argSetupParam = nullptr);
|
||||||
|
|
||||||
|
void PostCMethodCall(void* dukThisHeapPtr, duk_c_function func,
|
||||||
|
JSDukArgSetupFunc argSetupFunc = nullptr,
|
||||||
|
void* argSetupParam = nullptr, size_t argSetupParamSize = 0);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
duk_context* m_Ctx;
|
static uint64_t Timestamp();
|
||||||
duk_context* DukContext();
|
void Cleanup();
|
||||||
char* m_TempPath;
|
|
||||||
|
|
||||||
HANDLE m_hThread;
|
|
||||||
HANDLE m_hIOCompletionPort;
|
|
||||||
CriticalSection m_CS;
|
|
||||||
|
|
||||||
vector<IOFD> m_AsyncFiles;
|
|
||||||
vector<IOLISTENER*> m_Listeners;
|
|
||||||
UINT m_NextListenerId;
|
|
||||||
|
|
||||||
vector<FILE_FD> m_Files;
|
|
||||||
|
|
||||||
CDebuggerUI* m_Debugger;
|
|
||||||
|
|
||||||
CScriptSystem* m_ScriptSystem;
|
|
||||||
|
|
||||||
INSTANCE_STATE m_State;
|
|
||||||
|
|
||||||
static DWORD CALLBACK StartThread(CScriptInstance* _this);
|
|
||||||
void StartScriptProc();
|
|
||||||
void StartEventLoop();
|
|
||||||
bool HaveEvents();
|
|
||||||
EVENT_STATUS WaitForEvent(IOLISTENER** lpListener);
|
|
||||||
|
|
||||||
void SetState(INSTANCE_STATE state);
|
|
||||||
void StateChanged();
|
|
||||||
void CleanUp();
|
|
||||||
|
|
||||||
void QueueAPC(PAPCFUNC userProc, ULONG_PTR param = 0);
|
|
||||||
|
|
||||||
void AddAsyncFile(HANDLE fd, bool bSocket = false);
|
|
||||||
void CloseAsyncFile(HANDLE fd);
|
|
||||||
void CloseAllAsyncFiles();
|
|
||||||
void RemoveAsyncFile(HANDLE fd);
|
|
||||||
HANDLE CreateSocket();
|
|
||||||
|
|
||||||
IOLISTENER* AddListener(HANDLE fd, IOEVENTTYPE evt, void* jsCallback, void* data = nullptr, int dataLen = 0);
|
|
||||||
void RemoveListener(IOLISTENER* lpListener);
|
|
||||||
void RemoveListenerByIndex(UINT index);
|
|
||||||
void RemoveListenersByFd(HANDLE fd);
|
|
||||||
void InvokeListenerCallback(IOLISTENER* lpListener);
|
|
||||||
|
|
||||||
//static void CALLBACK EvalAsyncCallback(ULONG_PTR evalWait);
|
|
||||||
|
|
||||||
bool AddFile(const char* path, const char* mode, int* fd); // Return FD
|
|
||||||
void CloseFile(int fd);
|
|
||||||
FILE* GetFilePointer(int fd);
|
|
||||||
void CloseAllFiles();
|
|
||||||
|
|
||||||
const char* EvalFile(const char* jsPath);
|
|
||||||
|
|
||||||
// TODO: fix/remove?
|
|
||||||
// Handle dynamically loading CancelIoEx for Windows XP compatibility
|
|
||||||
HMODULE m_hKernel;
|
|
||||||
Dynamic_CancelIoEx m_CancelIoEx;
|
|
||||||
|
|
||||||
// Lookup list of CScriptInstance instances for static js_* functions
|
|
||||||
static vector<CScriptInstance*> Cache;
|
|
||||||
static void CacheInstance(CScriptInstance* _this);
|
|
||||||
static void UncacheInstance(CScriptInstance* _this);
|
|
||||||
static CScriptInstance* FetchInstance(duk_context* ctx);
|
|
||||||
|
|
||||||
// Bound functions (_native object)
|
|
||||||
static duk_ret_t js_ioSockCreate(duk_context*);
|
|
||||||
static duk_ret_t js_ioSockListen(duk_context*);
|
|
||||||
static duk_ret_t js_ioSockAccept(duk_context*); // async
|
|
||||||
static duk_ret_t js_ioSockConnect(duk_context*); // async
|
|
||||||
static duk_ret_t js_ioRead(duk_context*); // async
|
|
||||||
static duk_ret_t js_ioWrite(duk_context*); // async
|
|
||||||
static duk_ret_t js_ioClose(duk_context*); // (FD) ; file or socket
|
|
||||||
static duk_ret_t js_MsgBox(duk_context*); // (message, caption)
|
|
||||||
static duk_ret_t js_AddCallback(duk_context*); // (hookId, callback, tag) ; external events
|
|
||||||
static duk_ret_t js_RemoveCallback(duk_context*); // (callbackId)
|
|
||||||
static duk_ret_t js_GetPCVal(duk_context*); // ()
|
|
||||||
static duk_ret_t js_SetPCVal(duk_context*); // (value)
|
|
||||||
static duk_ret_t js_GetHIVal(duk_context*); // (bUpper)
|
|
||||||
static duk_ret_t js_SetHIVal(duk_context*); // (bUpper, value)
|
|
||||||
static duk_ret_t js_GetLOVal(duk_context*); // (bUpper)
|
|
||||||
static duk_ret_t js_SetLOVal(duk_context*); // (bUpper, value)
|
|
||||||
static duk_ret_t js_GetGPRVal(duk_context*); // (regNum, bUpper)
|
|
||||||
static duk_ret_t js_SetGPRVal(duk_context*); // (regNum, bUpper, value)
|
|
||||||
static duk_ret_t js_GetFPRVal(duk_context*); // (regNum, bDouble)
|
|
||||||
static duk_ret_t js_SetFPRVal(duk_context*); // (regNum, bDouble, value)
|
|
||||||
static duk_ret_t js_GetCauseVal(duk_context*); // ()
|
|
||||||
static duk_ret_t js_SetCauseVal(duk_context*); // (value)
|
|
||||||
static duk_ret_t js_GetROMInt(duk_context*); // (address, bitwidth, signed)
|
|
||||||
static duk_ret_t js_GetROMFloat(duk_context*); // (address, bDouble)
|
|
||||||
static duk_ret_t js_GetROMBlock(duk_context*); // (address, nBytes) ; returns Buffer
|
|
||||||
static duk_ret_t js_GetROMString(duk_context*); // (address[, maxLen]) ; fetch zero terminated string from memory
|
|
||||||
|
|
||||||
static duk_ret_t js_GetRDRAMInt(duk_context*); // (address, bitwidth, signed)
|
|
||||||
static duk_ret_t js_SetRDRAMInt(duk_context*); // (address, bitwidth, signed, newValue)
|
|
||||||
static duk_ret_t js_GetRDRAMFloat(duk_context*); // (address, bDouble)
|
|
||||||
static duk_ret_t js_SetRDRAMFloat(duk_context*); // (address, bDouble, newValue)
|
|
||||||
static duk_ret_t js_GetRDRAMBlock(duk_context*); // (address, nBytes) ; returns Buffer
|
|
||||||
static duk_ret_t js_GetRDRAMString(duk_context*); // (address[, maxLen]) ; fetch zero terminated string from memory
|
|
||||||
static duk_ret_t js_ConsolePrint(duk_context*);
|
|
||||||
static duk_ret_t js_ConsoleClear(duk_context*);
|
|
||||||
|
|
||||||
static duk_ret_t js_BreakHere(duk_context*);
|
|
||||||
static duk_ret_t js_Pause(duk_context*); // () ; Pauses emulation
|
|
||||||
static duk_ret_t js_ShowCommands(duk_context*); // ([address]) ; Shows commands window
|
|
||||||
|
|
||||||
static duk_ret_t js_ScreenPrint(duk_context*); // (x, y, text)
|
|
||||||
|
|
||||||
static duk_ret_t js_FSOpen(duk_context*); // (path, flags) ; returns fd
|
|
||||||
static duk_ret_t js_FSClose(duk_context*); // (fd)
|
|
||||||
static duk_ret_t js_FSWrite(duk_context*); // (fd, buffer[, offset[, length[, position]]])
|
|
||||||
static duk_ret_t js_FSRead(duk_context*); // (fd, buffer, offset, length, position)
|
|
||||||
static duk_ret_t js_FSFStat(duk_context*); // (fd)
|
|
||||||
static duk_ret_t js_FSStat(duk_context*); // (path)
|
|
||||||
static duk_ret_t js_FSMkDir(duk_context*); // (path)
|
|
||||||
static duk_ret_t js_FSRmDir(duk_context*); // (path)
|
|
||||||
static duk_ret_t js_FSUnlink(duk_context*); // (path)
|
|
||||||
static duk_ret_t js_FSReadDir(duk_context*); // (path)
|
|
||||||
|
|
||||||
static constexpr duk_function_list_entry NativeFunctions[] =
|
|
||||||
{
|
|
||||||
{ "addCallback", js_AddCallback, DUK_VARARGS },
|
|
||||||
{ "removeCallback", js_RemoveCallback, DUK_VARARGS },
|
|
||||||
|
|
||||||
{ "setPCVal", js_SetPCVal, DUK_VARARGS },
|
|
||||||
{ "getPCVal", js_GetPCVal, DUK_VARARGS },
|
|
||||||
{ "setHIVal", js_SetHIVal, DUK_VARARGS },
|
|
||||||
{ "getHIVal", js_GetHIVal, DUK_VARARGS },
|
|
||||||
{ "setLOVal", js_SetLOVal, DUK_VARARGS },
|
|
||||||
{ "getLOVal", js_GetLOVal, DUK_VARARGS },
|
|
||||||
{ "setGPRVal", js_SetGPRVal, DUK_VARARGS },
|
|
||||||
{ "getGPRVal", js_GetGPRVal, DUK_VARARGS },
|
|
||||||
{ "setFPRVal", js_SetFPRVal, DUK_VARARGS },
|
|
||||||
{ "getFPRVal", js_GetFPRVal, DUK_VARARGS },
|
|
||||||
{ "setCauseVal", js_SetCauseVal, DUK_VARARGS },
|
|
||||||
{ "getCauseVal", js_GetCauseVal, DUK_VARARGS },
|
|
||||||
|
|
||||||
{ "getROMInt", js_GetROMInt, DUK_VARARGS },
|
|
||||||
{ "getROMFloat", js_GetROMFloat, DUK_VARARGS },
|
|
||||||
{ "getROMString", js_GetROMString, DUK_VARARGS },
|
|
||||||
{ "getROMBlock", js_GetROMBlock, DUK_VARARGS },
|
|
||||||
|
|
||||||
{ "getRDRAMInt", js_GetRDRAMInt, DUK_VARARGS },
|
|
||||||
{ "setRDRAMInt", js_SetRDRAMInt, DUK_VARARGS },
|
|
||||||
{ "getRDRAMFloat", js_GetRDRAMFloat, DUK_VARARGS },
|
|
||||||
{ "setRDRAMFloat", js_SetRDRAMFloat, DUK_VARARGS },
|
|
||||||
{ "getRDRAMBlock", js_GetRDRAMBlock, DUK_VARARGS },
|
|
||||||
{ "getRDRAMString", js_GetRDRAMString, DUK_VARARGS },
|
|
||||||
|
|
||||||
{ "sockCreate", js_ioSockCreate, DUK_VARARGS },
|
|
||||||
{ "sockListen", js_ioSockListen, DUK_VARARGS },
|
|
||||||
{ "sockAccept", js_ioSockAccept, DUK_VARARGS },
|
|
||||||
{ "sockConnect", js_ioSockConnect, DUK_VARARGS },
|
|
||||||
{ "close", js_ioClose, DUK_VARARGS },
|
|
||||||
{ "write", js_ioWrite, DUK_VARARGS },
|
|
||||||
{ "read", js_ioRead, DUK_VARARGS },
|
|
||||||
{ "msgBox", js_MsgBox, DUK_VARARGS },
|
|
||||||
{ "consolePrint", js_ConsolePrint, DUK_VARARGS },
|
|
||||||
{ "consoleClear", js_ConsoleClear, DUK_VARARGS },
|
|
||||||
{ "pause", js_Pause, DUK_VARARGS },
|
|
||||||
{ "showCommands", js_ShowCommands, DUK_VARARGS },
|
|
||||||
|
|
||||||
{ "breakHere", js_BreakHere, DUK_VARARGS },
|
|
||||||
|
|
||||||
{ "screenPrint", js_ScreenPrint, DUK_VARARGS },
|
|
||||||
|
|
||||||
{ "fsOpen", js_FSOpen, DUK_VARARGS },
|
|
||||||
{ "fsClose", js_FSClose, DUK_VARARGS },
|
|
||||||
{ "fsWrite", js_FSWrite, DUK_VARARGS },
|
|
||||||
{ "fsRead", js_FSRead, DUK_VARARGS },
|
|
||||||
{ "fsFStat", js_FSFStat, DUK_VARARGS },
|
|
||||||
{ "fsStat", js_FSStat, DUK_VARARGS },
|
|
||||||
{ "fsUnlink", js_FSUnlink, DUK_VARARGS },
|
|
||||||
{ "fsMkDir", js_FSMkDir, DUK_VARARGS },
|
|
||||||
{ "fsRmDir", js_FSRmDir, DUK_VARARGS },
|
|
||||||
{ "fsReadDir", js_FSReadDir, DUK_VARARGS },
|
|
||||||
{ nullptr, nullptr, 0 }
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,202 +1,552 @@
|
||||||
#include <stdafx.h>
|
#include <stdafx.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sstream>
|
||||||
|
#include "ScriptTypes.h"
|
||||||
#include "ScriptSystem.h"
|
#include "ScriptSystem.h"
|
||||||
#include "Debugger-Scripts.h"
|
|
||||||
|
|
||||||
#include "ScriptInstance.h"
|
#include "ScriptInstance.h"
|
||||||
#include "ScriptHook.h"
|
#include "ScriptAPI/ScriptAPI.h"
|
||||||
|
#include "Debugger.h"
|
||||||
|
#include "Project64\UserInterface\DiscordRPC.h"
|
||||||
|
|
||||||
CScriptSystem::CScriptSystem(CDebuggerUI* debugger)
|
CScriptSystem::CScriptSystem(CDebuggerUI *debugger) :
|
||||||
|
m_Debugger(debugger),
|
||||||
|
m_NextAppCallbackId(0),
|
||||||
|
m_AppCallbackCount(0)
|
||||||
{
|
{
|
||||||
WSADATA wsaData;
|
InitDirectories();
|
||||||
WSAStartup(MAKEWORD(2, 2), &wsaData);
|
|
||||||
|
|
||||||
m_NextCallbackId = 0;
|
m_hCmdEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
|
||||||
m_NumCallbacks = 0;
|
m_hThread = CreateThread(nullptr, 0, ThreadProc, this, 0, nullptr);
|
||||||
|
|
||||||
m_Debugger = debugger;
|
|
||||||
|
|
||||||
m_HookCPUExec = new CScriptHook(this);
|
|
||||||
m_HookCPUExecOpcode = new CScriptHook(this);
|
|
||||||
m_HookCPURead = new CScriptHook(this);
|
|
||||||
m_HookCPUWrite = new CScriptHook(this);
|
|
||||||
m_HookCPUGPRValue = new CScriptHook(this);
|
|
||||||
m_HookFrameDrawn = new CScriptHook(this);
|
|
||||||
|
|
||||||
RegisterHook("exec", m_HookCPUExec);
|
|
||||||
RegisterHook("read", m_HookCPURead);
|
|
||||||
RegisterHook("write", m_HookCPUWrite);
|
|
||||||
RegisterHook("opcode", m_HookCPUExecOpcode);
|
|
||||||
RegisterHook("gprvalue", m_HookCPUGPRValue);
|
|
||||||
RegisterHook("draw", m_HookFrameDrawn);
|
|
||||||
|
|
||||||
HMODULE hInst = GetModuleHandle(nullptr);
|
|
||||||
HRSRC hRes = FindResource(hInst, MAKEINTRESOURCE(IDR_JSAPI_TEXT), L"TEXT");
|
|
||||||
|
|
||||||
HGLOBAL hGlob = LoadResource(hInst, hRes);
|
|
||||||
DWORD resSize = SizeofResource(hInst, hRes);
|
|
||||||
m_APIScript = (char*)malloc(resSize + 1);
|
|
||||||
|
|
||||||
void* resData = LockResource(hGlob);
|
|
||||||
memcpy(m_APIScript, resData, resSize);
|
|
||||||
m_APIScript[resSize] = '\0';
|
|
||||||
FreeResource(hGlob);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CScriptSystem::~CScriptSystem()
|
CScriptSystem::~CScriptSystem()
|
||||||
{
|
{
|
||||||
for (size_t i = 0; i < m_Hooks.size(); i++)
|
PostCommand(JS_CMD_SHUTDOWN);
|
||||||
|
WaitForSingleObject(m_hThread, INFINITE);
|
||||||
|
CloseHandle(m_hThread);
|
||||||
|
CloseHandle(m_hCmdEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
JSInstanceStatus CScriptSystem::GetStatus(const char* name)
|
||||||
|
{
|
||||||
|
CGuard guard(m_UIStateCS);
|
||||||
|
if (m_UIInstanceStatus.count(name) == 0)
|
||||||
{
|
{
|
||||||
delete m_Hooks[i].cbList;
|
return JS_STATUS_STOPPED;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return m_UIInstanceStatus[name];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CScriptSystem::NotifyStatus(const char* name, JSInstanceStatus status)
|
||||||
|
{
|
||||||
|
CGuard guard(m_UIStateCS);
|
||||||
|
if (status == JS_STATUS_STOPPED)
|
||||||
|
{
|
||||||
|
m_UIInstanceStatus.erase(name);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_UIInstanceStatus[name] = status;
|
||||||
|
}
|
||||||
|
m_Debugger->Debug_RefreshScriptsWindow();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CScriptSystem::ConsoleLog(const char* format, ...)
|
||||||
|
{
|
||||||
|
CGuard guard(m_UIStateCS);
|
||||||
|
|
||||||
|
va_list args;
|
||||||
|
va_start(args, format);
|
||||||
|
|
||||||
|
int size = vsnprintf(nullptr, 0, format, args) + 1;
|
||||||
|
char* str = new char[size];
|
||||||
|
vsnprintf(str, size, format, args);
|
||||||
|
|
||||||
|
stdstr formattedMsg = FixStringReturns(str) + "\r\n";
|
||||||
|
|
||||||
|
m_Debugger->Debug_LogScriptsWindow(formattedMsg.c_str());
|
||||||
|
m_UILog += formattedMsg;
|
||||||
|
|
||||||
|
delete[] str;
|
||||||
|
va_end(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CScriptSystem::ConsolePrint(const char* format, ...)
|
||||||
|
{
|
||||||
|
CGuard guard(m_UIStateCS);
|
||||||
|
|
||||||
|
va_list args;
|
||||||
|
va_start(args, format);
|
||||||
|
|
||||||
|
int size = vsnprintf(nullptr, 0, format, args) + 1;
|
||||||
|
char* str = new char[size];
|
||||||
|
vsnprintf(str, size, format, args);
|
||||||
|
|
||||||
|
stdstr formattedMsg = FixStringReturns(str);
|
||||||
|
|
||||||
|
m_Debugger->Debug_LogScriptsWindow(formattedMsg.c_str());
|
||||||
|
m_UILog += formattedMsg;
|
||||||
|
|
||||||
|
delete[] str;
|
||||||
|
va_end(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CScriptSystem::ConsoleClear()
|
||||||
|
{
|
||||||
|
CGuard guard(m_UIStateCS);
|
||||||
|
m_UILog.clear();
|
||||||
|
m_Debugger->Debug_ClearScriptsWindow();
|
||||||
|
}
|
||||||
|
|
||||||
|
stdstr CScriptSystem::GetConsoleBuffer()
|
||||||
|
{
|
||||||
|
CGuard guard(m_UIStateCS);
|
||||||
|
return stdstr(m_UILog);
|
||||||
|
}
|
||||||
|
|
||||||
|
stdstr CScriptSystem::InstallDirPath()
|
||||||
|
{
|
||||||
|
return m_InstallDirFullPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
stdstr CScriptSystem::ScriptsDirPath()
|
||||||
|
{
|
||||||
|
return m_ScriptsDirFullPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
stdstr CScriptSystem::ModulesDirPath()
|
||||||
|
{
|
||||||
|
return m_ModulesDirFullPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CScriptSystem::InitDirectories()
|
||||||
|
{
|
||||||
|
m_InstallDirFullPath = (std::string)CPath(CPath::MODULE_DIRECTORY);
|
||||||
|
m_ScriptsDirFullPath = m_InstallDirFullPath + SCRIPTSYS_SCRIPTS_DIR;
|
||||||
|
m_ModulesDirFullPath = m_InstallDirFullPath + SCRIPTSYS_MODULES_DIR;
|
||||||
|
|
||||||
|
if (!PathFileExistsA(m_ScriptsDirFullPath.c_str()))
|
||||||
|
{
|
||||||
|
CreateDirectoryA(m_ScriptsDirFullPath.c_str(), nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
UnregisterHooks();
|
if (!PathFileExistsA(m_ModulesDirFullPath.c_str()))
|
||||||
free(m_APIScript);
|
{
|
||||||
|
CreateDirectoryA(m_ModulesDirFullPath.c_str(), nullptr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* CScriptSystem::APIScript()
|
void CScriptSystem::StartScript(const char *name, const char *path)
|
||||||
{
|
{
|
||||||
return m_APIScript;
|
PostCommand(JS_CMD_START_SCRIPT, name, path);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CScriptSystem::RunScript(const char * path)
|
void CScriptSystem::StopScript(const char *name)
|
||||||
{
|
{
|
||||||
CGuard guard(m_CS);
|
PostCommand(JS_CMD_STOP_SCRIPT, name);
|
||||||
CScriptInstance* scriptInstance = new CScriptInstance(m_Debugger);
|
|
||||||
char* pathSaved = (char*)malloc(strlen(path)+1); // Freed via DeleteStoppedInstances
|
|
||||||
strcpy(pathSaved, path);
|
|
||||||
|
|
||||||
m_RunningInstances.push_back({ pathSaved, scriptInstance });
|
|
||||||
scriptInstance->Start(pathSaved);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CScriptSystem::StopScript(const char* path)
|
void CScriptSystem::Input(const char *name, const char *code)
|
||||||
{
|
{
|
||||||
CScriptInstance* scriptInstance = GetInstance(path);
|
PostCommand(JS_CMD_INPUT, name, code);
|
||||||
|
}
|
||||||
|
|
||||||
if (scriptInstance == nullptr)
|
bool CScriptSystem::HaveAppCallbacks(JSAppHookID hookId)
|
||||||
|
{
|
||||||
|
CGuard guard(m_InstancesCS);
|
||||||
|
|
||||||
|
return (m_AppCallbackHooks.count(hookId) > 0 &&
|
||||||
|
m_AppCallbackHooks[hookId].size() > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CScriptSystem::InvokeAppCallbacks(JSAppHookID hookId, void* env)
|
||||||
|
{
|
||||||
|
CGuard guard(m_InstancesCS);
|
||||||
|
|
||||||
|
if (m_AppCallbackHooks.count(hookId) == 0 ||
|
||||||
|
m_AppCallbackHooks[hookId].size() == 0)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
scriptInstance->ForceStop();
|
bool bNeedSweep = false;
|
||||||
DeleteStoppedInstances();
|
|
||||||
|
// note: have to copy the map so iterator doesn't break if a callback makes changes
|
||||||
|
// todo: use reference, queue callback additions/removals?
|
||||||
|
// JSAppCallbackMap& callbacks = m_AppCallbackHooks[hookId];
|
||||||
|
|
||||||
|
JSAppCallbackMap callbacks = m_AppCallbackHooks[hookId];
|
||||||
|
|
||||||
|
JSAppCallbackMap::iterator it;
|
||||||
|
for (it = callbacks.begin(); it != callbacks.end(); it++)
|
||||||
|
{
|
||||||
|
JSAppCallback& callback = it->second;
|
||||||
|
|
||||||
|
if (callback.m_Instance->IsStopping())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
callback.m_Instance->RawInvokeAppCallback(callback, env);
|
||||||
|
|
||||||
|
if (callback.m_Instance->GetRefCount() == 0)
|
||||||
|
{
|
||||||
|
bNeedSweep = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bNeedSweep)
|
||||||
|
{
|
||||||
|
PostCommand(JS_CMD_SWEEP);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CScriptSystem::DeleteStoppedInstances()
|
void CScriptSystem::DoMouseEvent(JSAppHookID hookId, int x, int y, DWORD uMsg)
|
||||||
{
|
{
|
||||||
CGuard guard(m_CS);
|
int button = -1;
|
||||||
|
|
||||||
int lastIndex = m_RunningInstances.size() - 1;
|
switch (uMsg)
|
||||||
for (int i = lastIndex; i >= 0; i--)
|
|
||||||
{
|
{
|
||||||
if (m_RunningInstances[i].scriptInstance->GetState() == STATE_STOPPED)
|
case WM_LBUTTONDOWN:
|
||||||
|
case WM_LBUTTONUP:
|
||||||
|
button = 0;
|
||||||
|
break;
|
||||||
|
case WM_MBUTTONDOWN:
|
||||||
|
case WM_MBUTTONUP:
|
||||||
|
button = 1;
|
||||||
|
break;
|
||||||
|
case WM_RBUTTONDOWN:
|
||||||
|
case WM_RBUTTONUP:
|
||||||
|
button = 2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSHookMouseEnv env = { x, y, button };
|
||||||
|
InvokeAppCallbacks(hookId, (void*)&env);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CScriptSystem::PostCMethodCall(const char* name, void *dukThisHeapPtr, duk_c_function func,
|
||||||
|
JSDukArgSetupFunc argSetupFunc, void *argSetupParam, size_t argSetupParamSize)
|
||||||
|
{
|
||||||
|
CGuard guard(m_CmdQueueCS);
|
||||||
|
// Will be deleted by command handler
|
||||||
|
JSSysCMethodCall* methodCall = new JSSysCMethodCall(dukThisHeapPtr, func, argSetupFunc, argSetupParam, argSetupParamSize);
|
||||||
|
PostCommand(JS_CMD_C_METHOD_CALL, name, "", (void*)methodCall);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CScriptSystem::PostCommand(JSSysCommandID id, stdstr paramA, stdstr paramB, void* paramC)
|
||||||
|
{
|
||||||
|
CGuard guard(m_CmdQueueCS);
|
||||||
|
JSSysCommand cmd = { id, paramA, paramB, paramC };
|
||||||
|
m_CmdQueue.push_back(cmd);
|
||||||
|
SetEvent(m_hCmdEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
DWORD CScriptSystem::ThreadProc(void *_this)
|
||||||
|
{
|
||||||
|
((CScriptSystem *)_this)->ThreadProc();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CScriptSystem::ThreadProc()
|
||||||
|
{
|
||||||
|
std::vector<JSSysCommand> queue;
|
||||||
|
|
||||||
|
while(true)
|
||||||
|
{
|
||||||
|
WaitForSingleObject(m_hCmdEvent, INFINITE);
|
||||||
|
|
||||||
{
|
{
|
||||||
free(m_RunningInstances[i].path);
|
CGuard guard(m_CmdQueueCS);
|
||||||
CScriptInstance* instance = m_RunningInstances[i].scriptInstance;
|
queue = m_CmdQueue;
|
||||||
delete instance;
|
m_CmdQueue.clear();
|
||||||
m_RunningInstances.erase(m_RunningInstances.begin() + i);
|
}
|
||||||
|
|
||||||
|
if (!ProcessCommandQueue(queue))
|
||||||
|
{
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
INSTANCE_STATE CScriptSystem::GetInstanceState(const char* path)
|
void CScriptSystem::OnStartScript(const char *name, const char *path)
|
||||||
{
|
{
|
||||||
CGuard guard(m_CS);
|
if (m_Instances.count(name) != 0)
|
||||||
|
|
||||||
for (size_t i = 0; i < m_RunningInstances.size(); i++)
|
|
||||||
{
|
{
|
||||||
if (strcmp(m_RunningInstances[i].path, path) == 0)
|
ConsoleLog("[SCRIPTSYS]: error: START_SCRIPT aborted; '%s' is already instanced", name);
|
||||||
|
}
|
||||||
|
|
||||||
|
CScriptInstance *inst = new CScriptInstance(this, name);
|
||||||
|
|
||||||
|
NotifyStatus(name, JS_STATUS_STARTING);
|
||||||
|
|
||||||
|
if(inst->Run(path) && inst->GetRefCount() > 0)
|
||||||
|
{
|
||||||
|
m_Instances[name] = inst;
|
||||||
|
NotifyStatus(name, JS_STATUS_STARTED);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
NotifyStatus(name, JS_STATUS_STOPPED);
|
||||||
|
delete inst;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CScriptSystem::OnStopScript(const char *name)
|
||||||
|
{
|
||||||
|
if (m_Instances.count(name) == 0)
|
||||||
|
{
|
||||||
|
ConsoleLog("[SCRIPTSYS]: error: STOP_SCRIPT aborted; instance '%s' does not exist", name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RawRemoveInstance(name);
|
||||||
|
NotifyStatus(name, JS_STATUS_STOPPED);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CScriptSystem::OnInput(const char *name, const char *code)
|
||||||
|
{
|
||||||
|
if(m_Instances.count(name) == 0)
|
||||||
|
{
|
||||||
|
ConsoleLog("[SCRIPTSYS]: error: INPUT aborted; instance '%s' does not exist", name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
CScriptInstance* inst = m_Instances[name];
|
||||||
|
|
||||||
|
inst->RawConsoleInput(code);
|
||||||
|
|
||||||
|
if(!inst->IsStopping() && inst->GetRefCount() == 0)
|
||||||
|
{
|
||||||
|
NotifyStatus(name, JS_STATUS_STOPPED);
|
||||||
|
RawRemoveInstance(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CScriptSystem::OnCMethodCall(const char *name, JSSysCMethodCall* methodCall)
|
||||||
|
{
|
||||||
|
if (m_Instances.count(name) == 0)
|
||||||
|
{
|
||||||
|
ConsoleLog("[SCRIPTSYS]: error: method call aborted; instance '%s' doesn't exist", name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_Instances.count(name) == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
CScriptInstance* inst = m_Instances[name];
|
||||||
|
|
||||||
|
inst->RawCMethodCall(methodCall->m_DukThisHeapPtr, methodCall->m_Func, methodCall->m_ArgSetupFunc, methodCall->m_ArgSetupParam);
|
||||||
|
|
||||||
|
if (!inst->IsStopping() && inst->GetRefCount() == 0)
|
||||||
|
{
|
||||||
|
NotifyStatus(name, JS_STATUS_STOPPED);
|
||||||
|
RawRemoveInstance(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CScriptSystem::OnSweep(bool bIfDone)
|
||||||
|
{
|
||||||
|
JSInstanceMap::iterator it = m_Instances.begin();
|
||||||
|
while(it != m_Instances.end())
|
||||||
|
{
|
||||||
|
CScriptInstance*& inst = it->second;
|
||||||
|
if(!bIfDone || inst->GetRefCount() == 0)
|
||||||
{
|
{
|
||||||
INSTANCE_STATE ret = m_RunningInstances[i].scriptInstance->GetState();
|
NotifyStatus(inst->Name().c_str(), JS_STATUS_STOPPED);
|
||||||
return ret;
|
delete inst;
|
||||||
|
m_Instances.erase(it++);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
it++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CScriptSystem::RawRemoveInstance(const char *name)
|
||||||
|
{
|
||||||
|
if(m_Instances.count(name) == 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
CScriptInstance*& inst = m_Instances[name];
|
||||||
|
|
||||||
|
if (inst->IsStopping())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
inst->SetStopping(true);
|
||||||
|
inst->StopRegisteredWorkers();
|
||||||
|
|
||||||
|
std::vector<JSSysCommand> pendingCalls;
|
||||||
|
PullCommands(JS_CMD_C_METHOD_CALL, pendingCalls);
|
||||||
|
ProcessCommandQueue(pendingCalls);
|
||||||
|
|
||||||
|
delete inst;
|
||||||
|
m_Instances.erase(name);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSAppCallbackID CScriptSystem::RawAddAppCallback(JSAppHookID hookId, JSAppCallback& callback)
|
||||||
|
{
|
||||||
|
if(hookId >= JS_NUM_APP_HOOKS)
|
||||||
|
{
|
||||||
|
return JS_INVALID_CALLBACK;
|
||||||
|
}
|
||||||
|
|
||||||
|
callback.m_CallbackId = m_NextAppCallbackId;
|
||||||
|
m_AppCallbackHooks[hookId][m_NextAppCallbackId] = callback;
|
||||||
|
m_AppCallbackCount++;
|
||||||
|
return m_NextAppCallbackId++;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CScriptSystem::RawRemoveAppCallback(JSAppHookID hookId, JSAppCallbackID callbackId)
|
||||||
|
{
|
||||||
|
if(m_AppCallbackHooks[hookId].count(callbackId) == 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_AppCallbackHooks[hookId].erase(callbackId);
|
||||||
|
m_AppCallbackCount--;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CScriptSystem::ExecAutorunList()
|
||||||
|
{
|
||||||
|
LoadAutorunList();
|
||||||
|
|
||||||
|
std::istringstream joinedNames(g_Settings->LoadStringVal(Debugger_AutorunScripts));
|
||||||
|
std::string scriptName;
|
||||||
|
while (std::getline(joinedNames, scriptName, '|'))
|
||||||
|
{
|
||||||
|
std::string scriptPath = m_ScriptsDirFullPath + scriptName;
|
||||||
|
ConsoleLog("[SCRIPTSYS]: autorun '%s'", scriptName.c_str());
|
||||||
|
StartScript(scriptName.c_str(), scriptPath.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::set<std::string>& CScriptSystem::AutorunList()
|
||||||
|
{
|
||||||
|
return m_AutorunList;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CScriptSystem::LoadAutorunList()
|
||||||
|
{
|
||||||
|
m_AutorunList.clear();
|
||||||
|
|
||||||
|
std::istringstream joinedNames(g_Settings->LoadStringVal(Debugger_AutorunScripts));
|
||||||
|
std::string scriptName;
|
||||||
|
|
||||||
|
while (std::getline(joinedNames, scriptName, '|'))
|
||||||
|
{
|
||||||
|
m_AutorunList.insert(scriptName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CScriptSystem::SaveAutorunList()
|
||||||
|
{
|
||||||
|
std::string joinedNames = "";
|
||||||
|
|
||||||
|
std::set<std::string>::iterator it;
|
||||||
|
for (it = m_AutorunList.begin(); it != m_AutorunList.end(); it++)
|
||||||
|
{
|
||||||
|
if (it != m_AutorunList.begin())
|
||||||
|
{
|
||||||
|
joinedNames += "|";
|
||||||
|
}
|
||||||
|
joinedNames += *it;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_Settings->SaveString(Debugger_AutorunScripts, joinedNames.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
CDebuggerUI* CScriptSystem::Debugger()
|
||||||
|
{
|
||||||
|
return m_Debugger;
|
||||||
|
}
|
||||||
|
|
||||||
|
stdstr CScriptSystem::FixStringReturns(const char* str)
|
||||||
|
{
|
||||||
|
stdstr fstr = str;
|
||||||
|
size_t pos = 0;
|
||||||
|
while ((pos = fstr.find("\n", pos)) != stdstr::npos)
|
||||||
|
{
|
||||||
|
fstr.replace(pos, 1, "\r\n");
|
||||||
|
pos += 2;
|
||||||
|
}
|
||||||
|
return fstr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CScriptSystem::ProcessCommandQueue(std::vector<JSSysCommand>& queue)
|
||||||
|
{
|
||||||
|
std::vector<JSSysCommand>::iterator it;
|
||||||
|
for (it = queue.begin(); it != queue.end(); it++)
|
||||||
|
{
|
||||||
|
if (!ProcessCommand(*it))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return STATE_INVALID;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
CScriptInstance* CScriptSystem::GetInstance(const char* path)
|
bool CScriptSystem::ProcessCommand(JSSysCommand& cmd)
|
||||||
{
|
{
|
||||||
CGuard guard(m_CS);
|
CGuard guard(m_InstancesCS);
|
||||||
|
|
||||||
for (size_t i = 0; i < m_RunningInstances.size(); i++)
|
switch (cmd.id)
|
||||||
{
|
{
|
||||||
if (strcmp(m_RunningInstances[i].path, path) == 0)
|
case JS_CMD_START_SCRIPT:
|
||||||
|
OnStartScript(cmd.paramA.c_str(), cmd.paramB.c_str());
|
||||||
|
break;
|
||||||
|
case JS_CMD_STOP_SCRIPT:
|
||||||
|
OnStopScript(cmd.paramA.c_str());
|
||||||
|
break;
|
||||||
|
case JS_CMD_INPUT:
|
||||||
|
OnInput(cmd.paramA.c_str(), cmd.paramB.c_str());
|
||||||
|
break;
|
||||||
|
case JS_CMD_C_METHOD_CALL:
|
||||||
{
|
{
|
||||||
CScriptInstance *ret = m_RunningInstances[i].scriptInstance;
|
JSSysCMethodCall* methodCall = (JSSysCMethodCall*)cmd.paramC;
|
||||||
return ret;
|
OnCMethodCall(cmd.paramA.c_str(), methodCall);
|
||||||
|
delete methodCall;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case JS_CMD_SWEEP:
|
||||||
|
OnSweep(true);
|
||||||
|
break;
|
||||||
|
case JS_CMD_SHUTDOWN:
|
||||||
|
OnSweep(false);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CScriptSystem::PullCommands(JSSysCommandID id, std::vector<JSSysCommand>& out)
|
||||||
|
{
|
||||||
|
CGuard guard(m_CmdQueueCS);
|
||||||
|
|
||||||
|
std::vector<JSSysCommand>::iterator it;
|
||||||
|
for (it = m_CmdQueue.begin(); it != m_CmdQueue.end();)
|
||||||
|
{
|
||||||
|
if ((*it).id == id)
|
||||||
|
{
|
||||||
|
out.push_back(*it);
|
||||||
|
it = m_CmdQueue.erase(it);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
it++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CScriptSystem::HasCallbacksForInstance(CScriptInstance* scriptInstance)
|
|
||||||
{
|
|
||||||
for (size_t i = 0; i < m_Hooks.size(); i++)
|
|
||||||
{
|
|
||||||
if (m_Hooks[i].cbList->HasContext(scriptInstance))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CScriptSystem::ClearCallbacksForInstance(CScriptInstance* scriptInstance)
|
|
||||||
{
|
|
||||||
for (size_t i = 0; i < m_Hooks.size(); i++)
|
|
||||||
{
|
|
||||||
m_Hooks[i].cbList->RemoveByInstance(scriptInstance);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CScriptSystem::RemoveCallbackById(int callbackId)
|
|
||||||
{
|
|
||||||
for (size_t i = 0; i < m_Hooks.size(); i++)
|
|
||||||
{
|
|
||||||
m_Hooks[i].cbList->RemoveById(callbackId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CScriptSystem::RegisterHook(const char* hookId, CScriptHook* cbList)
|
|
||||||
{
|
|
||||||
HOOKENTRY hook = { hookId, cbList };
|
|
||||||
m_Hooks.push_back(hook);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CScriptSystem::UnregisterHooks()
|
|
||||||
{
|
|
||||||
m_Hooks.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
CScriptHook* CScriptSystem::GetHook(const char* hookId)
|
|
||||||
{
|
|
||||||
size_t size = m_Hooks.size();
|
|
||||||
for (size_t i = 0; i < size; i++)
|
|
||||||
{
|
|
||||||
if (strcmp(m_Hooks[i].hookId, hookId) == 0)
|
|
||||||
{
|
|
||||||
return m_Hooks[i].cbList;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
int CScriptSystem::GetNextCallbackId()
|
|
||||||
{
|
|
||||||
return m_NextCallbackId++;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CScriptSystem::CallbackAdded()
|
|
||||||
{
|
|
||||||
m_NumCallbacks++;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CScriptSystem::CallbackRemoved()
|
|
||||||
{
|
|
||||||
if (m_NumCallbacks > 0)
|
|
||||||
{
|
|
||||||
m_NumCallbacks--;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,129 +1,103 @@
|
||||||
|
#include <windows.h>
|
||||||
|
#include <fstream>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
#include "ScriptTypes.h"
|
||||||
|
#include "debugger.h"
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <stdafx.h>
|
#define SCRIPTSYS_SCRIPTS_DIR "Scripts\\"
|
||||||
#include <3rdParty/duktape/duktape.h>
|
#define SCRIPTSYS_MODULES_DIR "Scripts\\modules\\"
|
||||||
|
|
||||||
#include "ScriptInstance.h"
|
|
||||||
|
|
||||||
class CScriptHook;
|
|
||||||
|
|
||||||
class CScriptSystem
|
class CScriptSystem
|
||||||
{
|
{
|
||||||
|
typedef std::map<JSInstanceName, CScriptInstance*> JSInstanceMap;
|
||||||
|
typedef std::map<JSAppCallbackID, JSAppCallback> JSAppCallbackMap;
|
||||||
|
typedef std::map<JSAppHookID, JSAppCallbackMap> JSAppHookMap;
|
||||||
|
typedef std::map<JSInstanceName, JSInstanceStatus> JSInstanceStatusMap;
|
||||||
|
typedef std::vector<JSSysCommand> JSSysCommandQueue;
|
||||||
|
|
||||||
|
HANDLE m_hThread;
|
||||||
|
|
||||||
|
CriticalSection m_CmdQueueCS;
|
||||||
|
JSSysCommandQueue m_CmdQueue;
|
||||||
|
HANDLE m_hCmdEvent;
|
||||||
|
|
||||||
|
CriticalSection m_InstancesCS;
|
||||||
|
JSInstanceMap m_Instances;
|
||||||
|
JSAppHookMap m_AppCallbackHooks;
|
||||||
|
JSAppCallbackID m_NextAppCallbackId;
|
||||||
|
size_t m_AppCallbackCount;
|
||||||
|
|
||||||
|
CriticalSection m_UIStateCS;
|
||||||
|
JSInstanceStatusMap m_UIInstanceStatus;
|
||||||
|
stdstr m_UILog;
|
||||||
|
|
||||||
|
CDebuggerUI* m_Debugger;
|
||||||
|
|
||||||
|
std::set<std::string> m_AutorunList;
|
||||||
|
|
||||||
|
stdstr m_InstallDirFullPath;
|
||||||
|
stdstr m_ScriptsDirFullPath;
|
||||||
|
stdstr m_ModulesDirFullPath;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CScriptSystem(CDebuggerUI* debugger);
|
CScriptSystem(CDebuggerUI* debugger);
|
||||||
~CScriptSystem();
|
~CScriptSystem();
|
||||||
// Run a script in its own context/thread
|
|
||||||
void RunScript(const char * path);
|
|
||||||
|
|
||||||
// Kill a script context/thread by its path
|
CDebuggerUI* Debugger();
|
||||||
void StopScript(const char * path);
|
|
||||||
|
|
||||||
const char* APIScript();
|
void StartScript(const char* instanceName, const char* path);
|
||||||
|
void StopScript(const char* instanceName);
|
||||||
|
void Input(const char* instanceName, const char* code);
|
||||||
|
|
||||||
|
stdstr InstallDirPath();
|
||||||
|
stdstr ScriptsDirPath();
|
||||||
|
stdstr ModulesDirPath();
|
||||||
|
|
||||||
|
JSInstanceStatus GetStatus(const char* instanceName);
|
||||||
|
void NotifyStatus(const char* instanceName, JSInstanceStatus status);
|
||||||
|
void ConsoleLog(const char* format, ...);
|
||||||
|
void ConsolePrint(const char* format, ...);
|
||||||
|
void ConsoleClear();
|
||||||
|
stdstr GetConsoleBuffer();
|
||||||
|
|
||||||
|
void PostCMethodCall(const char* instanceName, void* dukThisHeapPtr, duk_c_function func,
|
||||||
|
JSDukArgSetupFunc argSetupFunc = nullptr,
|
||||||
|
void* argSetupParam = nullptr,
|
||||||
|
size_t argSetupParamSize = 0);
|
||||||
|
|
||||||
|
bool HaveAppCallbacks(JSAppHookID hookId);
|
||||||
|
void InvokeAppCallbacks(JSAppHookID hookId, void* env = nullptr);
|
||||||
|
void DoMouseEvent(JSAppHookID hookId, int x, int y, DWORD uMsg = (DWORD)-1);
|
||||||
|
JSAppCallbackID RawAddAppCallback(JSAppHookID hookId, JSAppCallback& callback);
|
||||||
|
bool RawRemoveAppCallback(JSAppHookID hookId, JSAppCallbackID callbackId);
|
||||||
|
|
||||||
|
void ExecAutorunList();
|
||||||
|
std::set<std::string>& AutorunList();
|
||||||
|
void LoadAutorunList();
|
||||||
|
void SaveAutorunList();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
typedef struct {
|
void InitDirectories();
|
||||||
const char* hookId;
|
|
||||||
CScriptHook* cbList;
|
|
||||||
} HOOKENTRY;
|
|
||||||
|
|
||||||
typedef struct {
|
void PostCommand(JSSysCommandID id, stdstr paramA = "", stdstr paramB = "", void* paramC = nullptr);
|
||||||
char* path;
|
|
||||||
CScriptInstance* scriptInstance;
|
|
||||||
} INSTANCE_ENTRY;
|
|
||||||
|
|
||||||
CDebuggerUI* m_Debugger;
|
static DWORD WINAPI ThreadProc(void* _this);
|
||||||
int m_NumCallbacks;
|
void ThreadProc();
|
||||||
char* m_APIScript;
|
|
||||||
|
|
||||||
vector<HOOKENTRY> m_Hooks;
|
bool ProcessCommand(JSSysCommand& cmd);
|
||||||
vector<INSTANCE_ENTRY> m_RunningInstances;
|
bool ProcessCommandQueue(std::vector<JSSysCommand>& queue);
|
||||||
|
void PullCommands(JSSysCommandID id, std::vector<JSSysCommand>& out);
|
||||||
|
|
||||||
vector<std::string> m_LogData;
|
void OnStartScript(const char* name, const char* path);
|
||||||
|
void OnStopScript(const char* name);
|
||||||
|
void OnInput(const char* name, const char* code);
|
||||||
|
void OnCMethodCall(const char* name, JSSysCMethodCall* methodCall);
|
||||||
|
void OnSweep(bool bIfDone);
|
||||||
|
|
||||||
CScriptHook* m_HookCPUExec;
|
bool RawRemoveInstance(const char* key);
|
||||||
CScriptHook* m_HookCPURead;
|
|
||||||
CScriptHook* m_HookCPUWrite;
|
|
||||||
CScriptHook* m_HookCPUExecOpcode;
|
|
||||||
CScriptHook* m_HookCPUGPRValue;
|
|
||||||
CScriptHook* m_HookFrameDrawn;
|
|
||||||
|
|
||||||
CriticalSection m_CS;
|
static stdstr FixStringReturns(const char* str);
|
||||||
|
|
||||||
void RegisterHook(const char* hookId, CScriptHook* cbList); // Associate string ID with callback list
|
|
||||||
void UnregisterHooks();
|
|
||||||
|
|
||||||
HDC m_ScreenDC;
|
|
||||||
|
|
||||||
int m_NextCallbackId;
|
|
||||||
|
|
||||||
public:
|
|
||||||
// Returns true if any of the script hooks have callbacks for scriptInstance
|
|
||||||
|
|
||||||
void SetScreenDC(HDC hdc)
|
|
||||||
{
|
|
||||||
m_ScreenDC = hdc;
|
|
||||||
}
|
|
||||||
|
|
||||||
HDC GetScreenDC()
|
|
||||||
{
|
|
||||||
return m_ScreenDC;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void LogText(const char* text)
|
|
||||||
{
|
|
||||||
m_LogData.push_back(text);
|
|
||||||
m_Debugger->Debug_RefreshScriptsWindow();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool HasCallbacksForInstance(CScriptInstance* scriptInstance);
|
|
||||||
|
|
||||||
// Remove all hooked callbacks for an instance
|
|
||||||
void ClearCallbacksForInstance(CScriptInstance* scriptInstance);
|
|
||||||
|
|
||||||
void RemoveCallbackById(int callbackId);
|
|
||||||
|
|
||||||
CScriptHook* GetHook(const char* hookId);
|
|
||||||
|
|
||||||
int GetNextCallbackId();
|
|
||||||
void CallbackAdded();
|
|
||||||
void CallbackRemoved();
|
|
||||||
|
|
||||||
inline int HaveCallbacks()
|
|
||||||
{
|
|
||||||
return m_NumCallbacks != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void DeleteStoppedInstances();
|
|
||||||
INSTANCE_STATE GetInstanceState(const char* scriptName);
|
|
||||||
CScriptInstance* GetInstance(const char* scriptName);
|
|
||||||
|
|
||||||
CScriptHook* HookCPUExec()
|
|
||||||
{
|
|
||||||
return m_HookCPUExec;
|
|
||||||
}
|
|
||||||
|
|
||||||
CScriptHook* HookCPURead()
|
|
||||||
{
|
|
||||||
return m_HookCPURead;
|
|
||||||
}
|
|
||||||
|
|
||||||
CScriptHook* HookCPUWrite()
|
|
||||||
{
|
|
||||||
return m_HookCPUWrite;
|
|
||||||
}
|
|
||||||
|
|
||||||
CScriptHook* HookCPUExecOpcode()
|
|
||||||
{
|
|
||||||
return m_HookCPUExecOpcode;
|
|
||||||
}
|
|
||||||
|
|
||||||
CScriptHook* HookCPUGPRValue()
|
|
||||||
{
|
|
||||||
return m_HookCPUGPRValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
CScriptHook* HookFrameDrawn()
|
|
||||||
{
|
|
||||||
return m_HookFrameDrawn;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,199 @@
|
||||||
|
#include <3rdparty/duktape/duktape.h>
|
||||||
|
#include <3rdparty/duktape/duk_module_duktape.h>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "OpInfo.h"
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
JS_EXEC_TIMEOUT = 500
|
||||||
|
};
|
||||||
|
|
||||||
|
enum JSAppHookID
|
||||||
|
{
|
||||||
|
JS_HOOK_CPUSTEP,
|
||||||
|
JS_HOOK_PIFREAD,
|
||||||
|
JS_HOOK_PIDMA,
|
||||||
|
JS_HOOK_GFXUPDATE,
|
||||||
|
JS_HOOK_RSPTASK,
|
||||||
|
JS_HOOK_EMUSTATECHANGE,
|
||||||
|
JS_HOOK_MOUSEUP,
|
||||||
|
JS_HOOK_MOUSEDOWN,
|
||||||
|
JS_HOOK_MOUSEMOVE,
|
||||||
|
JS_NUM_APP_HOOKS
|
||||||
|
};
|
||||||
|
|
||||||
|
enum JSInstanceStatus
|
||||||
|
{
|
||||||
|
JS_STATUS_STOPPED,
|
||||||
|
JS_STATUS_STARTING,
|
||||||
|
JS_STATUS_STARTED
|
||||||
|
};
|
||||||
|
|
||||||
|
enum JSSysCommandID
|
||||||
|
{
|
||||||
|
JS_CMD_IDLE,
|
||||||
|
JS_CMD_START_SCRIPT,
|
||||||
|
JS_CMD_STOP_SCRIPT,
|
||||||
|
JS_CMD_SWEEP,
|
||||||
|
JS_CMD_INPUT,
|
||||||
|
JS_CMD_SHUTDOWN,
|
||||||
|
JS_CMD_C_METHOD_CALL
|
||||||
|
};
|
||||||
|
|
||||||
|
enum JSEmuState
|
||||||
|
{
|
||||||
|
JS_EMU_STARTED,
|
||||||
|
JS_EMU_STOPPED,
|
||||||
|
JS_EMU_RESETTING,
|
||||||
|
JS_EMU_RESET,
|
||||||
|
JS_EMU_PAUSED,
|
||||||
|
JS_EMU_RESUMED,
|
||||||
|
JS_EMU_LOADED_ROM,
|
||||||
|
JS_EMU_LOADED_STATE, // todo
|
||||||
|
JS_EMU_DEBUG_PAUSED,
|
||||||
|
JS_EMU_DEBUG_RESUMED
|
||||||
|
};
|
||||||
|
|
||||||
|
class CScriptSystem;
|
||||||
|
class CScriptInstance;
|
||||||
|
|
||||||
|
typedef std::string JSInstanceName;
|
||||||
|
|
||||||
|
struct JSAppCallback;
|
||||||
|
typedef duk_idx_t (*JSDukArgSetupFunc)(duk_context *ctx, void *argSetupParam);
|
||||||
|
typedef bool (*JSAppCallbackCondFunc)(JSAppCallback* cb, void* hookEnv);
|
||||||
|
typedef void (*JSAppCallbackCleanupFunc)(duk_context* ctx, void *hookEnv);
|
||||||
|
|
||||||
|
typedef size_t JSAppCallbackID;
|
||||||
|
#define JS_INVALID_CALLBACK ((JSAppCallbackID)(-1))
|
||||||
|
|
||||||
|
struct JSAppCallback
|
||||||
|
{
|
||||||
|
// assigned by scriptsys when this is added to a callback map
|
||||||
|
JSAppCallbackID m_CallbackId;
|
||||||
|
|
||||||
|
CScriptInstance *m_Instance;
|
||||||
|
void *m_DukFuncHeapPtr;
|
||||||
|
JSAppCallbackCondFunc m_ConditionFunc;
|
||||||
|
JSDukArgSetupFunc m_DukArgSetupFunc;
|
||||||
|
JSAppCallbackCleanupFunc m_CleanupFunc;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
uint32_t addrStart, addrEnd;
|
||||||
|
union {
|
||||||
|
struct { uint32_t opcode, opcodeMask; };
|
||||||
|
struct { uint32_t regIndices, regValue; };
|
||||||
|
};
|
||||||
|
} m_Params;
|
||||||
|
|
||||||
|
JSAppCallback(CScriptInstance* instance, void* dukFuncHeapPtr,
|
||||||
|
JSAppCallbackCondFunc condFunc = nullptr,
|
||||||
|
JSDukArgSetupFunc argSetupFunc = nullptr,
|
||||||
|
JSAppCallbackCleanupFunc cleanupFunc = nullptr) :
|
||||||
|
m_Instance(instance),
|
||||||
|
m_DukFuncHeapPtr(dukFuncHeapPtr),
|
||||||
|
m_ConditionFunc(condFunc),
|
||||||
|
m_DukArgSetupFunc(argSetupFunc),
|
||||||
|
m_CleanupFunc(cleanupFunc),
|
||||||
|
m_CallbackId(JS_INVALID_CALLBACK)
|
||||||
|
{
|
||||||
|
m_Params = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
JSAppCallback() :
|
||||||
|
m_Instance(nullptr),
|
||||||
|
m_DukFuncHeapPtr(nullptr),
|
||||||
|
m_ConditionFunc(nullptr),
|
||||||
|
m_DukArgSetupFunc(nullptr),
|
||||||
|
m_CleanupFunc(nullptr),
|
||||||
|
m_CallbackId(JS_INVALID_CALLBACK)
|
||||||
|
{
|
||||||
|
m_Params = {};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct JSHookCpuStepEnv
|
||||||
|
{
|
||||||
|
uint32_t pc;
|
||||||
|
COpInfo opInfo;
|
||||||
|
int outAffectedRegIndex; // set by the condition function
|
||||||
|
};
|
||||||
|
|
||||||
|
struct JSHookMouseEnv
|
||||||
|
{
|
||||||
|
int x, y;
|
||||||
|
int button; // 0=left,1=middle,2=right
|
||||||
|
};
|
||||||
|
|
||||||
|
struct JSHookSpTaskEnv
|
||||||
|
{
|
||||||
|
uint32_t taskType;
|
||||||
|
uint32_t taskFlags;
|
||||||
|
uint32_t ucodeBootAddress;
|
||||||
|
uint32_t ucodeBootSize;
|
||||||
|
uint32_t ucodeAddress;
|
||||||
|
uint32_t ucodeSize;
|
||||||
|
uint32_t ucodeDataAddress;
|
||||||
|
uint32_t ucodeDataSize;
|
||||||
|
uint32_t dramStackAddress;
|
||||||
|
uint32_t dramStackSize;
|
||||||
|
uint32_t outputBuffAddress;
|
||||||
|
uint32_t outputBuffSize;
|
||||||
|
uint32_t dataAddress;
|
||||||
|
uint32_t dataSize;
|
||||||
|
uint32_t yieldDataAddress;
|
||||||
|
uint32_t yieldDataSize;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct JSHookPiDmaEnv {
|
||||||
|
int direction;
|
||||||
|
uint32_t dramAddress;
|
||||||
|
uint32_t cartAddress;
|
||||||
|
uint32_t length;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct JSHookEmuStateChangeEnv
|
||||||
|
{
|
||||||
|
JSEmuState state;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct JSSysCommand
|
||||||
|
{
|
||||||
|
JSSysCommandID id;
|
||||||
|
stdstr paramA;
|
||||||
|
stdstr paramB;
|
||||||
|
void* paramC;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct JSSysCMethodCall
|
||||||
|
{
|
||||||
|
void* m_DukThisHeapPtr;
|
||||||
|
duk_c_function m_Func;
|
||||||
|
JSDukArgSetupFunc m_ArgSetupFunc;
|
||||||
|
void* m_ArgSetupParam;
|
||||||
|
|
||||||
|
JSSysCMethodCall(void* dukThisHeapPtr, duk_c_function func,
|
||||||
|
JSDukArgSetupFunc argSetupFunc = nullptr,
|
||||||
|
void* argSetupParam = nullptr,
|
||||||
|
size_t argSetupParamSize = 0) :
|
||||||
|
m_DukThisHeapPtr(dukThisHeapPtr),
|
||||||
|
m_Func(func),
|
||||||
|
m_ArgSetupFunc(argSetupFunc),
|
||||||
|
m_ArgSetupParam(nullptr)
|
||||||
|
{
|
||||||
|
if (argSetupParam != nullptr && argSetupParamSize != 0)
|
||||||
|
{
|
||||||
|
m_ArgSetupParam = (void*)(new char[argSetupParamSize]);
|
||||||
|
memcpy(m_ArgSetupParam, argSetupParam, argSetupParamSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
~JSSysCMethodCall()
|
||||||
|
{
|
||||||
|
delete[] (char*)m_ArgSetupParam;
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,66 @@
|
||||||
|
#include <stdafx.h>
|
||||||
|
#include "ScriptWorker.h"
|
||||||
|
#include "ScriptInstance.h"
|
||||||
|
|
||||||
|
CScriptWorker::CScriptWorker(CScriptInstance* instance, void* dukObjectHeapPtr) :
|
||||||
|
m_bStopping(false),
|
||||||
|
m_bRegistered(false),
|
||||||
|
m_hThread(nullptr),
|
||||||
|
m_Instance(instance),
|
||||||
|
m_DukObjectHeapPtr(dukObjectHeapPtr)
|
||||||
|
{
|
||||||
|
m_bRegistered = m_Instance->RegisterWorker(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
CScriptWorker::~CScriptWorker()
|
||||||
|
{
|
||||||
|
if (m_bRegistered)
|
||||||
|
{
|
||||||
|
StopWorkerProc();
|
||||||
|
m_Instance->UnregisterWorker(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DWORD WINAPI CScriptWorker::ThreadProc(void* _this) {
|
||||||
|
((CScriptWorker*)_this)->WorkerProc();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CScriptWorker::StartWorkerProc()
|
||||||
|
{
|
||||||
|
if (!m_bRegistered)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_hThread != nullptr)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_hThread = CreateThread(0, 0, ThreadProc, (void*)this, 0, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CScriptWorker::StopWorkerProc()
|
||||||
|
{
|
||||||
|
if (m_hThread == nullptr)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
CGuard guard(m_CS);
|
||||||
|
m_bStopping = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
WaitForSingleObject(m_hThread, INFINITE);
|
||||||
|
CloseHandle(m_hThread);
|
||||||
|
m_hThread = nullptr;
|
||||||
|
m_bStopping = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CScriptWorker::StopRequested()
|
||||||
|
{
|
||||||
|
CGuard guard(m_CS);
|
||||||
|
return m_bStopping;
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
#include <windows.h>
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
class CScriptInstance;
|
||||||
|
|
||||||
|
class CScriptWorker
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
bool m_bStopping;
|
||||||
|
bool m_bRegistered;
|
||||||
|
static DWORD WINAPI ThreadProc(void* _this);
|
||||||
|
|
||||||
|
public:
|
||||||
|
CScriptWorker(CScriptInstance* instance, void* dukObjectHeapPtr);
|
||||||
|
virtual ~CScriptWorker();
|
||||||
|
void StartWorkerProc();
|
||||||
|
virtual void StopWorkerProc();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
CScriptInstance* m_Instance;
|
||||||
|
void* m_DukObjectHeapPtr;
|
||||||
|
CriticalSection m_CS;
|
||||||
|
HANDLE m_hThread;
|
||||||
|
|
||||||
|
// Implementation should return when StopRequested() is true
|
||||||
|
virtual void WorkerProc() = 0;
|
||||||
|
bool StopRequested();
|
||||||
|
};
|
|
@ -58,6 +58,8 @@ public:
|
||||||
void Debug_RefreshCPULogWindow(void);
|
void Debug_RefreshCPULogWindow(void);
|
||||||
void OpenExcBreakpointsWindow(void);
|
void OpenExcBreakpointsWindow(void);
|
||||||
|
|
||||||
|
void StartAutorunScripts();
|
||||||
|
|
||||||
bool ExecutionBP(uint32_t address);
|
bool ExecutionBP(uint32_t address);
|
||||||
bool ReadBP8(uint32_t address);
|
bool ReadBP8(uint32_t address);
|
||||||
bool ReadBP16(uint32_t address);
|
bool ReadBP16(uint32_t address);
|
||||||
|
@ -76,11 +78,14 @@ public:
|
||||||
CDMALog* DMALog();
|
CDMALog* DMALog();
|
||||||
CCPULog* CPULog();
|
CCPULog* CPULog();
|
||||||
CSymbolTable* SymbolTable();
|
CSymbolTable* SymbolTable();
|
||||||
|
SyncEvent& StepEvent();
|
||||||
|
|
||||||
static void GameReset(CDebuggerUI * _this);
|
static void GameReset(CDebuggerUI * _this);
|
||||||
static void GameCpuRunningChanged(CDebuggerUI * _this);
|
static void GameCpuRunningChanged(CDebuggerUI * _this);
|
||||||
static void GameNameChanged(CDebuggerUI * _this);
|
static void GameNameChanged(CDebuggerUI * _this);
|
||||||
|
static void GamePausedChanged(CDebuggerUI * _this);
|
||||||
static void SteppingOpsChanged(CDebuggerUI * _this);
|
static void SteppingOpsChanged(CDebuggerUI * _this);
|
||||||
|
static void WaitingForStepChanged(CDebuggerUI * _this);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void TLBChanged(void);
|
void TLBChanged(void);
|
||||||
|
@ -88,6 +93,12 @@ protected:
|
||||||
void CPUStep(void);
|
void CPUStep(void);
|
||||||
void CPUStepEnded(void);
|
void CPUStepEnded(void);
|
||||||
void FrameDrawn(void);
|
void FrameDrawn(void);
|
||||||
|
void PIFReadStarted(void);
|
||||||
|
void RSPReceivedTask(void);
|
||||||
|
void PIDMAReadStarted(void);
|
||||||
|
void PIDMAWriteStarted(void);
|
||||||
|
void EmulationStarted(void);
|
||||||
|
void EmulationStopped(void);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
CDebuggerUI(const CDebuggerUI&);
|
CDebuggerUI(const CDebuggerUI&);
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include <Project64-core/Settings/SettingType/SettingsType-Application.h>
|
#include <Project64-core/Settings/SettingType/SettingsType-Application.h>
|
||||||
#include <Project64-core/N64System/Enhancement/Enhancements.h>
|
#include <Project64-core/N64System/Enhancement/Enhancements.h>
|
||||||
#include <Project64-core/N64System/N64Disk.h>
|
#include <Project64-core/N64System/N64Disk.h>
|
||||||
|
#include <Project64/UserInterface/Debugger/DebuggerUI.h>
|
||||||
#include "DiscordRPC.h"
|
#include "DiscordRPC.h"
|
||||||
|
|
||||||
void EnterLogOptions(HWND hwndOwner);
|
void EnterLogOptions(HWND hwndOwner);
|
||||||
|
@ -908,6 +909,87 @@ LRESULT CALLBACK CMainGui::MainGui_Proc(HWND hWnd, DWORD uMsg, DWORD wParam, DWO
|
||||||
_this->SetStatusText(1, L"");
|
_this->SetStatusText(1, L"");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case WM_JSAPI_ACTION:
|
||||||
|
{
|
||||||
|
switch (wParam)
|
||||||
|
{
|
||||||
|
case JSAPI_ACT_OPEN_ROM:
|
||||||
|
{
|
||||||
|
char* romPath = (char*)lParam;
|
||||||
|
stdstr ext = CPath(romPath).GetExtension();
|
||||||
|
if ((_stricmp(ext.c_str(), "ndd") != 0) &&
|
||||||
|
(_stricmp(ext.c_str(), "d64") != 0))
|
||||||
|
{
|
||||||
|
g_BaseSystem->RunFileImage(romPath);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
g_BaseSystem->RunDiskImage(romPath);
|
||||||
|
}
|
||||||
|
delete[] romPath;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case JSAPI_ACT_CLOSE_ROM:
|
||||||
|
if (g_BaseSystem)
|
||||||
|
{
|
||||||
|
g_BaseSystem->ExternalEvent(SysEvent_CloseCPU);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (UISettingsLoadBool(Setting_EnableDiscordRPC))
|
||||||
|
{
|
||||||
|
CDiscord::Update(false);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case JSAPI_ACT_RESET:
|
||||||
|
if (g_BaseSystem)
|
||||||
|
{
|
||||||
|
g_BaseSystem->ExternalEvent((bool)lParam ? SysEvent_ResetCPU_Soft : SysEvent_ResetCPU_Hard);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case JSAPI_ACT_PAUSE:
|
||||||
|
g_BaseSystem->ExternalEvent(SysEvent_PauseCPU_FromMenu);
|
||||||
|
break;
|
||||||
|
case JSAPI_ACT_RESUME:
|
||||||
|
g_BaseSystem->ExternalEvent(SysEvent_ResumeCPU_FromMenu);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case WM_LBUTTONDOWN:
|
||||||
|
case WM_MBUTTONDOWN:
|
||||||
|
case WM_RBUTTONDOWN:
|
||||||
|
if (g_Settings->LoadBool(Debugger_Enabled)) {
|
||||||
|
SetCapture(hWnd);
|
||||||
|
CDebuggerUI* debugger = (CDebuggerUI*)g_Debugger;
|
||||||
|
debugger->ScriptSystem()->DoMouseEvent(JS_HOOK_MOUSEDOWN,
|
||||||
|
GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), uMsg);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case WM_LBUTTONUP:
|
||||||
|
case WM_MBUTTONUP:
|
||||||
|
case WM_RBUTTONUP:
|
||||||
|
if (g_Settings->LoadBool(Debugger_Enabled)) {
|
||||||
|
ReleaseCapture();
|
||||||
|
CDebuggerUI* debugger = (CDebuggerUI*)g_Debugger;
|
||||||
|
debugger->ScriptSystem()->DoMouseEvent(JS_HOOK_MOUSEUP,
|
||||||
|
GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), uMsg);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case WM_MOUSEMOVE:
|
||||||
|
if (g_Settings->LoadBool(Debugger_Enabled)) {
|
||||||
|
static int lastX = 0;
|
||||||
|
static int lastY = 0;
|
||||||
|
int x = GET_X_LPARAM(lParam);
|
||||||
|
int y = GET_Y_LPARAM(lParam);
|
||||||
|
if (lastX != x || lastY != y)
|
||||||
|
{
|
||||||
|
CDebuggerUI* debugger = (CDebuggerUI*)g_Debugger;
|
||||||
|
debugger->ScriptSystem()->DoMouseEvent(JS_HOOK_MOUSEMOVE, x, y, (DWORD)-1);
|
||||||
|
lastX = x;
|
||||||
|
lastY = y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
case WM_COMMAND:
|
case WM_COMMAND:
|
||||||
{
|
{
|
||||||
CMainGui * _this = (CMainGui *)GetProp(hWnd, L"Class");
|
CMainGui * _this = (CMainGui *)GetProp(hWnd, L"Class");
|
||||||
|
|
|
@ -21,6 +21,16 @@ enum
|
||||||
WM_RESET_PLUGIN = WM_USER + 18,
|
WM_RESET_PLUGIN = WM_USER + 18,
|
||||||
WM_GAME_CLOSED = WM_USER + 19,
|
WM_GAME_CLOSED = WM_USER + 19,
|
||||||
WM_BROWSER_TOP = WM_USER + 40,
|
WM_BROWSER_TOP = WM_USER + 40,
|
||||||
|
WM_JSAPI_ACTION = WM_USER + 41
|
||||||
|
};
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
JSAPI_ACT_OPEN_ROM,
|
||||||
|
JSAPI_ACT_CLOSE_ROM,
|
||||||
|
JSAPI_ACT_RESET,
|
||||||
|
JSAPI_ACT_PAUSE,
|
||||||
|
JSAPI_ACT_RESUME
|
||||||
};
|
};
|
||||||
|
|
||||||
class CMainGui :
|
class CMainGui :
|
||||||
|
|
|
@ -835,18 +835,17 @@ STYLE DS_SETFONT | DS_FIXEDSYS | DS_CENTER | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | W
|
||||||
CAPTION "Scripts"
|
CAPTION "Scripts"
|
||||||
FONT 8, "MS Shell Dlg", 400, 0, 0x1
|
FONT 8, "MS Shell Dlg", 400, 0, 0x1
|
||||||
BEGIN
|
BEGIN
|
||||||
EDITTEXT IDC_CONSOLE_EDIT,136,15,274,147,ES_MULTILINE | ES_AUTOHSCROLL | ES_READONLY | WS_VSCROLL
|
|
||||||
EDITTEXT IDC_EVAL_EDIT,28,186,386,12,ES_MULTILINE | ES_AUTOHSCROLL | ES_WANTRETURN
|
|
||||||
CONTROL "",IDC_SCRIPT_LIST,"SysListView32",LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_OWNERDRAWFIXED | LVS_ALIGNLEFT | LVS_NOCOLUMNHEADER | WS_BORDER | WS_TABSTOP,13,15,106,147
|
|
||||||
PUSHBUTTON "Clear",IDC_CLEAR_BTN,373,165,37,13
|
|
||||||
PUSHBUTTON "Copy",IDC_COPY_BTN,333,165,37,13
|
|
||||||
LTEXT "Eval:",IDC_EVAL_LBL,7,187,18,8
|
|
||||||
GROUPBOX "Scripts",IDC_SCRIPTS_GRP,7,5,118,177,0,WS_EX_TRANSPARENT
|
GROUPBOX "Scripts",IDC_SCRIPTS_GRP,7,5,118,177,0,WS_EX_TRANSPARENT
|
||||||
GROUPBOX "Output",IDC_OUTPUT_GRP,129,5,285,177,0,WS_EX_TRANSPARENT
|
GROUPBOX "Output",IDC_OUTPUT_GRP,129,5,285,177,0,WS_EX_TRANSPARENT
|
||||||
CONTROL "",IDC_STATUSBAR,"msctls_statusbar32",WS_TABSTOP,0,205,420,11
|
CONTROL "",IDC_SCRIPT_LIST,"SysListView32",LVS_REPORT | LVS_SINGLESEL | LVS_OWNERDRAWFIXED | LVS_ALIGNLEFT | LVS_NOCOLUMNHEADER | WS_BORDER | WS_TABSTOP,13,15,106,147
|
||||||
|
EDITTEXT IDC_CONSOLE_EDIT,136,15,274,147,ES_MULTILINE | ES_AUTOHSCROLL | ES_READONLY | WS_VSCROLL
|
||||||
PUSHBUTTON "...",IDC_SCRIPTDIR_BTN,13,165,21,13
|
PUSHBUTTON "...",IDC_SCRIPTDIR_BTN,13,165,21,13
|
||||||
PUSHBUTTON "Run",IDC_RUN_BTN,43,165,37,13,WS_DISABLED
|
PUSHBUTTON "Run",IDC_RUN_BTN,83,165,37,13
|
||||||
PUSHBUTTON "Stop",IDC_STOP_BTN,83,165,37,13,WS_DISABLED
|
PUSHBUTTON "Copy",IDC_COPY_BTN,333,165,37,13
|
||||||
|
PUSHBUTTON "Clear",IDC_CLEAR_BTN,373,165,37,13
|
||||||
|
EDITTEXT IDC_EVAL_EDIT,13,186,401,12,ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL
|
||||||
|
LTEXT ">",IDC_EVAL_LBL,5,187,8,12
|
||||||
|
CONTROL "",IDC_STATUSBAR,"msctls_statusbar32",WS_TABSTOP,0,205,420,11
|
||||||
END
|
END
|
||||||
|
|
||||||
IDD_Debugger_RegPI DIALOGEX 0, 0, 190, 210
|
IDD_Debugger_RegPI DIALOGEX 0, 0, 190, 210
|
||||||
|
@ -1425,6 +1424,20 @@ BEGIN
|
||||||
EDITTEXT IDC_ADD_EDIT,7,7,109,12,ES_AUTOHSCROLL
|
EDITTEXT IDC_ADD_EDIT,7,7,109,12,ES_AUTOHSCROLL
|
||||||
END
|
END
|
||||||
|
|
||||||
|
IDD_Debugger_ScriptsAutorun DIALOGEX 0, 0, 323, 216
|
||||||
|
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU
|
||||||
|
CAPTION "Autorun scripts"
|
||||||
|
FONT 8, "MS Shell Dlg", 400, 0, 0x1
|
||||||
|
BEGIN
|
||||||
|
DEFPUSHBUTTON "OK",IDOK,266,195,50,14
|
||||||
|
GROUPBOX "Scripts",IDC_STATIC,7,6,152,185
|
||||||
|
PUSHBUTTON "Remove",IDC_REMOVE_BTN,266,173,46,14,BS_NOTIFY | WS_DISABLED
|
||||||
|
PUSHBUTTON "Add",IDC_ADD_BTN,107,173,46,14,BS_NOTIFY | WS_DISABLED
|
||||||
|
GROUPBOX "Run when Project64 starts",IDC_STATIC,164,6,152,185
|
||||||
|
CONTROL "",IDC_SCRIPT_LIST,"SysListView32",LVS_REPORT | LVS_SINGLESEL | LVS_ALIGNLEFT | LVS_NOCOLUMNHEADER | LVS_NOSORTHEADER | WS_BORDER | WS_TABSTOP,12,17,141,151
|
||||||
|
CONTROL "",IDC_AUTORUN_LIST,"SysListView32",LVS_REPORT | LVS_ALIGNLEFT | LVS_NOCOLUMNHEADER | LVS_NOSORTHEADER | WS_BORDER | WS_TABSTOP,170,17,141,151
|
||||||
|
END
|
||||||
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
|
@ -1690,7 +1703,7 @@ BEGIN
|
||||||
|
|
||||||
IDD_Debugger_Scripts, DIALOG
|
IDD_Debugger_Scripts, DIALOG
|
||||||
BEGIN
|
BEGIN
|
||||||
RIGHTMARGIN, 379
|
RIGHTMARGIN, 420
|
||||||
BOTTOMMARGIN, 214
|
BOTTOMMARGIN, 214
|
||||||
END
|
END
|
||||||
|
|
||||||
|
@ -1899,6 +1912,14 @@ BEGIN
|
||||||
TOPMARGIN, 7
|
TOPMARGIN, 7
|
||||||
BOTTOMMARGIN, 179
|
BOTTOMMARGIN, 179
|
||||||
END
|
END
|
||||||
|
|
||||||
|
IDD_Debugger_ScriptsAutorun, DIALOG
|
||||||
|
BEGIN
|
||||||
|
LEFTMARGIN, 7
|
||||||
|
RIGHTMARGIN, 316
|
||||||
|
TOPMARGIN, 6
|
||||||
|
BOTTOMMARGIN, 209
|
||||||
|
END
|
||||||
END
|
END
|
||||||
#endif // APSTUDIO_INVOKED
|
#endif // APSTUDIO_INVOKED
|
||||||
|
|
||||||
|
@ -2010,6 +2031,8 @@ BEGIN
|
||||||
MENUITEM "Run", ID_POPUP_RUN
|
MENUITEM "Run", ID_POPUP_RUN
|
||||||
MENUITEM "Stop", ID_POPUP_STOP
|
MENUITEM "Stop", ID_POPUP_STOP
|
||||||
MENUITEM "Edit", ID_POPUP_SCRIPT_EDIT
|
MENUITEM "Edit", ID_POPUP_SCRIPT_EDIT
|
||||||
|
MENUITEM SEPARATOR
|
||||||
|
MENUITEM "Autorun...", ID_POPUP_AUTORUN
|
||||||
END
|
END
|
||||||
END
|
END
|
||||||
|
|
||||||
|
@ -2321,14 +2344,6 @@ BEGIN
|
||||||
0
|
0
|
||||||
END
|
END
|
||||||
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// TEXT
|
|
||||||
//
|
|
||||||
|
|
||||||
IDR_JSAPI_TEXT TEXT "API.js"
|
|
||||||
|
|
||||||
#endif // English (United States) resources
|
#endif // English (United States) resources
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,74 @@
|
||||||
|
#include "stdafx.h"
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
enum {
|
||||||
|
CIN_SPECIALKEY
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
NMHDR nmh;
|
||||||
|
int vkey;
|
||||||
|
} NMCISPECIALKEY;
|
||||||
|
|
||||||
|
class CEditConInput : public CWindowImpl<CEditConInput, CEdit>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CEditConInput()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
LRESULT OnKeyDown(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled)
|
||||||
|
{
|
||||||
|
NMCISPECIALKEY nmsk;
|
||||||
|
|
||||||
|
switch (wParam)
|
||||||
|
{
|
||||||
|
case VK_UP:
|
||||||
|
case VK_DOWN:
|
||||||
|
case VK_RETURN:
|
||||||
|
nmsk = { { m_hWnd, (UINT_PTR)GetDlgCtrlID(), CIN_SPECIALKEY }, (int)wParam };
|
||||||
|
SendMessage(GetParent(), WM_NOTIFY, nmsk.nmh.idFrom, (LPARAM)&nmsk);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
bHandled = FALSE;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL Attach(HWND hWndNew)
|
||||||
|
{
|
||||||
|
return SubclassWindow(hWndNew);
|
||||||
|
}
|
||||||
|
|
||||||
|
BEGIN_MSG_MAP_EX(CEditEval)
|
||||||
|
MESSAGE_HANDLER(WM_KEYDOWN, OnKeyDown)
|
||||||
|
END_MSG_MAP()
|
||||||
|
};
|
||||||
|
|
||||||
|
class CEditConOutput : public CWindowImpl<CEditConOutput, CEdit>
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
LRESULT OnKeyDown(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/)
|
||||||
|
{
|
||||||
|
if (GetKeyState(VK_CONTROL) < 0)
|
||||||
|
{
|
||||||
|
if (wParam == 'A')
|
||||||
|
{
|
||||||
|
this->SetSelAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
public:
|
||||||
|
BOOL Attach(HWND hWndNew)
|
||||||
|
{
|
||||||
|
return SubclassWindow(hWndNew);
|
||||||
|
}
|
||||||
|
|
||||||
|
BEGIN_MSG_MAP_EX(CEditEval)
|
||||||
|
MESSAGE_HANDLER(WM_KEYDOWN, OnKeyDown)
|
||||||
|
END_MSG_MAP()
|
||||||
|
};
|
|
@ -81,6 +81,7 @@
|
||||||
#define IDD_Support_RequestCode 216
|
#define IDD_Support_RequestCode 216
|
||||||
#define IDD_Settings_GameDiskDrive 217
|
#define IDD_Settings_GameDiskDrive 217
|
||||||
#define IDD_Enhancement_Plugins 218
|
#define IDD_Enhancement_Plugins 218
|
||||||
|
#define IDD_Debugger_ScriptsAutorun 225
|
||||||
#define IDC_MENU_ITEM_TEXT 1000
|
#define IDC_MENU_ITEM_TEXT 1000
|
||||||
#define IDC_CLOSE_BUTTON 1001
|
#define IDC_CLOSE_BUTTON 1001
|
||||||
#define IDC_LIST2 1003
|
#define IDC_LIST2 1003
|
||||||
|
@ -871,6 +872,9 @@
|
||||||
#define IDC_RADIO_GLIDEN64 1728
|
#define IDC_RADIO_GLIDEN64 1728
|
||||||
#define IDC_RADIO_JABO 1729
|
#define IDC_RADIO_JABO 1729
|
||||||
#define IDC_RADIO_PJ64_VIDEO 1729
|
#define IDC_RADIO_PJ64_VIDEO 1729
|
||||||
|
#define IDC_REMOVE_BTN 1734
|
||||||
|
#define IDC_ADD_BTN 1735
|
||||||
|
#define IDC_AUTORUN_LIST 1737
|
||||||
#define ID_POPUPMENU_PLAYGAMEWITHDISK 40008
|
#define ID_POPUPMENU_PLAYGAMEWITHDISK 40008
|
||||||
#define ID_POPUPMENU_ADDSYMBOL 40013
|
#define ID_POPUPMENU_ADDSYMBOL 40013
|
||||||
#define ID_POPUPMENU_VIEWDISASM 40017
|
#define ID_POPUPMENU_VIEWDISASM 40017
|
||||||
|
@ -926,6 +930,7 @@
|
||||||
#define ID_POPUPMENU_COPYGAMESHARKCODE 40118
|
#define ID_POPUPMENU_COPYGAMESHARKCODE 40118
|
||||||
#define ID_POPUPMENU_COPYDATAWITHROWADDRESSES 40119
|
#define ID_POPUPMENU_COPYDATAWITHROWADDRESSES 40119
|
||||||
#define ID_POPUPMENU_COPYDATAWITHGROUPADDRESSES 40120
|
#define ID_POPUPMENU_COPYDATAWITHGROUPADDRESSES 40120
|
||||||
|
#define ID_POPUP_AUTORUN 40121
|
||||||
#define ID_POPUPMENU_ROMDIRECTORY 40137
|
#define ID_POPUPMENU_ROMDIRECTORY 40137
|
||||||
#define ID_POPUPMENU_REFRESHROMLIST 40138
|
#define ID_POPUPMENU_REFRESHROMLIST 40138
|
||||||
#define ID_POPUPMENU_PLAYGAME 40152
|
#define ID_POPUPMENU_PLAYGAME 40152
|
||||||
|
@ -942,9 +947,9 @@
|
||||||
//
|
//
|
||||||
#ifdef APSTUDIO_INVOKED
|
#ifdef APSTUDIO_INVOKED
|
||||||
#ifndef APSTUDIO_READONLY_SYMBOLS
|
#ifndef APSTUDIO_READONLY_SYMBOLS
|
||||||
#define _APS_NEXT_RESOURCE_VALUE 225
|
#define _APS_NEXT_RESOURCE_VALUE 227
|
||||||
#define _APS_NEXT_COMMAND_VALUE 40121
|
#define _APS_NEXT_COMMAND_VALUE 40122
|
||||||
#define _APS_NEXT_CONTROL_VALUE 1730
|
#define _APS_NEXT_CONTROL_VALUE 1738
|
||||||
#define _APS_NEXT_SYMED_VALUE 102
|
#define _APS_NEXT_SYMED_VALUE 102
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -25,6 +25,11 @@ int WINAPI WinMain(HINSTANCE /*hInstance*/, HINSTANCE /*hPrevInstance*/, LPSTR /
|
||||||
Notify().SetMainWindow(&MainWindow);
|
Notify().SetMainWindow(&MainWindow);
|
||||||
bool isROMLoaded = false;
|
bool isROMLoaded = false;
|
||||||
|
|
||||||
|
if (CDebuggerUI::HaveDebugger())
|
||||||
|
{
|
||||||
|
Debugger.StartAutorunScripts();
|
||||||
|
}
|
||||||
|
|
||||||
if (g_Settings->LoadStringVal(Cmd_RomFile).length() > 0 && g_Settings->LoadStringVal(Cmd_ComboDiskFile).length() > 0)
|
if (g_Settings->LoadStringVal(Cmd_RomFile).length() > 0 && g_Settings->LoadStringVal(Cmd_ComboDiskFile).length() > 0)
|
||||||
{
|
{
|
||||||
// Handle combo loading (N64 ROM and 64DD Disk)
|
// Handle combo loading (N64 ROM and 64DD Disk)
|
||||||
|
|
942
apidoc.htm
942
apidoc.htm
|
@ -1,942 +0,0 @@
|
||||||
<!DOCTYPE HTML>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<title>Project64 JS API</title>
|
|
||||||
<link rel="shortcut icon" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAAAlwSFlzAAAOwwAADsMBx2+oZAAAABl0RVh0U29mdHdhcmUAcGFpbnQubmV0IDQuMC4xOdTWsmQAAAOgSURBVDhPZZJ9UNMFGMd/f9Vf3PB4uS3EyxDmTZTXWFIdZEsxmOImTS4Zc3O8jMHaCxsOAhFhgwYTOF7magwGswXuUJcOhgiCuBgQYJyBpFB3HL3cmZ11peA35FZd9bl7/vne9/Pc88dD/MUT/Bpz68ux+UuOKy0Gg5bkjf/Fvfvzwv5h102tVrvVGxHE1YX6F5e/Wy5vGaj/QzmdhKyeA2i0tiw5Hc4kb+VvxtxuQd5AEpov63/qtnanb4Yz38xMyRw8NDxKx0WINufM3UNQtkkwMDRkKqoq8tssbjA8MiIoX2Ch/mEqVBez0GRs0ROWcQM6NyQ7ZPjwi6MwfM9HNyQw/y7cuIaJVptxxel0sp8vGBkbE5y+y4H5mQCa5cOQnVZ4iK4JIzrWc9C1LobIlopLN3tROZkBy1oeOp6IUDLLQlGbHH0ul+3G8I2Sstnj+ORxFjSLaSgolnoIs7sZ1T8no+5RKjItb8Pe02s0tpusZdey8dEPLNT9eBSalVQcaXoL10cGn8lvs6Ff5UA98y5EqnwPcX60Afn3wyF7EIFUQzSufd6nWfp2qVZ/uxJZd6LwwXw0sid3Q1ibsd7aer6PP3gA0jk6eEMRyFbkeojGwVqwxsk45qEgUReGr+7MrZ4b1ILhCkHarWAkOyngVh5bV8gUuaOjozmH7PFgD4WA0bMVQlmOh6jrq0a8g4SEq76IObMdVy47ZnlVJx5HmkiI6fQBX5P5VCKR8KenJ5mW6x8/pLfvBN0WCFqrL07kn/QQOocWsdYgxF14CRHFYbB9ajsr4Ar2KKoK50TVeU8L5YUZU9MTp+qd1Wu79DuwrZGEl5tIoKi2oEApdRO6Xg0SO+hItMQhWhkFV79rUafTxVOpVB8Oh7P/3oMFc6ldgVd1UaDqQxFWF4IwKRWnyksWExISwgntZ2fxWkMc9jbREVkQDavdipJm9Zrtgq1h6uvxidw2LhKr38QbdfHYW0PHPvk7UKmL+slkcsDmd1V0liJEuR2h6lcQyqOivceMCDkNaRVH8F4tE3sU4YhQ7wZNthMpksMQi8V6Go32wqb8HNU5JUjpPvDjbUEQKximLhMC2H4IzPBH4Pv+IHMD4Z/mj+OyzN/4mXyhV/sHvlAoOShM+YWUTEIQMxg12pohcbFklZJCgW/KxuKDAcgpFK2wmKzXvcr/Ye5n7hDknxyOZMdCo9FoGQzGttKKMncCdx8kKqkndldssLf6HwjiT/PF9/HEYv4PAAAAAElFTkSuQmCC"/>
|
|
||||||
<link href='https://fonts.googleapis.com/css?family=Open+Sans:400,400italic,700,700italic' rel='stylesheet' type='text/css'>
|
|
||||||
<style>
|
|
||||||
body {
|
|
||||||
font-family: "Open Sans", segoe ui, tahoma, sans;
|
|
||||||
font-size: 14px;
|
|
||||||
background-color: #EEE;
|
|
||||||
}
|
|
||||||
a {
|
|
||||||
color: #494;
|
|
||||||
text-decoration: none;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
div.inline-content {
|
|
||||||
display: inline-block;
|
|
||||||
vertical-align: top;
|
|
||||||
}
|
|
||||||
div.panel {
|
|
||||||
background-color: #FFF;
|
|
||||||
padding: 10px;
|
|
||||||
border-top: 4px solid #FFAAAA;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
box-shadow: -5px 5px 5px #DDD;
|
|
||||||
}
|
|
||||||
div.classname {
|
|
||||||
font-weight: bold;
|
|
||||||
font-size: 18px;
|
|
||||||
}
|
|
||||||
div.propertyname {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
div.property {
|
|
||||||
padding: 10px;
|
|
||||||
}
|
|
||||||
div.propertydesc {
|
|
||||||
padding: 10px;
|
|
||||||
}
|
|
||||||
pre.example {
|
|
||||||
background-color: #EFE;
|
|
||||||
border: 1px solid #BCB;
|
|
||||||
padding: 5px;
|
|
||||||
font-family: consolas, courier new, monospace;
|
|
||||||
white-space: pre;
|
|
||||||
margin: 10px;
|
|
||||||
font-size: 12px;
|
|
||||||
color: #020;
|
|
||||||
}
|
|
||||||
pre.example::before {
|
|
||||||
content: "Example";
|
|
||||||
float: right;
|
|
||||||
opacity: 0.5;
|
|
||||||
font-size: 11px;
|
|
||||||
font-family: "Open Sans", segoe ui, tahoma, sans;
|
|
||||||
}
|
|
||||||
span.snip {
|
|
||||||
background-color: #EFE;
|
|
||||||
border: 1px solid #BCB;
|
|
||||||
padding: 0px 2px;
|
|
||||||
font-size: 13px;
|
|
||||||
color: #020;
|
|
||||||
font-family: consolas, courier new, monospace;
|
|
||||||
border-radius: 3px;
|
|
||||||
}
|
|
||||||
span.tag {
|
|
||||||
float: right;
|
|
||||||
margin-left: 2px;
|
|
||||||
border: 2px solid #885;
|
|
||||||
border-radius: 5px;
|
|
||||||
background-color: #FFE;
|
|
||||||
color: #885;
|
|
||||||
font-weight: bold;
|
|
||||||
padding: 0px 2px;
|
|
||||||
font-size: 11px;
|
|
||||||
}
|
|
||||||
span.tag2 {
|
|
||||||
float: right;
|
|
||||||
margin-left: 2px;
|
|
||||||
border: 2px solid #588;
|
|
||||||
border-radius: 5px;
|
|
||||||
background-color: #EFF;
|
|
||||||
color: #588;
|
|
||||||
font-weight: bold;
|
|
||||||
padding: 0px 2px;
|
|
||||||
font-size: 11px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div style="margin: 10px; font-size: 24px;">Project64 JavaScript API</div>
|
|
||||||
<div class="inline-content" style="width: 150px; min-width: 150px; margin-left: 10px;">
|
|
||||||
<div class="panel" id="sidebar" style="width: 120px; position: absolute;">
|
|
||||||
<a href="#mem">mem</a><br>
|
|
||||||
<a href="#rom">rom</a><br>
|
|
||||||
<a href="#events">events</a><br>
|
|
||||||
<a href="#debug">debug</a><br>
|
|
||||||
<a href="#asm">asm</a><br>
|
|
||||||
<a href="#console">console</a><br>
|
|
||||||
<a href="#fs">fs</a><br>
|
|
||||||
<a href="#fs.Stats">fs.Stats</a><br>
|
|
||||||
<a href="#alert">alert</a><br>
|
|
||||||
<a href="#screen">screen</a><br>
|
|
||||||
<a href="#gpr">gpr</a><br>
|
|
||||||
<a href="#ugpr">ugpr</a><br>
|
|
||||||
<a href="#fpr">fpr</a><br>
|
|
||||||
<a href="#dfpr">dfpr</a><br>
|
|
||||||
<a href="#cop0">cop0</a><br>
|
|
||||||
<a href="#Server">Server</a><br>
|
|
||||||
<a href="#Socket">Socket</a><br>
|
|
||||||
<a href="#AddressRange">AddressRange</a><br>
|
|
||||||
<a href="#Number">Number</a><br>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="inline-content" style="max-width: 700px;">
|
|
||||||
<div class="panel" id="mem">
|
|
||||||
<div class="classname">mem</div>
|
|
||||||
<div class="property">
|
|
||||||
<div class="propertyname">mem.u8|u16|u32|s8|s16|s32|float|double</div>
|
|
||||||
<div class="propertydesc">
|
|
||||||
Arrays for reading and writing values in virtual memory.
|
|
||||||
Virtual addresses are always used for indices regardless of type size.
|
|
||||||
<pre class="example">
|
|
||||||
var addr_power = 0x8033B21E
|
|
||||||
|
|
||||||
if(mem.u8[addr_power] < 1)
|
|
||||||
{
|
|
||||||
mem.u8[addr_power] = 8
|
|
||||||
}
|
|
||||||
</pre>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="property">
|
|
||||||
<div class="propertyname">mem.getblock(baseAddr, size)</div>
|
|
||||||
<div class="propertydesc">
|
|
||||||
Returns a Buffer object from a block of data of <span class="snip">size</span> bytes at <span class="snip">baseAddr</span> in virtual memory.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="property">
|
|
||||||
<div class="propertyname">mem.getstring(baseAddr[, maxLen])</div>
|
|
||||||
<div class="propertydesc">
|
|
||||||
Returns a string from a zero-terminated ASCII string at <span class="snip">baseAddr</span> in virtual memory.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="property">
|
|
||||||
<div class="propertyname">mem.bindvar(obj, baseAddr, name, type)</div>
|
|
||||||
<div class="propertydesc">
|
|
||||||
Adds property <span class="snip">name</span> to <span class="snip">obj</span> and "binds" it to the virtual address specified by <span class="snip">baseAddr</span>.
|
|
||||||
Valid types are: <span class="snip">u8</span>, <span class="snip">u16</span>, <span class="snip">u32</span>, <span class="snip">s8</span>, <span class="snip">s16</span>, <span class="snip">s32</span>, <span class="snip">float</span>, and <span class="snip">double</span>.
|
|
||||||
<pre class="example">
|
|
||||||
mem.bindvar(this, 0x8033B21E, 'power', u8)
|
|
||||||
|
|
||||||
if(power < 1)
|
|
||||||
{
|
|
||||||
power = 8
|
|
||||||
}
|
|
||||||
</pre>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="property">
|
|
||||||
<div class="propertyname">mem.bindvars(obj, vars)</div>
|
|
||||||
<div class="propertydesc">
|
|
||||||
Adds multiple virtual memory-bound properties to <span class="snip">obj</span>.
|
|
||||||
Returns <span class="snip">obj</span>.
|
|
||||||
<pre class="example">
|
|
||||||
var mario = mem.bindvars({},
|
|
||||||
[
|
|
||||||
[0x8033B1B0, 'y', float],
|
|
||||||
[0x8033B21E, 'power', u8]
|
|
||||||
])
|
|
||||||
|
|
||||||
mario.power = 5
|
|
||||||
mario.y = 500.00
|
|
||||||
</pre>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="property">
|
|
||||||
<div class="propertyname">mem.bindstruct(obj, baseAddr, props)</div>
|
|
||||||
<div class="propertydesc">
|
|
||||||
Adds multiple virtual memory-bound properties to <span class="snip">obj</span>.
|
|
||||||
Addresses are determined by type sizes.
|
|
||||||
Returns <span class="snip">obj</span>.
|
|
||||||
<pre class="example">
|
|
||||||
var marioPos = mem.bindstruct(this, 0x8033B1AC,
|
|
||||||
{
|
|
||||||
x: float,
|
|
||||||
y: float,
|
|
||||||
z: float
|
|
||||||
})
|
|
||||||
</pre>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="property">
|
|
||||||
<div class="propertyname">mem.typedef(props)</div>
|
|
||||||
<div class="propertydesc">
|
|
||||||
Returns a "struct" class that may be constructed using the address of a real struct in virtual memory.
|
|
||||||
<pre class="example">
|
|
||||||
const Player = mem.typedef(
|
|
||||||
{
|
|
||||||
health: u32,
|
|
||||||
x: float,
|
|
||||||
y: float,
|
|
||||||
z: float
|
|
||||||
})
|
|
||||||
|
|
||||||
Player.prototype.move = function(x, y, z)
|
|
||||||
{
|
|
||||||
this.x = x
|
|
||||||
this.y = y
|
|
||||||
this.z = z
|
|
||||||
}
|
|
||||||
|
|
||||||
Player.prototype.heal = function()
|
|
||||||
{
|
|
||||||
this.health = 100
|
|
||||||
}
|
|
||||||
|
|
||||||
var player = new Player(0x8033B1AC)
|
|
||||||
|
|
||||||
player.move(100, 200, 300)
|
|
||||||
player.heal()
|
|
||||||
</pre>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div class="panel" id="rom">
|
|
||||||
<div class="classname">rom</div>
|
|
||||||
<div class="property">
|
|
||||||
<div class="propertyname">rom.u8|u16|u32|s8|s16|s32|float|double</div>
|
|
||||||
<div class="propertydesc">
|
|
||||||
Arrays for reading values in cartridge ROM. Indexing works in a similar manner to <a href="#mem">mem</a>'s.
|
|
||||||
<pre class="example">
|
|
||||||
var crc1 = rom.u32[0x00000010]
|
|
||||||
var crc2 = rom.u32[0x00000014]
|
|
||||||
</pre>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="property">
|
|
||||||
<div class="propertyname">rom.getblock(baseAddr, size)</div>
|
|
||||||
<div class="propertydesc">
|
|
||||||
Returns a Buffer object from a block of data of size bytes at <span class="snip">baseAddr</span> in cartridge ROM.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="property">
|
|
||||||
<div class="propertyname">rom.getstring(baseAddr[, maxLen])</div>
|
|
||||||
<div class="propertydesc">
|
|
||||||
Returns a string from a zero-terminated ASCII string at <span class="snip">baseAddr</span> in cartridge ROM.
|
|
||||||
<pre class="example">
|
|
||||||
var romName = rom.getstring(0x00000020)
|
|
||||||
console.log('Internal ROM name: ' + romName)</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div class="panel" id="events">
|
|
||||||
<div class="classname">events</div>
|
|
||||||
<!--
|
|
||||||
<div class="property">
|
|
||||||
<div class="propertyname">events.on(hook, callback, tag)</div>
|
|
||||||
<div class="propertydesc">
|
|
||||||
<pre class="example"></pre>
|
|
||||||
</div>
|
|
||||||
</div>-->
|
|
||||||
<div class="property">
|
|
||||||
<span class="tag2">emulation thread</span>
|
|
||||||
<span class="tag">interpreter mode only</span>
|
|
||||||
<div class="propertyname">events.onexec(address, callback)</div>
|
|
||||||
<div class="propertydesc">
|
|
||||||
Adds a CPU execution callback for a virtual address or <a href="#AddressRange">AddressRange</a> and returns a callback ID.
|
|
||||||
<span class="snip">callback</span> will be invoked at the beginning of a CPU step if the program counter is at <span class="snip">address</span>.
|
|
||||||
<span class="snip">callback</span> receives the program counter address at which the event is fired.
|
|
||||||
<pre class="example">
|
|
||||||
events.onexec(0x802CB1C0, function()
|
|
||||||
{
|
|
||||||
console.log('CPU is calling 0x802CB1C0')
|
|
||||||
})
|
|
||||||
</pre>
|
|
||||||
<pre class="example">
|
|
||||||
events.onexec(ADDR_ANY, function(pc)
|
|
||||||
{
|
|
||||||
// Log every step!
|
|
||||||
console.log('CPU is executing 0x' + pc.hex())
|
|
||||||
})
|
|
||||||
</pre>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="property">
|
|
||||||
<span class="tag2">emulation thread</span>
|
|
||||||
<span class="tag">interpreter mode only</span>
|
|
||||||
<div class="propertyname">events.onread(address, callback)</div>
|
|
||||||
<div class="propertydesc">
|
|
||||||
Adds a CPU read callback for a virtual address or <a href="#AddressRange">AddressRange</a> and returns a callback ID.
|
|
||||||
<span class="snip">callback</span> will be invoked at the beginning of a CPU step if the current instruction is going to read from <span class="snip">address</span>.
|
|
||||||
<span class="snip">callback</span> receives the virtual address that the CPU is going to read.
|
|
||||||
<pre class="example">
|
|
||||||
events.onread(0x8033B1B0, function()
|
|
||||||
{
|
|
||||||
console.log('CPU is reading 0x8033B1B0')
|
|
||||||
})
|
|
||||||
</pre>
|
|
||||||
<pre class="example">
|
|
||||||
events.onread(ADDR_ANY_CART_ROM_UNC, function(addr)
|
|
||||||
{
|
|
||||||
console.log('CPU is reading ROM 0x' + addr.hex())
|
|
||||||
})
|
|
||||||
</pre>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="property">
|
|
||||||
<span class="tag2">emulation thread</span>
|
|
||||||
<span class="tag">interpreter mode only</span>
|
|
||||||
<div class="propertyname">events.onwrite(address, callback)</div>
|
|
||||||
<div class="propertydesc">
|
|
||||||
Adds a CPU write callback for a virtual address or <a href="#AddressRange">AddressRange</a> and returns a callback ID.
|
|
||||||
<span class="snip">callback</span> will be invoked at the beginning of a CPU step if the current instruction is going to write to <span class="snip">address</span>.
|
|
||||||
<span class="snip">callback</span> receives the virtual address that the CPU is going to write to.
|
|
||||||
<pre class="example">
|
|
||||||
events.onwrite(0x8033B1B0, function()
|
|
||||||
{
|
|
||||||
console.log('CPU is modifying 0x8033B1B0')
|
|
||||||
})
|
|
||||||
</pre>
|
|
||||||
<pre class="example">
|
|
||||||
events.onwrite(ADDR_ANY_CART_ROM_UNC, function(addr)
|
|
||||||
{
|
|
||||||
console.log(gpr.pc.hex() + ': wrote to cartridge ' + addr.hex())
|
|
||||||
})
|
|
||||||
</pre>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="property">
|
|
||||||
<span class="tag2">emulation thread</span>
|
|
||||||
<span class="tag">interpreter mode only</span>
|
|
||||||
<div class="propertyname">events.onopcode(address, opcode, callback)</div>
|
|
||||||
<div class="propertydesc">
|
|
||||||
Adds a CPU execution callback for a virtual address or <a href="#AddressRange">AddressRange</a> and returns a callback ID.
|
|
||||||
<span class="snip">callback</span> will be invoked at the beginning of a CPU step if the program counter is at <span class="snip">address</span> and <span class="snip">opcode</span> is equal to the opcode to be executed.
|
|
||||||
<span class="snip">callback</span> receives the program counter address at which the event is fired.
|
|
||||||
<pre class="example">
|
|
||||||
const JR_RA = 0x03E00008 // 'jr ra' command
|
|
||||||
|
|
||||||
events.onopcode(ADDR_ANY, JR_RA, function(pc)
|
|
||||||
{
|
|
||||||
console.log(pc.hex()) // log pc at every 'jr ra'
|
|
||||||
})
|
|
||||||
</pre>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="property">
|
|
||||||
<span class="tag2">emulation thread</span>
|
|
||||||
<span class="tag">interpreter mode only</span>
|
|
||||||
<div class="propertyname">events.onopcode(address, opcode, mask, callback)</div>
|
|
||||||
<div class="propertydesc">
|
|
||||||
Adds a CPU execution callback for a virtual address or <a href="#AddressRange">AddressRange</a> and returns a callback ID.
|
|
||||||
<span class="snip">callback</span> will be invoked at the beginning of a CPU step if the program counter is at <span class="snip">address</span> and <span class="snip">opcode</span> is equal to the opcode to be executed ANDed with <span class="snip">mask</span>.
|
|
||||||
<span class="snip">callback</span> receives the program counter address at which the event is fired.
|
|
||||||
<pre class="example">
|
|
||||||
const ADDIU_SP_SP = 0x27BD0000 // 'addiu sp, sp, 0x0000'
|
|
||||||
const NO_IMM16 = 0xFFFF0000 // mask off immediate field
|
|
||||||
|
|
||||||
events.onopcode(ADDR_ANY, ADDIU_SP_SP, NO_IMM16, function(pc)
|
|
||||||
{
|
|
||||||
// log pc at every 'addiu sp, sp, x' regardless of the immediate value
|
|
||||||
console.log(pc.hex())
|
|
||||||
})
|
|
||||||
</pre>
|
|
||||||
<pre class="example">
|
|
||||||
const JAL = 0x0C000000 // 'jal 0x00000000'
|
|
||||||
const NO_TARGET = 0xFC000000 // mask off target field
|
|
||||||
|
|
||||||
events.onopcode(ADDR_ANY, JAL, NO_TARGET, function(pc)
|
|
||||||
{
|
|
||||||
// log pc at every 'jal' regardless of the target address
|
|
||||||
console.log(pc.hex())
|
|
||||||
})
|
|
||||||
</pre>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="property">
|
|
||||||
<span class="tag2">emulation thread</span>
|
|
||||||
<span class="tag">interpreter mode only</span>
|
|
||||||
<div class="propertyname">events.ongprvalue(address, registers, value, callback)</div>
|
|
||||||
<div class="propertydesc">
|
|
||||||
Adds a CPU execution callback for a virtual address or <a href="#AddressRange">AddressRange</a> and returns a callback ID.
|
|
||||||
<span class="snip">callback</span> will be invoked at the beginning of a CPU step if the program counter is at <span class="snip">address</span> and the lower 32 bits of the general purpose registers specified by <span class="snip">registers</span> are equal to <span class="snip">value</span>.
|
|
||||||
<span class="snip">callback</span> receives the program counter address at which the event is fired, and the index of the first register that contains the value.
|
|
||||||
<pre class="example">
|
|
||||||
events.ongprvalue(ADDR_ANY, GPR_ANY, 0x49533634, function(pc, reg)
|
|
||||||
{
|
|
||||||
// log pc whenever any general purpose register contains 0x49533634
|
|
||||||
console.log(pc.hex(), asm.gprname(reg))
|
|
||||||
})
|
|
||||||
</pre>
|
|
||||||
<pre class="example">
|
|
||||||
events.ongprvalue(ADDR_ANY, GPR_A0 | GPR_A1, 0x00001234, function(pc, reg)
|
|
||||||
{
|
|
||||||
// log pc whenever A0 or A1 contains 0x00001234
|
|
||||||
console.log(pc.hex(), asm.gprname(reg))
|
|
||||||
})
|
|
||||||
</pre>
|
|
||||||
Valid registers:
|
|
||||||
<pre style="font-size: 13px;">
|
|
||||||
GPR_R0 GPR_AT GPR_V0 GPR_V1 GPR_A0 GPR_A1 GPR_A2 GPR_A3
|
|
||||||
GPR_T0 GPR_T1 GPR_T2 GPR_T3 GPR_T4 GPR_T5 GPR_T6 GPR_T7
|
|
||||||
GPR_S0 GPR_S1 GPR_S2 GPR_S3 GPR_S4 GPR_S5 GPR_S6 GPR_S7
|
|
||||||
GPR_T8 GPR_T9 GPR_K0 GPR_K1 GPR_GP GPR_SP GPR_FP GPR_RA
|
|
||||||
|
|
||||||
GPR_ANY_ARG -- Any of the 'A' registers
|
|
||||||
GPR_ANY_TEMP -- Any of the 'T' registers
|
|
||||||
GPR_ANY_SAVE -- Any of the 'S' registers
|
|
||||||
GPR_ANY -- Any register
|
|
||||||
</pre>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="property">
|
|
||||||
<span class="tag2">emulation thread</span>
|
|
||||||
<div class="propertyname">events.ondraw(callback)</div>
|
|
||||||
<div class="propertydesc">
|
|
||||||
Adds a callback which will be invoked immediately after Project64 requests a screen update from the graphics plugin.
|
|
||||||
Returns a callback ID.
|
|
||||||
<pre class="example">
|
|
||||||
events.ondraw(function()
|
|
||||||
{
|
|
||||||
console.log('Frame drawn')
|
|
||||||
})
|
|
||||||
</pre>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="property">
|
|
||||||
<div class="propertyname">events.remove(callbackId)</div>
|
|
||||||
<div class="propertydesc">
|
|
||||||
Removes a registered callback by its ID.
|
|
||||||
<pre class="example">
|
|
||||||
var callbackId = events.onexec(0x80000180, function()
|
|
||||||
{
|
|
||||||
// Only invoked once
|
|
||||||
console.log('Interrupt fired')
|
|
||||||
events.remove(callbackId)
|
|
||||||
})
|
|
||||||
</pre>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- template
|
|
||||||
<div class="property">
|
|
||||||
<div class="propertyname"></div>
|
|
||||||
<div class="propertydesc">
|
|
||||||
<pre class="example"></pre>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
-->
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="panel" id="debug">
|
|
||||||
<div class="classname">debug</div>
|
|
||||||
<div class="property">
|
|
||||||
<div class="propertyname">debug.breakhere()</div>
|
|
||||||
<div class="propertydesc">
|
|
||||||
Pauses emulation and opens the debugger window. Useful for creating conditional breakpoints.
|
|
||||||
<pre class="example">
|
|
||||||
events.onexec(0x802CB1C0, function()
|
|
||||||
{
|
|
||||||
if(gpr.a0 == 0)
|
|
||||||
{
|
|
||||||
console.log('0 passed to 0x802CB1C0, breaking')
|
|
||||||
debug.breakhere()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
</pre>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="panel" id="asm">
|
|
||||||
<div class="classname">asm</div>
|
|
||||||
<div class="property">
|
|
||||||
<div class="propertyname">asm.gprname(regIndex)</div>
|
|
||||||
<div class="propertydesc">
|
|
||||||
Returns the name of the general purpose register specified by <span class="snip">regIndex</span>.
|
|
||||||
<pre class="example">
|
|
||||||
asm.gprname(4) // 'a0'
|
|
||||||
</pre>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="panel" id="fs">
|
|
||||||
<div class="classname">fs</div>
|
|
||||||
<div class="property">
|
|
||||||
<div class="propertyname">fs.open(path, mode)</div>
|
|
||||||
<div class="propertydesc">
|
|
||||||
Opens the file pointed to by <span class="snip">path</span> in the mode specified by <span class="snip">mode</span>.
|
|
||||||
Returns a file descriptor on success or <span class="snip">false</span> if the operation failed.
|
|
||||||
<pre class="example">var fd = fs.open('log.txt', 'w')</pre>
|
|
||||||
|
|
||||||
Valid modes:
|
|
||||||
|
|
||||||
<pre>
|
|
||||||
r or rb Open file for reading.
|
|
||||||
w or wb Truncate to zero length or create file for writing.
|
|
||||||
a or ab Append; open or create file for writing at end-of-file.
|
|
||||||
r+ or rb+ or r+b Open file for update (reading and writing).
|
|
||||||
w+ or wb+ or w+b Truncate to zero length or create file for update.
|
|
||||||
a+ or ab+ or a+b Append; open or create file for update, writing at end-of-file.
|
|
||||||
|
|
||||||
<a href="http://pubs.opengroup.org/onlinepubs/009695399/functions/fopen.html">http://pubs.opengroup.org/onlinepubs/009695399/functions/fopen.html</a>
|
|
||||||
</pre>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="property">
|
|
||||||
<div class="propertyname">fs.close(fd)</div>
|
|
||||||
<div class="propertydesc">
|
|
||||||
Closes the file referenced by <span class="snip">fd</span>.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="property">
|
|
||||||
<div class="propertyname">fs.write(fd, buffer[, offset[, length[, position]]])</div>
|
|
||||||
<div class="propertydesc">
|
|
||||||
Writes <span class="snip">buffer</span> to the file referenced by <span class="snip">fd</span>. Returns the number of bytes written.
|
|
||||||
<pre class="example">var fd = fs.open('log.txt', 'w')
|
|
||||||
fs.write(fd, 'Hello world!\n')
|
|
||||||
fs.close(fd)</pre>
|
|
||||||
<pre class="example">var buf = mem.getblock(0x8033B400, 4 * 32)
|
|
||||||
var fd = fs.open('data.bin', 'wb')
|
|
||||||
fs.write(fd, buf)
|
|
||||||
fs.close(fd)</pre>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="property">
|
|
||||||
<div class="propertyname">fs.writeFile(path, buffer)</div>
|
|
||||||
<div class="propertydesc">
|
|
||||||
Writes <span class="snip">buffer</span> to the file pointed to by <span class="snip">path</span>.
|
|
||||||
Returns <span class="snip">true</span> if the operation was successful or <span class="snip">false</span> if the operation failed.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="property">
|
|
||||||
<div class="propertyname">fs.read(fd, buffer, offset, length, position)</div>
|
|
||||||
<div class="propertydesc">
|
|
||||||
Reads the file referenced by <span class="snip">fd</span> into <span class="snip">buffer</span>. Returns the number of bytes read.
|
|
||||||
<pre class="example">var buf = new Buffer(128)
|
|
||||||
var fd = fs.open('data.bin', 'rb')
|
|
||||||
var nBytesRead = fs.read(fd, buf, 0, buf.length, 0)
|
|
||||||
fs.close(fd)</pre>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="property">
|
|
||||||
<div class="propertyname">fs.readFile(path)</div>
|
|
||||||
<div class="propertydesc">
|
|
||||||
Reads the file pointed to by <span class="snip">path</span>. Returns a Buffer object representing the file, or <span class="snip">false</span> if the operation failed.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="property">
|
|
||||||
<div class="propertyname">fs.fstat(fd)</div>
|
|
||||||
<div class="propertydesc">
|
|
||||||
Returns an <a href="#fs.Stats">fs.Stats</a> object describing the file referenced by <span class="snip">fd</span>.
|
|
||||||
<pre class="example">var stats = fs.fstat(fd)
|
|
||||||
console.log('size: ' + stats.size)
|
|
||||||
</pre>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="property">
|
|
||||||
<div class="propertyname">fs.stat(path)</div>
|
|
||||||
<div class="propertydesc">
|
|
||||||
Returns an <a href="#fs.Stats">fs.Stats</a> object describing the file or directory pointed to by <span class="snip">path</span>. Returns <span class="snip">false</span> if the operation failed.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="property">
|
|
||||||
<div class="propertyname">fs.unlink(path)</div>
|
|
||||||
<div class="propertydesc">Deletes a file. Returns <span class="snip">true</span> if the operation was successful.</div>
|
|
||||||
</div>
|
|
||||||
<div class="property">
|
|
||||||
<div class="propertyname">fs.mkdir(path)</div>
|
|
||||||
<div class="propertydesc">Creates a directory. Returns <span class="snip">true</span> if the operation was successful.</div>
|
|
||||||
</div>
|
|
||||||
<div class="property">
|
|
||||||
<div class="propertyname">fs.rmdir(path)</div>
|
|
||||||
<div class="propertydesc">Removes a directory. The directory must be empty. Returns <span class="snip">true</span> if the operation was successful.</div>
|
|
||||||
</div>
|
|
||||||
<div class="property">
|
|
||||||
<div class="propertyname">fs.readdir(path)</div>
|
|
||||||
<div class="propertydesc">
|
|
||||||
Returns an array of file names in the directory pointed to by <span class="snip">path</span>.
|
|
||||||
Returns <span class="snip">false</span> if the operation failed.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="panel" id="fs.Stats">
|
|
||||||
<div class="classname">fs.Stats</div>
|
|
||||||
<div class="propertydesc">
|
|
||||||
<table>
|
|
||||||
<tr><td><b>stats.dev</b> </td><td> ID of the device the file resides on</td></tr>
|
|
||||||
<tr><td><b>stats.ino</b> </td><td> inode number</td></tr>
|
|
||||||
<tr><td><b>stats.mode</b> </td><td> File permissions</td></tr>
|
|
||||||
<tr><td><b>stats.nlink</b> </td><td> Number of links to the file</td></tr>
|
|
||||||
<tr><td><b>stats.uid</b> </td><td> User ID</td></tr>
|
|
||||||
<tr><td><b>stats.gid</b> </td><td> Group ID</td></tr>
|
|
||||||
<tr><td><b>stats.rdev</b> </td><td> Device ID (if file is character or block special)</td></tr>
|
|
||||||
<tr><td><b>stats.size</b> </td><td> Size of the file in bytes</td></tr>
|
|
||||||
<tr><td><b>stats.atimeMs</b> </td><td> Last access timestamp in milliseconds</td></tr>
|
|
||||||
<tr><td><b>stats.mtimeMs</b> </td><td> Last modification timestamp in milliseconds</td></tr>
|
|
||||||
<tr><td><b>stats.ctimeMs</b> </td><td> Creation timestamp in milliseconds</td></tr>
|
|
||||||
<tr><td><b>stats.atime</b> </td><td> JS Date object representing the last access time</td></tr>
|
|
||||||
<tr><td><b>stats.mtime</b> </td><td> JS Date object representing the last modification time</td></tr>
|
|
||||||
<tr><td><b>stats.ctime</b> </td><td> JS Date object representing the creation time</td></tr>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
<div class="property">
|
|
||||||
<div class="propertyname">stats.isDirectory()</div>
|
|
||||||
<div class="propertydesc">
|
|
||||||
Returns <span class="snip">true</span> if the <a href="#fs.Stats">fs.Stats</a> object describes a directory.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="property">
|
|
||||||
<div class="propertyname">stats.isFile()</div>
|
|
||||||
<div class="propertydesc">
|
|
||||||
Returns <span class="snip">true</span> if the <a href="#fs.Stats">fs.Stats</a> object describes a regular file.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="panel" id="console">
|
|
||||||
<div class="classname">console</div>
|
|
||||||
<div class="property">
|
|
||||||
<div class="propertyname">console.print(text)</div>
|
|
||||||
<div class="propertydesc">
|
|
||||||
Prints text to the script console.
|
|
||||||
<pre class="example">console.print('Hello world\n')</pre>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="property">
|
|
||||||
<div class="propertyname">console.log(text[, text2, ...])</div>
|
|
||||||
<div class="propertydesc">
|
|
||||||
Concatenates all provided text arguments with spaces and prints the result to the script console with a trailing newline.
|
|
||||||
<pre class="example">console.log('Hello', 'world')</pre>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="property">
|
|
||||||
<div class="propertyname">console.clear()</div>
|
|
||||||
<div class="propertydesc">
|
|
||||||
Clears all previously printed text from the script console.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="panel" id="alert">
|
|
||||||
<div class="classname">alert</div>
|
|
||||||
<div class="property">
|
|
||||||
<div class="propertyname">alert(message[, caption])</div>
|
|
||||||
<div class="propertydesc">
|
|
||||||
Shows a message box with an optional caption.
|
|
||||||
The calling thread will be blocked until the message box is dismissed.
|
|
||||||
<pre class="example">
|
|
||||||
alert('Hello world') // Blocks the script's thread
|
|
||||||
|
|
||||||
events.onexec(0x80000180, function()
|
|
||||||
{
|
|
||||||
alert('Interrupt fired!') // Blocks the emulation thread
|
|
||||||
})</pre>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="panel" id="screen">
|
|
||||||
<div class="classname">screen</div>
|
|
||||||
<div class="property">
|
|
||||||
<div class="propertyname">screen.print(x, y, text)</div>
|
|
||||||
<div class="propertydesc">
|
|
||||||
Prints <span class="snip">text</span> to the screen at the provided <span class="snip">x</span> and <span class="snip">y</span> coordinates.
|
|
||||||
Should be called from an <span class="snip">events.ondraw</span> callback.
|
|
||||||
<b>(Unstable!)</b>
|
|
||||||
<pre class="example">
|
|
||||||
events.ondraw(function()
|
|
||||||
{
|
|
||||||
screen.print(20, 20, 'power: ' + mem.u8[0x8033B21E])
|
|
||||||
})</pre>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="panel" id="gpr">
|
|
||||||
<div class="classname">gpr</div>
|
|
||||||
<div class="property">
|
|
||||||
<div class="propertyname">gpr.r0|at|v0|v1|a0 ...</div>
|
|
||||||
<div class="propertyname">gpr[0|1|2 ...]</div>
|
|
||||||
<div class="propertydesc">
|
|
||||||
Variables representing the lower 32 bits of each general purpose register.
|
|
||||||
<pre class="example">
|
|
||||||
events.onexec(0x802CB1C0, function()
|
|
||||||
{
|
|
||||||
if(gpr.a0 == 2)
|
|
||||||
{
|
|
||||||
gpr.a0 = 3
|
|
||||||
}
|
|
||||||
})
|
|
||||||
</pre>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="property">
|
|
||||||
<div class="propertyname">gpr.pc</div>
|
|
||||||
<div class="propertydesc">
|
|
||||||
Variable representing the CPU's program counter.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="property">
|
|
||||||
<div class="propertyname">gpr.hi</div>
|
|
||||||
<div class="propertydesc">
|
|
||||||
Variable representing the lower 32 bits of the HI register.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="property">
|
|
||||||
<div class="propertyname">gpr.lo</div>
|
|
||||||
<div class="propertydesc">
|
|
||||||
Variable representing the lower 32 bits of the LO register.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="panel" id="ugpr">
|
|
||||||
<div class="classname">ugpr</div>
|
|
||||||
<div class="property">
|
|
||||||
<div class="propertyname">ugpr.r0|at|v0|v1|a0 ...</div>
|
|
||||||
<div class="propertyname">ugpr[0|1|2 ...]</div>
|
|
||||||
<div class="propertydesc">
|
|
||||||
Variables representing the upper 32 bits of each general purpose register.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="property">
|
|
||||||
<div class="propertyname">ugpr.hi</div>
|
|
||||||
<div class="propertydesc">
|
|
||||||
Variable representing the upper 32 bits of the HI register.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="property">
|
|
||||||
<div class="propertyname">ugpr.lo</div>
|
|
||||||
<div class="propertydesc">
|
|
||||||
Variable representing the upper 32 bits of the LO register.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="panel" id="fpr">
|
|
||||||
<div class="classname">fpr</div>
|
|
||||||
<div class="property">
|
|
||||||
<div class="propertyname">fpr.f0|f1|f2|f3|f4 ...</div>
|
|
||||||
<div class="propertyname">fpr[0|1|2 ...]</div>
|
|
||||||
<div class="propertydesc">
|
|
||||||
Variables representing the 32-bit floating point registers.
|
|
||||||
<pre class="example">
|
|
||||||
events.onexec(0x802CB1C0, function()
|
|
||||||
{
|
|
||||||
if(gpr.f0 == 2.0)
|
|
||||||
{
|
|
||||||
gpr.f0 = 3.0
|
|
||||||
}
|
|
||||||
})
|
|
||||||
</pre>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="panel" id="dfpr">
|
|
||||||
<div class="classname">dfpr</div>
|
|
||||||
<div class="property">
|
|
||||||
<div class="propertyname">dfpr.f0|f2|f4|f6 ...</div>
|
|
||||||
<div class="propertyname">dfpr[0|2|4 ...]</div>
|
|
||||||
<div class="propertydesc">
|
|
||||||
Variables representing the 64-bit floating point registers.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="panel" id="cop0">
|
|
||||||
<div class="classname">cop0</div>
|
|
||||||
<div class="property">
|
|
||||||
<div class="propertyname">cop0.cause</div>
|
|
||||||
<div class="propertydesc">
|
|
||||||
Variable representing the Cause register. Updates interrupts when written.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div class="panel" id="Server">
|
|
||||||
<div class="classname">Server</div>
|
|
||||||
<div class="property">
|
|
||||||
<div class="propertyname">new Server(settings)</div>
|
|
||||||
<div class="propertydesc">
|
|
||||||
Creates a new server socket.
|
|
||||||
If <span class="snip">port</span> is provided in <span class="snip">settings</span>, the server will start listening immediately.
|
|
||||||
<pre class="example">var server = new Server({port: 80})</pre>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="property">
|
|
||||||
<div class="propertyname">server.listen(port)</div>
|
|
||||||
<div class="propertydesc">
|
|
||||||
Binds the server socket to a port and starts listening.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="property">
|
|
||||||
<div class="propertyname">server.on('connection', callback)</div>
|
|
||||||
<div class="propertydesc">
|
|
||||||
Starts accepting clients.
|
|
||||||
When a client is accepted, a Socket object for it is created and passed to <span class="snip">callback</span>.
|
|
||||||
<pre class="example">
|
|
||||||
server.on('connection', function(socket)
|
|
||||||
{
|
|
||||||
socket.on('data', function(data)
|
|
||||||
{
|
|
||||||
socket.write('hello')
|
|
||||||
})
|
|
||||||
})</pre>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!--
|
|
||||||
server.listen(port)
|
|
||||||
server.on(evt, callback)
|
|
||||||
'connection' -> callback(socket)-->
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="panel" id="Socket">
|
|
||||||
<div class="classname">Socket</div>
|
|
||||||
<div class="property">
|
|
||||||
<div class="propertyname">new Socket([fd])</div>
|
|
||||||
<div class="propertydesc">
|
|
||||||
Creates a new socket object.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="property">
|
|
||||||
<div class="propertyname">socket.connect(settings[, callback])</div>
|
|
||||||
<div class="propertydesc">
|
|
||||||
Connects the socket to the <span class="snip">host</span> and <span class="snip">port</span> specified by the <span class="snip">settings</span> object.
|
|
||||||
The default values for the host and port are <span class="snip">"127.0.0.1"</span> and <span class="snip">80</span> respectively.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="property">
|
|
||||||
<div class="propertyname">socket.write(data[, callback])</div>
|
|
||||||
<div class="propertydesc">
|
|
||||||
Writes <span class="snip">data</span> to the socket.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="property">
|
|
||||||
<div class="propertyname">socket.close()</div>
|
|
||||||
<div class="propertydesc">
|
|
||||||
Closes the socket.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="property">
|
|
||||||
<div class="propertyname">socket.on('data', callback)</div>
|
|
||||||
<div class="propertydesc">
|
|
||||||
Starts reading data from the socket asynchronously. When data arrives, it is passed to <span class="snip">callback</span> as a Buffer object.
|
|
||||||
<!--<pre class="example"></pre>-->
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="property">
|
|
||||||
<div class="propertyname">socket.on('close', callback)</div>
|
|
||||||
<div class="propertydesc">
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="panel" id="AddressRange">
|
|
||||||
<div class="classname">AddressRange</div>
|
|
||||||
<div class="property">
|
|
||||||
<div class="propertyname">new AddressRange(start, end)</div>
|
|
||||||
<div class="propertydesc">
|
|
||||||
Creates an immutable object with <span class="snip">start</span> and <span class="snip">end</span> address properties.<br>
|
|
||||||
The following <b>AddressRange</b> objects are defined globally:
|
|
||||||
<pre>
|
|
||||||
<b>ADDR_ANY</b> 0x00000000 : 0xFFFFFFFF Any 32-bit address
|
|
||||||
|
|
||||||
<b>ADDR_ANY_KUSEG</b> 0x00000000 : 0x7FFFFFFF MIPS user mode TLB mapped segment
|
|
||||||
<b>ADDR_ANY_KSEG0</b> 0x80000000 : 0x9FFFFFFF MIPS cached unmapped segment
|
|
||||||
<b>ADDR_ANY_KSEG1</b> 0xA0000000 : 0xBFFFFFFF MIPS uncached unmapped segment
|
|
||||||
<b>ADDR_ANY_KSEG2</b> 0xC0000000 : 0xFFFFFFFF MIPS kernel mode TLB mapped segment
|
|
||||||
|
|
||||||
<b>ADDR_ANY_RDRAM</b> 0x80000000 : 0x807FFFFF Cached RDRAM
|
|
||||||
<b>ADDR_ANY_RDRAM_UNC</b> 0xA0000000 : 0xA07FFFFF Uncached RDRAM
|
|
||||||
|
|
||||||
<b>ADDR_ANY_CART_ROM</b> 0x90000000 : 0x95FFFFFF Cached cartridge ROM
|
|
||||||
<b>ADDR_ANY_CART_ROM_UNC</b> 0xB0000000 : 0xB5FFFFFF Uncached cartridge ROM
|
|
||||||
</pre>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="panel" id="Number">
|
|
||||||
<div class="classname">Number</div>
|
|
||||||
<div class="property">
|
|
||||||
<div class="propertyname">number.hex([nChars])</div>
|
|
||||||
<div class="propertydesc">
|
|
||||||
Returns a hexadecimal string representation of the number object. The resulting string is prepended with zeroes until its character length meets <span class="snip">nChars</span> or 8 by default.
|
|
||||||
<pre class="example">
|
|
||||||
var sm64EntryPC = rom.u32[0x00000008]
|
|
||||||
console.log('Entry: ' + sm64EntryPC.hex()) // "Entry: 80246000"</pre>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!--
|
|
||||||
<div class="panel">
|
|
||||||
alert(message)<br>
|
|
||||||
_native<br>
|
|
||||||
_native.addCallback(hook, callback, tag)<br>
|
|
||||||
_native.setGPRVal(regnum, val)<br>
|
|
||||||
_native.getGPRVal(regnum)<br>
|
|
||||||
_native.getRDRAMInt(address, bitWidth)<br>
|
|
||||||
_native.setRDRAMInt(address, bitWidth, val)<br>
|
|
||||||
_native.getRDRAMFloat(address[, bDouble])<br>
|
|
||||||
_native.setRDRAMFloat(address, val[, bDouble])<br>
|
|
||||||
_native.sockCreate(port)<br>
|
|
||||||
_native.sockListen(fd)<br>
|
|
||||||
_native.sockAccept(fd)<br>
|
|
||||||
_native.sockRead(fd, callback)<br>
|
|
||||||
_native.sockWrite(fd, data, callback)<br>
|
|
||||||
_native.msgBox(message[, caption])<br>
|
|
||||||
</div>
|
|
||||||
-->
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
(function(){
|
|
||||||
var domSidebar = document.getElementById('sidebar');
|
|
||||||
var baseTop = domSidebar.getBoundingClientRect().top + window.scrollY;
|
|
||||||
document.onscroll = function(){
|
|
||||||
if(window.scrollY > baseTop) {
|
|
||||||
domSidebar.style.position = "fixed";
|
|
||||||
domSidebar.style.top = '0px';
|
|
||||||
} else {
|
|
||||||
domSidebar.style.position = "absolute";
|
|
||||||
domSidebar.style.top = baseTop + 'px';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
Loading…
Reference in New Issue