[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>
|
||||
* Julien Hamaide (https://github.com/crazyjul)
|
||||
* 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
|
||||
===================
|
||||
|
@ -66,7 +105,11 @@ bugs, provided ideas, etc; roughly in order of appearance):
|
|||
* Michael Drake (https://github.com/tlsa)
|
||||
* https://github.com/chris-y
|
||||
* 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
|
||||
(``sami.vaarala@iki.fi``) and I'll fix the omission.
|
|
@ -1,10 +1,6 @@
|
|||
===============
|
||||
Duktape license
|
||||
===============
|
||||
The MIT License (MIT)
|
||||
|
||||
(http://opensource.org/licenses/MIT)
|
||||
|
||||
Copyright (c) 2013-2016 by Duktape authors (see AUTHORS.rst)
|
||||
Copyright (c) 2013-present, Duktape authors (see AUTHORS.rst)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
|
@ -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>
|
||||
<DisableSpecificWarnings>
|
||||
</DisableSpecificWarnings>
|
||||
<ExceptionHandling Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Async</ExceptionHandling>
|
||||
<ExceptionHandling Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Async</ExceptionHandling>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="duktape.h" />
|
||||
<ClInclude Include="duk_module_duktape.h" />
|
||||
<ClInclude Include="duk_config.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="duktape.c" />
|
||||
<ClCompile Include="duktape.cpp" />
|
||||
<ClCompile Include="duk_module_duktape.cpp" />
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -17,9 +17,15 @@
|
|||
<ClInclude Include="duktape.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="duk_module_duktape.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</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>
|
||||
</ClCompile>
|
||||
</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\RSP\RSP 1.7.dll"; DestDir: "{app}\Plugin\RSP"
|
||||
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]
|
||||
Name: "{app}\Config"; Permissions: everyone-full
|
||||
|
|
|
@ -30,4 +30,10 @@ __interface CDebugger
|
|||
virtual void CPUStepStarted(void) = 0;
|
||||
virtual void CPUStep(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/N64Disk.h>
|
||||
#include <Project64-core/N64System/N64System.h>
|
||||
#include <Project64-core/Debugger.h>
|
||||
|
||||
CDMA::CDMA(CFlashram & FlashRam, CSram & Sram) :
|
||||
m_FlashRam(FlashRam),
|
||||
|
@ -43,6 +44,11 @@ void CDMA::OnFirstDMA()
|
|||
|
||||
void CDMA::PI_DMA_READ()
|
||||
{
|
||||
if (g_Debugger != NULL && HaveDebugger())
|
||||
{
|
||||
g_Debugger->PIDMAReadStarted();
|
||||
}
|
||||
|
||||
// PI_STATUS_REG |= PI_STATUS_DMA_BUSY;
|
||||
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()
|
||||
{
|
||||
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.
|
||||
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;
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include <Project64-core/N64System/Mips/Rumblepak.h>
|
||||
#include <Project64-core/N64System/Mips/Mempak.h>
|
||||
#include <Project64-core/Logging.h>
|
||||
#include <Project64-core/Debugger.h>
|
||||
|
||||
CPifRam::CPifRam(bool SavesReadOnly) :
|
||||
CEeprom(SavesReadOnly)
|
||||
|
@ -260,6 +261,12 @@ void CPifRam::SI_DMA_READ()
|
|||
}
|
||||
|
||||
PifRamRead();
|
||||
|
||||
if (CDebugSettings::HaveDebugger())
|
||||
{
|
||||
g_Debugger->PIFReadStarted();
|
||||
}
|
||||
|
||||
SI_DRAM_ADDR_REG &= 0xFFFFFFF8;
|
||||
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)
|
||||
g_Settings->SaveString(File_DiskIPLTOOLPath, FileLoc);
|
||||
}
|
||||
|
||||
RunLoadedImage();
|
||||
return true;
|
||||
}
|
||||
|
@ -1182,6 +1181,11 @@ void CN64System::ExecuteCPU()
|
|||
{
|
||||
m_SyncCPU->m_Plugins->RomOpened();
|
||||
}
|
||||
|
||||
if (g_Debugger != nullptr && HaveDebugger())
|
||||
{
|
||||
g_Debugger->EmulationStarted();
|
||||
}
|
||||
#ifdef _WIN32
|
||||
_controlfp(_PC_53, _MCW_PC);
|
||||
#endif
|
||||
|
@ -1211,6 +1215,12 @@ void CN64System::ExecuteCPU()
|
|||
{
|
||||
m_SyncCPU->m_Plugins->RomClosed();
|
||||
}
|
||||
|
||||
if (g_Debugger != nullptr && HaveDebugger())
|
||||
{
|
||||
g_Debugger->EmulationStopped();
|
||||
}
|
||||
|
||||
WriteTrace(TraceN64System, TraceDebug, "Done");
|
||||
}
|
||||
|
||||
|
@ -2280,6 +2290,11 @@ void CN64System::RunRSP()
|
|||
return;
|
||||
}
|
||||
|
||||
if (g_Debugger != NULL && HaveDebugger())
|
||||
{
|
||||
g_Debugger->RSPReceivedTask();
|
||||
}
|
||||
|
||||
switch (Task)
|
||||
{
|
||||
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_ShowRecompMemSize, new CSettingTypeApplication("Debugger", "Show Recompiler Memory size", 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_SkipOp, 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_AppLogFlush, new CSettingTypeApplication("Logging", "Log Auto Flush", (uint32_t)false));
|
||||
AddHandler(Debugger_RecordRecompilerAsm, new CSettingTypeApplication("Debugger", "Record Recompiler Asm", false));
|
||||
AddHandler(Debugger_AutorunScripts, new CSettingTypeApplication("Debugger", "Autorun Scripts", ""));
|
||||
|
||||
// Logging
|
||||
AddHandler(Debugger_TraceMD5, new CSettingTypeApplication("Logging", "MD5", (uint32_t)g_ModuleLogLevel[TraceMD5]));
|
||||
|
|
|
@ -253,6 +253,7 @@ enum SettingID
|
|||
Debugger_ShowRecompMemSize,
|
||||
Debugger_DebugLanguage,
|
||||
Debugger_RecordExecutionTimes,
|
||||
Debugger_SilentBreak,
|
||||
Debugger_SteppingOps,
|
||||
Debugger_SkipOp,
|
||||
Debugger_HaveExecutionBP,
|
||||
|
@ -265,6 +266,7 @@ enum SettingID
|
|||
Debugger_FpExceptionBreakpoints,
|
||||
Debugger_IntrBreakpoints,
|
||||
Debugger_RcpIntrBreakpoints,
|
||||
Debugger_AutorunScripts,
|
||||
|
||||
// Trace
|
||||
Debugger_TraceMD5,
|
||||
|
|
|
@ -73,6 +73,7 @@
|
|||
<ClCompile Include="UserInterface\Debugger\Debugger-MemoryDump.cpp" />
|
||||
<ClCompile Include="UserInterface\Debugger\Debugger-MemorySearch.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-StackTrace.cpp" />
|
||||
<ClCompile Include="UserInterface\Debugger\Debugger-StackView.cpp" />
|
||||
|
@ -81,11 +82,34 @@
|
|||
<ClCompile Include="UserInterface\Debugger\Debugger-ViewMemory.cpp" />
|
||||
<ClCompile Include="UserInterface\Debugger\Debugger.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\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\ScriptSystem.cpp" />
|
||||
<ClCompile Include="UserInterface\Debugger\ScriptWorker.cpp" />
|
||||
<ClCompile Include="UserInterface\Debugger\Symbols.cpp" />
|
||||
<ClCompile Include="UserInterface\DiscordRPC.cpp" />
|
||||
<ClCompile Include="UserInterface\EnhancementUI.cpp" />
|
||||
|
@ -146,6 +170,7 @@
|
|||
<ClInclude Include="UserInterface\Debugger\Debugger-MemoryDump.h" />
|
||||
<ClInclude Include="UserInterface\Debugger\Debugger-MemorySearch.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-StackTrace.h" />
|
||||
<ClInclude Include="UserInterface\Debugger\Debugger-StackView.h" />
|
||||
|
@ -156,12 +181,18 @@
|
|||
<ClInclude Include="UserInterface\Debugger\debugger.h" />
|
||||
<ClInclude Include="UserInterface\Debugger\DebuggerUI.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\MemoryScanner.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\ScriptSystem.h" />
|
||||
<ClInclude Include="UserInterface\Debugger\ScriptTypes.h" />
|
||||
<ClInclude Include="UserInterface\Debugger\ScriptWorker.h" />
|
||||
<ClInclude Include="UserInterface\Debugger\Symbols.h" />
|
||||
<ClInclude Include="UserInterface\EnhancementUI.h" />
|
||||
<ClInclude Include="UserInterface\MainWindow.h" />
|
||||
|
@ -193,6 +224,7 @@
|
|||
<ClInclude Include="UserInterface\SupportWindow.h" />
|
||||
<ClInclude Include="UserInterface\WelcomeScreen.h" />
|
||||
<ClInclude Include="UserInterface\WTLControls\DisplayMode.h" />
|
||||
<ClInclude Include="UserInterface\WTLControls\EditConInput.h" />
|
||||
<ClInclude Include="UserInterface\WTLControls\EditNumber32.h" />
|
||||
<ClInclude Include="UserInterface\WTLControls\GetCWindowText.h" />
|
||||
<ClInclude Include="UserInterface\WTLControls\HexEditCtrl.h" />
|
||||
|
@ -215,6 +247,9 @@
|
|||
<ProjectReference Include="..\3rdParty\duktape\duktape.vcxproj">
|
||||
<Project>{e8d9a652-a354-4374-b6c5-a51ee62749fd}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\3rdParty\png\png.vcxproj">
|
||||
<Project>{17836496-31b0-46f2-b1b1-366d7df6f04c}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\3rdParty\zlib\zlib.vcxproj">
|
||||
<Project>{731bd205-2826-4631-b7af-117658e88dbc}</Project>
|
||||
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
|
||||
|
@ -245,7 +280,6 @@
|
|||
<ItemGroup>
|
||||
<None Include="res\divider.cur" />
|
||||
<None Include="res\hand.cur" />
|
||||
<None Include="UserInterface\API.js" />
|
||||
<None Include="UserInterface\icons\bin00001.bin" />
|
||||
<None Include="UserInterface\Icons\divider.cur" />
|
||||
<None Include="UserInterface\Icons\hand.cur" />
|
||||
|
|
|
@ -46,6 +46,12 @@
|
|||
<Filter Include="Header Files\User Interface Headers\Debugger Headers">
|
||||
<UniqueIdentifier>{c4249d55-df70-4453-b017-b548514ad094}</UniqueIdentifier>
|
||||
</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>
|
||||
<ClCompile Include="stdafx.cpp">
|
||||
|
@ -171,9 +177,6 @@
|
|||
<ClCompile Include="UserInterface\Debugger\DMALog.cpp">
|
||||
<Filter>Source Files\User Interface Source\Debugger Source</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="UserInterface\Debugger\ScriptHook.cpp">
|
||||
<Filter>Source Files\User Interface Source\Debugger Source</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="UserInterface\Debugger\ScriptInstance.cpp">
|
||||
<Filter>Source Files\User Interface Source\Debugger Source</Filter>
|
||||
</ClCompile>
|
||||
|
@ -255,6 +258,81 @@
|
|||
<ClCompile Include="UserInterface\RomBrowser.cpp">
|
||||
<Filter>Source Files\User Interface Source</Filter>
|
||||
</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>
|
||||
<ClInclude Include="UserInterface\resource.h">
|
||||
|
@ -416,9 +494,6 @@
|
|||
<ClInclude Include="UserInterface\Debugger\OpInfo.h">
|
||||
<Filter>Header Files\User Interface Headers\Debugger Headers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="UserInterface\Debugger\ScriptHook.h">
|
||||
<Filter>Header Files\User Interface Headers\Debugger Headers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="UserInterface\Debugger\ScriptInstance.h">
|
||||
<Filter>Header Files\User Interface Headers\Debugger Headers</Filter>
|
||||
</ClInclude>
|
||||
|
@ -494,6 +569,33 @@
|
|||
<ClInclude Include="UserInterface\CheatUI.h">
|
||||
<Filter>Header Files\User Interface Headers</Filter>
|
||||
</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>
|
||||
<None Include="res\divider.cur">
|
||||
|
@ -514,7 +616,6 @@
|
|||
<None Include="UserInterface\icons\javascri.bin">
|
||||
<Filter>Resource Files</Filter>
|
||||
</None>
|
||||
<None Include="UserInterface\API.js" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Image Include="res\ListItems.bmp">
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,17 +1,24 @@
|
|||
#include "stdafx.h"
|
||||
|
||||
#include "DebuggerUI.h"
|
||||
|
||||
CDebugScripts::CDebugScripts(CDebuggerUI* debugger) :
|
||||
CDebugDialog<CDebugScripts>(debugger),
|
||||
CToolTipDialog<CDebugScripts>(),
|
||||
m_hQuitScriptDirWatchEvent(nullptr),
|
||||
m_hScriptDirWatchThread(nullptr)
|
||||
m_hScriptDirWatchThread(nullptr),
|
||||
m_InputHistoryIndex(0),
|
||||
m_MonoFont(nullptr),
|
||||
m_MonoBoldFont(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
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*/)
|
||||
|
@ -20,11 +27,15 @@ LRESULT CDebugScripts::OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*l
|
|||
DlgSavePos_Init(DebuggerUI_ScriptsPos);
|
||||
DlgToolTip_Init();
|
||||
|
||||
HFONT monoFont = CreateFont(-11, 0, 0, 0,
|
||||
m_MonoFont = CreateFont(-12, 0, 0, 0,
|
||||
FW_DONTCARE, FALSE, FALSE, FALSE, DEFAULT_CHARSET,
|
||||
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.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.ModifyStyle(LVS_OWNERDRAWFIXED, 0, 0);
|
||||
|
||||
m_EvalEdit.Attach(GetDlgItem(IDC_EVAL_EDIT));
|
||||
m_EvalEdit.SetScriptWindow(this);
|
||||
m_EvalEdit.SetFont(monoFont);
|
||||
m_EvalEdit.EnableWindow(FALSE);
|
||||
m_ConInputEdit.Attach(GetDlgItem(IDC_EVAL_EDIT));
|
||||
m_ConInputEdit.SetFont(m_MonoFont);
|
||||
m_ConInputEdit.EnableWindow(FALSE);
|
||||
|
||||
m_ConsoleEdit.Attach(GetDlgItem(IDC_CONSOLE_EDIT));
|
||||
m_ConsoleEdit.SetLimitText(0);
|
||||
m_ConsoleEdit.SetFont(monoFont);
|
||||
m_ConOutputEdit.Attach(GetDlgItem(IDC_CONSOLE_EDIT));
|
||||
m_ConOutputEdit.SetLimitText(0);
|
||||
m_ConOutputEdit.SetFont(m_MonoFont);
|
||||
|
||||
::SendMessage(GetDlgItem(IDC_EVAL_LBL), WM_SETFONT, (WPARAM)m_MonoBoldFont, 0);
|
||||
|
||||
int statusPaneWidths[] = { -1 };
|
||||
m_StatusBar.Attach(GetDlgItem(IDC_STATUSBAR));
|
||||
m_StatusBar.SetParts(1, statusPaneWidths);
|
||||
|
||||
m_Debugger->ScriptSystem()->LoadAutorunList();
|
||||
|
||||
RefreshList();
|
||||
|
||||
m_InstallDir = (std::string)CPath(CPath::MODULE_DIRECTORY);
|
||||
m_ScriptsDir = m_InstallDir + "Scripts\\";
|
||||
m_InstallDir = m_Debugger->ScriptSystem()->InstallDirPath();
|
||||
m_ScriptsDir = m_Debugger->ScriptSystem()->ScriptsDirPath();
|
||||
|
||||
if (!PathFileExistsA(m_ScriptsDir.c_str()))
|
||||
{
|
||||
CreateDirectoryA(m_ScriptsDir.c_str(), nullptr);
|
||||
}
|
||||
SetTimer(CONFLUSH_TIMER_ID, CONFLUSH_TIMER_INTERVAL, nullptr);
|
||||
|
||||
m_hQuitScriptDirWatchEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
|
||||
m_hScriptDirWatchThread = CreateThread(nullptr, 0, ScriptDirWatchProc, (void*)this, 0, nullptr);
|
||||
|
||||
m_ConOutputEdit.SetWindowText(m_Debugger->ScriptSystem()->GetConsoleBuffer().ToUTF16().c_str());
|
||||
|
||||
LoadWindowPos();
|
||||
WindowCreated();
|
||||
|
||||
|
@ -68,10 +81,15 @@ LRESULT CDebugScripts::OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*l
|
|||
|
||||
LRESULT CDebugScripts::OnDestroy(void)
|
||||
{
|
||||
KillTimer(CONFLUSH_TIMER_ID);
|
||||
|
||||
SetEvent(m_hQuitScriptDirWatchEvent);
|
||||
WaitForSingleObject(m_hScriptDirWatchThread, INFINITE);
|
||||
CloseHandle(m_hQuitScriptDirWatchEvent);
|
||||
CloseHandle(m_hScriptDirWatchThread);
|
||||
|
||||
DeleteObject(m_MonoFont);
|
||||
DeleteObject(m_MonoBoldFont);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -83,8 +101,26 @@ LRESULT CDebugScripts::OnCtlColorStatic(UINT /*uMsg*/, WPARAM wParam, LPARAM lPa
|
|||
|
||||
if (ctrlId == IDC_CONSOLE_EDIT)
|
||||
{
|
||||
SetBkColor(hDC, RGB(255, 255, 255));
|
||||
SetDCBrushColor(hDC, RGB(255, 255, 255));
|
||||
SetTextColor(hDC, RGB(0xEE, 0xEE, 0xEE));
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -108,23 +144,26 @@ DWORD WINAPI CDebugScripts::ScriptDirWatchProc(void* ctx)
|
|||
|
||||
while (true)
|
||||
{
|
||||
DWORD status = WaitForMultipleObjects(2, hEvents, FALSE, INFINITE);
|
||||
DWORD nHandle = WaitForMultipleObjects(2, hEvents, FALSE, INFINITE);
|
||||
|
||||
switch (status)
|
||||
switch (nHandle)
|
||||
{
|
||||
case WAIT_OBJECT_0:
|
||||
if (FindNextChangeNotification(hEvents[0]) == FALSE)
|
||||
{
|
||||
return 0;
|
||||
goto done;
|
||||
}
|
||||
_this->PostMessage(WM_REFRESH_LIST, 0, 0);
|
||||
break;
|
||||
case WAIT_OBJECT_0 + 1:
|
||||
return 0;
|
||||
default:
|
||||
case WAIT_OBJECT_0 + 1:
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
FindCloseChangeNotification(hEvents[0]);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CDebugScripts::OnExitSizeMove(void)
|
||||
|
@ -141,12 +180,25 @@ void CDebugScripts::ConsoleCopy()
|
|||
|
||||
EmptyClipboard();
|
||||
|
||||
size_t nChars = m_ConsoleEdit.GetWindowTextLength() + 1;
|
||||
size_t nChars = m_ConOutputEdit.GetWindowTextLength() + 1;
|
||||
|
||||
HGLOBAL hMem = GlobalAlloc(GMEM_MOVEABLE, nChars * sizeof(wchar_t));
|
||||
|
||||
if (hMem == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
SetClipboardData(CF_UNICODETEXT, hMem);
|
||||
|
@ -159,7 +211,9 @@ void CDebugScripts::ConsolePrint(const char* text)
|
|||
{
|
||||
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)
|
||||
{
|
||||
SendMessage(WM_CONSOLE_CLEAR);
|
||||
PostMessage(WM_CONSOLE_CLEAR);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -187,18 +241,23 @@ LRESULT CDebugScripts::OnClicked(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*
|
|||
EndDialog(0);
|
||||
break;
|
||||
case ID_POPUP_RUN:
|
||||
case IDC_RUN_BTN:
|
||||
RunSelected();
|
||||
break;
|
||||
case IDC_RUN_BTN:
|
||||
ToggleSelected();
|
||||
break;
|
||||
case ID_POPUP_STOP:
|
||||
case IDC_STOP_BTN:
|
||||
StopSelected();
|
||||
break;
|
||||
case ID_POPUP_SCRIPT_EDIT:
|
||||
EditSelected();
|
||||
break;
|
||||
case ID_POPUP_AUTORUN:
|
||||
m_AutorunDlg.DoModal(m_Debugger, m_SelectedScriptName);
|
||||
m_ScriptList.RedrawWindow();
|
||||
break;
|
||||
case IDC_CLEAR_BTN:
|
||||
ConsoleClear();
|
||||
m_Debugger->ScriptSystem()->ConsoleClear();
|
||||
break;
|
||||
case IDC_COPY_BTN:
|
||||
ConsoleCopy();
|
||||
|
@ -212,7 +271,6 @@ LRESULT CDebugScripts::OnClicked(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*
|
|||
|
||||
LRESULT CDebugScripts::OnScriptListDblClicked(NMHDR* pNMHDR)
|
||||
{
|
||||
// Run script on double click
|
||||
NMITEMACTIVATE* pIA = reinterpret_cast<NMITEMACTIVATE*>(pNMHDR);
|
||||
int nItem = pIA->iItem;
|
||||
|
||||
|
@ -228,22 +286,23 @@ LRESULT CDebugScripts::OnScriptListDblClicked(NMHDR* pNMHDR)
|
|||
|
||||
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;
|
||||
|
||||
if (state == STATE_RUNNING)
|
||||
if (status == JS_STATUS_STARTED)
|
||||
{
|
||||
statusText += " (Running)";
|
||||
m_EvalEdit.EnableWindow(TRUE);
|
||||
statusText += " (Started)";
|
||||
m_ConInputEdit.EnableWindow(TRUE);
|
||||
m_ConInputEdit.SetFocus();
|
||||
}
|
||||
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());
|
||||
|
@ -259,12 +318,12 @@ LRESULT CDebugScripts::OnScriptListRClicked(NMHDR* pNMHDR)
|
|||
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 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);
|
||||
}
|
||||
|
@ -303,13 +362,18 @@ LRESULT CDebugScripts::OnScriptListCustomDraw(NMHDR* pNMHDR)
|
|||
wchar_t 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);
|
||||
}
|
||||
else if (state == STATE_RUNNING)
|
||||
else if (status == JS_STATUS_STARTED)
|
||||
{
|
||||
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_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);
|
||||
::EnableWindow(GetDlgItem(IDC_RUN_BTN), state == STATE_STOPPED || state == STATE_INVALID);
|
||||
::SetWindowText(GetDlgItem(IDC_RUN_BTN), status == JS_STATUS_STOPPED ? L"Run" : L"Stop");
|
||||
|
||||
RefreshStatus();
|
||||
}
|
||||
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;
|
||||
|
||||
::ShowWindow(*this, SW_SHOWNOACTIVATE);
|
||||
|
||||
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();
|
||||
}
|
||||
char *text = (char*)wParam;
|
||||
m_ConOutputBuffer += text;
|
||||
free(text);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
LRESULT CDebugScripts::OnConsoleClear(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
|
||||
{
|
||||
m_ConsoleEdit.SetWindowText(L"");
|
||||
m_ConOutputBuffer = "";
|
||||
m_ConOutputEdit.SetWindowText(L"");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
@ -374,10 +424,11 @@ LRESULT CDebugScripts::OnRefreshList(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*
|
|||
{
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -388,16 +439,21 @@ LRESULT CDebugScripts::OnRefreshList(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*
|
|||
|
||||
do
|
||||
{
|
||||
stdstr scriptFileName = SearchPath.GetNameExtension();
|
||||
INSTANCE_STATE state = m_Debugger->ScriptSystem()->GetInstanceState(scriptFileName.c_str());
|
||||
if (searchPath.GetExtension() != "js")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
stdstr scriptFileName = searchPath.GetNameExtension();
|
||||
JSInstanceStatus status = m_Debugger->ScriptSystem()->GetStatus(scriptFileName.c_str());
|
||||
const wchar_t *statusIcon = L"";
|
||||
|
||||
switch (state)
|
||||
switch (status)
|
||||
{
|
||||
case STATE_STARTED:
|
||||
case JS_STATUS_STARTING:
|
||||
statusIcon = L"*";
|
||||
break;
|
||||
case STATE_RUNNING:
|
||||
case JS_STATUS_STARTED:
|
||||
statusIcon = L">";
|
||||
break;
|
||||
default:
|
||||
|
@ -408,7 +464,7 @@ LRESULT CDebugScripts::OnRefreshList(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*
|
|||
m_ScriptList.AddItem(nItem, 0, statusIcon);
|
||||
m_ScriptList.SetItemText(nItem, 1, scriptFileName.ToUTF16().c_str());
|
||||
nItem++;
|
||||
} while (SearchPath.FindNext());
|
||||
} while (searchPath.FindNext());
|
||||
|
||||
m_ScriptList.SetRedraw(true);
|
||||
m_ScriptList.Invalidate();
|
||||
|
@ -421,15 +477,9 @@ LRESULT CDebugScripts::OnRefreshList(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*
|
|||
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());
|
||||
|
||||
if (state == STATE_RUNNING || state == STATE_STARTED)
|
||||
{
|
||||
CScriptInstance* instance = m_Debugger->ScriptSystem()->GetInstance(m_SelectedScriptName.c_str());
|
||||
instance->Eval(code);
|
||||
}
|
||||
m_Debugger->ScriptSystem()->Input(name, code);
|
||||
}
|
||||
|
||||
void CDebugScripts::RunSelected()
|
||||
|
@ -439,16 +489,9 @@ void CDebugScripts::RunSelected()
|
|||
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()->RunScript(m_SelectedScriptName.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Debugger->Debug_LogScriptsWindow("[Error: script is already running]\n");
|
||||
}
|
||||
m_Debugger->ScriptSystem()->StartScript(m_SelectedScriptName.c_str(), path.c_str());
|
||||
}
|
||||
|
||||
void CDebugScripts::StopSelected()
|
||||
|
@ -458,9 +501,9 @@ void CDebugScripts::StopSelected()
|
|||
|
||||
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();
|
||||
}
|
||||
|
@ -472,71 +515,111 @@ void CDebugScripts::ToggleSelected()
|
|||
|
||||
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 CEditEval::OnKeyDown(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled)
|
||||
LRESULT CDebugScripts::OnInputSpecialKey(NMHDR* pNMHDR)
|
||||
{
|
||||
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];
|
||||
SetWindowText(stdstr(Code).ToUTF16().c_str());
|
||||
SetSel((int)Code.length(), (int)Code.length());
|
||||
wchar_t* code = m_InputHistory[--m_InputHistoryIndex];
|
||||
m_ConInputEdit.SetWindowText(code);
|
||||
int selEnd = wcslen(code);
|
||||
m_ConInputEdit.SetSel(selEnd, selEnd);
|
||||
}
|
||||
}
|
||||
else if (wParam == VK_DOWN)
|
||||
{
|
||||
int size = m_History.size();
|
||||
if (m_HistoryIdx < size - 1)
|
||||
{
|
||||
const std::string & Code = m_History[++m_HistoryIdx];
|
||||
SetWindowText(stdstr(Code).ToUTF16().c_str());
|
||||
SetSel((int)Code.length(), (int)Code.length());
|
||||
}
|
||||
else if (m_HistoryIdx < size)
|
||||
{
|
||||
SetWindowText(L"");
|
||||
m_HistoryIdx++;
|
||||
}
|
||||
}
|
||||
else if (wParam == VK_RETURN)
|
||||
{
|
||||
if (m_ScriptWindow == nullptr)
|
||||
{
|
||||
bHandled = FALSE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::string Code = GetCWindowText(*this);
|
||||
m_ScriptWindow->EvaluateInSelectedInstance(Code.c_str());
|
||||
|
||||
SetWindowText(L"");
|
||||
int historySize = m_History.size();
|
||||
|
||||
// Remove duplicate
|
||||
for (int i = 0; i < historySize; i++)
|
||||
if (pnmsk->vkey == VK_DOWN)
|
||||
{
|
||||
if (strcmp(Code.c_str(), m_History[i].c_str()) == 0)
|
||||
size_t size = m_InputHistory.size();
|
||||
|
||||
if (m_InputHistoryIndex < size)
|
||||
{
|
||||
m_History.erase(m_History.begin() + i);
|
||||
historySize--;
|
||||
break;
|
||||
m_InputHistoryIndex++;
|
||||
}
|
||||
|
||||
if (m_InputHistoryIndex < size)
|
||||
{
|
||||
wchar_t* code = m_InputHistory[m_InputHistoryIndex];
|
||||
m_ConInputEdit.SetWindowText(code);
|
||||
int selEnd = wcslen(code);
|
||||
m_ConInputEdit.SetSel(selEnd, selEnd);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_ConInputEdit.SetWindowText(L"");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (pnmsk->vkey == VK_RETURN)
|
||||
{
|
||||
size_t codeLength = m_ConInputEdit.GetWindowTextLength();
|
||||
|
||||
if (codeLength == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
wchar_t* code = new wchar_t[codeLength + 1];
|
||||
m_ConInputEdit.GetWindowText(code, codeLength + 1);
|
||||
m_ConInputEdit.SetWindowText(L"");
|
||||
|
||||
SendInput(m_SelectedScriptName.c_str(), stdstr().FromUTF16(code).c_str());
|
||||
|
||||
// If there is a duplicate entry move it to the bottom
|
||||
for (size_t i = 0; i < m_InputHistory.size(); i++)
|
||||
{
|
||||
if (wcscmp(code, m_InputHistory[i]) == 0)
|
||||
{
|
||||
wchar_t* str = m_InputHistory[i];
|
||||
m_InputHistory.erase(m_InputHistory.begin() + i);
|
||||
m_InputHistory.push_back(str);
|
||||
m_InputHistoryIndex = m_InputHistory.size();
|
||||
delete[] code;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Remove oldest if maxed
|
||||
if (historySize >= HISTORY_MAX_ENTRIES)
|
||||
{
|
||||
m_History.erase(m_History.begin() + 0);
|
||||
historySize--;
|
||||
m_InputHistory.push_back(code);
|
||||
m_InputHistoryIndex = m_InputHistory.size();
|
||||
return 0;
|
||||
}
|
||||
|
||||
m_History.push_back(Code);
|
||||
m_HistoryIdx = historySize++;
|
||||
}
|
||||
bHandled = FALSE;
|
||||
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
|
||||
#include "DebuggerUI.h"
|
||||
#include "ScriptSystem.h"
|
||||
#include <Project64/UserInterface/WTLControls/EditConInput.h>
|
||||
#include <Project64/UserInterface/WTLControls/TooltipDialog.h>
|
||||
#include "Debugger-ScriptsAutorun.h"
|
||||
#include <string>
|
||||
#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 :
|
||||
public CDebugDialog<CDebugScripts>,
|
||||
public CDialogResize<CDebugScripts>,
|
||||
|
@ -76,18 +15,34 @@ class CDebugScripts :
|
|||
private:
|
||||
enum {
|
||||
WM_REFRESH_LIST = WM_USER + 1,
|
||||
WM_CONSOLE_PRINT = WM_USER + 2,
|
||||
WM_CONSOLE_CLEAR = WM_USER + 3
|
||||
WM_SCRIPT_STATUS = WM_USER + 2,
|
||||
WM_CONSOLE_PRINT = WM_USER + 3,
|
||||
WM_CONSOLE_CLEAR = WM_USER + 4
|
||||
};
|
||||
|
||||
CEditEval m_EvalEdit;
|
||||
CEditConsole m_ConsoleEdit;
|
||||
CScriptList m_ScriptList;
|
||||
enum {
|
||||
CONFLUSH_TIMER_ID = 0,
|
||||
CONFLUSH_TIMER_INTERVAL = 50
|
||||
};
|
||||
|
||||
CScriptsAutorunDlg m_AutorunDlg;
|
||||
|
||||
CEditConInput m_ConInputEdit;
|
||||
CEditConOutput m_ConOutputEdit;
|
||||
CListViewCtrl m_ScriptList;
|
||||
CStatusBarCtrl m_StatusBar;
|
||||
stdstr m_SelectedScriptName;
|
||||
|
||||
HFONT m_MonoFont, m_MonoBoldFont;
|
||||
|
||||
stdstr m_InstallDir;
|
||||
stdstr m_ScriptsDir;
|
||||
|
||||
stdstr m_SelectedScriptName;
|
||||
std::vector<wchar_t*> m_InputHistory;
|
||||
size_t m_InputHistoryIndex;
|
||||
|
||||
stdstr m_ConOutputBuffer;
|
||||
|
||||
HANDLE m_hQuitScriptDirWatchEvent;
|
||||
HANDLE m_hScriptDirWatchThread;
|
||||
static DWORD WINAPI ScriptDirWatchProc(void *ctx);
|
||||
|
@ -99,28 +54,32 @@ private:
|
|||
void RefreshStatus();
|
||||
void ConsoleCopy();
|
||||
|
||||
void SendInput(const char* name, const char* code);
|
||||
|
||||
public:
|
||||
enum { IDD = IDD_Debugger_Scripts };
|
||||
|
||||
CDebugScripts(CDebuggerUI * debugger);
|
||||
virtual ~CDebugScripts(void);
|
||||
|
||||
void EvaluateInSelectedInstance(const char* code);
|
||||
void ConsolePrint(const char* text);
|
||||
void ConsoleClear();
|
||||
void RefreshList();
|
||||
|
||||
LRESULT OnInitDialog(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 OnClicked(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/);
|
||||
LRESULT OnScriptListDblClicked(NMHDR* pNMHDR);
|
||||
LRESULT OnScriptListRClicked(NMHDR* pNMHDR);
|
||||
LRESULT OnScriptListCustomDraw(NMHDR* pNMHDR);
|
||||
LRESULT OnScriptListItemChanged(NMHDR* pNMHDR);
|
||||
LRESULT OnInputSpecialKey(NMHDR* pNMHDR);
|
||||
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 OnRefreshList(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
|
||||
|
||||
|
@ -128,17 +87,19 @@ public:
|
|||
COMMAND_CODE_HANDLER(BN_CLICKED, OnClicked)
|
||||
MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)
|
||||
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_RCLICK, OnScriptListRClicked)
|
||||
NOTIFY_HANDLER_EX(IDC_SCRIPT_LIST, NM_CUSTOMDRAW, OnScriptListCustomDraw)
|
||||
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_EXITSIZEMOVE(OnExitSizeMove);
|
||||
MESSAGE_HANDLER(WM_CONSOLE_PRINT, OnConsoleLog)
|
||||
MESSAGE_HANDLER(WM_CONSOLE_PRINT, OnConsolePrint)
|
||||
MESSAGE_HANDLER(WM_CONSOLE_CLEAR, OnConsoleClear)
|
||||
MESSAGE_HANDLER(WM_REFRESH_LIST, OnRefreshList)
|
||||
CHAIN_MSG_MAP(CDialogResize<CDebugScripts>)
|
||||
CHAIN_MSG_MAP_MEMBER(m_ScriptList)
|
||||
END_MSG_MAP()
|
||||
|
||||
BEGIN_DLGRESIZE_MAP(CDebugScripts)
|
||||
|
@ -151,7 +112,6 @@ public:
|
|||
DLGRESIZE_CONTROL(IDC_EVAL_LBL, 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_STOP_BTN, DLSZ_MOVE_Y)
|
||||
DLGRESIZE_CONTROL(IDC_SCRIPTDIR_BTN, DLSZ_MOVE_Y)
|
||||
DLGRESIZE_CONTROL(IDC_STATUSBAR, DLSZ_SIZE_X | DLSZ_MOVE_Y)
|
||||
END_DLGRESIZE_MAP()
|
||||
|
@ -159,8 +119,7 @@ public:
|
|||
BEGIN_TOOLTIP_MAP()
|
||||
TOOLTIP(IDC_CLEAR_BTN, "Clear console output")
|
||||
TOOLTIP(IDC_COPY_BTN, "Copy console output to the clipboard")
|
||||
TOOLTIP(IDC_RUN_BTN, "Run selected script")
|
||||
TOOLTIP(IDC_STOP_BTN, "Stop selected script")
|
||||
TOOLTIP(IDC_RUN_BTN, "Toggle selected script")
|
||||
TOOLTIP(IDC_SCRIPTDIR_BTN, "Open scripts directory in file explorer")
|
||||
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 "DebuggerUI.h"
|
||||
#include "ScriptHook.h"
|
||||
|
||||
#include "CPULog.h"
|
||||
#include "DMALog.h"
|
||||
#include "Symbols.h"
|
||||
#include <sstream>
|
||||
|
||||
CPj64Module _Module;
|
||||
|
||||
|
@ -41,6 +41,8 @@ CDebuggerUI::CDebuggerUI() :
|
|||
g_Settings->RegisterChangeCB(Debugger_SteppingOps, this, (CSettings::SettingChangedFunc)SteppingOpsChanged);
|
||||
g_Settings->RegisterChangeCB(GameRunning_CPU_Running, this, (CSettings::SettingChangedFunc)GameCpuRunningChanged);
|
||||
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)
|
||||
|
@ -49,6 +51,8 @@ CDebuggerUI::~CDebuggerUI(void)
|
|||
g_Settings->UnregisterChangeCB(GameRunning_InReset, this, (CSettings::SettingChangedFunc)GameReset);
|
||||
g_Settings->RegisterChangeCB(GameRunning_CPU_Running, this, (CSettings::SettingChangedFunc)GameCpuRunningChanged);
|
||||
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();
|
||||
delete m_MemoryView;
|
||||
delete m_CommandsView;
|
||||
|
@ -69,9 +73,13 @@ CDebuggerUI::~CDebuggerUI(void)
|
|||
void CDebuggerUI::SteppingOpsChanged(CDebuggerUI * _this)
|
||||
{
|
||||
if (g_Settings->LoadBool(Debugger_SteppingOps))
|
||||
{
|
||||
if (!g_Settings->LoadBool(Debugger_SilentBreak))
|
||||
{
|
||||
_this->OpenCommandWindow();
|
||||
}
|
||||
g_Settings->SaveBool(Debugger_SilentBreak, false);
|
||||
}
|
||||
}
|
||||
|
||||
void CDebuggerUI::GameCpuRunningChanged(CDebuggerUI * _this)
|
||||
|
@ -91,15 +99,49 @@ void CDebuggerUI::GameNameChanged(CDebuggerUI * _this)
|
|||
{
|
||||
_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)
|
||||
{
|
||||
if (!g_Settings->LoadBool(GameRunning_InReset))
|
||||
{
|
||||
JSHookEmuStateChangeEnv env;
|
||||
env.state = JS_EMU_RESET;
|
||||
_this->m_ScriptSystem->InvokeAppCallbacks(JS_HOOK_EMUSTATECHANGE, &env);
|
||||
return;
|
||||
}
|
||||
|
||||
JSHookEmuStateChangeEnv env;
|
||||
env.state = JS_EMU_RESETTING;
|
||||
_this->m_ScriptSystem->InvokeAppCallbacks(JS_HOOK_EMUSTATECHANGE, &env);
|
||||
|
||||
if (_this->m_CommandsView)
|
||||
{
|
||||
_this->m_CommandsView->Reset();
|
||||
|
@ -450,6 +492,11 @@ CSymbolTable* CDebuggerUI::SymbolTable()
|
|||
return m_SymbolTable;
|
||||
}
|
||||
|
||||
SyncEvent& CDebuggerUI::StepEvent()
|
||||
{
|
||||
return m_StepEvent;
|
||||
}
|
||||
|
||||
// CDebugger implementation
|
||||
|
||||
void CDebuggerUI::TLBChanged()
|
||||
|
@ -552,34 +599,11 @@ void CDebuggerUI::CPUStepStarted()
|
|||
}
|
||||
}
|
||||
|
||||
if (m_ScriptSystem->HaveCallbacks())
|
||||
{
|
||||
m_ScriptSystem->HookCPUExec()->InvokeByAddressInRange(pc);
|
||||
if (SkipOp()) { return; }
|
||||
JSHookCpuStepEnv hookEnv = { 0 };
|
||||
hookEnv.pc = pc;
|
||||
hookEnv.opInfo = opInfo;
|
||||
|
||||
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();
|
||||
}
|
||||
m_ScriptSystem->InvokeAppCallbacks(JS_HOOK_CPUSTEP, (void*)&hookEnv);
|
||||
|
||||
if (CDebugSettings::ExceptionBreakpoints() != 0)
|
||||
{
|
||||
|
@ -665,41 +689,78 @@ void CDebuggerUI::CPUStepEnded()
|
|||
|
||||
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,
|
||||
FW_DONTCARE, FALSE, FALSE, FALSE, DEFAULT_CHARSET,
|
||||
OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS,
|
||||
PROOF_QUALITY, FF_DONTCARE, L"Consolas"
|
||||
);
|
||||
void CDebuggerUI::PIFReadStarted(void)
|
||||
{
|
||||
m_ScriptSystem->InvokeAppCallbacks(JS_HOOK_PIFREAD);
|
||||
}
|
||||
|
||||
if (hMainWnd == nullptr)
|
||||
{
|
||||
RenderWindow* mainWindow = g_Plugins->MainWindow();
|
||||
void CDebuggerUI::RSPReceivedTask(void)
|
||||
{
|
||||
JSHookSpTaskEnv env;
|
||||
|
||||
if (mainWindow == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
DebugLoad_VAddr(0xA4000FC0, env.taskType);
|
||||
DebugLoad_VAddr(0xA4000FC4, env.taskFlags);
|
||||
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);
|
||||
SetBkColor(hdc, RGB(0, 0, 0));
|
||||
m_ScriptSystem->InvokeAppCallbacks(JS_HOOK_PIDMA, &env);
|
||||
}
|
||||
|
||||
SelectObject(hdc, monoFont);
|
||||
SetTextColor(hdc, RGB(255, 255, 255));
|
||||
SetBkColor(hdc, RGB(0, 0, 0));
|
||||
void CDebuggerUI::PIDMAWriteStarted(void)
|
||||
{
|
||||
JSHookPiDmaEnv env;
|
||||
|
||||
m_ScriptSystem->SetScreenDC(hdc);
|
||||
m_ScriptSystem->HookFrameDrawn()->InvokeAll();
|
||||
env.direction = 0;
|
||||
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)
|
||||
|
@ -709,6 +770,16 @@ void CDebuggerUI::WaitForStep(void)
|
|||
g_Settings->SaveBool(Debugger_WaitingForStep, false);
|
||||
}
|
||||
|
||||
void CDebuggerUI::StartAutorunScripts(void)
|
||||
{
|
||||
if (m_ScriptSystem == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_ScriptSystem->ExecAutorunList();
|
||||
}
|
||||
|
||||
bool CDebuggerUI::ExecutionBP(uint32_t address)
|
||||
{
|
||||
return m_Breakpoints != nullptr && m_Breakpoints->ExecutionBPExists(address, true) != CBreakpoints::BP_NOT_SET;
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include "Debugger-TLB.h"
|
||||
#include "Debugger-Commands.h"
|
||||
#include "Debugger-Scripts.h"
|
||||
#include "Debugger-ScriptsAutorun.h"
|
||||
#include "Debugger-Symbols.h"
|
||||
#include "Debugger-AddBreakpoint.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
|
||||
|
||||
#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
|
||||
{
|
||||
typedef enum {
|
||||
EVENT_READ,
|
||||
EVENT_WRITE,
|
||||
EVENT_ACCEPT,
|
||||
EVENT_CONNECT
|
||||
} IOEVENTTYPE;
|
||||
private:
|
||||
JSInstanceName m_InstanceName;
|
||||
CScriptSystem* m_System;
|
||||
duk_context* m_Ctx;
|
||||
size_t m_RefCount;
|
||||
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:
|
||||
|
||||
CScriptInstance(CDebuggerUI* debugger);
|
||||
CScriptInstance(CScriptSystem* sys, const char* name);
|
||||
~CScriptInstance();
|
||||
|
||||
void Start(char* path);
|
||||
void ForceStop();
|
||||
void Invoke(void* heapptr, uint32_t param = 0);
|
||||
void Invoke2(void* heapptr, uint32_t param = 0, uint32_t param2 = 0);
|
||||
INSTANCE_STATE GetState();
|
||||
JSInstanceName& Name();
|
||||
CScriptSystem* System();
|
||||
CDebuggerUI* Debugger();
|
||||
JSAppCallbackID CallbackId();
|
||||
|
||||
friend class PendingEval;
|
||||
const char* Eval(const char* jsCode);
|
||||
bool Run(const char* path);
|
||||
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:
|
||||
duk_context* m_Ctx;
|
||||
duk_context* DukContext();
|
||||
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 }
|
||||
};
|
||||
static uint64_t Timestamp();
|
||||
void Cleanup();
|
||||
};
|
||||
|
|
|
@ -1,202 +1,552 @@
|
|||
#include <stdafx.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sstream>
|
||||
#include "ScriptTypes.h"
|
||||
#include "ScriptSystem.h"
|
||||
#include "Debugger-Scripts.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;
|
||||
WSAStartup(MAKEWORD(2, 2), &wsaData);
|
||||
InitDirectories();
|
||||
|
||||
m_NextCallbackId = 0;
|
||||
m_NumCallbacks = 0;
|
||||
|
||||
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);
|
||||
m_hCmdEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
|
||||
m_hThread = CreateThread(nullptr, 0, ThreadProc, this, 0, nullptr);
|
||||
}
|
||||
|
||||
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();
|
||||
free(m_APIScript);
|
||||
if (!PathFileExistsA(m_ModulesDirFullPath.c_str()))
|
||||
{
|
||||
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);
|
||||
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);
|
||||
PostCommand(JS_CMD_STOP_SCRIPT, name);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
scriptInstance->ForceStop();
|
||||
DeleteStoppedInstances();
|
||||
bool bNeedSweep = false;
|
||||
|
||||
// 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;
|
||||
for (int i = lastIndex; i >= 0; i--)
|
||||
switch (uMsg)
|
||||
{
|
||||
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)
|
||||
{
|
||||
free(m_RunningInstances[i].path);
|
||||
CScriptInstance* instance = m_RunningInstances[i].scriptInstance;
|
||||
delete instance;
|
||||
m_RunningInstances.erase(m_RunningInstances.begin() + i);
|
||||
WaitForSingleObject(m_hCmdEvent, INFINITE);
|
||||
|
||||
{
|
||||
CGuard guard(m_CmdQueueCS);
|
||||
queue = m_CmdQueue;
|
||||
m_CmdQueue.clear();
|
||||
}
|
||||
|
||||
if (!ProcessCommandQueue(queue))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
INSTANCE_STATE CScriptSystem::GetInstanceState(const char* path)
|
||||
void CScriptSystem::OnStartScript(const char *name, const char *path)
|
||||
{
|
||||
CGuard guard(m_CS);
|
||||
|
||||
for (size_t i = 0; i < m_RunningInstances.size(); i++)
|
||||
if (m_Instances.count(name) != 0)
|
||||
{
|
||||
if (strcmp(m_RunningInstances[i].path, path) == 0)
|
||||
{
|
||||
INSTANCE_STATE ret = m_RunningInstances[i].scriptInstance->GetState();
|
||||
return ret;
|
||||
}
|
||||
ConsoleLog("[SCRIPTSYS]: error: START_SCRIPT aborted; '%s' is already instanced", name);
|
||||
}
|
||||
|
||||
return STATE_INVALID;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
CScriptInstance* CScriptSystem::GetInstance(const char* path)
|
||||
void CScriptSystem::OnStopScript(const char *name)
|
||||
{
|
||||
CGuard guard(m_CS);
|
||||
|
||||
for (size_t i = 0; i < m_RunningInstances.size(); i++)
|
||||
if (m_Instances.count(name) == 0)
|
||||
{
|
||||
if (strcmp(m_RunningInstances[i].path, path) == 0)
|
||||
{
|
||||
CScriptInstance *ret = m_RunningInstances[i].scriptInstance;
|
||||
return ret;
|
||||
}
|
||||
ConsoleLog("[SCRIPTSYS]: error: STOP_SCRIPT aborted; instance '%s' does not exist", name);
|
||||
return;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
RawRemoveInstance(name);
|
||||
NotifyStatus(name, JS_STATUS_STOPPED);
|
||||
}
|
||||
|
||||
bool CScriptSystem::HasCallbacksForInstance(CScriptInstance* scriptInstance)
|
||||
void CScriptSystem::OnInput(const char *name, const char *code)
|
||||
{
|
||||
for (size_t i = 0; i < m_Hooks.size(); i++)
|
||||
if(m_Instances.count(name) == 0)
|
||||
{
|
||||
if (m_Hooks[i].cbList->HasContext(scriptInstance))
|
||||
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)
|
||||
{
|
||||
return true;
|
||||
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)
|
||||
{
|
||||
NotifyStatus(inst->Name().c_str(), JS_STATUS_STOPPED);
|
||||
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;
|
||||
}
|
||||
|
||||
void CScriptSystem::ClearCallbacksForInstance(CScriptInstance* scriptInstance)
|
||||
JSAppCallbackID CScriptSystem::RawAddAppCallback(JSAppHookID hookId, JSAppCallback& callback)
|
||||
{
|
||||
for (size_t i = 0; i < m_Hooks.size(); i++)
|
||||
if(hookId >= JS_NUM_APP_HOOKS)
|
||||
{
|
||||
m_Hooks[i].cbList->RemoveByInstance(scriptInstance);
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
void CScriptSystem::RemoveCallbackById(int callbackId)
|
||||
std::set<std::string>& CScriptSystem::AutorunList()
|
||||
{
|
||||
for (size_t i = 0; i < m_Hooks.size(); i++)
|
||||
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_Hooks[i].cbList->RemoveById(callbackId);
|
||||
m_AutorunList.insert(scriptName);
|
||||
}
|
||||
}
|
||||
|
||||
void CScriptSystem::RegisterHook(const char* hookId, CScriptHook* cbList)
|
||||
void CScriptSystem::SaveAutorunList()
|
||||
{
|
||||
HOOKENTRY hook = { hookId, cbList };
|
||||
m_Hooks.push_back(hook);
|
||||
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());
|
||||
}
|
||||
|
||||
void CScriptSystem::UnregisterHooks()
|
||||
CDebuggerUI* CScriptSystem::Debugger()
|
||||
{
|
||||
m_Hooks.clear();
|
||||
return m_Debugger;
|
||||
}
|
||||
|
||||
CScriptHook* CScriptSystem::GetHook(const char* hookId)
|
||||
stdstr CScriptSystem::FixStringReturns(const char* str)
|
||||
{
|
||||
size_t size = m_Hooks.size();
|
||||
for (size_t i = 0; i < size; i++)
|
||||
stdstr fstr = str;
|
||||
size_t pos = 0;
|
||||
while ((pos = fstr.find("\n", pos)) != stdstr::npos)
|
||||
{
|
||||
if (strcmp(m_Hooks[i].hookId, hookId) == 0)
|
||||
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++)
|
||||
{
|
||||
return m_Hooks[i].cbList;
|
||||
if (!ProcessCommand(*it))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int CScriptSystem::GetNextCallbackId()
|
||||
bool CScriptSystem::ProcessCommand(JSSysCommand& cmd)
|
||||
{
|
||||
return m_NextCallbackId++;
|
||||
}
|
||||
CGuard guard(m_InstancesCS);
|
||||
|
||||
void CScriptSystem::CallbackAdded()
|
||||
{
|
||||
m_NumCallbacks++;
|
||||
}
|
||||
|
||||
void CScriptSystem::CallbackRemoved()
|
||||
{
|
||||
if (m_NumCallbacks > 0)
|
||||
switch (cmd.id)
|
||||
{
|
||||
m_NumCallbacks--;
|
||||
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:
|
||||
{
|
||||
JSSysCMethodCall* methodCall = (JSSysCMethodCall*)cmd.paramC;
|
||||
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++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,129 +1,103 @@
|
|||
#include <windows.h>
|
||||
#include <fstream>
|
||||
#include <map>
|
||||
|
||||
#include "ScriptTypes.h"
|
||||
#include "debugger.h"
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdafx.h>
|
||||
#include <3rdParty/duktape/duktape.h>
|
||||
|
||||
#include "ScriptInstance.h"
|
||||
|
||||
class CScriptHook;
|
||||
#define SCRIPTSYS_SCRIPTS_DIR "Scripts\\"
|
||||
#define SCRIPTSYS_MODULES_DIR "Scripts\\modules\\"
|
||||
|
||||
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:
|
||||
CScriptSystem(CDebuggerUI* debugger);
|
||||
~CScriptSystem();
|
||||
// Run a script in its own context/thread
|
||||
void RunScript(const char * path);
|
||||
|
||||
// Kill a script context/thread by its path
|
||||
void StopScript(const char * path);
|
||||
CDebuggerUI* Debugger();
|
||||
|
||||
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:
|
||||
typedef struct {
|
||||
const char* hookId;
|
||||
CScriptHook* cbList;
|
||||
} HOOKENTRY;
|
||||
void InitDirectories();
|
||||
|
||||
typedef struct {
|
||||
char* path;
|
||||
CScriptInstance* scriptInstance;
|
||||
} INSTANCE_ENTRY;
|
||||
void PostCommand(JSSysCommandID id, stdstr paramA = "", stdstr paramB = "", void* paramC = nullptr);
|
||||
|
||||
CDebuggerUI* m_Debugger;
|
||||
int m_NumCallbacks;
|
||||
char* m_APIScript;
|
||||
static DWORD WINAPI ThreadProc(void* _this);
|
||||
void ThreadProc();
|
||||
|
||||
vector<HOOKENTRY> m_Hooks;
|
||||
vector<INSTANCE_ENTRY> m_RunningInstances;
|
||||
bool ProcessCommand(JSSysCommand& cmd);
|
||||
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;
|
||||
CScriptHook* m_HookCPURead;
|
||||
CScriptHook* m_HookCPUWrite;
|
||||
CScriptHook* m_HookCPUExecOpcode;
|
||||
CScriptHook* m_HookCPUGPRValue;
|
||||
CScriptHook* m_HookFrameDrawn;
|
||||
bool RawRemoveInstance(const char* key);
|
||||
|
||||
CriticalSection m_CS;
|
||||
|
||||
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;
|
||||
}
|
||||
static stdstr FixStringReturns(const char* str);
|
||||
};
|
||||
|
|
|
@ -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 OpenExcBreakpointsWindow(void);
|
||||
|
||||
void StartAutorunScripts();
|
||||
|
||||
bool ExecutionBP(uint32_t address);
|
||||
bool ReadBP8(uint32_t address);
|
||||
bool ReadBP16(uint32_t address);
|
||||
|
@ -76,11 +78,14 @@ public:
|
|||
CDMALog* DMALog();
|
||||
CCPULog* CPULog();
|
||||
CSymbolTable* SymbolTable();
|
||||
SyncEvent& StepEvent();
|
||||
|
||||
static void GameReset(CDebuggerUI * _this);
|
||||
static void GameCpuRunningChanged(CDebuggerUI * _this);
|
||||
static void GameNameChanged(CDebuggerUI * _this);
|
||||
static void GamePausedChanged(CDebuggerUI * _this);
|
||||
static void SteppingOpsChanged(CDebuggerUI * _this);
|
||||
static void WaitingForStepChanged(CDebuggerUI * _this);
|
||||
|
||||
protected:
|
||||
void TLBChanged(void);
|
||||
|
@ -88,6 +93,12 @@ protected:
|
|||
void CPUStep(void);
|
||||
void CPUStepEnded(void);
|
||||
void FrameDrawn(void);
|
||||
void PIFReadStarted(void);
|
||||
void RSPReceivedTask(void);
|
||||
void PIDMAReadStarted(void);
|
||||
void PIDMAWriteStarted(void);
|
||||
void EmulationStarted(void);
|
||||
void EmulationStopped(void);
|
||||
|
||||
private:
|
||||
CDebuggerUI(const CDebuggerUI&);
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include <Project64-core/Settings/SettingType/SettingsType-Application.h>
|
||||
#include <Project64-core/N64System/Enhancement/Enhancements.h>
|
||||
#include <Project64-core/N64System/N64Disk.h>
|
||||
#include <Project64/UserInterface/Debugger/DebuggerUI.h>
|
||||
#include "DiscordRPC.h"
|
||||
|
||||
void EnterLogOptions(HWND hwndOwner);
|
||||
|
@ -908,6 +909,87 @@ LRESULT CALLBACK CMainGui::MainGui_Proc(HWND hWnd, DWORD uMsg, DWORD wParam, DWO
|
|||
_this->SetStatusText(1, L"");
|
||||
}
|
||||
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:
|
||||
{
|
||||
CMainGui * _this = (CMainGui *)GetProp(hWnd, L"Class");
|
||||
|
|
|
@ -21,6 +21,16 @@ enum
|
|||
WM_RESET_PLUGIN = WM_USER + 18,
|
||||
WM_GAME_CLOSED = WM_USER + 19,
|
||||
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 :
|
||||
|
|
|
@ -835,18 +835,17 @@ STYLE DS_SETFONT | DS_FIXEDSYS | DS_CENTER | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | W
|
|||
CAPTION "Scripts"
|
||||
FONT 8, "MS Shell Dlg", 400, 0, 0x1
|
||||
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 "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 "Run",IDC_RUN_BTN,43,165,37,13,WS_DISABLED
|
||||
PUSHBUTTON "Stop",IDC_STOP_BTN,83,165,37,13,WS_DISABLED
|
||||
PUSHBUTTON "Run",IDC_RUN_BTN,83,165,37,13
|
||||
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
|
||||
|
||||
IDD_Debugger_RegPI DIALOGEX 0, 0, 190, 210
|
||||
|
@ -1425,6 +1424,20 @@ BEGIN
|
|||
EDITTEXT IDC_ADD_EDIT,7,7,109,12,ES_AUTOHSCROLL
|
||||
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
|
||||
BEGIN
|
||||
RIGHTMARGIN, 379
|
||||
RIGHTMARGIN, 420
|
||||
BOTTOMMARGIN, 214
|
||||
END
|
||||
|
||||
|
@ -1899,6 +1912,14 @@ BEGIN
|
|||
TOPMARGIN, 7
|
||||
BOTTOMMARGIN, 179
|
||||
END
|
||||
|
||||
IDD_Debugger_ScriptsAutorun, DIALOG
|
||||
BEGIN
|
||||
LEFTMARGIN, 7
|
||||
RIGHTMARGIN, 316
|
||||
TOPMARGIN, 6
|
||||
BOTTOMMARGIN, 209
|
||||
END
|
||||
END
|
||||
#endif // APSTUDIO_INVOKED
|
||||
|
||||
|
@ -2010,6 +2031,8 @@ BEGIN
|
|||
MENUITEM "Run", ID_POPUP_RUN
|
||||
MENUITEM "Stop", ID_POPUP_STOP
|
||||
MENUITEM "Edit", ID_POPUP_SCRIPT_EDIT
|
||||
MENUITEM SEPARATOR
|
||||
MENUITEM "Autorun...", ID_POPUP_AUTORUN
|
||||
END
|
||||
END
|
||||
|
||||
|
@ -2321,14 +2344,6 @@ BEGIN
|
|||
0
|
||||
END
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// TEXT
|
||||
//
|
||||
|
||||
IDR_JSAPI_TEXT TEXT "API.js"
|
||||
|
||||
#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_Settings_GameDiskDrive 217
|
||||
#define IDD_Enhancement_Plugins 218
|
||||
#define IDD_Debugger_ScriptsAutorun 225
|
||||
#define IDC_MENU_ITEM_TEXT 1000
|
||||
#define IDC_CLOSE_BUTTON 1001
|
||||
#define IDC_LIST2 1003
|
||||
|
@ -871,6 +872,9 @@
|
|||
#define IDC_RADIO_GLIDEN64 1728
|
||||
#define IDC_RADIO_JABO 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_ADDSYMBOL 40013
|
||||
#define ID_POPUPMENU_VIEWDISASM 40017
|
||||
|
@ -926,6 +930,7 @@
|
|||
#define ID_POPUPMENU_COPYGAMESHARKCODE 40118
|
||||
#define ID_POPUPMENU_COPYDATAWITHROWADDRESSES 40119
|
||||
#define ID_POPUPMENU_COPYDATAWITHGROUPADDRESSES 40120
|
||||
#define ID_POPUP_AUTORUN 40121
|
||||
#define ID_POPUPMENU_ROMDIRECTORY 40137
|
||||
#define ID_POPUPMENU_REFRESHROMLIST 40138
|
||||
#define ID_POPUPMENU_PLAYGAME 40152
|
||||
|
@ -942,9 +947,9 @@
|
|||
//
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
#ifndef APSTUDIO_READONLY_SYMBOLS
|
||||
#define _APS_NEXT_RESOURCE_VALUE 225
|
||||
#define _APS_NEXT_COMMAND_VALUE 40121
|
||||
#define _APS_NEXT_CONTROL_VALUE 1730
|
||||
#define _APS_NEXT_RESOURCE_VALUE 227
|
||||
#define _APS_NEXT_COMMAND_VALUE 40122
|
||||
#define _APS_NEXT_CONTROL_VALUE 1738
|
||||
#define _APS_NEXT_SYMED_VALUE 102
|
||||
#endif
|
||||
#endif
|
||||
|
|
|
@ -25,6 +25,11 @@ int WINAPI WinMain(HINSTANCE /*hInstance*/, HINSTANCE /*hPrevInstance*/, LPSTR /
|
|||
Notify().SetMainWindow(&MainWindow);
|
||||
bool isROMLoaded = false;
|
||||
|
||||
if (CDebuggerUI::HaveDebugger())
|
||||
{
|
||||
Debugger.StartAutorunScripts();
|
||||
}
|
||||
|
||||
if (g_Settings->LoadStringVal(Cmd_RomFile).length() > 0 && g_Settings->LoadStringVal(Cmd_ComboDiskFile).length() > 0)
|
||||
{
|
||||
// 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