[Debugger] Rewrite script API (#2110)

* [Debugger] Rewrite script API

* Update js api paths in installer script
This commit is contained in:
shyguyhex 2021-09-03 00:11:04 -05:00 committed by GitHub
parent f9597fc3c2
commit ac94f2505e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
74 changed files with 60728 additions and 40142 deletions

2071
JS-API-Documentation.html Normal file

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,3 @@
const docpath = pj64.installDirectory + "JS-API-Documentation.html";
console.log("Opening " + docpath + " ...");
exec("start " + docpath);

View File

@ -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);

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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>

View File

@ -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>

View File

@ -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

View File

@ -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;
};

View File

@ -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;

View File

@ -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)
{

View File

@ -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:

View File

@ -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]));

View File

@ -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,

View File

@ -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" />

View File

@ -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

View File

@ -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 = "";
}
}

View File

@ -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()
};

View File

@ -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);
}
}

View File

@ -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();
};

View File

@ -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;

View File

@ -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"

View File

@ -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();
}

View File

@ -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();
};

View File

@ -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;
}

View File

@ -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);
};

View File

@ -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();
}

View File

@ -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);
};

View File

@ -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";
}

View File

@ -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();
};

View File

@ -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

View File

@ -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);
};

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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);
}
}
}

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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();
}

View File

@ -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

View File

@ -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();
};

View File

@ -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++;
}
}
}

View File

@ -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);
};

View File

@ -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;
}
};

View File

@ -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;
}

View File

@ -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();
};

View File

@ -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&);

View File

@ -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");

View File

@ -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 :

View File

@ -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
/////////////////////////////////////////////////////////////////////////////

View File

@ -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()
};

View File

@ -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

View File

@ -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)

View File

@ -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] &lt; 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 &lt; 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>