[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Gnash-commit] gnash/server action.cpp
From: |
Rob Savoye |
Subject: |
[Gnash-commit] gnash/server action.cpp |
Date: |
Mon, 10 Apr 2006 17:48:59 +0000 |
CVSROOT: /sources/gnash
Module name: gnash
Branch:
Changes by: Rob Savoye <address@hidden> 06/04/10 17:48:59
Modified files:
server : action.cpp
Log message:
Implement getUrl() so it clicking buttons can load web pages.
CVSWeb URLs:
http://cvs.savannah.gnu.org/viewcvs/gnash/gnash/server/action.cpp.diff?tr1=1.58&tr2=1.59&r1=text&r2=text
Patches:
Index: gnash/server/action.cpp
diff -u gnash/server/action.cpp:1.58 gnash/server/action.cpp:1.59
--- gnash/server/action.cpp:1.58 Thu Mar 30 19:12:53 2006
+++ gnash/server/action.cpp Mon Apr 10 17:48:59 2006
@@ -9,6 +9,7 @@
#include <typeinfo>
#include <pthread.h>
+#include <string>
#include "action.h"
#include "Object.h"
@@ -113,444 +114,446 @@
//
// String.fromCharCode()
+using namespace std;
+
namespace gnash {
- //
- // action stuff
- //
-
- void action_init();
-
- // Statics.
- bool s_inited = false;
- smart_ptr<as_object> s_global;
- fscommand_callback s_fscommand_handler = NULL;
+//
+// action stuff
+//
+
+void action_init();
+
+// Statics.
+bool s_inited = false;
+smart_ptr<as_object> s_global;
+fscommand_callback s_fscommand_handler = NULL;
#define EXTERN_MOVIE
#ifdef EXTERN_MOVIE
- void attach_extern_movie(const char* url, const movie* target, const
movie* root_movie)
+void attach_extern_movie(const char* url, const movie* target, const movie*
root_movie)
+{
+ tu_string infile = get_workdir();
+ infile += url;
+
+ movie_definition* md = create_library_movie(infile.c_str());
+ if (md == NULL)
{
- tu_string infile = get_workdir();
- infile += url;
+ log_error("can't create movie_definition for %s\n", infile.c_str());
+ return;
+ }
+
+ gnash::movie_interface* extern_movie;
- movie_definition* md = create_library_movie(infile.c_str());
- if (md == NULL)
+ if (target == root_movie)
+ {
+ extern_movie = create_library_movie_inst(md);
+ if (extern_movie == NULL)
{
- log_error("can't create movie_definition for %s\n",
infile.c_str());
- return;
+ log_error("can't create extern root movie_interface for
%s\n", infile.c_str());
+ return;
}
+ set_current_root(extern_movie);
+ movie* m = extern_movie->get_root_movie();
- gnash::movie_interface* extern_movie;
-
- if (target == root_movie)
+ m->on_event(event_id::LOAD);
+ }
+ else
+ {
+ extern_movie = md->create_instance();
+ if (extern_movie == NULL)
{
- extern_movie = create_library_movie_inst(md);
- if (extern_movie == NULL)
- {
- log_error("can't create extern root
movie_interface for %s\n", infile.c_str());
- return;
- }
- set_current_root(extern_movie);
- movie* m = extern_movie->get_root_movie();
-
- m->on_event(event_id::LOAD);
+ log_error("can't create extern movie_interface for %s\n",
infile.c_str());
+ return;
}
- else
- {
- extern_movie = md->create_instance();
- if (extern_movie == NULL)
- {
- log_error("can't create extern movie_interface
for %s\n", infile.c_str());
- return;
- }
- save_extern_movie(extern_movie);
+ save_extern_movie(extern_movie);
- const character* tar = (const character*)target;
- const char* name = tar->get_name().c_str();
- Uint16 depth = tar->get_depth();
- bool use_cxform = false;
- cxform color_transform = tar->get_cxform();
- bool use_matrix = false;
- matrix mat = tar->get_matrix();
- float ratio = tar->get_ratio();
- Uint16 clip_depth = tar->get_clip_depth();
+ const character* tar = (const character*)target;
+ const char* name = tar->get_name().c_str();
+ Uint16 depth = tar->get_depth();
+ bool use_cxform = false;
+ cxform color_transform = tar->get_cxform();
+ bool use_matrix = false;
+ matrix mat = tar->get_matrix();
+ float ratio = tar->get_ratio();
+ Uint16 clip_depth = tar->get_clip_depth();
- movie* parent = tar->get_parent();
- movie* new_movie = extern_movie->get_root_movie();
+ movie* parent = tar->get_parent();
+ movie* new_movie = extern_movie->get_root_movie();
- assert(parent != NULL);
+ assert(parent != NULL);
- ((character*)new_movie)->set_parent(parent);
+ ((character*)new_movie)->set_parent(parent);
- parent->replace_display_object(
- (character*) new_movie,
- name,
- depth,
- use_cxform,
- color_transform,
- use_matrix,
- mat,
- ratio,
- clip_depth);
- }
+ parent->replace_display_object(
+ (character*) new_movie,
+ name,
+ depth,
+ use_cxform,
+ color_transform,
+ use_matrix,
+ mat,
+ ratio,
+ clip_depth);
}
+}
#endif // EXTERN_MOVIE
- void register_fscommand_callback(fscommand_callback handler)
- // External interface.
- {
- s_fscommand_handler = handler;
- }
+void register_fscommand_callback(fscommand_callback handler)
+ // External interface.
+{
+ s_fscommand_handler = handler;
+}
- static bool string_to_number(double* result, const char* str)
- // Utility. Try to convert str to a number. If successful,
- // put the result in *result, and return true. If not
- // successful, put 0 in *result, and return false.
+static bool string_to_number(double* result, const char* str)
+ // Utility. Try to convert str to a number. If successful,
+ // put the result in *result, and return true. If not
+ // successful, put 0 in *result, and return false.
+{
+ char* tail = 0;
+ *result = strtod(str, &tail);
+ if (tail == str || *tail != 0)
{
- char* tail = 0;
- *result = strtod(str, &tail);
- if (tail == str || *tail != 0)
- {
- // Failed conversion to Number.
- return false;
- }
- return true;
+ // Failed conversion to Number.
+ return false;
}
+ return true;
+}
- //
- // Function/method dispatch.
- //
-
+//
+// Function/method dispatch.
+//
- as_value call_method(
- const as_value& method,
- as_environment* env,
- as_object* this_ptr, // this is ourself
- int nargs,
- int first_arg_bottom_index)
- // first_arg_bottom_index is the stack index, from the bottom, of the
first argument.
- // Subsequent arguments are at *lower* indices. E.g. if
first_arg_bottom_index = 7,
- // then arg1 is at env->bottom(7), arg2 is at env->bottom(6), etc.
- {
- as_value val;
- as_c_function_ptr func = method.to_c_function();
- if (func)
- {
- // It's a C function. Call it.
- (*func)(fn_call(&val, this_ptr, env, nargs,
first_arg_bottom_index));
- }
- else if (function_as_object* as_func = method.to_as_function())
- {
- // It's an ActionScript function. Call it.
- (*as_func)(fn_call(&val, this_ptr, env, nargs,
first_arg_bottom_index));
- }
- else
- {
- log_error("error in call_method(): method is not a
function\n");
- }
+as_value call_method(
+ const as_value& method,
+ as_environment* env,
+ as_object* this_ptr, // this is ourself
+ int nargs,
+ int first_arg_bottom_index)
+ // first_arg_bottom_index is the stack index, from the bottom, of the
first argument.
+ // Subsequent arguments are at *lower* indices. E.g. if
first_arg_bottom_index = 7,
+ // then arg1 is at env->bottom(7), arg2 is at env->bottom(6), etc.
+{
+ as_value val;
- return val;
+ as_c_function_ptr func = method.to_c_function();
+ if (func)
+ {
+ // It's a C function. Call it.
+ (*func)(fn_call(&val, this_ptr, env, nargs,
first_arg_bottom_index));
}
-
-
- as_value call_method0(
- const as_value& method,
- as_environment* env,
- as_object* this_ptr)
+ else if (function_as_object* as_func = method.to_as_function())
{
- return call_method(method, env, this_ptr, 0,
env->get_top_index() + 1);
+ // It's an ActionScript function. Call it.
+ (*as_func)(fn_call(&val, this_ptr, env, nargs,
first_arg_bottom_index));
}
-
- const char* call_method_parsed(
- as_environment* env,
- as_object* this_ptr,
- const char* method_name,
- const char* method_arg_fmt,
- va_list args)
- // Printf-like vararg interface for calling ActionScript.
- // Handy for external binding.
+ else
{
- log_msg("FIXME(%d): %s\n", __LINE__, __FUNCTION__);
+ log_error("error in call_method(): method is not a function\n");
+ }
+
+ return val;
+}
+
+
+as_value call_method0(
+ const as_value& method,
+ as_environment* env,
+ as_object* this_ptr)
+{
+ return call_method(method, env, this_ptr, 0, env->get_top_index() + 1);
+}
+
+const char* call_method_parsed(
+ as_environment* env,
+ as_object* this_ptr,
+ const char* method_name,
+ const char* method_arg_fmt,
+ va_list args)
+ // Printf-like vararg interface for calling ActionScript.
+ // Handy for external binding.
+{
+ log_msg("FIXME(%d): %s\n", __LINE__, __FUNCTION__);
#if 0
- static const int BUFSIZE = 1000;
- char buffer[BUFSIZE];
- std::vector<const char*> tokens;
-
- // Brutal crap parsing. Basically null out any
- // delimiter characters, so that the method name and
- // args sit in the buffer as null-terminated C
- // strings. Leave an intial ' character as the first
- // char in a string argument.
- // Don't verify parens or matching quotes or anything.
- {
- strncpy(buffer, method_call, BUFSIZE);
- buffer[BUFSIZE - 1] = 0;
- char* p = buffer;
-
- char in_quote = 0;
- bool in_arg = false;
- for (;; p++)
- {
- char c = *p;
- if (c == 0)
- {
- // End of string.
- break;
- }
- else if (c == in_quote)
- {
- // End of quotation.
- assert(in_arg);
+ static const int BUFSIZE = 1000;
+ char buffer[BUFSIZE];
+ std::vector<const char*> tokens;
+
+ // Brutal crap parsing. Basically null out any
+ // delimiter characters, so that the method name and
+ // args sit in the buffer as null-terminated C
+ // strings. Leave an intial ' character as the first
+ // char in a string argument.
+ // Don't verify parens or matching quotes or anything.
+ {
+ strncpy(buffer, method_call, BUFSIZE);
+ buffer[BUFSIZE - 1] = 0;
+ char* p = buffer;
+
+ char in_quote = 0;
+ bool in_arg = false;
+ for (;; p++)
+ {
+ char c = *p;
+ if (c == 0)
+ {
+ // End of string.
+ break;
+ }
+ else if (c == in_quote)
+ {
+ // End of quotation.
+ assert(in_arg);
+ *p = 0;
+ in_quote = 0;
+ in_arg = false;
+ }
+ else if (in_arg)
+ {
+ if (in_quote == 0)
+ {
+ if (c == ')' || c == '(' || c == ',' || c == '
')
+ {
+ // End of arg.
*p = 0;
- in_quote = 0;
in_arg = false;
- }
- else if (in_arg)
- {
- if (in_quote == 0)
- {
- if (c == ')' || c == '(' || c
== ',' || c == ' ')
- {
- // End of arg.
- *p = 0;
- in_arg = false;
- }
- }
- }
- else
- {
- // Not in arg. Watch for start of arg.
- assert(in_quote == 0);
- if (c == '\'' || c == '\"')
- {
- // Start of quote.
- in_quote = c;
- in_arg = true;
- *p = '\''; // ' at the
start of the arg, so later we know this is a string.
- tokens.push_back(p);
- }
- else if (c == ' ' || c == ',')
- {
- // Non-arg junk, null it out.
- *p = 0;
- }
- else
- {
- // Must be the start of a
numeric arg.
- in_arg = true;
- tokens.push_back(p);
- }
- }
- }
- }
+ }
+ }
+ }
+ else
+ {
+ // Not in arg. Watch for start of arg.
+ assert(in_quote == 0);
+ if (c == '\'' || c == '\"')
+ {
+ // Start of quote.
+ in_quote = c;
+ in_arg = true;
+ *p = '\''; // ' at the start of the arg,
so later we know this is a string.
+ tokens.push_back(p);
+ }
+ else if (c == ' ' || c == ',')
+ {
+ // Non-arg junk, null it out.
+ *p = 0;
+ }
+ else
+ {
+ // Must be the start of a numeric arg.
+ in_arg = true;
+ tokens.push_back(p);
+ }
+ }
+ }
+ }
#endif // 0
- // Parse va_list args
- int starting_index = env->get_top_index();
- const char* p = method_arg_fmt;
- for (;; p++)
+ // Parse va_list args
+ int starting_index = env->get_top_index();
+ const char* p = method_arg_fmt;
+ for (;; p++)
+ {
+ char c = *p;
+ if (c == 0)
+ {
+ // End of args.
+ break;
+ }
+ else if (c == '%')
{
- char c = *p;
- if (c == 0)
+ p++;
+ c = *p;
+ // Here's an arg.
+ if (c == 'd')
{
- // End of args.
- break;
+ // Integer.
+ env->push(va_arg(args, int));
}
- else if (c == '%')
+ else if (c == 'f')
{
- p++;
- c = *p;
- // Here's an arg.
- if (c == 'd')
- {
- // Integer.
- env->push(va_arg(args, int));
- }
- else if (c == 'f')
- {
- // Double
- env->push(va_arg(args, double));
- }
- else if (c == 's')
- {
- // String
- env->push(va_arg(args, const char *));
- }
- else if (c == 'l')
- {
- p++;
- c = *p;
- if (c == 's')
- {
- // Wide string.
- env->push(va_arg(args, const
wchar_t *));
- }
- else
- {
-
log_error("call_method_parsed('%s','%s') -- invalid fmt '%%l%c'\n",
- method_name,
- method_arg_fmt,
- c);
- }
- }
- else
- {
- // Invalid fmt, warn.
-
log_error("call_method_parsed('%s','%s') -- invalid fmt '%%%c'\n",
- method_name,
- method_arg_fmt,
- c);
- }
+ // Double
+ env->push(va_arg(args, double));
}
- else
+ else if (c == 's')
{
- // Ignore whitespace and commas.
- if (c == ' ' || c == '\t' || c == ',')
+ // String
+ env->push(va_arg(args, const char *));
+ }
+ else if (c == 'l')
+ {
+ p++;
+ c = *p;
+ if (c == 's')
{
- // OK
+ // Wide string.
+ env->push(va_arg(args, const wchar_t *));
}
- else
+ else
{
- // Invalid arg; warn.
-
log_error("call_method_parsed('%s','%s') -- invalid char '%c'\n",
- method_name,
- method_arg_fmt,
- c);
+ log_error("call_method_parsed('%s','%s') --
invalid fmt '%%l%c'\n",
+ method_name,
+ method_arg_fmt,
+ c);
}
}
+ else
+ {
+ // Invalid fmt, warn.
+ log_error("call_method_parsed('%s','%s') -- invalid
fmt '%%%c'\n",
+ method_name,
+ method_arg_fmt,
+ c);
+ }
}
+ else
+ {
+ // Ignore whitespace and commas.
+ if (c == ' ' || c == '\t' || c == ',')
+ {
+ // OK
+ }
+ else
+ {
+ // Invalid arg; warn.
+ log_error("call_method_parsed('%s','%s') -- invalid
char '%c'\n",
+ method_name,
+ method_arg_fmt,
+ c);
+ }
+ }
+ }
- std::vector<with_stack_entry> dummy_with_stack;
- as_value method = env->get_variable(method_name,
dummy_with_stack);
+ std::vector<with_stack_entry> dummy_with_stack;
+ as_value method = env->get_variable(method_name, dummy_with_stack);
- // check method
+ // check method
- // Reverse the order of pushed args
- int nargs = env->get_top_index() - starting_index;
- for (int i = 0; i < (nargs >> 1); i++)
- {
- int i0 = starting_index + 1 + i;
- int i1 = starting_index + nargs - i;
- assert(i0 < i1);
+ // Reverse the order of pushed args
+ int nargs = env->get_top_index() - starting_index;
+ for (int i = 0; i < (nargs >> 1); i++)
+ {
+ int i0 = starting_index + 1 + i;
+ int i1 = starting_index + nargs - i;
+ assert(i0 < i1);
- swap(&(env->bottom(i0)), &(env->bottom(i1)));
- }
+ swap(&(env->bottom(i0)), &(env->bottom(i1)));
+ }
- // Do the call.
- as_value result = call_method(method, env, this_ptr,
nargs, env->get_top_index());
- env->drop(nargs);
+ // Do the call.
+ as_value result = call_method(method, env, this_ptr, nargs,
env->get_top_index());
+ env->drop(nargs);
- // Return pointer to static string for return value.
- static tu_string s_retval;
- s_retval = result.to_tu_string();
- return s_retval.c_str();
- }
+ // Return pointer to static string for return value.
+ static tu_string s_retval;
+ s_retval = result.to_tu_string();
+ return s_retval.c_str();
+}
- //
- // sound object
- //
+//
+// sound object
+//
- struct sound_as_object : public as_object
- {
- tu_string sound;
- int sound_id;
- };
+struct sound_as_object : public as_object
+{
+ tu_string sound;
+ int sound_id;
+};
- void movie_load()
- {
- IF_VERBOSE_ACTION(log_msg("-- start movie \n"));
- }
+void movie_load()
+{
+ IF_VERBOSE_ACTION(log_msg("-- start movie \n"));
+}
- void sound_start(const fn_call& fn)
+void sound_start(const fn_call& fn)
+{
+ IF_VERBOSE_ACTION(log_msg("-- start sound \n"));
+ sound_handler* s = get_sound_handler();
+ if (s != NULL)
{
- IF_VERBOSE_ACTION(log_msg("-- start sound \n"));
- sound_handler* s = get_sound_handler();
- if (s != NULL)
- {
- sound_as_object* so = (sound_as_object*)
(as_object*) fn.this_ptr;
- assert(so);
- s->play_sound(so->sound_id, 0);
- }
+ sound_as_object* so = (sound_as_object*) (as_object*)
fn.this_ptr;
+ assert(so);
+ s->play_sound(so->sound_id, 0);
}
+}
- void sound_stop(const fn_call& fn)
+void sound_stop(const fn_call& fn)
+{
+ IF_VERBOSE_ACTION(log_msg("-- stop sound \n"));
+ sound_handler* s = get_sound_handler();
+ if (s != NULL)
{
- IF_VERBOSE_ACTION(log_msg("-- stop sound \n"));
- sound_handler* s = get_sound_handler();
- if (s != NULL)
- {
- sound_as_object* so = (sound_as_object*)
(as_object*) fn.this_ptr;
- assert(so);
- s->stop_sound(so->sound_id);
- }
+ sound_as_object* so = (sound_as_object*) (as_object*)
fn.this_ptr;
+ assert(so);
+ s->stop_sound(so->sound_id);
}
+}
- void sound_attach(const fn_call& fn)
+void sound_attach(const fn_call& fn)
+{
+ IF_VERBOSE_ACTION(log_msg("-- attach sound \n"));
+ if (fn.nargs < 1)
{
- IF_VERBOSE_ACTION(log_msg("-- attach sound \n"));
- if (fn.nargs < 1)
- {
- log_error("attach sound needs one argument\n");
- return;
- }
-
- sound_as_object* so = (sound_as_object*) (as_object*)
fn.this_ptr;
- assert(so);
+ log_error("attach sound needs one argument\n");
+ return;
+ }
- so->sound = fn.arg(0).to_tu_string();
+ sound_as_object* so = (sound_as_object*) (as_object*) fn.this_ptr;
+ assert(so);
- // check the import.
- movie_definition* def =
fn.env->get_target()->get_root_movie()->get_movie_definition();
- assert(def);
- smart_ptr<resource> res = def->get_exported_resource(so->sound);
- if (res == NULL)
- {
- log_error("import error: resource '%s' is not
exported\n", so->sound.c_str());
- return;
- }
+ so->sound = fn.arg(0).to_tu_string();
- int si = 0;
- sound_sample_impl* ss = (sound_sample_impl*)
res->cast_to_sound_sample();
+ // check the import.
+ movie_definition* def =
fn.env->get_target()->get_root_movie()->get_movie_definition();
+ assert(def);
+ smart_ptr<resource> res = def->get_exported_resource(so->sound);
+ if (res == NULL)
+ {
+ log_error("import error: resource '%s' is not exported\n",
so->sound.c_str());
+ return;
+ }
- if (ss != NULL)
- {
- si = ss->m_sound_handler_id;
- }
- else
- {
- log_error("sound sample is NULL\n");
- return;
- }
+ int si = 0;
+ sound_sample_impl* ss = (sound_sample_impl*) res->cast_to_sound_sample();
- // sanity check
- assert(si >= 0 && si < 1000);
- so->sound_id = si;
+ if (ss != NULL)
+ {
+ si = ss->m_sound_handler_id;
+ }
+ else
+ {
+ log_error("sound sample is NULL\n");
+ return;
}
- //
- // Built-in objects
- //
+ // sanity check
+ assert(si >= 0 && si < 1000);
+ so->sound_id = si;
+}
+
+//
+// Built-in objects
+//
- //
- // math object
- //
+//
+// math object
+//
#if 0
- // One-argument simple functions.
- #define MATH_WRAP_FUNC1(funcname)
\
+// One-argument simple functions.
+#define MATH_WRAP_FUNC1(funcname)
\
void math_##funcname(as_value* result, as_object* this_ptr,
\
as_environment* env, int nargs, int
first_arg_bottom_index) \
{
\
@@ -558,8 +561,8 @@
result->set_double(funcname(arg));
\
}
#else
- // One-argument simple functions.
- #define MATH_WRAP_FUNC1(funcname)
\
+// One-argument simple functions.
+#define MATH_WRAP_FUNC1(funcname)
\
void math_##funcname(const fn_call& fn)
\
{
\
double arg = fn.arg(0).to_number();
\
@@ -567,22 +570,22 @@
}
#endif
- MATH_WRAP_FUNC1(fabs);
- MATH_WRAP_FUNC1(acos);
- MATH_WRAP_FUNC1(asin);
- MATH_WRAP_FUNC1(atan);
- MATH_WRAP_FUNC1(ceil);
- MATH_WRAP_FUNC1(cos);
- MATH_WRAP_FUNC1(exp);
- MATH_WRAP_FUNC1(floor);
- MATH_WRAP_FUNC1(log);
- MATH_WRAP_FUNC1(sin);
- MATH_WRAP_FUNC1(sqrt);
- MATH_WRAP_FUNC1(tan);
+MATH_WRAP_FUNC1(fabs);
+MATH_WRAP_FUNC1(acos);
+MATH_WRAP_FUNC1(asin);
+MATH_WRAP_FUNC1(atan);
+MATH_WRAP_FUNC1(ceil);
+MATH_WRAP_FUNC1(cos);
+MATH_WRAP_FUNC1(exp);
+MATH_WRAP_FUNC1(floor);
+MATH_WRAP_FUNC1(log);
+MATH_WRAP_FUNC1(sin);
+MATH_WRAP_FUNC1(sqrt);
+MATH_WRAP_FUNC1(tan);
#if 0
- // Two-argument functions.
- #define MATH_WRAP_FUNC2_EXP(funcname, expr)
\
+// Two-argument functions.
+#define MATH_WRAP_FUNC2_EXP(funcname, expr)
\
void math_##funcname(as_value* result, as_object* this_ptr,
as_environment* env, int nargs, int first_arg_bottom_index) \
{
\
double arg0 = env->bottom(first_arg_bottom_index).to_number();
\
@@ -590,8 +593,8 @@
result->set_double(expr);
\
}
#else
- // Two-argument functions.
- #define MATH_WRAP_FUNC2_EXP(funcname, expr)
\
+// Two-argument functions.
+#define MATH_WRAP_FUNC2_EXP(funcname, expr)
\
void math_##funcname(const fn_call& fn)
\
{
\
double arg0 = fn.arg(0).to_number();
\
@@ -599,4257 +602,4271 @@
fn.result->set_double(expr);
\
}
#endif
- MATH_WRAP_FUNC2_EXP(atan2, (atan2(arg0, arg1)));
- MATH_WRAP_FUNC2_EXP(max, (arg0 > arg1 ? arg0 : arg1));
- MATH_WRAP_FUNC2_EXP(min, (arg0 < arg1 ? arg0 : arg1));
- MATH_WRAP_FUNC2_EXP(pow, (pow(arg0, arg1)));
-
- // A couple of oddballs.
- void math_random(const fn_call& fn)
- {
- // Random number between 0 and 1.
- fn.result->set_double(tu_random::next_random() /
double(Uint32(0x0FFFFFFFF)));
- }
- void math_round(const fn_call& fn)
- {
- // round argument to nearest int.
- double arg0 = fn.arg(0).to_number();
- fn.result->set_double(floor(arg0 + 0.5));
- }
+MATH_WRAP_FUNC2_EXP(atan2, (atan2(arg0, arg1)));
+MATH_WRAP_FUNC2_EXP(max, (arg0 > arg1 ? arg0 : arg1));
+MATH_WRAP_FUNC2_EXP(min, (arg0 < arg1 ? arg0 : arg1));
+MATH_WRAP_FUNC2_EXP(pow, (pow(arg0, arg1)));
+
+// A couple of oddballs.
+void math_random(const fn_call& fn)
+{
+ // Random number between 0 and 1.
+ fn.result->set_double(tu_random::next_random() /
double(Uint32(0x0FFFFFFFF)));
+}
+void math_round(const fn_call& fn)
+{
+ // round argument to nearest int.
+ double arg0 = fn.arg(0).to_number();
+ fn.result->set_double(floor(arg0 + 0.5));
+}
- void math_init()
- {
- // Create built-in math object.
- as_object* math_obj = new as_object;
+void math_init()
+{
+ // Create built-in math object.
+ as_object* math_obj = new as_object;
+
+ // constant
+ math_obj->set_member("e", 2.7182818284590452354);
+ math_obj->set_member("ln2", 0.69314718055994530942);
+ math_obj->set_member("log2e", 1.4426950408889634074);
+ math_obj->set_member("ln10", 2.30258509299404568402);
+ math_obj->set_member("log10e", 0.43429448190325182765);
+ math_obj->set_member("pi", 3.14159265358979323846);
+ math_obj->set_member("sqrt1_2", 0.7071067811865475244);
+ math_obj->set_member("sqrt2", 1.4142135623730950488);
+
+ // math methods
+ math_obj->set_member("abs", &math_fabs);
+ math_obj->set_member("acos", &math_acos);
+ math_obj->set_member("asin", &math_asin);
+ math_obj->set_member("atan", &math_atan);
+ math_obj->set_member("ceil", &math_ceil);
+ math_obj->set_member("cos", &math_cos);
+ math_obj->set_member("exp", &math_exp);
+ math_obj->set_member("floor", &math_floor);
+ math_obj->set_member("log", &math_log);
+ math_obj->set_member("random", &math_random);
+ math_obj->set_member("round", &math_round);
+ math_obj->set_member("sin", &math_sin);
+ math_obj->set_member("sqrt", &math_sqrt);
+ math_obj->set_member("tan", &math_tan);
+
+ math_obj->set_member("atan2", &math_atan2);
+ math_obj->set_member("max", &math_max);
+ math_obj->set_member("min", &math_min);
+ math_obj->set_member("pow", &math_pow);
- // constant
- math_obj->set_member("e", 2.7182818284590452354);
- math_obj->set_member("ln2", 0.69314718055994530942);
- math_obj->set_member("log2e", 1.4426950408889634074);
- math_obj->set_member("ln10", 2.30258509299404568402);
- math_obj->set_member("log10e", 0.43429448190325182765);
- math_obj->set_member("pi", 3.14159265358979323846);
- math_obj->set_member("sqrt1_2", 0.7071067811865475244);
- math_obj->set_member("sqrt2", 1.4142135623730950488);
-
- // math methods
- math_obj->set_member("abs", &math_fabs);
- math_obj->set_member("acos", &math_acos);
- math_obj->set_member("asin", &math_asin);
- math_obj->set_member("atan", &math_atan);
- math_obj->set_member("ceil", &math_ceil);
- math_obj->set_member("cos", &math_cos);
- math_obj->set_member("exp", &math_exp);
- math_obj->set_member("floor", &math_floor);
- math_obj->set_member("log", &math_log);
- math_obj->set_member("random", &math_random);
- math_obj->set_member("round", &math_round);
- math_obj->set_member("sin", &math_sin);
- math_obj->set_member("sqrt", &math_sqrt);
- math_obj->set_member("tan", &math_tan);
-
- math_obj->set_member("atan2", &math_atan2);
- math_obj->set_member("max", &math_max);
- math_obj->set_member("min", &math_min);
- math_obj->set_member("pow", &math_pow);
-
- s_global->set_member("math", math_obj);
- }
+ s_global->set_member("math", math_obj);
+}
- void event_test(const fn_call& fn)
- {
- log_msg("FIXME: %s\n", __FUNCTION__);
- }
+void event_test(const fn_call& fn)
+{
+ log_msg("FIXME: %s\n", __FUNCTION__);
+}
- //
- // key object
- //
+//
+// key object
+//
- struct key_as_object : public as_object
- {
- Uint8 m_keymap[key::KEYCOUNT / 8 + 1]; // bit-array
- std::vector<weak_ptr<as_object> > m_listeners;
- int m_last_key_pressed;
+struct key_as_object : public as_object
+{
+ Uint8 m_keymap[key::KEYCOUNT / 8 + 1]; // bit-array
+ std::vector<weak_ptr<as_object> > m_listeners;
+ int m_last_key_pressed;
- key_as_object()
- :
- m_last_key_pressed(0)
- {
- memset(m_keymap, 0, sizeof(m_keymap));
- }
+ key_as_object()
+ :
+ m_last_key_pressed(0)
+ {
+ memset(m_keymap, 0, sizeof(m_keymap));
+ }
- bool is_key_down(int code)
- {
- if (code < 0 || code >= key::KEYCOUNT) return false;
+ bool is_key_down(int code)
+ {
+ if (code < 0 || code >= key::KEYCOUNT) return false;
- int byte_index = code >> 3;
- int bit_index = code - (byte_index << 3);
- int mask = 1 << bit_index;
+ int byte_index = code >> 3;
+ int bit_index = code - (byte_index << 3);
+ int mask = 1 << bit_index;
- assert(byte_index >= 0 && byte_index <
int(sizeof(m_keymap)/sizeof(m_keymap[0])));
+ assert(byte_index >= 0 && byte_index <
int(sizeof(m_keymap)/sizeof(m_keymap[0])));
- if (m_keymap[byte_index] & mask)
- {
- return true;
- }
- else
- {
- return false;
- }
+ if (m_keymap[byte_index] & mask)
+ {
+ return true;
}
-
- void set_key_down(int code)
+ else
{
- if (code < 0 || code >= key::KEYCOUNT) return;
+ return false;
+ }
+ }
- m_last_key_pressed = code;
+ void set_key_down(int code)
+ {
+ if (code < 0 || code >= key::KEYCOUNT) return;
+
+ m_last_key_pressed = code;
- int byte_index = code >> 3;
- int bit_index = code - (byte_index << 3);
- int mask = 1 << bit_index;
+ int byte_index = code >> 3;
+ int bit_index = code - (byte_index << 3);
+ int mask = 1 << bit_index;
- assert(byte_index >= 0 && byte_index <
int(sizeof(m_keymap)/sizeof(m_keymap[0])));
+ assert(byte_index >= 0 && byte_index <
int(sizeof(m_keymap)/sizeof(m_keymap[0])));
- m_keymap[byte_index] |= mask;
+ m_keymap[byte_index] |= mask;
- // Notify listeners.
- int i;
- int n = m_listeners.size();
- for (i = 0; i < n; i++)
+ // Notify listeners.
+ int i;
+ int n = m_listeners.size();
+ for (i = 0; i < n; i++)
+ {
+ smart_ptr<as_object> listener = m_listeners[i];
+ as_value method;
+ if (listener != NULL
+ &&
listener->get_member(event_id(event_id::KEY_DOWN).get_function_name(), &method))
{
- smart_ptr<as_object> listener =
m_listeners[i];
- as_value method;
- if (listener != NULL
- &&
listener->get_member(event_id(event_id::KEY_DOWN).get_function_name(), &method))
- {
- call_method(method, NULL /* or root?
*/, listener.get_ptr(), 0, 0);
- }
+ call_method(method, NULL /* or root? */,
listener.get_ptr(), 0, 0);
}
}
+ }
- void set_key_up(int code)
- {
- if (code < 0 || code >= key::KEYCOUNT) return;
-
- int byte_index = code >> 3;
- int bit_index = code - (byte_index << 3);
- int mask = 1 << bit_index;
-
- assert(byte_index >= 0 && byte_index <
int(sizeof(m_keymap)/sizeof(m_keymap[0])));
+ void set_key_up(int code)
+ {
+ if (code < 0 || code >= key::KEYCOUNT) return;
- m_keymap[byte_index] &= ~mask;
+ int byte_index = code >> 3;
+ int bit_index = code - (byte_index << 3);
+ int mask = 1 << bit_index;
- // Notify listeners.
- for (int i = 0, n = m_listeners.size(); i < n; i++)
- {
- smart_ptr<as_object> listener =
m_listeners[i];
+ assert(byte_index >= 0 && byte_index <
int(sizeof(m_keymap)/sizeof(m_keymap[0])));
- as_value method;
- if (listener != NULL
- &&
listener->get_member(event_id(event_id::KEY_UP).get_function_name(), &method))
- {
- call_method(method, NULL /* or root?
*/, listener.get_ptr(), 0, 0);
- }
- }
- }
+ m_keymap[byte_index] &= ~mask;
- void cleanup_listeners()
- // Remove dead entries in the listeners list. (Since
- // we use weak_ptr's, listeners can disappear without
- // notice.)
+ // Notify listeners.
+ for (int i = 0, n = m_listeners.size(); i < n; i++)
{
- for (int i = m_listeners.size() - 1; i >= 0; i--)
+ smart_ptr<as_object> listener = m_listeners[i];
+
+ as_value method;
+ if (listener != NULL
+ &&
listener->get_member(event_id(event_id::KEY_UP).get_function_name(), &method))
{
- if (m_listeners[i] == NULL)
- {
- m_listeners.erase(m_listeners.begin() +
i);
- }
+ call_method(method, NULL /* or root? */,
listener.get_ptr(), 0, 0);
}
}
+ }
- void add_listener(as_object* listener)
+ void cleanup_listeners()
+ // Remove dead entries in the listeners list. (Since
+ // we use weak_ptr's, listeners can disappear without
+ // notice.)
+ {
+ for (int i = m_listeners.size() - 1; i >= 0; i--)
{
- cleanup_listeners();
-
- for (int i = 0, n = m_listeners.size(); i < n; i++)
+ if (m_listeners[i] == NULL)
{
- if (m_listeners[i] == listener)
- {
- // Already in the list.
- return;
- }
+ m_listeners.erase(m_listeners.begin() + i);
}
-
- m_listeners.push_back(listener);
}
+ }
- void remove_listener(as_object* listener)
- {
- cleanup_listeners();
+ void add_listener(as_object* listener)
+ {
+ cleanup_listeners();
- for (int i = m_listeners.size() - 1; i >= 0; i--)
+ for (int i = 0, n = m_listeners.size(); i < n; i++)
+ {
+ if (m_listeners[i] == listener)
{
- if (m_listeners[i] == listener)
- {
- m_listeners.erase(m_listeners.begin() +
i);
- }
+ // Already in the list.
+ return;
}
}
- int get_last_key_pressed() const { return
m_last_key_pressed; }
- };
-
+ m_listeners.push_back(listener);
+ }
- void key_add_listener(const fn_call& fn)
- // Add a listener (first arg is object reference) to our list.
- // Listeners will have "onKeyDown" and "onKeyUp" methods
- // called on them when a key changes state.
+ void remove_listener(as_object* listener)
{
- if (fn.nargs < 1)
- {
- log_error("key_add_listener needs one argument (the
listener object)\n");
- return;
- }
+ cleanup_listeners();
- as_object* listener = fn.arg(0).to_object();
- if (listener == NULL)
+ for (int i = m_listeners.size() - 1; i >= 0; i--)
{
- log_error("key_add_listener passed a NULL object;
ignored\n");
- return;
+ if (m_listeners[i] == listener)
+ {
+ m_listeners.erase(m_listeners.begin() + i);
+ }
}
+ }
- key_as_object* ko = (key_as_object*) (as_object*) fn.this_ptr;
- assert(ko);
+ int get_last_key_pressed() const { return m_last_key_pressed; }
+};
- ko->add_listener(listener);
- }
- void key_get_ascii(const fn_call& fn)
- // Return the ascii value of the last key pressed.
+void key_add_listener(const fn_call& fn)
+ // Add a listener (first arg is object reference) to our list.
+ // Listeners will have "onKeyDown" and "onKeyUp" methods
+ // called on them when a key changes state.
+{
+ if (fn.nargs < 1)
{
- key_as_object* ko = (key_as_object*) (as_object*) fn.this_ptr;
- assert(ko);
+ log_error("key_add_listener needs one argument (the listener
object)\n");
+ return;
+ }
- fn.result->set_undefined();
+ as_object* listener = fn.arg(0).to_object();
+ if (listener == NULL)
+ {
+ log_error("key_add_listener passed a NULL object; ignored\n");
+ return;
+ }
- int code = ko->get_last_key_pressed();
- if (code > 0)
- {
- // @@ Crude for now; just jamming the key code in a
string, as a character.
- // Need to apply shift/capslock/numlock, etc...
- char buf[2];
- buf[0] = (char) code;
- buf[1] = 0;
+ key_as_object* ko = (key_as_object*) (as_object*) fn.this_ptr;
+ assert(ko);
- fn.result->set_string(buf);
- }
- }
+ ko->add_listener(listener);
+}
- void key_get_code(const fn_call& fn)
- // Returns the keycode of the last key pressed.
- {
- key_as_object* ko = (key_as_object*) (as_object*) fn.this_ptr;
- assert(ko);
+void key_get_ascii(const fn_call& fn)
+ // Return the ascii value of the last key pressed.
+{
+ key_as_object* ko = (key_as_object*) (as_object*) fn.this_ptr;
+ assert(ko);
- fn.result->set_int(ko->get_last_key_pressed());
- }
+ fn.result->set_undefined();
- void key_is_down(const fn_call& fn)
- // Return true if the specified (first arg keycode) key is pressed.
+ int code = ko->get_last_key_pressed();
+ if (code > 0)
{
- if (fn.nargs < 1)
- {
- log_error("key_is_down needs one argument (the key
code)\n");
- return;
- }
+ // @@ Crude for now; just jamming the key code in a string, as a
character.
+ // Need to apply shift/capslock/numlock, etc...
+ char buf[2];
+ buf[0] = (char) code;
+ buf[1] = 0;
- int code = (int) fn.arg(0).to_number();
+ fn.result->set_string(buf);
+ }
+}
- key_as_object* ko = (key_as_object*) (as_object*) fn.this_ptr;
- assert(ko);
+void key_get_code(const fn_call& fn)
+ // Returns the keycode of the last key pressed.
+{
+ key_as_object* ko = (key_as_object*) (as_object*) fn.this_ptr;
+ assert(ko);
- fn.result->set_bool(ko->is_key_down(code));
- }
+ fn.result->set_int(ko->get_last_key_pressed());
+}
- void key_is_toggled(const fn_call& fn)
- // Given the keycode of NUM_LOCK or CAPSLOCK, returns true if
- // the associated state is on.
+void key_is_down(const fn_call& fn)
+ // Return true if the specified (first arg keycode) key is pressed.
+{
+ if (fn.nargs < 1)
{
- // @@ TODO
- fn.result->set_bool(false);
+ log_error("key_is_down needs one argument (the key code)\n");
+ return;
}
- void key_remove_listener(const fn_call& fn)
- // Remove a previously-added listener.
- {
- if (fn.nargs < 1)
- {
- log_error("key_remove_listener needs one argument (the
listener object)\n");
- return;
- }
+ int code = (int) fn.arg(0).to_number();
- as_object* listener = fn.arg(0).to_object();
- if (listener == NULL)
- {
- log_error("key_remove_listener passed a NULL object;
ignored\n");
- return;
- }
+ key_as_object* ko = (key_as_object*) (as_object*) fn.this_ptr;
+ assert(ko);
+
+ fn.result->set_bool(ko->is_key_down(code));
+}
- key_as_object* ko = (key_as_object*) (as_object*) fn.this_ptr;
- assert(ko);
+void key_is_toggled(const fn_call& fn)
+ // Given the keycode of NUM_LOCK or CAPSLOCK, returns true if
+ // the associated state is on.
+{
+ // @@ TODO
+ fn.result->set_bool(false);
+}
- ko->remove_listener(listener);
+void key_remove_listener(const fn_call& fn)
+ // Remove a previously-added listener.
+{
+ if (fn.nargs < 1)
+ {
+ log_error("key_remove_listener needs one argument (the listener
object)\n");
+ return;
}
- void key_init()
+ as_object* listener = fn.arg(0).to_object();
+ if (listener == NULL)
{
-// GNASH_REPORT_FUNCTION;
- // Create built-in key object.
- as_object* key_obj = new key_as_object;
+ log_error("key_remove_listener passed a NULL object; ignored\n");
+ return;
+ }
- // constants
-#define KEY_CONST(k) key_obj->set_member(#k, key::k)
- KEY_CONST(BACKSPACE);
- KEY_CONST(CAPSLOCK);
- KEY_CONST(CONTROL);
- KEY_CONST(DELETEKEY);
- KEY_CONST(DOWN);
- KEY_CONST(END);
- KEY_CONST(ENTER);
- KEY_CONST(ESCAPE);
- KEY_CONST(HOME);
- KEY_CONST(INSERT);
- KEY_CONST(LEFT);
- KEY_CONST(PGDN);
- KEY_CONST(PGUP);
- KEY_CONST(RIGHT);
- KEY_CONST(SHIFT);
- KEY_CONST(SPACE);
- KEY_CONST(TAB);
- KEY_CONST(UP);
-
- // methods
- key_obj->set_member("addListener", &key_add_listener);
- key_obj->set_member("getAscii", &key_get_ascii);
- key_obj->set_member("getCode", &key_get_code);
- key_obj->set_member("isDown", &key_is_down);
- key_obj->set_member("isToggled", &key_is_toggled);
- key_obj->set_member("removeListener", &key_remove_listener);
+ key_as_object* ko = (key_as_object*) (as_object*) fn.this_ptr;
+ assert(ko);
- s_global->set_member("Key", key_obj);
- }
+ ko->remove_listener(listener);
+}
+void key_init()
+{
+// GNASH_REPORT_FUNCTION;
+ // Create built-in key object.
+ as_object* key_obj = new key_as_object;
- void notify_key_event(key::code k, bool down)
- // External interface for the host to report key events.
- {
+ // constants
+#define KEY_CONST(k) key_obj->set_member(#k, key::k)
+ KEY_CONST(BACKSPACE);
+ KEY_CONST(CAPSLOCK);
+ KEY_CONST(CONTROL);
+ KEY_CONST(DELETEKEY);
+ KEY_CONST(DOWN);
+ KEY_CONST(END);
+ KEY_CONST(ENTER);
+ KEY_CONST(ESCAPE);
+ KEY_CONST(HOME);
+ KEY_CONST(INSERT);
+ KEY_CONST(LEFT);
+ KEY_CONST(PGDN);
+ KEY_CONST(PGUP);
+ KEY_CONST(RIGHT);
+ KEY_CONST(SHIFT);
+ KEY_CONST(SPACE);
+ KEY_CONST(TAB);
+ KEY_CONST(UP);
+
+ // methods
+ key_obj->set_member("addListener", &key_add_listener);
+ key_obj->set_member("getAscii", &key_get_ascii);
+ key_obj->set_member("getCode", &key_get_code);
+ key_obj->set_member("isDown", &key_is_down);
+ key_obj->set_member("isToggled", &key_is_toggled);
+ key_obj->set_member("removeListener", &key_remove_listener);
+
+ s_global->set_member("Key", key_obj);
+}
+
+
+void notify_key_event(key::code k, bool down)
+ // External interface for the host to report key events.
+{
// GNASH_REPORT_FUNCTION;
- action_init(); // @@ put this in some global init somewhere
else...
+ action_init(); // @@ put this in some global init somewhere else...
- static tu_string key_obj_name("Key");
+ static tu_string key_obj_name("Key");
- as_value kval;
- s_global->get_member(key_obj_name, &kval);
- if (kval.get_type() == as_value::OBJECT)
- {
- key_as_object* ko = (key_as_object*) kval.to_object();
- assert(ko);
+ as_value kval;
+ s_global->get_member(key_obj_name, &kval);
+ if (kval.get_type() == as_value::OBJECT)
+ {
+ key_as_object* ko = (key_as_object*) kval.to_object();
+ assert(ko);
- if (down) ko->set_key_down(k);
- else ko->set_key_up(k);
- }
- else
- {
- log_error("gnash::notify_key_event(): no Key
built-in\n");
- }
+ if (down) ko->set_key_down(k);
+ else ko->set_key_up(k);
}
+ else
+ {
+ log_error("gnash::notify_key_event(): no Key built-in\n");
+ }
+}
- //
- // global init
- //
+//
+// global init
+//
- void as_global_trace(const fn_call& fn)
- {
- assert(fn.nargs >= 1);
+void as_global_trace(const fn_call& fn)
+{
+ assert(fn.nargs >= 1);
// @@ NOTHING should get special treatment,
// as_value::to_string() will take care of everything
#if 0
- // Special case for objects: try the toString() method.
- if (fn.arg(0).get_type() == as_value::OBJECT)
- {
- as_object* obj = fn.arg(0).to_object();
- assert(obj);
+ // Special case for objects: try the toString() method.
+ if (fn.arg(0).get_type() == as_value::OBJECT)
+ {
+ as_object* obj = fn.arg(0).to_object();
+ assert(obj);
- as_value method;
- if (obj->get_member("toString", &method)
- && method.is_function())
- {
- as_value result = call_method0(method, fn.env,
obj);
- log_msg("%s\n", result.to_string());
+ as_value method;
+ if (obj->get_member("toString", &method)
+ && method.is_function())
+ {
+ as_value result = call_method0(method, fn.env, obj);
+ log_msg("%s\n", result.to_string());
- return;
- }
+ return;
}
-#endif
-
- // Log our argument.
- //
- // @@ what if we get extra args?
- //
- // @@ Array gets special treatment.
- // @@ NOTHING should get special treatment,
- // as_value::to_string() will take care of everything
- const char* arg0 = fn.arg(0).to_string();
- log_msg("%s\n", arg0);
}
+#endif
+ // Log our argument.
+ //
+ // @@ what if we get extra args?
+ //
+ // @@ Array gets special treatment.
+ // @@ NOTHING should get special treatment,
+ // as_value::to_string() will take care of everything
+ const char* arg0 = fn.arg(0).to_string();
+ log_msg("%s\n", arg0);
+}
- void as_global_sound_ctor(const fn_call& fn)
- // Constructor for ActionScript class Sound.
- {
- smart_ptr<as_object> sound_obj(new sound_as_object);
- // methods
- sound_obj->set_member("attachSound", &sound_attach);
- sound_obj->set_member("start", &sound_start);
- sound_obj->set_member("stop", &sound_stop);
+void as_global_sound_ctor(const fn_call& fn)
+ // Constructor for ActionScript class Sound.
+{
+ smart_ptr<as_object> sound_obj(new sound_as_object);
- fn.result->set_as_object(sound_obj.get_ptr());
- }
+ // methods
+ sound_obj->set_member("attachSound", &sound_attach);
+ sound_obj->set_member("start", &sound_start);
+ sound_obj->set_member("stop", &sound_stop);
+ fn.result->set_as_object(sound_obj.get_ptr());
+}
- void as_global_object_ctor(const fn_call& fn)
- // Constructor for ActionScript class Object.
- {
- as_object *new_obj;
- if ( fn.nargs == 0 )
- {
- new_obj = new as_object();
- }
- else if ( fn.nargs == 1 ) // copy constructor
- {
- as_object *src_obj = fn.arg(0).to_object();
- new_obj = new as_object(src_obj);
- }
- else
- {
- IF_VERBOSE_DEBUG(log_msg("Too many args to Object
constructor"));
- new_obj = new as_object();
- }
+void as_global_object_ctor(const fn_call& fn)
+ // Constructor for ActionScript class Object.
+{
+ as_object *new_obj;
- fn.result->set_as_object(new_obj);
+ if ( fn.nargs == 0 )
+ {
+ new_obj = new as_object();
}
-
- void as_global_isnan(const fn_call& fn)
+ else if ( fn.nargs == 1 ) // copy constructor
{
- assert(fn.nargs == 1);
-
- fn.result->set_bool(fn.arg(0).is_nan());
+ as_object *src_obj = fn.arg(0).to_object();
+ new_obj = new as_object(src_obj);
}
-
- void as_global_isfinite(const fn_call& fn)
+ else
{
- assert(fn.nargs == 1);
-
- fn.result->set_bool(fn.arg(0).is_finite());
+ IF_VERBOSE_DEBUG(log_msg("Too many args to Object constructor"));
+ new_obj = new as_object();
}
- void as_global_unescape(const fn_call& fn)
- {
- assert(fn.nargs == 1);
+ fn.result->set_as_object(new_obj);
+}
- std::string input = fn.arg(0).to_string();
- std::string insertst;
- int hexcode;
+void as_global_isnan(const fn_call& fn)
+{
+ assert(fn.nargs == 1);
- for (unsigned int i=0;i<input.length();)
- {
- if ((input.length() > i + 2) && input[i] == '%' &&
- isxdigit(input[i+1]) && isxdigit(input[i+2]))
- {
- input[i+1] = toupper(input[i+1]);
- input[i+2] = toupper(input[i+2]);
- if (isdigit(input[i+1]))
- hexcode = (input[i+1] - '0') * 16;
- else
- hexcode = (input[i+1] - 'A' + 10) * 16;
-
- if (isdigit(input[i+2]))
- hexcode += (input[i+2] - '0');
- else
- hexcode += (input[i+2] - 'A' + 10);
+ fn.result->set_bool(fn.arg(0).is_nan());
+}
- input.erase(i,3);
-
- switch (hexcode)
- {
- case 0x20: // space
- insertst = ' ';
- break;
- case 0x22: // "
- insertst = '\"';
- break;
- case 0x23: // #
- insertst = '#';
- break;
- case 0x24: // $
- insertst = '$';
- break;
- case 0x25: // %
- insertst = '%';
- break;
- case 0x26: // &
- insertst = '&';
- break;
- case 0x2B: // +
- insertst = '+';
- break;
- case 0x2C: // ,
- insertst = ',';
- break;
- case 0x2F: // /
- insertst = '/';
- break;
- case 0x3A: // :
- insertst = ':';
- break;
- case 0x3B: // ;
- insertst = ';';
- break;
- case 0x3C: // <
- insertst = '<';
- break;
- case 0x3D: // =
- insertst = '=';
- break;
- case 0x3E: // >
- insertst = '>';
- break;
- case 0x3F: // ?
- insertst = '?';
- break;
- case 0x40: // @
- insertst = '@';
- break;
- case 0x5B: // [
- insertst = '[';
- break;
- case 0x5C: // \ (backslash)
- insertst = '\\';
- break;
- case 0x5D: // ]
- insertst = ']';
- break;
- case 0x5E: // ^
- insertst = '^';
- break;
- case 0x60: // `
- insertst = '`';
- break;
- case 0x7B: // {
- insertst = '{';
- break;
- case 0x7C: // |
- insertst = '|';
- break;
- case 0x7D: // }
- insertst = '}';
- break;
- case 0x7E: // ~
- insertst = '~';
- break;
- default:
- IF_VERBOSE_ACTION(log_error("unescape()
function reached "
- "unknown hexcode %d, aborting
unescape()\n",hexcode));
-
fn.result->set_string(fn.arg(0).to_string());
- return;
- }
- input.insert(i,insertst);
- }
- else
- i++;
- }
- fn.result->set_string(input.c_str());
- }
+void as_global_isfinite(const fn_call& fn)
+{
+ assert(fn.nargs == 1);
- void as_global_parsefloat(const fn_call& fn)
- {
- assert(fn.nargs == 1);
+ fn.result->set_bool(fn.arg(0).is_finite());
+}
- float result;
+void as_global_unescape(const fn_call& fn)
+{
+ assert(fn.nargs == 1);
- // sscanf will handle the whitespace / unneeded characters etc.
automatically
- if (1 == sscanf(fn.arg(0).to_string(), "%f", &result))
- fn.result->set_double(double(result));
- else
- // if sscanf didn't find anything, return NaN
- fn.result->set_nan();
- }
+ std::string input = fn.arg(0).to_string();
+ std::string insertst;
+ int hexcode;
- void as_global_parseint(const fn_call& fn)
+ for (unsigned int i=0;i<input.length();)
{
- assert(fn.nargs == 2 || fn.nargs == 1);
-
- // Make sure our argument is the correct type
- if (fn.nargs > 1)
- fn.arg(1).convert_to_number();
-
- // Set up some variables
- const std::string digits =
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
- char *input = new char[strlen(fn.arg(0).to_string())+1];
- strcpy(input,fn.arg(0).to_string());
- int base, result = 0, i;
- bool bNegative;
-
- // Skip leading whitespace
- while ((input[0] == ' ') || (input[0] == 0x9))
- input++;
-
- if (input[0] == '-')
+ if ((input.length() > i + 2) && input[i] == '%' &&
+ isxdigit(input[i+1]) && isxdigit(input[i+2]))
{
- bNegative = true;
- input++;
- }
- else
- bNegative = false;
+ input[i+1] = toupper(input[i+1]);
+ input[i+2] = toupper(input[i+2]);
+ if (isdigit(input[i+1]))
+ hexcode = (input[i+1] - '0') * 16;
+ else
+ hexcode = (input[i+1] - 'A' + 10) * 16;
- // Convert the string to uppercase
- for (i=0;i<int(strlen(input));i++)
- input[i] = toupper(input[i]);
+ if (isdigit(input[i+2]))
+ hexcode += (input[i+2] - '0');
+ else
+ hexcode += (input[i+2] - 'A' + 10);
- // if we were sent a second argument, that's our base
- if (fn.nargs > 1)
- {
- base = int(fn.arg(1).to_number());
- }
- // if the string starts with "0x" then a hex digit
- else if (strlen(input) > 2 && input[0] == '0' && input[1] == 'X'
- && (isdigit(input[2]) || (input[2] >= 'A' && input[2]
<= 'F')))
- {
- base = 16; // the base is 16
- input = input + 2; // skip the leading "0x"
- }
- // if the string starts with "0" then an octal digit
- else if (strlen(input) > 1 && input[0] == '0' &&
- (input[1] >= '0' && input[1] <= '7'))
- {
- base = 8;
- input++; // skip the leading '0'
- }
- else
- // default base is 10
- base = 10;
+ input.erase(i,3);
+
+ switch (hexcode)
+ {
+ case 0x20: // space
+ insertst = ' ';
+ break;
+ case 0x22: // "
+ insertst = '\"';
+ break;
+ case 0x23: // #
+ insertst = '#';
+ break;
+ case 0x24: // $
+ insertst = '$';
+ break;
+ case 0x25: // %
+ insertst = '%';
+ break;
+ case 0x26: // &
+ insertst = '&';
+ break;
+ case 0x2B: // +
+ insertst = '+';
+ break;
+ case 0x2C: // ,
+ insertst = ',';
+ break;
+ case 0x2F: // /
+ insertst = '/';
+ break;
+ case 0x3A: // :
+ insertst = ':';
+ break;
+ case 0x3B: // ;
+ insertst = ';';
+ break;
+ case 0x3C: // <
+ insertst = '<';
+ break;
+ case 0x3D: // =
+ insertst = '=';
+ break;
+ case 0x3E: // >
+ insertst = '>';
+ break;
+ case 0x3F: // ?
+ insertst = '?';
+ break;
+ case 0x40: // @
+ insertst = '@';
+ break;
+ case 0x5B: // [
+ insertst = '[';
+ break;
+ case 0x5C: // \ (backslash)
+ insertst = '\\';
+ break;
+ case 0x5D: // ]
+ insertst = ']';
+ break;
+ case 0x5E: // ^
+ insertst = '^';
+ break;
+ case 0x60: // `
+ insertst = '`';
+ break;
+ case 0x7B: // {
+ insertst = '{';
+ break;
+ case 0x7C: // |
+ insertst = '|';
+ break;
+ case 0x7D: // }
+ insertst = '}';
+ break;
+ case 0x7E: // ~
+ insertst = '~';
+ break;
+ default:
+ IF_VERBOSE_ACTION(log_error("unescape() function
reached "
+ "unknown hexcode %d,
aborting unescape()\n",hexcode));
+ fn.result->set_string(fn.arg(0).to_string());
+ return;
+ }
+ input.insert(i,insertst);
+ }
+ else
+ i++;
+ }
+ fn.result->set_string(input.c_str());
+}
+
+void as_global_parsefloat(const fn_call& fn)
+{
+ assert(fn.nargs == 1);
+
+ float result;
+
+ // sscanf will handle the whitespace / unneeded characters etc.
automatically
+ if (1 == sscanf(fn.arg(0).to_string(), "%f", &result))
+ fn.result->set_double(double(result));
+ else
+ // if sscanf didn't find anything, return NaN
+ fn.result->set_nan();
+}
+
+void as_global_parseint(const fn_call& fn)
+{
+ assert(fn.nargs == 2 || fn.nargs == 1);
+
+ // Make sure our argument is the correct type
+ if (fn.nargs > 1)
+ fn.arg(1).convert_to_number();
+
+ // Set up some variables
+ const std::string digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+ char *input = new char[strlen(fn.arg(0).to_string())+1];
+ strcpy(input,fn.arg(0).to_string());
+ int base, result = 0, i;
+ bool bNegative;
+
+ // Skip leading whitespace
+ while ((input[0] == ' ') || (input[0] == 0x9))
+ input++;
+
+ if (input[0] == '-')
+ {
+ bNegative = true;
+ input++;
+ }
+ else
+ bNegative = false;
+
+ // Convert the string to uppercase
+ for (i=0;i<int(strlen(input));i++)
+ input[i] = toupper(input[i]);
+
+ // if we were sent a second argument, that's our base
+ if (fn.nargs > 1)
+ {
+ base = int(fn.arg(1).to_number());
+ }
+ // if the string starts with "0x" then a hex digit
+ else if (strlen(input) > 2 && input[0] == '0' && input[1] == 'X'
+ && (isdigit(input[2]) || (input[2] >= 'A' && input[2] <= 'F')))
+ {
+ base = 16; // the base is 16
+ input = input + 2; // skip the leading "0x"
+ }
+ // if the string starts with "0" then an octal digit
+ else if (strlen(input) > 1 && input[0] == '0' &&
+ (input[1] >= '0' && input[1] <= '7'))
+ {
+ base = 8;
+ input++; // skip the leading '0'
+ }
+ else
+ // default base is 10
+ base = 10;
+
+ assert (base >= 2 && base <= 36);
+
+ int numdigits = 0;
+
+ // Start at the beginning, see how many valid digits we have
+ // in the base we're dealing with
+ while (numdigits < int(strlen(input))
+ && int(digits.find(input[numdigits])) < base
+ && digits.find(input[numdigits]) != std::string::npos)
+ numdigits++;
+
+ // If we didn't get any digits, we should return NaN
+ if (numdigits == 0)
+ {
+ fn.result->set_nan();
+ return;
+ }
+
+ for (i=0;i<numdigits;i++)
+ {
+ result += digits.find(input[i])*int(pow(base,numdigits - i - 1));
+ }
+
+ if (bNegative)
+ result = -result;
+
+ // Now return the parsed string
+ fn.result->set_int(result);
+}
+
+void as_global_assetpropflags(const fn_call& fn)
+ // ASSetPropFlags function
+{
+ const int version =
fn.env->get_target()->get_movie_definition()->get_version();
+
+ // Check the arguments
+ assert(fn.nargs == 3 || fn.nargs == 4);
+ assert((version == 5) ? (fn.nargs == 3) : true);
+
+ // object
+ as_object* const obj = fn.arg(0).to_object();
+ assert(obj != NULL);
+
+ // list of child names
+ as_object* props = fn.arg(1).to_object();
+ if (props == NULL) {
+ // tulrich: this fires in test_ASSetPropFlags -- is it correct?
+ assert(fn.arg(1).get_type() == as_value::NULLTYPE);
+ }
+
+ // a number which represents three bitwise flags which
+ // are used to determine whether the list of child names should be hidden,
+ // un-hidden, protected from over-write, un-protected from over-write,
+ // protected from deletion and un-protected from deletion
+ int set_true = int(fn.arg(2).to_number()) &
as_prop_flags::as_prop_flags_mask;
+
+ // Is another integer bitmask that works like set_true,
+ // except it sets the attributes to false. The
+ // set_false bitmask is applied before set_true is applied
+
+ // ASSetPropFlags was exposed in Flash 5, however the fourth argument
'set_false'
+ // was not required as it always defaulted to the value '~0'.
+ int set_false = (fn.nargs == 3 ?
+ (version == 5 ? ~0 : 0) : int(fn.arg(3).to_number()))
+ & as_prop_flags::as_prop_flags_mask;
+
+ // Evan: it seems that if set_true == 0 and set_false == 0, this function
+ // acts as if the parameters where (object, null, 0x1, 0) ...
+ if (set_false == 0 && set_true == 0)
+ {
+ props = NULL;
+ set_false = 0;
+ set_true = 0x1;
+ }
+
+ if (props == NULL)
+ {
+ // Take all the members of the object
+
+ as_object* object = (as_object*) obj;
+
+ stringi_hash<as_member>::const_iterator it =
object->m_members.begin();
+ while (it != object->m_members.end())
+ {
+ as_member member = it->second;
+
+ as_prop_flags f = member.get_member_flags();
+ //const int oldflags =
+ f.get_flags();
+ //const int newflags =
+ f.set_flags(set_true, set_false);
+ member.set_member_flags(f);
+
+ object->m_members[it->first] = member;
+
+ ++it;
+ }
+
+ if (object->m_prototype != NULL)
+ {
+ const as_object* prototype = (as_object*)
object->m_prototype;
+
+ it = prototype->m_members.begin();
+ while (it != prototype->m_members.end())
+ {
+ as_member member = it->second;
+
+ as_prop_flags f = member.get_member_flags();
+ //const int oldflags =
+ f.get_flags();
+ //const int newflags =
+ f.set_flags(set_true, set_false);
+ member.set_member_flags(f);
+
+ object->m_members[it->first] = member;
+
+ ++it;
+ }
+ }
+ } else {
+ as_object* object = (as_object*) obj;
+ as_object* object_props = (as_object*) props;
+
+ stringi_hash<as_member>::iterator it =
object_props->m_members.begin();
+ while(it != object_props->m_members.end())
+ {
+ const tu_stringi key =
(it->second).get_member_value().to_string();
+ stringi_hash<as_member>::iterator it2 =
object->m_members.find(key);
+
+ if (it2 != object->m_members.end())
+ {
+ as_member member = it2->second;
+
+ as_prop_flags f = member.get_member_flags();
+ //const int oldflags =
+ f.get_flags();
+ //const int newflags =
+ f.set_flags(set_true, set_false);
+ member.set_member_flags(f);
+
+
object->m_members[it->second.get_member_value().to_string()] = member;
+ }
+
+ ++it;
+ }
+ }
+}
+
+
+
+void action_init()
+ // Create/hook built-ins.
+{
+ if (s_inited == false)
+ {
+ s_inited = true;
+
+ // @@ s_global should really be a
+ // client-visible player object, which
+ // contains one or more actual movie
+ // instances. We're currently just hacking it
+ // in as an app-global mutable object :(
+ assert(s_global == NULL);
+ s_global = new as_object;
+ s_global->set_member("trace", as_value(as_global_trace));
+ s_global->set_member("Object", as_value(as_global_object_ctor));
+ s_global->set_member("Sound", as_value(as_global_sound_ctor));
+ s_global->set_member("Array", as_value(as_global_array_ctor));
- assert (base >= 2 && base <= 36);
+ s_global->set_member("TextFormat", as_value(textformat_new));
+#ifdef HAVE_LIBXML
+ s_global->set_member("XML", as_value(xml_new));
+ s_global->set_member("XMLNode", as_value(xmlnode_new));
+ //s_global->set_member("XML", as_value(xmlsocket_xml_new));
+ s_global->set_member("XMLSocket", as_value(xmlsocket_new));
+#endif // HAVE_LIBXML
+ s_global->set_member("MovieClipLoader",
as_value(moviecliploader_new));
+ s_global->set_member("String", as_value(string_ctor));
+ // This next set are all the unimplemented classes whose
+ // code was machine generated.
+ s_global->set_member("Boolean", as_value(boolean_new));
+ s_global->set_member("Camera", as_value(camera_new));
+ s_global->set_member("Color", as_value(color_new));
+ s_global->set_member("ContextMenu", as_value(contextmenu_new));
+ s_global->set_member("CustomActions", as_value(customactions_new));
+ s_global->set_member("Date", as_value(date_new));
+ s_global->set_member("Error", as_value(error_new));
+ s_global->set_member("LoadVars", as_value(loadvars_new));
+ s_global->set_member("LocalConnection",
as_value(localconnection_new));
+ s_global->set_member("Microphone", as_value(microphone_new));
+ s_global->set_member("Mouse", as_value(mouse_new));
+ s_global->set_member("NetConnection", as_value(netconnection_new));
+ s_global->set_member("NetStream", as_value(netstream_new));
+ s_global->set_member("Selection", as_value(selection_new));
+ s_global->set_member("SharedObject", as_value(sharedobject_new));
+ s_global->set_member("Stage", as_value(stage_new));
+ s_global->set_member("System", as_value(system_new));
+ s_global->set_member("TextSnapshot", as_value(textsnapshot_new));
+ s_global->set_member("Video", as_value(video_new));
+ // ASSetPropFlags
+ s_global->set_member("ASSetPropFlags", as_global_assetpropflags);
+ // unescape
+ s_global->set_member("unescape", as_global_unescape);
+ // parseFloat
+ s_global->set_member("parseFloat", as_global_parsefloat);
+ // parseInt
+ s_global->set_member("parseInt", as_global_parseint);
+ // isNan
+ s_global->set_member("isNan", as_global_isnan);
+ // isFinite
+ s_global->set_member("isFinite", as_global_isfinite);
+
+ function_init(s_global.get_ptr());
+ movieclip_init(s_global.get_ptr());
+ math_init();
+ key_init();
+ }
+}
+
+
+void action_clear()
+{
+ if (s_inited)
+ {
+ s_inited = false;
- int numdigits = 0;
+ s_global->clear();
+ s_global = NULL;
+ }
+}
- // Start at the beginning, see how many valid digits we have
- // in the base we're dealing with
- while (numdigits < int(strlen(input))
- && int(digits.find(input[numdigits])) < base
- && digits.find(input[numdigits]) != std::string::npos)
- numdigits++;
- // If we didn't get any digits, we should return NaN
- if (numdigits == 0)
- {
- fn.result->set_nan();
- return;
- }
+//
+// properties by number
+//
- for (i=0;i<numdigits;i++)
- {
- result += digits.find(input[i])*int(pow(base,numdigits
- i - 1));
- }
+static const tu_string s_property_names[] =
+{
+ tu_string("_x"),
+ tu_string("_y"),
+ tu_string("_xscale"),
+ tu_string("_yscale"),
+ tu_string("_currentframe"),
+ tu_string("_totalframes"),
+ tu_string("_alpha"),
+ tu_string("_visible"),
+ tu_string("_width"),
+ tu_string("_height"),
+ tu_string("_rotation"),
+ tu_string("_target"),
+ tu_string("_framesloaded"),
+ tu_string("_name"),
+ tu_string("_droptarget"),
+ tu_string("_url"),
+ tu_string("_highquality"),
+ tu_string("_focusrect"),
+ tu_string("_soundbuftime"),
+ tu_string("@@ mystery quality member"),
+ tu_string("_xmouse"),
+ tu_string("_ymouse"),
+};
- if (bNegative)
- result = -result;
- // Now return the parsed string
- fn.result->set_int(result);
+static as_value get_property(as_object* obj, int prop_number)
+{
+ as_value val;
+ if (prop_number >= 0 && prop_number <
int(sizeof(s_property_names)/sizeof(s_property_names[0])))
+ {
+ obj->get_member(s_property_names[prop_number], &val);
}
-
- void as_global_assetpropflags(const fn_call& fn)
- // ASSetPropFlags function
+ else
{
- const int version =
fn.env->get_target()->get_movie_definition()->get_version();
+ log_error("error: invalid property query, property number %d\n",
prop_number);
+ }
+ return val;
+}
- // Check the arguments
- assert(fn.nargs == 3 || fn.nargs == 4);
- assert((version == 5) ? (fn.nargs == 3) : true);
+static void set_property(as_object* obj, int prop_number, const as_value&
val)
+{
+ if (prop_number >= 0 && prop_number <
int(sizeof(s_property_names)/sizeof(s_property_names[0])))
+ {
+ obj->set_member(s_property_names[prop_number], val);
+ }
+ else
+ {
+ log_error("error: invalid set_property, property number %d\n",
prop_number);
+ }
+}
- // object
- as_object* const obj = fn.arg(0).to_object();
- assert(obj != NULL);
- // list of child names
- as_object* props = fn.arg(1).to_object();
- if (props == NULL) {
- // tulrich: this fires in test_ASSetPropFlags -- is it
correct?
- assert(fn.arg(1).get_type() == as_value::NULLTYPE);
- }
+//
+// do_action
+//
- // a number which represents three bitwise flags which
- // are used to determine whether the list of child names should
be hidden,
- // un-hidden, protected from over-write, un-protected from
over-write,
- // protected from deletion and un-protected from deletion
- int set_true = int(fn.arg(2).to_number()) &
as_prop_flags::as_prop_flags_mask;
- // Is another integer bitmask that works like set_true,
- // except it sets the attributes to false. The
- // set_false bitmask is applied before set_true is applied
+/// Thin wrapper around action_buffer.
+struct do_action : public execute_tag
+{
+ action_buffer m_buf;
- // ASSetPropFlags was exposed in Flash 5, however the fourth
argument 'set_false'
- // was not required as it always defaulted to the value '~0'.
- int set_false = (fn.nargs == 3 ?
- (version == 5 ? ~0 : 0) :
int(fn.arg(3).to_number()))
- & as_prop_flags::as_prop_flags_mask;
+ void read(stream* in)
+ {
+ m_buf.read(in);
+ }
- // Evan: it seems that if set_true == 0 and set_false == 0,
this function
- // acts as if the parameters where (object, null, 0x1, 0) ...
- if (set_false == 0 && set_true == 0)
- {
- props = NULL;
- set_false = 0;
- set_true = 0x1;
- }
+ virtual void execute(movie* m)
+ {
+ m->add_action_buffer(&m_buf);
+ }
- if (props == NULL)
- {
- // Take all the members of the object
+ // Don't override because actions should not be replayed when seeking the
movie.
+ //void execute_state(movie* m) {}
- as_object* object = (as_object*) obj;
+ virtual bool is_action_tag() const
+ // Tell the caller that we are an action tag.
+ {
+ return true;
+ }
+};
- stringi_hash<as_member>::const_iterator it =
object->m_members.begin();
- while (it != object->m_members.end())
- {
- as_member member = it->second;
+void do_action_loader(stream* in, int tag_type, movie_definition* m)
+{
+ IF_VERBOSE_PARSE(log_msg("tag %d: do_action_loader\n", tag_type));
+
+ IF_VERBOSE_ACTION(log_msg("-- actions in frame %d\n",
m->get_loading_frame()));
+
+ assert(in);
+ assert(tag_type == 12);
+ assert(m);
+
+ do_action* da = new do_action;
+ da->read(in);
- as_prop_flags f = member.get_member_flags();
- //const int oldflags =
- f.get_flags();
- //const int newflags =
- f.set_flags(set_true, set_false);
- member.set_member_flags(f);
+ m->add_execute_tag(da);
+}
- object->m_members[it->first] = member;
+
+//
+// do_init_action
+//
- ++it;
- }
- if (object->m_prototype != NULL)
- {
- const as_object* prototype = (as_object*)
object->m_prototype;
+void do_init_action_loader(stream* in, int tag_type, movie_definition* m)
+{
+ assert(tag_type == 59);
+
+ int sprite_character_id = in->read_u16();
+
+ IF_VERBOSE_PARSE(log_msg(" tag %d: do_init_action_loader\n", tag_type));
+ IF_VERBOSE_ACTION(log_msg(" -- init actions for sprite %d\n",
sprite_character_id));
+
+ do_action* da = new do_action;
+ da->read(in);
+ m->add_init_action(sprite_character_id, da);
+}
- it = prototype->m_members.begin();
- while (it != prototype->m_members.end())
- {
- as_member member = it->second;
- as_prop_flags f =
member.get_member_flags();
- //const int oldflags =
- f.get_flags();
- //const int newflags =
- f.set_flags(set_true, set_false);
- member.set_member_flags(f);
+//
+// action_buffer
+//
- object->m_members[it->first] = member;
+// Disassemble one instruction to the log.
+static void log_disasm(const unsigned char* instruction_data);
- ++it;
- }
- }
- } else {
- as_object* object = (as_object*) obj;
- as_object* object_props = (as_object*) props;
+action_buffer::action_buffer()
+ :
+ m_decl_dict_processed_at(-1)
+{
+}
- stringi_hash<as_member>::iterator it =
object_props->m_members.begin();
- while(it != object_props->m_members.end())
- {
- const tu_stringi key =
(it->second).get_member_value().to_string();
- stringi_hash<as_member>::iterator it2 =
object->m_members.find(key);
- if (it2 != object->m_members.end())
- {
- as_member member = it2->second;
+void action_buffer::read(stream* in)
+{
+ // Read action bytes.
+ for (;;)
+ {
+ int instruction_start = m_buffer.size();
- as_prop_flags f =
member.get_member_flags();
- //const int oldflags =
- f.get_flags();
- //const int newflags =
- f.set_flags(set_true, set_false);
- member.set_member_flags(f);
+ int pc = m_buffer.size();
-
object->m_members[it->second.get_member_value().to_string()] = member;
- }
+ int action_id = in->read_u8();
+ m_buffer.push_back(action_id);
- ++it;
+ if (action_id & 0x80)
+ {
+ // Action contains extra data. Read it.
+ int length = in->read_u16();
+ m_buffer.push_back(length & 0x0FF);
+ m_buffer.push_back((length >> 8) & 0x0FF);
+ for (int i = 0; i < length; i++)
+ {
+ unsigned char b = in->read_u8();
+ m_buffer.push_back(b);
}
}
- }
-
+ IF_VERBOSE_ACTION(log_msg("%4d\t", pc);
log_disasm(&m_buffer[instruction_start]); );
- void action_init()
- // Create/hook built-ins.
- {
- if (s_inited == false)
+ if (action_id == 0)
{
- s_inited = true;
-
- // @@ s_global should really be a
- // client-visible player object, which
- // contains one or more actual movie
- // instances. We're currently just hacking it
- // in as an app-global mutable object :(
- assert(s_global == NULL);
- s_global = new as_object;
- s_global->set_member("trace",
as_value(as_global_trace));
- s_global->set_member("Object",
as_value(as_global_object_ctor));
- s_global->set_member("Sound",
as_value(as_global_sound_ctor));
- s_global->set_member("Array",
as_value(as_global_array_ctor));
+ // end of action buffer.
+ break;
+ }
+ }
+}
- s_global->set_member("TextFormat",
as_value(textformat_new));
-#ifdef HAVE_LIBXML
- s_global->set_member("XML", as_value(xml_new));
- s_global->set_member("XMLNode", as_value(xmlnode_new));
- //s_global->set_member("XML",
as_value(xmlsocket_xml_new));
- s_global->set_member("XMLSocket",
as_value(xmlsocket_new));
-#endif // HAVE_LIBXML
- s_global->set_member("MovieClipLoader",
as_value(moviecliploader_new));
- s_global->set_member("String", as_value(string_ctor));
- // This next set are all the unimplemented classes whose
- // code was machine generated.
- s_global->set_member("Boolean", as_value(boolean_new));
- s_global->set_member("Camera", as_value(camera_new));
- s_global->set_member("Color", as_value(color_new));
- s_global->set_member("ContextMenu",
as_value(contextmenu_new));
- s_global->set_member("CustomActions",
as_value(customactions_new));
- s_global->set_member("Date", as_value(date_new));
- s_global->set_member("Error", as_value(error_new));
- s_global->set_member("LoadVars",
as_value(loadvars_new));
- s_global->set_member("LocalConnection",
as_value(localconnection_new));
- s_global->set_member("Microphone",
as_value(microphone_new));
- s_global->set_member("Mouse", as_value(mouse_new));
- s_global->set_member("NetConnection",
as_value(netconnection_new));
- s_global->set_member("NetStream",
as_value(netstream_new));
- s_global->set_member("Selection",
as_value(selection_new));
- s_global->set_member("SharedObject",
as_value(sharedobject_new));
- s_global->set_member("Stage", as_value(stage_new));
- s_global->set_member("System", as_value(system_new));
- s_global->set_member("TextSnapshot",
as_value(textsnapshot_new));
- s_global->set_member("Video", as_value(video_new));
- // ASSetPropFlags
- s_global->set_member("ASSetPropFlags",
as_global_assetpropflags);
- // unescape
- s_global->set_member("unescape", as_global_unescape);
- // parseFloat
- s_global->set_member("parseFloat",
as_global_parsefloat);
- // parseInt
- s_global->set_member("parseInt", as_global_parseint);
- // isNan
- s_global->set_member("isNan", as_global_isnan);
- // isFinite
- s_global->set_member("isFinite", as_global_isfinite);
-
- function_init(s_global.get_ptr());
- movieclip_init(s_global.get_ptr());
- math_init();
- key_init();
- }
- }
-
-
- void action_clear()
- {
- if (s_inited)
- {
- s_inited = false;
-
- s_global->clear();
- s_global = NULL;
- }
- }
-
-
- //
- // properties by number
- //
-
- static const tu_string s_property_names[] =
- {
- tu_string("_x"),
- tu_string("_y"),
- tu_string("_xscale"),
- tu_string("_yscale"),
- tu_string("_currentframe"),
- tu_string("_totalframes"),
- tu_string("_alpha"),
- tu_string("_visible"),
- tu_string("_width"),
- tu_string("_height"),
- tu_string("_rotation"),
- tu_string("_target"),
- tu_string("_framesloaded"),
- tu_string("_name"),
- tu_string("_droptarget"),
- tu_string("_url"),
- tu_string("_highquality"),
- tu_string("_focusrect"),
- tu_string("_soundbuftime"),
- tu_string("@@ mystery quality member"),
- tu_string("_xmouse"),
- tu_string("_ymouse"),
- };
+/*private*/
+void action_buffer::process_decl_dict(int start_pc, int stop_pc)
+ // Interpret the decl_dict opcode. Don't read stop_pc or
+ // later. A dictionary is some static strings embedded in the
+ // action buffer; there should only be one dictionary per
+ // action buffer.
+ //
+ // NOTE: Normally the dictionary is declared as the first
+ // action in an action buffer, but I've seen what looks like
+ // some form of copy protection that amounts to:
+ //
+ // <start of action buffer>
+ // push true
+ // branch_if_true label
+ // decl_dict [0] // this is never executed, but has lots of
orphan data declared in the opcode
+ // label: // (embedded inside the previous opcode; looks like an invalid
jump)
+ // ... "protected" code here, including the real decl_dict opcode
...
+ // <end of the dummy decl_dict [0] opcode>
+ //
+ // So we just interpret the first decl_dict we come to, and
+ // cache the results. If we ever hit a different decl_dict in
+ // the same action_buffer, then we log an error and ignore it.
+{
+ assert(stop_pc <= (int) m_buffer.size());
- static as_value get_property(as_object* obj, int prop_number)
+ if (m_decl_dict_processed_at == start_pc)
{
- as_value val;
- if (prop_number >= 0 && prop_number <
int(sizeof(s_property_names)/sizeof(s_property_names[0])))
- {
- obj->get_member(s_property_names[prop_number], &val);
- }
- else
- {
- log_error("error: invalid property query, property
number %d\n", prop_number);
- }
- return val;
+ // We've already processed this decl_dict.
+ int count = m_buffer[start_pc + 3] | (m_buffer[start_pc + 4] << 8);
+ assert((int) m_dictionary.size() == count);
+ UNUSED(count);
+ return;
}
- static void set_property(as_object* obj, int prop_number, const
as_value& val)
+ if (m_decl_dict_processed_at != -1)
{
- if (prop_number >= 0 && prop_number <
int(sizeof(s_property_names)/sizeof(s_property_names[0])))
- {
- obj->set_member(s_property_names[prop_number], val);
- }
- else
- {
- log_error("error: invalid set_property, property number
%d\n", prop_number);
- }
+ log_error("error: process_decl_dict(%d, %d): decl_dict was already
processed at %d\n",
+ start_pc,
+ stop_pc,
+ m_decl_dict_processed_at);
+ return;
}
+ m_decl_dict_processed_at = start_pc;
- //
- // do_action
- //
-
+ // Actual processing.
+ int i = start_pc;
+ int length = m_buffer[i + 1] | (m_buffer[i + 2] << 8);
+ int count = m_buffer[i + 3] | (m_buffer[i + 4] << 8);
+ i += 2;
- /// Thin wrapper around action_buffer.
- struct do_action : public execute_tag
- {
- action_buffer m_buf;
+ UNUSED(length);
- void read(stream* in)
- {
- m_buf.read(in);
- }
+ assert(start_pc + 3 + length == stop_pc);
- virtual void execute(movie* m)
- {
- m->add_action_buffer(&m_buf);
- }
+ m_dictionary.resize(count);
- // Don't override because actions should not be replayed when
seeking the movie.
- //void execute_state(movie* m) {}
+ // Index the strings.
+ for (int ct = 0; ct < count; ct++)
+ {
+ // Point into the current action buffer.
+ m_dictionary[ct] = (const char*) &m_buffer[3 + i];
- virtual bool is_action_tag() const
- // Tell the caller that we are an action tag.
+ while (m_buffer[3 + i])
{
- return true;
+ // safety check.
+ if (i >= stop_pc)
+ {
+ log_error("error: action buffer dict length
exceeded\n");
+
+ // Jam something into the remaining (invalid)
entries.
+ while (ct < count)
+ {
+ m_dictionary[ct] = "<invalid>";
+ ct++;
+ }
+ return;
+ }
+ i++;
}
- };
+ i++;
+ }
+}
- void do_action_loader(stream* in, int tag_type, movie_definition* m)
- {
- IF_VERBOSE_PARSE(log_msg("tag %d: do_action_loader\n",
tag_type));
- IF_VERBOSE_ACTION(log_msg("-- actions in frame %d\n",
m->get_loading_frame()));
+void action_buffer::execute(as_environment* env)
+ // Interpret the actions in this action buffer, and evaluate
+ // them in the given environment. Execute our whole buffer,
+ // without any arguments passed in.
+{
+ int local_stack_top = env->get_local_frame_top();
+ env->add_frame_barrier();
- assert(in);
- assert(tag_type == 12);
- assert(m);
-
- do_action* da = new do_action;
- da->read(in);
+ std::vector<with_stack_entry> empty_with_stack;
+ execute(env, 0, m_buffer.size(), NULL, empty_with_stack, false /* not
function2 */);
- m->add_execute_tag(da);
- }
+ env->set_local_frame_top(local_stack_top);
+}
-
- //
- // do_init_action
- //
+/*private*/
+void
+action_buffer::doActionNew(as_environment* env,
+ std::vector<with_stack_entry>& with_stack)
+{
+ as_value classname = env->pop();
+ IF_VERBOSE_ACTION(log_msg("---new object: %s\n",
+ classname.to_tu_string().c_str()));
+ int nargs = (int) env->pop().to_number();
- void do_init_action_loader(stream* in, int tag_type,
movie_definition* m)
+ as_value constructor = env->get_variable(classname.to_tu_string(),
with_stack);
+ as_value new_obj;
+ if (constructor.get_type() == as_value::C_FUNCTION)
+ {
+ //log_msg("Constructor is a C_FUNCTION\n");
+ // C function is responsible for creating the new object and
setting members.
+ (constructor.to_c_function())(fn_call(&new_obj, NULL, env, nargs,
env->get_top_index()));
+ }
+ else if (function_as_object* ctor_as_func = constructor.to_as_function())
{
- assert(tag_type == 59);
+ // This function is being used as a constructor; make sure
+ // it has a prototype object.
+ //log_msg("Constructor is an AS_FUNCTION\n");
+
+ // Set up the prototype.
+ as_value proto;
+ bool func_has_prototype = \
+ ctor_as_func->get_member("prototype", &proto);
+ assert(func_has_prototype);
- int sprite_character_id = in->read_u16();
+ //log_msg("constructor prototype is %s\n", proto.to_string());
- IF_VERBOSE_PARSE(log_msg(" tag %d: do_init_action_loader\n",
tag_type));
- IF_VERBOSE_ACTION(log_msg(" -- init actions for sprite %d\n",
sprite_character_id));
+ // Create an empty object, with a ref to the constructor's
prototype.
+ smart_ptr<as_object> new_obj_ptr(new
as_object(proto.to_object()));
+
+ new_obj.set_as_object(new_obj_ptr.get_ptr());
- do_action* da = new do_action;
- da->read(in);
- m->add_init_action(sprite_character_id, da);
+ // Call the actual constructor function; new_obj is its 'this'.
+ // We don't need the function result.
+ call_method(constructor, env, new_obj_ptr.get_ptr(), nargs,
env->get_top_index());
+ }
+ else
+ {
+ if (classname != "String") {
+ log_error("can't create object with unknown class '%s'\n",
+ classname.to_tu_string().c_str());
+ } else {
+ log_msg("Created special String class\n");
+ }
}
+ env->drop(nargs);
+ env->push(new_obj);
+#if 0
+ log_msg("new object %s at %p\n", classname.to_tu_string().c_str(),
new_obj);
+#endif
+}
- //
- // action_buffer
- //
+/*private*/
+void
+action_buffer::doActionInstanceOf(as_environment* env)
+{
+ // Get the "super" function
+ function_as_object* super = env->top(0).to_as_function();
- // Disassemble one instruction to the log.
- static void log_disasm(const unsigned char* instruction_data);
+ // Get the "instance"
+ as_object* instance = env->top(1).to_object();
- action_buffer::action_buffer()
- :
- m_decl_dict_processed_at(-1)
+ // Invalid args!
+ if ( ! super || ! instance )
{
- }
+ //IF_VERBOSE_ACTION(
+ log_msg("-- %s instance_of %s (invalid args?)\n",
+ env->top(1).to_string(),
+ env->top(0).to_string());
+ //);
+ env->drop(1);
+ env->top(0) = as_value(false);
+ return;
+ }
- void action_buffer::read(stream* in)
- {
- // Read action bytes.
- for (;;)
- {
- int instruction_start = m_buffer.size();
+ env->drop(1);
+ env->top(0) = as_value(instance->instanceOf(super));
- int pc = m_buffer.size();
+ //log_msg("tocheck: opcode %x\n", SWF::ACTION_INSTANCEOF);
+}
- int action_id = in->read_u8();
- m_buffer.push_back(action_id);
+void
+action_buffer::doActionCast(as_environment* env)
+{
+ // Get the "super" function
+ function_as_object* super = env->top(0).to_as_function();
- if (action_id & 0x80)
- {
- // Action contains extra data. Read it.
- int length = in->read_u16();
- m_buffer.push_back(length & 0x0FF);
- m_buffer.push_back((length >> 8) & 0x0FF);
- for (int i = 0; i < length; i++)
- {
- unsigned char b = in->read_u8();
- m_buffer.push_back(b);
- }
- }
+ // Get the "instance"
+ as_object* instance = env->top(1).to_object();
- IF_VERBOSE_ACTION(log_msg("%4d\t", pc);
log_disasm(&m_buffer[instruction_start]); );
+ // Invalid args!
+ if ( ! super || ! instance )
+ {
+ //IF_VERBOSE_ACTION(
+ log_msg("-- %s instance_of %s (invalid args?)\n",
+ env->top(1).to_string(),
+ env->top(0).to_string());
+ //);
- if (action_id == 0)
- {
- // end of action buffer.
- break;
- }
- }
+ env->drop(1);
+ env->top(0) = as_value();
+ return;
}
+ env->drop(1);
+ if ( instance->instanceOf(super) ) {
+ env->top(0) = as_value(instance);
+ } else {
+ env->top(0) = as_value();
+ }
- /*private*/
- void action_buffer::process_decl_dict(int start_pc, int stop_pc)
- // Interpret the decl_dict opcode. Don't read stop_pc or
- // later. A dictionary is some static strings embedded in the
- // action buffer; there should only be one dictionary per
- // action buffer.
- //
- // NOTE: Normally the dictionary is declared as the first
- // action in an action buffer, but I've seen what looks like
- // some form of copy protection that amounts to:
- //
- // <start of action buffer>
- // push true
- // branch_if_true label
- // decl_dict [0] // this is never executed, but has lots
of orphan data declared in the opcode
- // label: // (embedded inside the previous opcode; looks like an
invalid jump)
- // ... "protected" code here, including the real decl_dict
opcode ...
- // <end of the dummy decl_dict [0] opcode>
- //
- // So we just interpret the first decl_dict we come to, and
- // cache the results. If we ever hit a different decl_dict in
- // the same action_buffer, then we log an error and ignore it.
- {
- assert(stop_pc <= (int) m_buffer.size());
-
- if (m_decl_dict_processed_at == start_pc)
- {
- // We've already processed this decl_dict.
- int count = m_buffer[start_pc + 3] |
(m_buffer[start_pc + 4] << 8);
- assert((int) m_dictionary.size() == count);
- UNUSED(count);
- return;
- }
-
- if (m_decl_dict_processed_at != -1)
- {
- log_error("error: process_decl_dict(%d, %d): decl_dict
was already processed at %d\n",
- start_pc,
- stop_pc,
- m_decl_dict_processed_at);
- return;
- }
-
- m_decl_dict_processed_at = start_pc;
-
- // Actual processing.
- int i = start_pc;
- int length = m_buffer[i + 1] | (m_buffer[i + 2] << 8);
- int count = m_buffer[i + 3] | (m_buffer[i + 4] << 8);
- i += 2;
-
- UNUSED(length);
-
- assert(start_pc + 3 + length == stop_pc);
-
- m_dictionary.resize(count);
+ //log_msg("tocheck: opcode %x\n", SWF::ACTION_CASTOP);
+}
- // Index the strings.
- for (int ct = 0; ct < count; ct++)
- {
- // Point into the current action buffer.
- m_dictionary[ct] = (const char*) &m_buffer[3 + i];
- while (m_buffer[3 + i])
- {
- // safety check.
- if (i >= stop_pc)
- {
- log_error("error: action buffer dict
length exceeded\n");
+/*private*/
+void
+action_buffer::doActionCallMethod(as_environment* env)
+{
+ // Some corner case behaviors depend on the SWF file version.
+ //int version = env->get_target()->get_movie_definition()->get_version();
- // Jam something into the remaining
(invalid) entries.
- while (ct < count)
- {
- m_dictionary[ct] = "<invalid>";
- ct++;
- }
- return;
- }
- i++;
- }
- i++;
- }
- }
+ // Get name of the method
+ const tu_string& method_name = env->top(0).to_tu_string();
+ //log_msg(" method name: %s\n", method_name.c_str());
+ // Get an object
+ as_value& obj_value = env->top(1);
+ as_object* obj = obj_value.to_object();
+ //log_msg(" method object: %p\n", obj);
- void action_buffer::execute(as_environment* env)
- // Interpret the actions in this action buffer, and evaluate
- // them in the given environment. Execute our whole buffer,
- // without any arguments passed in.
- {
- int local_stack_top = env->get_local_frame_top();
- env->add_frame_barrier();
+ // Get number of arguments
+ int nargs = (int) env->top(2).to_number();
+ //log_msg(" method nargs: %d\n", nargs);
- std::vector<with_stack_entry> empty_with_stack;
- execute(env, 0, m_buffer.size(), NULL, empty_with_stack, false
/* not function2 */);
+ as_value result;
- env->set_local_frame_top(local_stack_top);
+ if (!obj)
+ {
+ log_error("error: call_method invoked in something that "
+ "doesn't cast to an as_object: %s\n",
+ typeid(obj_value).name());
}
-
-
- /*private*/
- void
- action_buffer::doActionNew(as_environment* env,
- std::vector<with_stack_entry>& with_stack)
+ else
{
- as_value classname = env->pop();
- IF_VERBOSE_ACTION(log_msg("---new object: %s\n",
- classname.to_tu_string().c_str()));
- int nargs = (int) env->pop().to_number();
-
- as_value constructor =
env->get_variable(classname.to_tu_string(), with_stack);
- as_value new_obj;
- if (constructor.get_type() == as_value::C_FUNCTION)
+ as_value method;
+ if (obj->get_member(method_name, &method))
{
- //log_msg("Constructor is a C_FUNCTION\n");
- // C function is responsible for creating the new
object and setting members.
- (constructor.to_c_function())(fn_call(&new_obj, NULL,
env, nargs, env->get_top_index()));
+ if (method.get_type() != as_value::AS_FUNCTION &&
+ method.get_type() != as_value::C_FUNCTION)
+ {
+ log_error("error: call_method: '%s' is not a
method\n",
+ method_name.c_str());
+ }
+ else
+ {
+ result = call_method( method, env, obj, nargs,
+ env->get_top_index() - 3);
+ }
}
- else if (function_as_object* ctor_as_func =
constructor.to_as_function())
+ else
{
- // This function is being used as a constructor; make
sure
- // it has a prototype object.
- //log_msg("Constructor is an AS_FUNCTION\n");
+ log_error("error: call_method can't find method %s "
+ "for object %s (%p)\n", method_name.c_str(),
+ typeid(*obj).name(), obj);
+ }
+ }
- // Set up the prototype.
- as_value proto;
- bool func_has_prototype = \
- ctor_as_func->get_member("prototype", &proto);
- assert(func_has_prototype);
+ env->drop(nargs + 2);
+ env->top(0) = result;
- //log_msg("constructor prototype is %s\n",
proto.to_string());
+ // This is to check stack status after call method
+ //log_msg("at doActionCallMethod() end, stack: \n"); env->dump_stack();
+}
- // Create an empty object, with a ref to the
constructor's prototype.
- smart_ptr<as_object> new_obj_ptr(new
as_object(proto.to_object()));
-
- new_obj.set_as_object(new_obj_ptr.get_ptr());
+/*private*/
+void
+action_buffer::doActionCallFunction(as_environment* env,
+ std::vector<with_stack_entry>& with_stack)
+{
+ as_value function;
+ if (env->top(0).get_type() == as_value::STRING)
+ {
+ // Function is a string; lookup the function.
+ const tu_string& function_name = env->top(0).to_tu_string();
+ function = env->get_variable(function_name, with_stack);
- // Call the actual constructor function; new_obj is its
'this'.
- // We don't need the function result.
- call_method(constructor, env, new_obj_ptr.get_ptr(),
nargs, env->get_top_index());
- }
- else
+ if (function.get_type() != as_value::AS_FUNCTION &&
+ function.get_type() != as_value::C_FUNCTION)
{
- if (classname != "String") {
- log_error("can't create object with unknown
class '%s'\n",
- classname.to_tu_string().c_str());
- } else {
- log_msg("Created special String class\n");
- }
+ log_error("error in call_function: '%s' is not a
function\n",
+ function_name.c_str());
}
-
- env->drop(nargs);
- env->push(new_obj);
-#if 0
- log_msg("new object %s at %p\n",
classname.to_tu_string().c_str(), new_obj);
-#endif
}
-
- /*private*/
- void
- action_buffer::doActionInstanceOf(as_environment* env)
+ else
{
- // Get the "super" function
- function_as_object* super = env->top(0).to_as_function();
-
- // Get the "instance"
- as_object* instance = env->top(1).to_object();
-
- // Invalid args!
- if ( ! super || ! instance )
- {
- //IF_VERBOSE_ACTION(
- log_msg("-- %s instance_of %s (invalid args?)\n",
- env->top(1).to_string(),
- env->top(0).to_string());
- //);
-
- env->drop(1);
- env->top(0) = as_value(false);
- return;
- }
+ // Hopefully the actual
+ // function object is here.
+ // QUESTION: would this be
+ // an ActionScript-defined
+ // function ?
+ function = env->top(0);
+ }
+ int nargs = (int) env->top(1).to_number();
- env->drop(1);
- env->top(0) = as_value(instance->instanceOf(super));
+ as_value result = call_method(function, env, env->get_target(),
+ nargs, env->get_top_index() - 2);
- //log_msg("tocheck: opcode %x\n", SWF::ACTION_INSTANCEOF);
- }
+ env->drop(nargs + 1);
+ env->top(0) = result;
+}
- void
- action_buffer::doActionCast(as_environment* env)
- {
- // Get the "super" function
- function_as_object* super = env->top(0).to_as_function();
+/*private*/
+void
+action_buffer::doActionDefineFunction(as_environment* env,
+ std::vector<with_stack_entry>&
with_stack, int pc, int* next_pc)
+{
- // Get the "instance"
- as_object* instance = env->top(1).to_object();
+ // Create a new function_as_object
+ function_as_object* func = new function_as_object(this, env, *next_pc,
with_stack);
- // Invalid args!
- if ( ! super || ! instance )
- {
- //IF_VERBOSE_ACTION(
- log_msg("-- %s instance_of %s (invalid args?)\n",
- env->top(1).to_string(),
- env->top(0).to_string());
- //);
+ int i = pc;
+ i += 3;
- env->drop(1);
- env->top(0) = as_value();
- return;
- }
+ // Extract name.
+ // @@ security: watch out for possible missing terminator here!
+ tu_string name = (const char*) &m_buffer[i];
+ i += name.length() + 1;
- env->drop(1);
- if ( instance->instanceOf(super) ) {
- env->top(0) = as_value(instance);
- } else {
- env->top(0) = as_value();
- }
+ // Get number of arguments.
+ int nargs = m_buffer[i] | (m_buffer[i + 1] << 8);
+ i += 2;
- //log_msg("tocheck: opcode %x\n", SWF::ACTION_CASTOP);
+ // Get the names of the arguments.
+ for (int n = 0; n < nargs; n++)
+ {
+ // @@ security: watch out for possible missing terminator here!
+ func->add_arg(0, (const char*) &m_buffer[i]);
+ i += func->m_args.back().m_name.length() + 1;
}
+ // Get the length of the actual function code.
+ int length = m_buffer[i] | (m_buffer[i + 1] << 8);
+ i += 2;
+ func->set_length(length);
- /*private*/
- void
- action_buffer::doActionCallMethod(as_environment* env)
+ // Skip the function body (don't interpret it now).
+ *next_pc += length;
+
+ // If we have a name, then save the function in this
+ // environment under that name.
+ as_value function_value(func);
+ if (name.length() > 0)
{
- // Some corner case behaviors depend on the SWF file version.
- //int version =
env->get_target()->get_movie_definition()->get_version();
+ // @@ NOTE: should this be m_target->set_variable()???
+ env->set_member(name, function_value);
+ }
- // Get name of the method
- const tu_string& method_name =
env->top(0).to_tu_string();
- //log_msg(" method name: %s\n", method_name.c_str());
+ // Also leave it on the stack.
+ env->push_val(function_value);
+}
- // Get an object
- as_value& obj_value = env->top(1);
- as_object* obj = obj_value.to_object();
- //log_msg(" method object: %p\n", obj);
+/*private*/
+void
+action_buffer::doActionDefineFunction2(as_environment* env,
+ std::vector<with_stack_entry>&
with_stack, int pc, int* next_pc)
+{
+ function_as_object* func = new function_as_object(this, env,
*next_pc, with_stack);
+ func->set_is_function2();
- // Get number of arguments
- int nargs = (int) env->top(2).to_number();
- //log_msg(" method nargs: %d\n", nargs);
+ int i = pc;
+ i += 3;
- as_value result;
+ // Extract name.
+ // @@ security: watch out for possible missing terminator here!
+ tu_string name = (const char*) &m_buffer[i];
+ i += name.length() + 1;
- if (!obj)
- {
- log_error("error: call_method invoked in something that
"
- "doesn't cast to an as_object: %s\n",
- typeid(obj_value).name());
- }
- else
- {
- as_value method;
- if (obj->get_member(method_name, &method))
- {
- if (method.get_type() != as_value::AS_FUNCTION
&&
- method.get_type() !=
as_value::C_FUNCTION)
- {
- log_error("error: call_method: '%s' is
not a method\n",
- method_name.c_str());
- }
- else
- {
- result = call_method( method, env, obj,
nargs,
- env->get_top_index() - 3);
- }
- }
- else
- {
- log_error("error: call_method can't find method
%s "
- "for object %s (%p)\n",
method_name.c_str(),
- typeid(*obj).name(), obj);
- }
- }
+ // Get number of arguments.
+ int nargs = m_buffer[i] | (m_buffer[i + 1] << 8);
+ i += 2;
- env->drop(nargs + 2);
- env->top(0) = result;
+ // Get the count of local registers used by this function.
+ uint8 register_count = m_buffer[i];
+ i += 1;
+ func->set_local_register_count(register_count);
- // This is to check stack status after call method
- //log_msg("at doActionCallMethod() end, stack: \n");
env->dump_stack();
- }
+ // Flags, for controlling register assignment of implicit args.
+ uint16 flags = m_buffer[i] | (m_buffer[i + 1] << 8);
+ i += 2;
+ func->set_function2_flags(flags);
- /*private*/
- void
- action_buffer::doActionCallFunction(as_environment* env,
- std::vector<with_stack_entry>& with_stack)
+ // Get the register assignments and names of the arguments.
+ for (int n = 0; n < nargs; n++)
{
- as_value function;
- if (env->top(0).get_type() == as_value::STRING)
- {
- // Function is a string; lookup the function.
- const tu_string& function_name =
env->top(0).to_tu_string();
- function = env->get_variable(function_name, with_stack);
-
- if (function.get_type() != as_value::AS_FUNCTION &&
- function.get_type() != as_value::C_FUNCTION)
- {
- log_error("error in call_function: '%s' is not
a function\n",
- function_name.c_str());
- }
- }
- else
- {
- // Hopefully the actual
- // function object is here.
- // QUESTION: would this be
- // an ActionScript-defined
- // function ?
- function = env->top(0);
- }
- int nargs = (int) env->top(1).to_number();
-
- as_value result = call_method(function, env, env->get_target(),
- nargs, env->get_top_index() - 2);
+ int arg_register = m_buffer[i];
+ i++;
- env->drop(nargs + 1);
- env->top(0) = result;
+ // @@ security: watch out for possible missing terminator here!
+ func->add_arg(arg_register, (const char*) &m_buffer[i]);
+ i += func->m_args.back().m_name.length() + 1;
}
- /*private*/
- void
- action_buffer::doActionDefineFunction(as_environment* env,
- std::vector<with_stack_entry>& with_stack, int pc, int*
next_pc)
- {
+ // Get the length of the actual function code.
+ int length = m_buffer[i] | (m_buffer[i + 1] << 8);
+ i += 2;
+ func->set_length(length);
- // Create a new function_as_object
- function_as_object* func = new function_as_object(this, env,
*next_pc, with_stack);
+ // Skip the function body (don't interpret it now).
+ *next_pc += length;
- int i = pc;
- i += 3;
+ // If we have a name, then save the function in this
+ // environment under that name.
+ as_value function_value(func);
+ if (name.length() > 0)
+ {
+ // @@ NOTE: should this be m_target->set_variable()???
+ env->set_member(name, function_value);
+ }
- // Extract name.
- // @@ security: watch out for possible missing terminator here!
- tu_string name = (const char*) &m_buffer[i];
- i += name.length() + 1;
+ // Also leave it on the stack.
+ env->push_val(function_value);
- // Get number of arguments.
- int nargs = m_buffer[i] | (m_buffer[i + 1] << 8);
- i += 2;
+}
- // Get the names of the arguments.
- for (int n = 0; n < nargs; n++)
- {
- // @@ security: watch out for possible missing
terminator here!
- func->add_arg(0, (const char*) &m_buffer[i]);
- i += func->m_args.back().m_name.length() + 1;
- }
+/*private*/
+void
+action_buffer::doActionGetMember(as_environment* env)
+{
+ // Some corner case behaviors depend on the SWF file version.
+ int version = env->get_target()->get_movie_definition()->get_version();
- // Get the length of the actual function code.
- int length = m_buffer[i] | (m_buffer[i + 1] << 8);
- i += 2;
- func->set_length(length);
+ as_value member_name = env->top(0);
+ as_value target = env->top(1);
- // Skip the function body (don't interpret it now).
- *next_pc += length;
+ as_object* obj = target.to_object();
+ if (! obj)
+ {
+ IF_VERBOSE_DEBUG(log_msg("getMember called against "
+ "a value that does not cast "
+ "to an as_object: %s\n",
target.to_string()));
+ env->top(1).set_undefined();
+ env->drop(1);
+ return;
+ }
- // If we have a name, then save the function in this
- // environment under that name.
- as_value function_value(func);
- if (name.length() > 0)
- {
- // @@ NOTE: should this be m_target->set_variable()???
- env->set_member(name, function_value);
- }
+ IF_VERBOSE_ACTION(log_msg(" doActionGetMember: target: %s (object %p)\n",
target.to_string(), obj));
- // Also leave it on the stack.
- env->push_val(function_value);
+ // Special case: String has a member "length"
+ // @@ FIXME: we shouldn't have all this "special" cases --strk;
+ if (target.get_type() == as_value::STRING && member_name.to_tu_stringi()
== "length")
+ {
+ int len = target.to_tu_string_versioned(version).utf8_length();
+ env->top(1).set_int(len);
}
-
- /*private*/
- void
- action_buffer::doActionDefineFunction2(as_environment* env,
- std::vector<with_stack_entry>& with_stack, int pc, int*
next_pc)
+ else
{
- function_as_object* func = new function_as_object(this,
env, *next_pc, with_stack);
- func->set_is_function2();
+ if ( ! obj->get_member(member_name.to_tu_string(), &(env->top(1))) )
+ env->top(1).set_undefined();
+
+ IF_VERBOSE_ACTION(log_msg("-- get_member %s=%s\n",
+ member_name.to_tu_string().c_str(),
+ env->top(1).to_tu_string().c_str()));
+ }
+ env->drop(1);
+}
+
+/*private*/
+void action_buffer::doActionStrictEquals(as_environment* env)
+{
+//log_msg("%s\n", __PRETTY_FUNCTION__);
+ // @@ identical to untyped equal, as far as I can tell...
+ env->top(1).set_bool(env->top(1) == env->top(0));
+ env->drop(1);
+}
+
+/*private*/
+void action_buffer::doActionEquals(as_environment* env)
+{
+//log_msg("%s\n", __PRETTY_FUNCTION__);
+ env->top(1).set_bool(env->top(1).to_tu_string() ==
env->top(0).to_tu_string());
+ env->drop(1);
+}
+
+/*private*/
+void action_buffer::doActionDelete(as_environment* env,
+ std::vector<with_stack_entry>& with_stack)
+{
+ log_error("todo opcode: %02X\n", SWF::ACTION_DELETEVAR);
+}
+
+/*private*/
+void action_buffer::doActionDelete2(as_environment* env,
+ std::vector<with_stack_entry>& with_stack)
+{
+ as_value var = env->top(0);
+
+ //log_msg("delete %s\n", var.to_string());
+
+ as_value oldval = env->get_variable_raw(var.to_tu_string(),
+ with_stack);
+
+ if ( ! oldval.get_type() == as_value::UNDEFINED )
+ {
+ // set variable to 'undefined'
+ // that hopefully --ref_count and eventually
+ // release memory.
+ env->set_variable_raw(var.to_tu_string(),
+ as_value(), with_stack);
+ env->top(0).set_bool(true);
+ }
+ else
+ {
+ env->top(0).set_bool(false);
+ }
+
+ //log_error("tocheck opcode: %02X\n", SWF::ACTION_DELETE);
+}
+
+
+void action_buffer::execute(
+ as_environment* env,
+ int start_pc,
+ int exec_bytes,
+ as_value* retval,
+ const std::vector<with_stack_entry>& initial_with_stack,
+ bool is_function2)
+ // Interpret the specified subset of the actions in our
+ // buffer. Caller is responsible for cleaning up our local
+ // stack frame (it may have passed its arguments in via the
+ // local stack frame).
+ //
+ // The is_function2 flag determines whether to use global or local
registers.
+{
+ action_init(); // @@ stick this somewhere else; need some global
static init function
- int i = pc;
- i += 3;
+ assert(env);
- // Extract name.
- // @@ security: watch out for possible missing terminator here!
- tu_string name = (const char*) &m_buffer[i];
- i += name.length() + 1;
+ std::vector<with_stack_entry> with_stack(initial_with_stack);
- // Get number of arguments.
- int nargs = m_buffer[i] | (m_buffer[i + 1] << 8);
- i += 2;
+ // Some corner case behaviors depend on the SWF file version.
+ int version = env->get_target()->get_movie_definition()->get_version();
- // Get the count of local registers used by this function.
- uint8 register_count = m_buffer[i];
- i += 1;
- func->set_local_register_count(register_count);
+#if 0
+ // Check the time
+ if (periodic_events.expired()) {
+ periodic_events.poll_event_handlers(env);
+ }
+#endif
+
+ movie* original_target = env->get_target();
+ UNUSED(original_target); // Avoid warnings.
- // Flags, for controlling register assignment of implicit args.
- uint16 flags = m_buffer[i] | (m_buffer[i + 1] << 8);
- i += 2;
- func->set_function2_flags(flags);
+ int stop_pc = start_pc + exec_bytes;
- // Get the register assignments and names of the arguments.
- for (int n = 0; n < nargs; n++)
+ for (int pc = start_pc; pc < stop_pc; )
+ {
+ // Cleanup any expired "with" blocks.
+ while (with_stack.size() > 0
+ && pc >= with_stack.back().m_block_end_pc)
{
- int arg_register = m_buffer[i];
- i++;
-
- // @@ security: watch out for possible missing
terminator here!
- func->add_arg(arg_register, (const char*) &m_buffer[i]);
- i += func->m_args.back().m_name.length() + 1;
+ // Drop this stack element
+ with_stack.resize(with_stack.size() - 1);
}
- // Get the length of the actual function code.
- int length = m_buffer[i] | (m_buffer[i + 1] << 8);
- i += 2;
- func->set_length(length);
-
- // Skip the function body (don't interpret it now).
- *next_pc += length;
-
- // If we have a name, then save the function in this
- // environment under that name.
- as_value function_value(func);
- if (name.length() > 0)
+ // Get the opcode.
+ int action_id = m_buffer[pc];
+ if ((action_id & 0x80) == 0)
{
- // @@ NOTE: should this be m_target->set_variable()???
- env->set_member(name, function_value);
- }
+ IF_VERBOSE_ACTION(log_msg("EX:\t");
log_disasm(&m_buffer[pc]));
- // Also leave it on the stack.
- env->push_val(function_value);
+ // IF_VERBOSE_ACTION(log_msg("Action ID is: 0x%x\n",
action_id));
+
+ // Simple action; no extra data.
+ switch (action_id)
+ {
+ default:
+ break;
- }
+ case SWF::ACTION_END: // end of actions.
+ return;
- /*private*/
- void
- action_buffer::doActionGetMember(as_environment* env)
- {
- // Some corner case behaviors depend on the SWF file version.
- int version =
env->get_target()->get_movie_definition()->get_version();
+ case SWF::ACTION_NEXTFRAME: // next frame.
+
env->get_target()->goto_frame(env->get_target()->get_current_frame() + 1);
+ break;
+
+ case SWF::ACTION_PREVFRAME: // prev frame.
+
env->get_target()->goto_frame(env->get_target()->get_current_frame() - 1);
+ break;
+
+ case SWF::ACTION_PLAY: // action play
+ env->get_target()->set_play_state(movie::PLAY);
+ break;
+
+ case SWF::ACTION_STOP: // action stop
+ env->get_target()->set_play_state(movie::STOP);
+ break;
+
+ case SWF::ACTION_TOGGLEQUALITY: // toggle
quality
+ case SWF::ACTION_STOPSOUNDS: // stop sounds
+ break;
+
+ case SWF::ACTION_ADD: // add
+ {
+ env->top(1) += env->top(0);
+ env->drop(1);
+ break;
+ }
+ case SWF::ACTION_SUBTRACT: // subtract
+ {
+ env->top(1) -= env->top(0);
+ env->drop(1);
+ break;
+ }
+ case SWF::ACTION_MULTIPLY: // multiply
+ {
+ env->top(1) *= env->top(0);
+ env->drop(1);
+ break;
+ }
+ case SWF::ACTION_DIVIDE: // divide
+ {
+ env->top(1) /= env->top(0);
+ env->drop(1);
+ break;
+ }
+ case SWF::ACTION_EQUAL: // equal
+ {
+ env->top(1).set_bool(env->top(1) == env->top(0));
+ env->drop(1);
+ break;
+ }
+ case SWF::ACTION_LESSTHAN: // less than
+ {
+ env->top(1).set_bool(env->top(1) < env->top(0));
+ env->drop(1);
+ break;
+ }
+ case SWF::ACTION_LOGICALAND: // logical and
+ {
+ env->top(1).set_bool(env->top(1).to_bool() &&
env->top(0).to_bool());
+ env->drop(1);
+ break;
+ }
+ case SWF::ACTION_LOGICALOR: // logical or
+ {
+ env->top(1).set_bool(env->top(1).to_bool() &&
env->top(0).to_bool());
+ env->drop(1);
+ break;
+ }
+ case SWF::ACTION_LOGICALNOT: // logical not
+ {
+ env->top(0).set_bool(! env->top(0).to_bool());
+ break;
+ }
+ case SWF::ACTION_STRINGEQ: // string equal
+ doActionEquals(env);
+ break;
+ case SWF::ACTION_STRINGLENGTH: // string length
+ {
+
env->top(0).set_int(env->top(0).to_tu_string_versioned(version).utf8_length());
+ break;
+ }
+ case SWF::ACTION_SUBSTRING: // substring
+ {
+ int size = int(env->top(0).to_number());
+ int base = int(env->top(1).to_number()) -
1; // 1-based indices
+ const tu_string& str =
env->top(2).to_tu_string_versioned(version);
+
+ // Keep base within range.
+ base = iclamp(base, 0, str.length());
+
+ // Truncate if necessary.
+ size = imin(str.length() - base, size);
+
+ // @@ This can be done without new allocations if
we get dirtier w/ internals
+ // of as_value and tu_string...
+ tu_string new_string = str.c_str() + base;
+ new_string.resize(size);
+
+ env->drop(2);
+ env->top(0).set_tu_string(new_string);
+
+ break;
+ }
+ case SWF::ACTION_POP: // pop
+ {
+ env->drop(1);
+ break;
+ }
+ case SWF::ACTION_INT: // int
+ {
+
env->top(0).set_int(int(floor(env->top(0).to_number())));
+ break;
+ }
+ case SWF::ACTION_GETVARIABLE: // get variable
+ {
+ as_value var_name = env->pop();
+ tu_string var_string = var_name.to_tu_string();
+
+ as_value variable = env->get_variable(var_string,
with_stack);
+ env->push(variable);
+ if (variable.to_object() == NULL) {
+ IF_VERBOSE_ACTION(log_msg("-- get var:
%s=%s\n",
+ var_string.c_str(),
+
variable.to_tu_string().c_str()));
+ } else {
+ IF_VERBOSE_ACTION(log_msg("-- get var: %s=%s
at %p\n",
+ var_string.c_str(),
+
variable.to_tu_string().c_str(), variable.to_object()));
+ }
+
- as_value member_name = env->top(0);
- as_value target = env->top(1);
+ break;
+ }
+ case SWF::ACTION_SETVARIABLE: // set variable
+ {
+ env->set_variable(env->top(1).to_tu_string(),
env->top(0), with_stack);
+ IF_VERBOSE_ACTION(log_msg("-- set var: %s \n",
+
env->top(1).to_tu_string().c_str()));
+
+ env->drop(2);
+ break;
+ }
+ case SWF::ACTION_SETTARGETEXPRESSION: // set target
expression
+ {
+ const char * target_name =
env->top(0).to_string();
+ env->drop(1); // pop the target name off the stack
+ movie *new_target;
+
+ // if the string is blank, we set target to the
root movie
+ // TODO - double check this is correct?
+ if (target_name[0] == '\0')
+ new_target = env->find_target((tu_string)"/");
+ else
+ new_target =
env->find_target((tu_string)target_name);
+
+ if (new_target == NULL)
+ {
+ IF_VERBOSE_ACTION(log_error(
+ "Couldn't find
movie \"%s\" to set target to!"
+ " Not setting
target at all...",
+ (const char
*)target_name));
+ }
+ else
+ env->set_target(new_target);
+
+ break;
+ }
+ case SWF::ACTION_STRINGCONCAT: // string concat
+ {
+ env->top(1).convert_to_string_versioned(version);
+
env->top(1).string_concat(env->top(0).to_tu_string_versioned(version));
+ env->drop(1);
+ break;
+ }
+ case SWF::ACTION_GETPROPERTY: // get property
+ {
+
+ movie* target = env->find_target(env->top(1));
+ if (target)
+ {
+ env->top(1) = get_property(target, (int)
env->top(0).to_number());
+ }
+ else
+ {
+ env->top(1) = as_value();
+ }
+ env->drop(1);
+ break;
+ }
+
+ case SWF::ACTION_SETPROPERTY: // set property
+ {
+
+ movie* target = env->find_target(env->top(2));
+ if (target)
+ {
+ set_property(target, (int)
env->top(1).to_number(), env->top(0));
+ }
+ env->drop(3);
+ break;
+ }
+
+ case SWF::ACTION_DUPLICATECLIP: // duplicate
clip (sprite?)
+ {
+ env->get_target()->clone_display_object(
+ env->top(2).to_tu_string(),
+ env->top(1).to_tu_string(),
+ (int) env->top(0).to_number());
+ env->drop(3);
+ break;
+ }
+
+ case SWF::ACTION_REMOVECLIP: // remove clip
+
env->get_target()->remove_display_object(env->top(0).to_tu_string());
+ env->drop(1);
+ break;
+
+ case SWF::ACTION_TRACE: // trace
+ {
+ // Log the stack val.
+ as_global_trace(fn_call(&env->top(0), NULL, env,
1, env->get_top_index()));
+ env->drop(1);
+ break;
+ }
+
+ case SWF::ACTION_STARTDRAGMOVIE: // start drag
movie
+ {
+ movie::drag_state st;
+
+ st.m_character = env->find_target(env->top(0));
+ if (st.m_character == NULL)
+ {
+ log_error("error: start_drag of invalid
target '%s'.\n",
+ env->top(0).to_string());
+ }
+
+ st.m_lock_center = env->top(1).to_bool();
+ st.m_bound = env->top(2).to_bool();
+ if (st.m_bound)
+ {
+ st.m_bound_x0 = (float)
env->top(6).to_number();
+ st.m_bound_y0 = (float)
env->top(5).to_number();
+ st.m_bound_x1 = (float)
env->top(4).to_number();
+ st.m_bound_y1 = (float)
env->top(3).to_number();
+ env->drop(4);
+ }
+ env->drop(3);
+
+ movie* root_movie =
env->get_target()->get_root_movie();
+ assert(root_movie);
+
+ if (root_movie && st.m_character)
+ {
+ root_movie->set_drag_state(st);
+ }
+
+ break;
+ }
- as_object* obj = target.to_object();
- if (! obj)
- {
- IF_VERBOSE_DEBUG(log_msg("getMember called against "
- "a value that does not cast "
- "to an as_object: %s\n", target.to_string()));
- env->top(1).set_undefined();
- env->drop(1);
- return;
- }
-
- IF_VERBOSE_ACTION(log_msg(" doActionGetMember: target: %s
(object %p)\n", target.to_string(), obj));
-
- // Special case: String has a member "length"
- // @@ FIXME: we shouldn't have all this "special" cases --strk;
- if (target.get_type() == as_value::STRING &&
member_name.to_tu_stringi() == "length")
- {
- int len =
target.to_tu_string_versioned(version).utf8_length();
- env->top(1).set_int(len);
- }
- else
- {
- if ( ! obj->get_member(member_name.to_tu_string(),
&(env->top(1))) )
- env->top(1).set_undefined();
-
- IF_VERBOSE_ACTION(log_msg("-- get_member %s=%s\n",
- member_name.to_tu_string().c_str(),
- env->top(1).to_tu_string().c_str()));
- }
- env->drop(1);
- }
-
- /*private*/
- void action_buffer::doActionStrictEquals(as_environment* env)
- {
-//log_msg("%s\n", __PRETTY_FUNCTION__);
- // @@ identical to untyped equal, as far as I can tell...
- env->top(1).set_bool(env->top(1) == env->top(0));
- env->drop(1);
- }
-
- /*private*/
- void action_buffer::doActionEquals(as_environment* env)
- {
-//log_msg("%s\n", __PRETTY_FUNCTION__);
- env->top(1).set_bool(env->top(1).to_tu_string() ==
env->top(0).to_tu_string());
- env->drop(1);
- }
-
- /*private*/
- void action_buffer::doActionDelete(as_environment* env,
- std::vector<with_stack_entry>& with_stack)
- {
- log_error("todo opcode: %02X\n", SWF::ACTION_DELETEVAR);
- }
-
- /*private*/
- void action_buffer::doActionDelete2(as_environment* env,
- std::vector<with_stack_entry>& with_stack)
- {
- as_value var = env->top(0);
-
- //log_msg("delete %s\n", var.to_string());
-
- as_value oldval = env->get_variable_raw(var.to_tu_string(),
- with_stack);
-
- if ( ! oldval.get_type() == as_value::UNDEFINED )
- {
- // set variable to 'undefined'
- // that hopefully --ref_count and eventually
- // release memory.
- env->set_variable_raw(var.to_tu_string(),
- as_value(), with_stack);
- env->top(0).set_bool(true);
- }
- else
- {
- env->top(0).set_bool(false);
- }
-
- //log_error("tocheck opcode: %02X\n", SWF::ACTION_DELETE);
- }
-
-
- void action_buffer::execute(
- as_environment* env,
- int start_pc,
- int exec_bytes,
- as_value* retval,
- const std::vector<with_stack_entry>& initial_with_stack,
- bool is_function2)
- // Interpret the specified subset of the actions in our
- // buffer. Caller is responsible for cleaning up our local
- // stack frame (it may have passed its arguments in via the
- // local stack frame).
- //
- // The is_function2 flag determines whether to use global or local
registers.
- {
- action_init(); // @@ stick this somewhere else; need some
global static init function
-
- assert(env);
-
- std::vector<with_stack_entry> with_stack(initial_with_stack);
-
- // Some corner case behaviors depend on the SWF file version.
- int version =
env->get_target()->get_movie_definition()->get_version();
-
-#if 0
- // Check the time
- if (periodic_events.expired()) {
- periodic_events.poll_event_handlers(env);
- }
-#endif
-
- movie* original_target = env->get_target();
- UNUSED(original_target); // Avoid warnings.
-
- int stop_pc = start_pc + exec_bytes;
-
- for (int pc = start_pc; pc < stop_pc; )
- {
- // Cleanup any expired "with" blocks.
- while (with_stack.size() > 0
- && pc >= with_stack.back().m_block_end_pc)
- {
- // Drop this stack element
- with_stack.resize(with_stack.size() - 1);
- }
-
- // Get the opcode.
- int action_id = m_buffer[pc];
- if ((action_id & 0x80) == 0)
- {
- IF_VERBOSE_ACTION(log_msg("EX:\t");
log_disasm(&m_buffer[pc]));
-
- // IF_VERBOSE_ACTION(log_msg("Action ID is:
0x%x\n", action_id));
-
- // Simple action; no extra data.
- switch (action_id)
- {
- default:
- break;
-
- case SWF::ACTION_END: // end of actions.
- return;
-
- case SWF::ACTION_NEXTFRAME: // next frame.
-
env->get_target()->goto_frame(env->get_target()->get_current_frame() + 1);
- break;
-
- case SWF::ACTION_PREVFRAME: // prev frame.
-
env->get_target()->goto_frame(env->get_target()->get_current_frame() - 1);
- break;
-
- case SWF::ACTION_PLAY: // action play
-
env->get_target()->set_play_state(movie::PLAY);
- break;
-
- case SWF::ACTION_STOP: // action stop
-
env->get_target()->set_play_state(movie::STOP);
- break;
-
- case SWF::ACTION_TOGGLEQUALITY: // toggle
quality
- case SWF::ACTION_STOPSOUNDS: // stop sounds
- break;
-
- case SWF::ACTION_ADD: // add
- {
- env->top(1) += env->top(0);
- env->drop(1);
- break;
- }
- case SWF::ACTION_SUBTRACT: // subtract
- {
- env->top(1) -= env->top(0);
- env->drop(1);
- break;
- }
- case SWF::ACTION_MULTIPLY: // multiply
- {
- env->top(1) *= env->top(0);
- env->drop(1);
- break;
- }
- case SWF::ACTION_DIVIDE: // divide
- {
- env->top(1) /= env->top(0);
- env->drop(1);
- break;
- }
- case SWF::ACTION_EQUAL: // equal
- {
- env->top(1).set_bool(env->top(1) ==
env->top(0));
- env->drop(1);
- break;
- }
- case SWF::ACTION_LESSTHAN: // less than
- {
- env->top(1).set_bool(env->top(1) <
env->top(0));
- env->drop(1);
- break;
- }
- case SWF::ACTION_LOGICALAND: // logical and
- {
-
env->top(1).set_bool(env->top(1).to_bool() && env->top(0).to_bool());
- env->drop(1);
- break;
- }
- case SWF::ACTION_LOGICALOR: // logical or
- {
-
env->top(1).set_bool(env->top(1).to_bool() && env->top(0).to_bool());
- env->drop(1);
- break;
- }
- case SWF::ACTION_LOGICALNOT: // logical not
- {
- env->top(0).set_bool(!
env->top(0).to_bool());
- break;
- }
- case SWF::ACTION_STRINGEQ: // string equal
- doActionEquals(env);
- break;
- case SWF::ACTION_STRINGLENGTH: // string length
- {
-
env->top(0).set_int(env->top(0).to_tu_string_versioned(version).utf8_length());
- break;
- }
- case SWF::ACTION_SUBSTRING: // substring
- {
- int size =
int(env->top(0).to_number());
- int base =
int(env->top(1).to_number()) - 1; // 1-based indices
- const tu_string& str =
env->top(2).to_tu_string_versioned(version);
-
- // Keep base within range.
- base = iclamp(base, 0, str.length());
-
- // Truncate if necessary.
- size = imin(str.length() - base, size);
-
- // @@ This can be done without new
allocations if we get dirtier w/ internals
- // of as_value and tu_string...
- tu_string new_string =
str.c_str() + base;
- new_string.resize(size);
-
- env->drop(2);
- env->top(0).set_tu_string(new_string);
-
- break;
- }
- case SWF::ACTION_POP: // pop
- {
- env->drop(1);
- break;
- }
- case SWF::ACTION_INT: // int
- {
-
env->top(0).set_int(int(floor(env->top(0).to_number())));
- break;
- }
- case SWF::ACTION_GETVARIABLE: // get variable
- {
- as_value var_name = env->pop();
- tu_string var_string =
var_name.to_tu_string();
-
- as_value variable =
env->get_variable(var_string, with_stack);
- env->push(variable);
- if (variable.to_object() == NULL) {
- IF_VERBOSE_ACTION(log_msg("--
get var: %s=%s\n",
-
var_string.c_str(),
-
variable.to_tu_string().c_str()));
- } else {
- IF_VERBOSE_ACTION(log_msg("--
get var: %s=%s at %p\n",
-
var_string.c_str(),
-
variable.to_tu_string().c_str(), variable.to_object()));
- }
-
-
- break;
- }
- case SWF::ACTION_SETVARIABLE: // set variable
- {
-
env->set_variable(env->top(1).to_tu_string(), env->top(0), with_stack);
- IF_VERBOSE_ACTION(log_msg("-- set var:
%s \n",
-
env->top(1).to_tu_string().c_str()));
-
- env->drop(2);
- break;
- }
- case SWF::ACTION_SETTARGETEXPRESSION: // set
target expression
- {
- const char * target_name =
env->top(0).to_string();
- env->drop(1); // pop the target name
off the stack
- movie *new_target;
-
- // if the string is blank, we set
target to the root movie
- // TODO - double check this is correct?
- if (target_name[0] == '\0')
- new_target =
env->find_target((tu_string)"/");
- else
- new_target =
env->find_target((tu_string)target_name);
-
- if (new_target == NULL)
- {
- IF_VERBOSE_ACTION(log_error(
- "Couldn't find movie
\"%s\" to set target to!"
- " Not setting target at
all...",
- (const char
*)target_name));
- }
- else
- env->set_target(new_target);
-
- break;
- }
- case SWF::ACTION_STRINGCONCAT: // string concat
- {
-
env->top(1).convert_to_string_versioned(version);
-
env->top(1).string_concat(env->top(0).to_tu_string_versioned(version));
- env->drop(1);
- break;
- }
- case SWF::ACTION_GETPROPERTY: // get property
- {
-
- movie* target =
env->find_target(env->top(1));
- if (target)
- {
- env->top(1) =
get_property(target, (int) env->top(0).to_number());
- }
- else
- {
- env->top(1) = as_value();
- }
- env->drop(1);
- break;
- }
-
- case SWF::ACTION_SETPROPERTY: // set property
- {
-
- movie* target =
env->find_target(env->top(2));
- if (target)
- {
- set_property(target, (int)
env->top(1).to_number(), env->top(0));
- }
- env->drop(3);
- break;
- }
-
- case SWF::ACTION_DUPLICATECLIP: // duplicate
clip (sprite?)
- {
- env->get_target()->clone_display_object(
- env->top(2).to_tu_string(),
- env->top(1).to_tu_string(),
- (int) env->top(0).to_number());
- env->drop(3);
- break;
- }
-
- case SWF::ACTION_REMOVECLIP: // remove clip
-
env->get_target()->remove_display_object(env->top(0).to_tu_string());
- env->drop(1);
- break;
-
- case SWF::ACTION_TRACE: // trace
- {
- // Log the stack val.
- as_global_trace(fn_call(&env->top(0),
NULL, env, 1, env->get_top_index()));
- env->drop(1);
- break;
- }
-
- case SWF::ACTION_STARTDRAGMOVIE: //
start drag movie
- {
- movie::drag_state st;
-
- st.m_character =
env->find_target(env->top(0));
- if (st.m_character == NULL)
- {
- log_error("error: start_drag of
invalid target '%s'.\n",
-
env->top(0).to_string());
- }
-
- st.m_lock_center =
env->top(1).to_bool();
- st.m_bound = env->top(2).to_bool();
- if (st.m_bound)
- {
- st.m_bound_x0 = (float)
env->top(6).to_number();
- st.m_bound_y0 = (float)
env->top(5).to_number();
- st.m_bound_x1 = (float)
env->top(4).to_number();
- st.m_bound_y1 = (float)
env->top(3).to_number();
- env->drop(4);
- }
- env->drop(3);
-
- movie* root_movie =
env->get_target()->get_root_movie();
- assert(root_movie);
-
- if (root_movie && st.m_character)
- {
- root_movie->set_drag_state(st);
- }
-
- break;
- }
-
- case SWF::ACTION_STOPDRAGMOVIE: // stop drag
movie
- {
- movie* root_movie =
env->get_target()->get_root_movie();
- assert(root_movie);
-
- root_movie->stop_drag();
-
- break;
- }
-
- case SWF::ACTION_STRINGCOMPARE: // string less
than
- {
-
env->top(1).set_bool(env->top(1).to_tu_string() < env->top(0).to_tu_string());
- break;
- }
-
- case SWF::ACTION_THROW: // 0x2A
- {
- log_error("todo opcode: %02X\n",
action_id);
- break;
- }
-
- case SWF::ACTION_CASTOP: // 0x2B
- doActionCast(env);
- break;
- case SWF::ACTION_IMPLEMENTSOP: // 0x2C
- {
- // Declare that a class s1 implements
one or more
- // interfaces (i2 == number of
interfaces, s3..sn are the names
- // of the interfaces).
- log_error("todo opcode: %02X\n",
action_id);
- break;
- }
-
- case SWF::ACTION_RANDOM: // random
- {
- int max =
int(env->top(0).to_number());
- if (max < 1) max = 1;
-
env->top(0).set_int(tu_random::next_random() % max);
- break;
- }
- case SWF::ACTION_MBLENGTH: // mb length
- {
- // @@ TODO
- log_error("todo opcode: %02X\n",
action_id);
- break;
- }
- case SWF::ACTION_ORD: // ord
- {
- // ASCII code of first character
-
env->top(0).set_int(env->top(0).to_string()[0]);
- break;
- }
- case SWF::ACTION_CHR: // chr
- {
- char buf[2];
- buf[0] = int(env->top(0).to_number());
- buf[1] = 0;
- env->top(0).set_string(buf);
- break;
- }
-
- case SWF::ACTION_GETTIMER: // get timer
- // Push milliseconds since we started
playing.
-
env->push(floorf(env->m_target->get_timer() * 1000.0f));
- break;
-
- case SWF::ACTION_MBSUBSTRING: // mb substring
- {
- // @@ TODO
- log_error("todo opcode: %02X\n",
action_id);
- break;
- }
- case SWF::ACTION_MBCHR: // mb chr
- {
- // @@ TODO
- log_error("todo opcode: %02X\n",
action_id);
- break;
- }
- case SWF::ACTION_DELETEVAR: // delete
- doActionDelete(env, with_stack);
- break;
- case SWF::ACTION_DELETE: // delete2
- doActionDelete2(env, with_stack);
- break;
-
- case SWF::ACTION_VAREQUALS: // set local
- {
- as_value value = env->pop();
- as_value varname = env->pop();
- env->set_local(varname.to_tu_string(),
value);
- break;
- }
-
- case SWF::ACTION_CALLFUNCTION: // call function
- doActionCallFunction(env, with_stack);
- break;
- case SWF::ACTION_RETURN: // return
- {
- // Put top of stack in the provided
return slot, if
- // it's not NULL.
- if (retval)
- {
- *retval = env->top(0);
- }
- env->drop(1);
-
- // Skip the rest of this buffer (return
from this action_buffer).
- pc = stop_pc;
-
- break;
- }
- case SWF::ACTION_MODULO: // modulo
- {
- as_value result;
- double y = env->pop().to_number();
- double x = env->pop().to_number();
- // Don't need to check for y being 0
here - if it's zero, fmod returns NaN, which is what flash would do too
- result = fmod(x, y);
+ case SWF::ACTION_STOPDRAGMOVIE: // stop drag
movie
+ {
+ movie* root_movie =
env->get_target()->get_root_movie();
+ assert(root_movie);
+
+ root_movie->stop_drag();
+
+ break;
+ }
+
+ case SWF::ACTION_STRINGCOMPARE: // string less
than
+ {
+ env->top(1).set_bool(env->top(1).to_tu_string() <
env->top(0).to_tu_string());
+ break;
+ }
+
+ case SWF::ACTION_THROW: // 0x2A
+ {
+ log_error("todo opcode: %02X\n", action_id);
+ break;
+ }
+
+ case SWF::ACTION_CASTOP: // 0x2B
+ doActionCast(env);
+ break;
+ case SWF::ACTION_IMPLEMENTSOP: // 0x2C
+ {
+ // Declare that a class s1 implements one or more
+ // interfaces (i2 == number of interfaces, s3..sn
are the names
+ // of the interfaces).
+ log_error("todo opcode: %02X\n", action_id);
+ break;
+ }
+
+ case SWF::ACTION_RANDOM: // random
+ {
+ int max = int(env->top(0).to_number());
+ if (max < 1) max = 1;
+ env->top(0).set_int(tu_random::next_random() %
max);
+ break;
+ }
+ case SWF::ACTION_MBLENGTH: // mb length
+ {
+ // @@ TODO
+ log_error("todo opcode: %02X\n", action_id);
+ break;
+ }
+ case SWF::ACTION_ORD: // ord
+ {
+ // ASCII code of first character
+ env->top(0).set_int(env->top(0).to_string()[0]);
+ break;
+ }
+ case SWF::ACTION_CHR: // chr
+ {
+ char buf[2];
+ buf[0] = int(env->top(0).to_number());
+ buf[1] = 0;
+ env->top(0).set_string(buf);
+ break;
+ }
+
+ case SWF::ACTION_GETTIMER: // get timer
+ // Push milliseconds since we started playing.
+ env->push(floorf(env->m_target->get_timer() *
1000.0f));
+ break;
+
+ case SWF::ACTION_MBSUBSTRING: // mb substring
+ {
+ // @@ TODO
+ log_error("todo opcode: %02X\n", action_id);
+ break;
+ }
+ case SWF::ACTION_MBCHR: // mb chr
+ {
+ // @@ TODO
+ log_error("todo opcode: %02X\n", action_id);
+ break;
+ }
+ case SWF::ACTION_DELETEVAR: // delete
+ doActionDelete(env, with_stack);
+ break;
+ case SWF::ACTION_DELETE: // delete2
+ doActionDelete2(env, with_stack);
+ break;
+
+ case SWF::ACTION_VAREQUALS: // set local
+ {
+ as_value value = env->pop();
+ as_value varname = env->pop();
+ env->set_local(varname.to_tu_string(), value);
+ break;
+ }
+
+ case SWF::ACTION_CALLFUNCTION: // call function
+ doActionCallFunction(env, with_stack);
+ break;
+ case SWF::ACTION_RETURN: // return
+ {
+ // Put top of stack in the provided return slot,
if
+ // it's not NULL.
+ if (retval)
+ {
+ *retval = env->top(0);
+ }
+ env->drop(1);
+
+ // Skip the rest of this buffer (return from this
action_buffer).
+ pc = stop_pc;
+
+ break;
+ }
+ case SWF::ACTION_MODULO: // modulo
+ {
+ as_value result;
+ double y = env->pop().to_number();
+ double x = env->pop().to_number();
+ // Don't need to check for y being 0 here - if
it's zero, fmod returns NaN, which is what flash would do too
+ result = fmod(x, y);
//
env->top(1).set_double(fmod(env->top(1).to_bool() && env->top(0).to_bool());
// env->drop(1);
// log_error("modulo x=%f, y=%f,
z=%f\n",x,y,result.to_number());
- env->push(result);
- break;
- }
- case SWF::ACTION_NEW: // new
- doActionNew(env, with_stack);
- break;
- case SWF::ACTION_VAR: // declare local
- {
- const tu_string& varname =
env->top(0).to_tu_string();
- env->declare_local(varname);
- env->drop(1);
- break;
- }
- case SWF::ACTION_INITARRAY: // init array
- {
- int array_size = (int)
env->pop().to_number();
-
- //log_msg("xxx init array: size = %d,
top of stack = %d\n", array_size, env->get_top_index());//xxxxx
-
- // Call the array constructor, to
create an empty array.
- as_value result;
- as_global_array_ctor(fn_call(&result,
NULL, env, 0, env->get_top_index()));
-
- as_object* ao = result.to_object();
- assert(ao);
-
- // Fill the elements with the initial
values from the stack.
- as_value index_number;
- for (int i = 0; i < array_size; i++)
- {
- // @@ TODO a set_member that
takes an int or as_value?
- index_number.set_int(i);
-
ao->set_member(index_number.to_string(), env->pop());
- }
-
- env->push(result);
-
- //log_msg("xxx init array end: top of
stack = %d, trace(top(0)) =", env->get_top_index());//xxxxxxx
-
- //as_global_trace(fn_call(NULL, NULL,
env, 1, env->get_top_index())); //xxxx
-
- break;
- }
- case SWF::ACTION_INITOBJECT: // declare
object
- {
- //
- // SWFACTION_PUSH
- // [000] Constant: 1 "obj"
- // [001] Constant: 0 "member" <--
we handle up to here
- // [002] Integer: 1
- // [003] Integer: 1
- // SWFACTION_INITOBJECT
-
- int nmembers = (int)
env->pop().to_number();
-
- smart_ptr<as_object> new_obj_ptr(new
as_object);
-
- // Set provided members
- for (int i=0; i<nmembers; ++i) {
- as_value member_value =
env->pop();
- tu_stringi member_name =
env->pop().to_tu_stringi();
-
new_obj_ptr->set_member(member_name, member_value);
- }
-
- // @@ TODO
- //log_error("checkme opcode: %02X\n",
action_id);
-
- as_value new_obj;
-
new_obj.set_as_object(new_obj_ptr.get_ptr());
-
- //env->drop(nmembers*2);
- env->push(new_obj);
-
- break;
- }
- case SWF::ACTION_TYPEOF: // type of
- {
- switch(env->top(0).get_type())
- {
- case as_value::UNDEFINED:
-
env->top(0).set_string("undefined");
- break;
- case as_value::STRING:
-
env->top(0).set_string("string");
- break;
- case as_value::NUMBER:
-
env->top(0).set_string("number");
- break;
- case as_value::BOOLEAN:
-
env->top(0).set_string("boolean");
- break;
- case as_value::OBJECT:
-
env->top(0).set_string("object");
- break;
- case as_value::NULLTYPE:
- env->top(0).set_string("null");
- break;
- case as_value::AS_FUNCTION:
-
env->top(0).set_string("function");
- break;
- default:
- log_error("typeof unknown type:
%02X\n", env->top(0).get_type());
- break;
- }
- break;
- }
- case SWF::ACTION_TARGETPATH: // get target
- {
- // @@ TODO
- log_error("todo opcode: %02X\n",
action_id);
- break;
- }
- case SWF::ACTION_ENUMERATE: // enumerate
- {
- as_value var_name = env->pop();
- const tu_string& var_string =
var_name.to_tu_string();
-
- as_value variable =
env->get_variable(var_string, with_stack);
-
- if (variable.to_object() == NULL)
- {
- break;
- }
- const as_object* object = (as_object*)
(variable.to_object());
-
- // The end of the enumeration
- as_value nullvalue;
- nullvalue.set_null();
- env->push(nullvalue);
- IF_VERBOSE_ACTION(log_msg("---enumerate
- push: NULL\n"));
-
- stringi_hash<as_member>::const_iterator
it = object->m_members.begin();
- while (it != object->m_members.end())
- {
- const as_member member =
(it->second);
-
- if (!
member.get_member_flags().get_dont_enum())
- {
-
env->push(as_value(it->first.c_str()));
-
-
IF_VERBOSE_ACTION(log_msg("---enumerate - push: %s\n",
-
it->first.c_str()));
- }
+ env->push(result);
+ break;
+ }
+ case SWF::ACTION_NEW: // new
+ doActionNew(env, with_stack);
+ break;
+ case SWF::ACTION_VAR: // declare local
+ {
+ const tu_string& varname =
env->top(0).to_tu_string();
+ env->declare_local(varname);
+ env->drop(1);
+ break;
+ }
+ case SWF::ACTION_INITARRAY: // init array
+ {
+ int array_size = (int)
env->pop().to_number();
+
+ //log_msg("xxx init array: size = %d, top of
stack = %d\n", array_size, env->get_top_index());//xxxxx
+
+ // Call the array constructor, to create an empty
array.
+ as_value result;
+ as_global_array_ctor(fn_call(&result, NULL, env,
0, env->get_top_index()));
+
+ as_object* ao = result.to_object();
+ assert(ao);
+
+ // Fill the elements with the initial values from
the stack.
+ as_value index_number;
+ for (int i = 0; i < array_size; i++)
+ {
+ // @@ TODO a set_member that takes an int
or as_value?
+ index_number.set_int(i);
+ ao->set_member(index_number.to_string(),
env->pop());
+ }
+
+ env->push(result);
+
+ //log_msg("xxx init array end: top of stack = %d,
trace(top(0)) =", env->get_top_index());//xxxxxxx
+
+ //as_global_trace(fn_call(NULL, NULL, env, 1,
env->get_top_index())); //xxxx
+
+ break;
+ }
+ case SWF::ACTION_INITOBJECT: // declare object
+ {
+ //
+ // SWFACTION_PUSH
+ // [000] Constant: 1 "obj"
+ // [001] Constant: 0 "member" <-- we handle
up to here
+ // [002] Integer: 1
+ // [003] Integer: 1
+ // SWFACTION_INITOBJECT
+
+ int nmembers = (int) env->pop().to_number();
+
+ smart_ptr<as_object> new_obj_ptr(new as_object);
+
+ // Set provided members
+ for (int i=0; i<nmembers; ++i) {
+ as_value member_value = env->pop();
+ tu_stringi member_name =
env->pop().to_tu_stringi();
+ new_obj_ptr->set_member(member_name,
member_value);
+ }
+
+ // @@ TODO
+ //log_error("checkme opcode: %02X\n", action_id);
+
+ as_value new_obj;
+ new_obj.set_as_object(new_obj_ptr.get_ptr());
+
+ //env->drop(nmembers*2);
+ env->push(new_obj);
+
+ break;
+ }
+ case SWF::ACTION_TYPEOF: // type of
+ {
+ switch(env->top(0).get_type())
+ {
+ case as_value::UNDEFINED:
+ env->top(0).set_string("undefined");
+ break;
+ case as_value::STRING:
+ env->top(0).set_string("string");
+ break;
+ case as_value::NUMBER:
+ env->top(0).set_string("number");
+ break;
+ case as_value::BOOLEAN:
+ env->top(0).set_string("boolean");
+ break;
+ case as_value::OBJECT:
+ env->top(0).set_string("object");
+ break;
+ case as_value::NULLTYPE:
+ env->top(0).set_string("null");
+ break;
+ case as_value::AS_FUNCTION:
+ env->top(0).set_string("function");
+ break;
+ default:
+ log_error("typeof unknown type:
%02X\n", env->top(0).get_type());
+ break;
+ }
+ break;
+ }
+ case SWF::ACTION_TARGETPATH: // get target
+ {
+ // @@ TODO
+ log_error("todo opcode: %02X\n", action_id);
+ break;
+ }
+ case SWF::ACTION_ENUMERATE: // enumerate
+ {
+ as_value var_name = env->pop();
+ const tu_string& var_string =
var_name.to_tu_string();
+
+ as_value variable = env->get_variable(var_string,
with_stack);
+
+ if (variable.to_object() == NULL)
+ {
+ break;
+ }
+ const as_object* object = (as_object*)
(variable.to_object());
+
+ // The end of the enumeration
+ as_value nullvalue;
+ nullvalue.set_null();
+ env->push(nullvalue);
+ IF_VERBOSE_ACTION(log_msg("---enumerate - push:
NULL\n"));
+
+ stringi_hash<as_member>::const_iterator it =
object->m_members.begin();
+ while (it != object->m_members.end())
+ {
+ const as_member member = (it->second);
+
+ if (!
member.get_member_flags().get_dont_enum())
+ {
+
env->push(as_value(it->first.c_str()));
+
+
IF_VERBOSE_ACTION(log_msg("---enumerate - push: %s\n",
+
it->first.c_str()));
+ }
- ++it;
- }
-
- const as_object * prototype =
(as_object *) object->m_prototype;
- if (prototype != NULL)
- {
-
stringi_hash<as_member>::const_iterator it = prototype->m_members.begin();
- while (it !=
prototype->m_members.end())
- {
- const as_member member
= (it->second);
-
- if (!
member.get_member_flags().get_dont_enum())
- {
-
env->push(as_value(it->first.c_str()));
-
-
IF_VERBOSE_ACTION(log_msg("---enumerate - push: %s\n",
-
it->first.c_str()));
- }
-
- ++it;
- };
- }
-
- break;
- }
- case SWF::ACTION_NEWADD: // add_t (typed)
- {
- if (env->top(0).get_type() ==
as_value::STRING
- || env->top(1).get_type() ==
as_value::STRING)
- {
-
env->top(1).convert_to_string_versioned(version);
-
env->top(1).string_concat(env->top(0).to_tu_string_versioned(version));
- }
- else
- {
- env->top(1) += env->top(0);
- }
- env->drop(1);
- break;
- }
- case SWF::ACTION_NEWLESSTHAN: // less than
(typed)
- {
- if (env->top(1).get_type() ==
as_value::STRING)
- {
-
env->top(1).set_bool(env->top(1).to_tu_string() < env->top(0).to_tu_string());
- }
- else
- {
-
env->top(1).set_bool(env->top(1) < env->top(0));
- }
- env->drop(1);
- break;
- }
- case SWF::ACTION_NEWEQUALS: // equal (typed)
- doActionStrictEquals(env);
- break;
- case SWF::ACTION_TONUMBER: // to number
- {
- env->top(0).convert_to_number();
- break;
- }
- case SWF::ACTION_TOSTRING: // to string
- {
-
env->top(0).convert_to_string_versioned(version);
- break;
- }
- case SWF::ACTION_DUP: // dup
- env->push(env->top(0));
- break;
-
- case SWF::ACTION_SWAP: // swap
- {
- as_value temp = env->top(1);
- env->top(1) = env->top(0);
- env->top(0) = temp;
- break;
- }
- case SWF::ACTION_GETMEMBER: // get member
- doActionGetMember(env);
- break;
- case SWF::ACTION_SETMEMBER: // set member
- {
- as_object* obj =
env->top(2).to_object();
- if (obj)
- {
-
obj->set_member(env->top(1).to_tu_string(), env->top(0));
- IF_VERBOSE_ACTION(
- log_msg("-- set_member
%s.%s=%s\n",
-
env->top(2).to_tu_string().c_str(),
-
env->top(1).to_tu_string().c_str(),
-
env->top(0).to_tu_string().c_str()));
- }
- else
- {
- // Invalid object, can't set.
- IF_VERBOSE_ACTION(
- log_msg("-- set_member
%s.%s=%s on invalid object!\n",
-
env->top(2).to_tu_string().c_str(),
-
env->top(1).to_tu_string().c_str(),
-
env->top(0).to_tu_string().c_str()));
- }
- env->drop(3);
- break;
- }
- case SWF::ACTION_INCREMENT: // increment
- env->top(0) += 1;
- break;
- case SWF::ACTION_DECREMENT: // decrement
- env->top(0) -= 1;
- break;
-
- case SWF::ACTION_CALLMETHOD: // call method
- doActionCallMethod(env);
- break;
- case SWF::ACTION_NEWMETHOD: // new method
- // @@ TODO
- log_error("todo opcode: %02X\n",
action_id);
- break;
- case SWF::ACTION_INSTANCEOF: // instance of
- // @@ TODO
- doActionInstanceOf(env);
- break;
- case SWF::ACTION_ENUM2: // enumerate object
- // @@ TODO
- log_error("todo opcode: %02X\n",
action_id);
- break;
- case SWF::ACTION_BITWISEAND: // bitwise and
- env->top(1) &= env->top(0);
- env->drop(1);
- break;
- case SWF::ACTION_BITWISEOR: // bitwise or
- env->top(1) |= env->top(0);
- env->drop(1);
- break;
- case SWF::ACTION_BITWISEXOR: // bitwise xor
- env->top(1) ^= env->top(0);
- env->drop(1);
- break;
- case SWF::ACTION_SHIFTLEFT: // shift left
- env->top(1).shl(env->top(0));
- env->drop(1);
- break;
- case SWF::ACTION_SHIFTRIGHT: // shift right
(signed)
- env->top(1).asr(env->top(0));
- env->drop(1);
- break;
- case SWF::ACTION_SHIFTRIGHT2: // shift right
(unsigned)
- env->top(1).lsr(env->top(0));
- env->drop(1);
- break;
- case SWF::ACTION_STRICTEQ: // strict equal
- if (env->top(1).get_type() !=
env->top(0).get_type())
- {
- // Types don't match.
- env->top(1).set_bool(false);
- env->drop(1);
- }
- else
- {
-
env->top(1).set_bool(env->top(1) == env->top(0));
- env->drop(1);
- }
- break;
- case SWF::ACTION_GREATER: // 0x67:
- if (env->top(1).get_type() ==
as_value::STRING)
- {
-
env->top(1).set_bool(env->top(1).to_tu_string() > env->top(0).to_tu_string());
- }
- else
- {
-
env->top(1).set_bool(env->top(1).to_number() > env->top(0).to_number());
- }
- env->drop(1);
- break;
- case SWF::ACTION_STRINGGREATER: // 0x68
-
env->top(1).set_bool(env->top(1).to_tu_string() > env->top(0).to_tu_string());
- env->drop(1);
- break;
-
- case SWF::ACTION_EXTENDS: // 0x69
- log_error("todo opcode: %02X\n",
action_id);
- break;
-
- }
- pc++; // advance to next action.
- }
- else
- {
- IF_VERBOSE_ACTION(log_msg("EX:\t");
log_disasm(&m_buffer[pc]));
-
- // Action containing extra data.
- int length = m_buffer[pc + 1] |
(m_buffer[pc + 2] << 8);
- int next_pc = pc + length + 3;
-
- switch (action_id)
- {
- default:
- break;
-
- case SWF::ACTION_GOTOFRAME: // goto frame
- {
- int frame = m_buffer[pc + 3] |
(m_buffer[pc + 4] << 8);
- // 0-based already?
- //// Convert from 1-based to 0-based
- //frame--;
- env->get_target()->goto_frame(frame);
- break;
- }
-
- case SWF::ACTION_GETURL: // get url
- {
- // If this is an FSCommand, then call
the callback
- // handler, if any.
+ ++it;
+ }
- // Two strings as args.
- const char* url = (const char*)
&(m_buffer[pc + 3]);
- int url_len = strlen(url);
- const char* target = (const char*)
&(m_buffer[pc + 3 + url_len + 1]);
-
- // If the url starts with "FSCommand:",
then this is
- // a message for the host app.
- if (strncmp(url, "FSCommand:", 10) == 0)
- {
- if (s_fscommand_handler)
- {
- // Call into the app.
-
(*s_fscommand_handler)(env->get_target()->get_root_interface(), url + 10,
target);
- }
- }
- else
- {
+ const as_object * prototype = (as_object *)
object->m_prototype;
+ if (prototype != NULL)
+ {
+ stringi_hash<as_member>::const_iterator
it = prototype->m_members.begin();
+ while (it != prototype->m_members.end())
+ {
+ const as_member member =
(it->second);
+
+ if (!
member.get_member_flags().get_dont_enum())
+ {
+
env->push(as_value(it->first.c_str()));
+
+
IF_VERBOSE_ACTION(log_msg("---enumerate - push: %s\n",
+
it->first.c_str()));
+ }
+
+ ++it;
+ };
+ }
+
+ break;
+ }
+ case SWF::ACTION_NEWADD: // add_t (typed)
+ {
+ if (env->top(0).get_type() == as_value::STRING
+ || env->top(1).get_type() == as_value::STRING)
+ {
+
env->top(1).convert_to_string_versioned(version);
+
env->top(1).string_concat(env->top(0).to_tu_string_versioned(version));
+ }
+ else
+ {
+ env->top(1) += env->top(0);
+ }
+ env->drop(1);
+ break;
+ }
+ case SWF::ACTION_NEWLESSTHAN: // less than (typed)
+ {
+ if (env->top(1).get_type() == as_value::STRING)
+ {
+
env->top(1).set_bool(env->top(1).to_tu_string() < env->top(0).to_tu_string());
+ }
+ else
+ {
+ env->top(1).set_bool(env->top(1) <
env->top(0));
+ }
+ env->drop(1);
+ break;
+ }
+ case SWF::ACTION_NEWEQUALS: // equal (typed)
+ doActionStrictEquals(env);
+ break;
+ case SWF::ACTION_TONUMBER: // to number
+ {
+ env->top(0).convert_to_number();
+ break;
+ }
+ case SWF::ACTION_TOSTRING: // to string
+ {
+ env->top(0).convert_to_string_versioned(version);
+ break;
+ }
+ case SWF::ACTION_DUP: // dup
+ env->push(env->top(0));
+ break;
+
+ case SWF::ACTION_SWAP: // swap
+ {
+ as_value temp = env->top(1);
+ env->top(1) = env->top(0);
+ env->top(0) = temp;
+ break;
+ }
+ case SWF::ACTION_GETMEMBER: // get member
+ doActionGetMember(env);
+ break;
+ case SWF::ACTION_SETMEMBER: // set member
+ {
+ as_object* obj = env->top(2).to_object();
+ if (obj)
+ {
+
obj->set_member(env->top(1).to_tu_string(), env->top(0));
+ IF_VERBOSE_ACTION(
+ log_msg("-- set_member %s.%s=%s\n",
+
env->top(2).to_tu_string().c_str(),
+
env->top(1).to_tu_string().c_str(),
+
env->top(0).to_tu_string().c_str()));
+ }
+ else
+ {
+ // Invalid object, can't set.
+ IF_VERBOSE_ACTION(
+ log_msg("-- set_member %s.%s=%s on
invalid object!\n",
+
env->top(2).to_tu_string().c_str(),
+
env->top(1).to_tu_string().c_str(),
+
env->top(0).to_tu_string().c_str()));
+ }
+ env->drop(3);
+ break;
+ }
+ case SWF::ACTION_INCREMENT: // increment
+ env->top(0) += 1;
+ break;
+ case SWF::ACTION_DECREMENT: // decrement
+ env->top(0) -= 1;
+ break;
+
+ case SWF::ACTION_CALLMETHOD: // call method
+ doActionCallMethod(env);
+ break;
+ case SWF::ACTION_NEWMETHOD: // new method
+ // @@ TODO
+ log_error("todo opcode: %02X\n", action_id);
+ break;
+ case SWF::ACTION_INSTANCEOF: // instance of
+ // @@ TODO
+ doActionInstanceOf(env);
+ break;
+ case SWF::ACTION_ENUM2: // enumerate object
+ // @@ TODO
+ log_error("todo opcode: %02X\n", action_id);
+ break;
+ case SWF::ACTION_BITWISEAND: // bitwise and
+ env->top(1) &= env->top(0);
+ env->drop(1);
+ break;
+ case SWF::ACTION_BITWISEOR: // bitwise or
+ env->top(1) |= env->top(0);
+ env->drop(1);
+ break;
+ case SWF::ACTION_BITWISEXOR: // bitwise xor
+ env->top(1) ^= env->top(0);
+ env->drop(1);
+ break;
+ case SWF::ACTION_SHIFTLEFT: // shift left
+ env->top(1).shl(env->top(0));
+ env->drop(1);
+ break;
+ case SWF::ACTION_SHIFTRIGHT: // shift right (signed)
+ env->top(1).asr(env->top(0));
+ env->drop(1);
+ break;
+ case SWF::ACTION_SHIFTRIGHT2: // shift right
(unsigned)
+ env->top(1).lsr(env->top(0));
+ env->drop(1);
+ break;
+ case SWF::ACTION_STRICTEQ: // strict equal
+ if (env->top(1).get_type() !=
env->top(0).get_type())
+ {
+ // Types don't match.
+ env->top(1).set_bool(false);
+ env->drop(1);
+ }
+ else
+ {
+ env->top(1).set_bool(env->top(1) ==
env->top(0));
+ env->drop(1);
+ }
+ break;
+ case SWF::ACTION_GREATER: // 0x67:
+ if (env->top(1).get_type() == as_value::STRING)
+ {
+
env->top(1).set_bool(env->top(1).to_tu_string() > env->top(0).to_tu_string());
+ }
+ else
+ {
+
env->top(1).set_bool(env->top(1).to_number() > env->top(0).to_number());
+ }
+ env->drop(1);
+ break;
+ case SWF::ACTION_STRINGGREATER: // 0x68
+ env->top(1).set_bool(env->top(1).to_tu_string() >
env->top(0).to_tu_string());
+ env->drop(1);
+ break;
+
+ case SWF::ACTION_EXTENDS: // 0x69
+ log_error("todo opcode: %02X\n", action_id);
+ break;
+
+ }
+ pc++; // advance to next action.
+ }
+ else
+ {
+ IF_VERBOSE_ACTION(log_msg("EX:\t");
log_disasm(&m_buffer[pc]));
+
+ // Action containing extra data.
+ int length = m_buffer[pc + 1] | (m_buffer[pc + 2] << 8);
+ int next_pc = pc + length + 3;
+
+ switch (action_id)
+ {
+ default:
+ break;
+
+ case SWF::ACTION_GOTOFRAME: // goto frame
+ {
+ int frame = m_buffer[pc + 3] | (m_buffer[pc
+ 4] << 8);
+ // 0-based already?
+ //// Convert from 1-based to 0-based
+ //frame--;
+ env->get_target()->goto_frame(frame);
+ break;
+ }
+
+ case SWF::ACTION_GETURL: // get url
+ {
+ // If this is an FSCommand, then call the callback
+ // handler, if any.
+
+ // Two strings as args.
+ const char* url = (const char*)
&(m_buffer[pc + 3]);
+ int url_len = strlen(url);
+ const char* target = (const char*)
&(m_buffer[pc + 3 + url_len + 1]);
+
+ // If the url starts with an "http:", then we
+ // want to load it into a web browser.
+ if (strncmp(url, "http:", 5) == 0) {
+
+ string command = "firefox -remote \"openurl(";
+ command += url;
+ command += ")\"";
+ dbglogfile << "Launching URL... " << command
<< endl;
+ system(command.c_str());
+// movie *target = env->get_target();
+// target->get_url(url);
+ break;
+ }
+
+ // If the url starts with "FSCommand:", then this
is
+ // a message for the host app.
+ if (strncmp(url, "FSCommand:", 10) == 0)
+ {
+ if (s_fscommand_handler)
+ {
+ // Call into the app.
+
(*s_fscommand_handler)(env->get_target()->get_root_interface(), url + 10,
target);
+ }
+ }
+ else
+ {
#ifdef EXTERN_MOVIE
-// log_error("get url: target=%s,
url=%s\n", target, url);
+// log_error("get url: target=%s, url=%s\n",
target, url);
- tu_string tu_target = target;
- movie* target_movie =
env->find_target(tu_target);
- if (target_movie != NULL)
- {
- movie* root_movie =
env->get_target()->get_root_movie();
-
attach_extern_movie(url, target_movie, root_movie);
- }
- else
- {
- log_error("get url:
target %s not found\n", target);
- }
+ tu_string tu_target = target;
+ movie* target_movie =
env->find_target(tu_target);
+ if (target_movie != NULL)
+ {
+ movie* root_movie =
env->get_target()->get_root_movie();
+ attach_extern_movie(url,
target_movie, root_movie);
+ }
+ else
+ {
+ log_error("get url: target %s not
found\n", target);
+ }
#endif // EXTERN_MOVIE
- }
-
- break;
- }
+ }
- case SWF::ACTION_SETREGISTER: //
store_register
- {
- int reg = m_buffer[pc + 3];
- // Save top of stack in specified
register.
- if (is_function2)
- {
- *(env->local_register_ptr(reg))
= env->top(0);
+ break;
+ }
- IF_VERBOSE_ACTION(
- log_msg("--------------
local register[%d] = '%s'\n",
- reg,
-
env->top(0).to_string()));
- }
- else if (reg >= 0 && reg < 4)
- {
- env->m_global_register[reg] =
env->top(0);
+ case SWF::ACTION_SETREGISTER: // store_register
+ {
+ int reg = m_buffer[pc + 3];
+ // Save top of stack in specified register.
+ if (is_function2)
+ {
+ *(env->local_register_ptr(reg)) =
env->top(0);
+
+ IF_VERBOSE_ACTION(
+ log_msg("-------------- local
register[%d] = '%s'\n",
+ reg,
+ env->top(0).to_string()));
+ }
+ else if (reg >= 0 && reg < 4)
+ {
+ env->m_global_register[reg] = env->top(0);
- IF_VERBOSE_ACTION(
- log_msg("--------------
global register[%d] = '%s'\n",
- reg,
-
env->top(0).to_string()));
- }
- else
- {
- log_error("store_register[%d]
-- register out of bounds!", reg);
- }
-
- break;
- }
-
- case SWF::ACTION_CONSTANTPOOL: // decl_dict:
declare dictionary
- {
- int i = pc;
- //int count = m_buffer[pc + 3] |
(m_buffer[pc + 4] << 8);
- i += 2;
-
- process_decl_dict(pc, next_pc);
-
- break;
- }
-
- case SWF::ACTION_WAITFORFRAME: // wait for
frame
- {
- // If we haven't loaded a specified
frame yet, then we're supposed to skip
- // some specified number of actions.
- //
- // Since we don't load incrementally,
just ignore this opcode.
- break;
- }
-
- case SWF::ACTION_SETTARGET: // set target
- {
- // Change the movie we're working on.
- const char* target_name = (const char*)
&m_buffer[pc + 3];
- movie *new_target;
-
- // if the string is blank, we set
target to the root movie
- // TODO - double check this is correct?
- if (target_name[0] == '\0')
- new_target =
env->find_target((tu_string)"/");
- else
- new_target =
env->find_target((tu_string)target_name);
-
- if (new_target == NULL)
- {
- IF_VERBOSE_ACTION(log_error(
- "Couldn't find movie
\"%s\" to set target to!"
- " Not setting target at
all...",
- (const char
*)target_name));
- }
- else
- env->set_target(new_target);
-
- break;
- }
-
- case SWF::ACTION_GOTOLABEL: // go to
labeled frame, goto_frame_lbl
- {
- char* frame_label = (char*)
&m_buffer[pc + 3];
- movie *target = env->get_target();
- target->goto_labeled_frame(frame_label);
- break;
- }
-
- case SWF::ACTION_WAITFORFRAMEEXPRESSION:
// wait for frame expression (?)
- {
- // Pop the frame number to wait for; if
it's not loaded skip the
- // specified number of actions.
- //
- // Since we don't support incremental
loading, pop our arg and
- // don't do anything.
- env->drop(1);
- break;
- }
-
- case SWF::ACTION_DEFINEFUNCTION2: // 0x8E
- doActionDefineFunction2(env,
with_stack, pc, &next_pc);
- break;
-
- case SWF::ACTION_WITH: // with
- {
- int frame = m_buffer[pc + 3] |
(m_buffer[pc + 4] << 8);
- UNUSED(frame);
-
IF_VERBOSE_ACTION(log_msg("-------------- with block start: stack size is
%d\n", with_stack.size()));
- if (with_stack.size() < 8)
- {
- int block_length =
m_buffer[pc + 3] | (m_buffer[pc + 4] << 8);
- int block_end = next_pc +
block_length;
- as_object* with_obj =
env->top(0).to_object();
-
with_stack.push_back(with_stack_entry(with_obj, block_end));
- }
- env->drop(1);
- break;
- }
- case SWF::ACTION_PUSHDATA: // push_data
- {
- int i = pc;
- while (i - pc < length)
- {
- int type = m_buffer[3 + i];
- i++;
- if (type == 0)
- {
- // string
- const char* str =
(const char*) &m_buffer[3 + i];
- i += strlen(str) + 1;
- env->push(str);
-
-
IF_VERBOSE_ACTION(log_msg("-------------- pushed '%s'\n", str));
- }
- else if (type == 1)
- {
- // float (little-endian)
- union {
- float f;
- Uint32 i;
- } u;
-
compiler_assert(sizeof(u) == sizeof(u.i));
-
- memcpy(&u.i,
&m_buffer[3 + i], 4);
- u.i = swap_le32(u.i);
- i += 4;
-
- env->push(u.f);
-
-
IF_VERBOSE_ACTION(log_msg("-------------- pushed '%f'\n", u.f));
- }
- else if (type == 2)
- {
- as_value nullvalue;
- nullvalue.set_null();
- env->push(nullvalue);
-
-
IF_VERBOSE_ACTION(log_msg("-------------- pushed NULL\n"));
- }
- else if (type == 3)
- {
- env->push(as_value());
-
-
IF_VERBOSE_ACTION(log_msg("-------------- pushed UNDEFINED\n"));
- }
- else if (type == 4)
- {
- // contents of register
- int reg =
m_buffer[3 + i];
- UNUSED(reg);
- i++;
- if (is_function2)
- {
-
env->push(*(env->local_register_ptr(reg)));
-
IF_VERBOSE_ACTION(
-
log_msg("-------------- pushed local register[%d] = '%s'\n",
-
reg,
-
env->top(0).to_string()));
- }
- else if (reg < 0 || reg
>= 4)
- {
-
env->push(as_value());
- log_error("push
register[%d] -- register out of bounds!\n", reg);
- }
- else
- {
-
env->push(env->m_global_register[reg]);
-
IF_VERBOSE_ACTION(
-
log_msg("-------------- pushed global register[%d] = '%s'\n",
-
reg,
-
env->top(0).to_string()));
- }
-
- }
- else if (type == 5)
- {
- bool bool_val =
m_buffer[3 + i] ? true : false;
- i++;
+ IF_VERBOSE_ACTION(
+ log_msg("-------------- global
register[%d] = '%s'\n",
+ reg,
+ env->top(0).to_string()));
+ }
+ else
+ {
+ log_error("store_register[%d] -- register
out of bounds!", reg);
+ }
+
+ break;
+ }
+
+ case SWF::ACTION_CONSTANTPOOL: // decl_dict:
declare dictionary
+ {
+ int i = pc;
+ //int count = m_buffer[pc + 3] | (m_buffer[pc
+ 4] << 8);
+ i += 2;
+
+ process_decl_dict(pc, next_pc);
+
+ break;
+ }
+
+ case SWF::ACTION_WAITFORFRAME: // wait for
frame
+ {
+ // If we haven't loaded a specified frame yet,
then we're supposed to skip
+ // some specified number of actions.
+ //
+ // Since we don't load incrementally, just ignore
this opcode.
+ break;
+ }
+
+ case SWF::ACTION_SETTARGET: // set target
+ {
+ // Change the movie we're working on.
+ const char* target_name = (const char*)
&m_buffer[pc + 3];
+ movie *new_target;
+
+ // if the string is blank, we set target to the
root movie
+ // TODO - double check this is correct?
+ if (target_name[0] == '\0')
+ new_target = env->find_target((tu_string)"/");
+ else
+ new_target =
env->find_target((tu_string)target_name);
+
+ if (new_target == NULL)
+ {
+ IF_VERBOSE_ACTION(log_error(
+ "Couldn't find
movie \"%s\" to set target to!"
+ " Not setting
target at all...",
+ (const char
*)target_name));
+ }
+ else
+ env->set_target(new_target);
+
+ break;
+ }
+
+ case SWF::ACTION_GOTOLABEL: // go to labeled frame,
goto_frame_lbl
+ {
+ char* frame_label = (char*) &m_buffer[pc + 3];
+ movie *target = env->get_target();
+ target->goto_labeled_frame(frame_label);
+ break;
+ }
+
+ case SWF::ACTION_WAITFORFRAMEEXPRESSION: // wait
for frame expression (?)
+ {
+ // Pop the frame number to wait for; if it's not
loaded skip the
+ // specified number of actions.
+ //
+ // Since we don't support incremental loading,
pop our arg and
+ // don't do anything.
+ env->drop(1);
+ break;
+ }
+
+ case SWF::ACTION_DEFINEFUNCTION2: // 0x8E
+ doActionDefineFunction2(env, with_stack, pc,
&next_pc);
+ break;
+
+ case SWF::ACTION_WITH: // with
+ {
+ int frame = m_buffer[pc + 3] | (m_buffer[pc
+ 4] << 8);
+ UNUSED(frame);
+ IF_VERBOSE_ACTION(log_msg("-------------- with
block start: stack size is %d\n", with_stack.size()));
+ if (with_stack.size() < 8)
+ {
+ int block_length = m_buffer[pc + 3]
| (m_buffer[pc + 4] << 8);
+ int block_end = next_pc +
block_length;
+ as_object* with_obj =
env->top(0).to_object();
+
with_stack.push_back(with_stack_entry(with_obj, block_end));
+ }
+ env->drop(1);
+ break;
+ }
+ case SWF::ACTION_PUSHDATA: // push_data
+ {
+ int i = pc;
+ while (i - pc < length)
+ {
+ int type = m_buffer[3 + i];
+ i++;
+ if (type == 0)
+ {
+ // string
+ const char* str = (const
char*) &m_buffer[3 + i];
+ i += strlen(str) + 1;
+ env->push(str);
+
+
IF_VERBOSE_ACTION(log_msg("-------------- pushed '%s'\n", str));
+ }
+ else if (type == 1)
+ {
+ // float (little-endian)
+ union {
+ float f;
+ Uint32 i;
+ } u;
+ compiler_assert(sizeof(u) ==
sizeof(u.i));
+
+ memcpy(&u.i, &m_buffer[3 + i], 4);
+ u.i = swap_le32(u.i);
+ i += 4;
+
+ env->push(u.f);
+
+
IF_VERBOSE_ACTION(log_msg("-------------- pushed '%f'\n", u.f));
+ }
+ else if (type == 2)
+ {
+ as_value nullvalue;
+ nullvalue.set_null();
+ env->push(nullvalue);
+
+
IF_VERBOSE_ACTION(log_msg("-------------- pushed NULL\n"));
+ }
+ else if (type == 3)
+ {
+ env->push(as_value());
+
+
IF_VERBOSE_ACTION(log_msg("-------------- pushed UNDEFINED\n"));
+ }
+ else if (type == 4)
+ {
+ // contents of register
+ int reg = m_buffer[3 + i];
+ UNUSED(reg);
+ i++;
+ if (is_function2)
+ {
+
env->push(*(env->local_register_ptr(reg)));
+ IF_VERBOSE_ACTION(
+
log_msg("-------------- pushed local register[%d] = '%s'\n",
+ reg,
+
env->top(0).to_string()));
+ }
+ else if (reg < 0 || reg >= 4)
+ {
+ env->push(as_value());
+ log_error("push
register[%d] -- register out of bounds!\n", reg);
+ }
+ else
+ {
+
env->push(env->m_global_register[reg]);
+ IF_VERBOSE_ACTION(
+
log_msg("-------------- pushed global register[%d] = '%s'\n",
+ reg,
+
env->top(0).to_string()));
+ }
+
+ }
+ else if (type == 5)
+ {
+ bool bool_val = m_buffer[3 +
i] ? true : false;
+ i++;
// log_msg("bool(%d)\n",
bool_val);
- env->push(bool_val);
+ env->push(bool_val);
-
IF_VERBOSE_ACTION(log_msg("-------------- pushed %s\n", bool_val ? "true" :
"false"));
- }
- else if (type == 6)
- {
- // double
- // wacky format:
45670123
- union {
- double d;
- Uint64 i;
- struct {
- Uint32
lo;
- Uint32
hi;
- } sub;
- } u;
-
compiler_assert(sizeof(u) == sizeof(u.i));
-
- memcpy(&u.sub.hi,
&m_buffer[3 + i], 4);
- memcpy(&u.sub.lo,
&m_buffer[3 + i + 4], 4);
- u.i = swap_le64(u.i);
- i += 8;
-
- env->push(u.d);
-
-
IF_VERBOSE_ACTION(log_msg("-------------- pushed double %f\n", u.d));
- }
- else if (type == 7)
- {
- // int32
- Sint32 val =
m_buffer[3 + i]
- | (m_buffer[3 +
i + 1] << 8)
- | (m_buffer[3 +
i + 2] << 16)
- | (m_buffer[3 +
i + 3] << 24);
- i += 4;
+
IF_VERBOSE_ACTION(log_msg("-------------- pushed %s\n", bool_val ? "true" :
"false"));
+ }
+ else if (type == 6)
+ {
+ // double
+ // wacky format: 45670123
+ union {
+ double d;
+ Uint64 i;
+ struct {
+ Uint32 lo;
+ Uint32 hi;
+ } sub;
+ } u;
+ compiler_assert(sizeof(u) ==
sizeof(u.i));
+
+ memcpy(&u.sub.hi, &m_buffer[3 +
i], 4);
+ memcpy(&u.sub.lo, &m_buffer[3 + i
+ 4], 4);
+ u.i = swap_le64(u.i);
+ i += 8;
+
+ env->push(u.d);
+
+
IF_VERBOSE_ACTION(log_msg("-------------- pushed double %f\n", u.d));
+ }
+ else if (type == 7)
+ {
+ // int32
+ Sint32 val = m_buffer[3 + i]
+ | (m_buffer[3 + i + 1] << 8)
+ | (m_buffer[3 + i + 2] << 16)
+ | (m_buffer[3 + i + 3] << 24);
+ i += 4;
- env->push(val);
+ env->push(val);
-
IF_VERBOSE_ACTION(log_msg("-------------- pushed int32 %d\n", val));
- }
- else if (type == 8)
- {
- int id = m_buffer[3
+ i];
- i++;
- if (id < (int)
m_dictionary.size())
- {
-
env->push(m_dictionary[id]);
-
-
IF_VERBOSE_ACTION(log_msg("-------------- pushed '%s'\n", m_dictionary[id]));
- }
- else
- {
-
log_error("error: dict_lookup(%d) is out of bounds!\n", id);
- env->push(0);
-
IF_VERBOSE_ACTION(log_msg("-------------- pushed 0\n"));
- }
- }
- else if (type == 9)
- {
- int id = m_buffer[3
+ i] | (m_buffer[4 + i] << 8);
- i += 2;
- if (id < (int)
m_dictionary.size())
- {
-
env->push(m_dictionary[id]);
-
IF_VERBOSE_ACTION(log_msg("-------------- pushed '%s'\n", m_dictionary[id]));
- }
- else
- {
-
log_error("error: dict_lookup(%d) is out of bounds!\n", id);
- env->push(0);
-
-
IF_VERBOSE_ACTION(log_msg("-------------- pushed 0"));
- }
- }
- }
+
IF_VERBOSE_ACTION(log_msg("-------------- pushed int32 %d\n", val));
+ }
+ else if (type == 8)
+ {
+ int id = m_buffer[3 + i];
+ i++;
+ if (id < (int)
m_dictionary.size())
+ {
+
env->push(m_dictionary[id]);
+
+
IF_VERBOSE_ACTION(log_msg("-------------- pushed '%s'\n", m_dictionary[id]));
+ }
+ else
+ {
+ log_error("error:
dict_lookup(%d) is out of bounds!\n", id);
+ env->push(0);
+
IF_VERBOSE_ACTION(log_msg("-------------- pushed 0\n"));
+ }
+ }
+ else if (type == 9)
+ {
+ int id = m_buffer[3 + i] |
(m_buffer[4 + i] << 8);
+ i += 2;
+ if (id < (int)
m_dictionary.size())
+ {
+
env->push(m_dictionary[id]);
+
IF_VERBOSE_ACTION(log_msg("-------------- pushed '%s'\n", m_dictionary[id]));
+ }
+ else
+ {
+ log_error("error:
dict_lookup(%d) is out of bounds!\n", id);
+ env->push(0);
+
+
IF_VERBOSE_ACTION(log_msg("-------------- pushed 0"));
+ }
+ }
+ }
- break;
- }
- case SWF::ACTION_BRANCHALWAYS: // branch
always (goto)
- {
- Sint16 offset = m_buffer[pc + 3] |
(m_buffer[pc + 4] << 8);
- next_pc += offset;
- // @@ TODO range checks
- break;
- }
- case SWF::ACTION_GETURL2: // get url 2
- {
- int method = m_buffer[pc + 3];
- UNUSED(method);
-
- const char* target =
env->top(0).to_string();
- const char* url =
env->top(1).to_string();
-
- // If the url starts with "FSCommand:",
then this is
- // a message for the host app.
- if (strncmp(url, "FSCommand:", 10) == 0)
- {
- if (s_fscommand_handler)
- {
- // Call into the app.
-
(*s_fscommand_handler)(env->get_target()->get_root_interface(), url + 10,
target);
- }
- }
- else
- {
+ break;
+ }
+ case SWF::ACTION_BRANCHALWAYS: // branch
always (goto)
+ {
+ Sint16 offset = m_buffer[pc + 3] |
(m_buffer[pc + 4] << 8);
+ next_pc += offset;
+ // @@ TODO range checks
+ break;
+ }
+ case SWF::ACTION_GETURL2: // get url 2
+ {
+ int method = m_buffer[pc + 3];
+ UNUSED(method);
+
+ const char* target =
env->top(0).to_string();
+ const char* url = env->top(1).to_string();
+
+ // If the url starts with "FSCommand:", then this
is
+ // a message for the host app.
+ if (strncmp(url, "FSCommand:", 10) == 0)
+ {
+ if (s_fscommand_handler)
+ {
+ // Call into the app.
+
(*s_fscommand_handler)(env->get_target()->get_root_interface(), url + 10,
target);
+ }
+ }
+ else
+ {
#ifdef EXTERN_MOVIE
// log_error("get url2: target=%s, url=%s\n", target, url);
- movie* target_movie =
env->find_target(env->top(0));
- if (target_movie != NULL)
- {
- movie* root_movie =
env->get_target()->get_root_movie();
-
attach_extern_movie(url, target_movie, root_movie);
- }
- else
- {
- log_error("get url2:
target %s not found\n", target);
- }
+ movie* target_movie =
env->find_target(env->top(0));
+ if (target_movie != NULL)
+ {
+ movie* root_movie =
env->get_target()->get_root_movie();
+ attach_extern_movie(url,
target_movie, root_movie);
+ }
+ else
+ {
+ log_error("get url2: target %s
not found\n", target);
+ }
#endif // EXTERN_MOVIE
- }
- env->drop(2);
- break;
- }
-
- case SWF::ACTION_DEFINEFUNCTION: // declare
function
- doActionDefineFunction(env, with_stack,
pc, &next_pc);
- break;
-
- case SWF::ACTION_BRANCHIFTRUE: // branch if
true
- {
- Sint16 offset = m_buffer[pc + 3] |
(m_buffer[pc + 4] << 8);
+ }
+ env->drop(2);
+ break;
+ }
+
+ case SWF::ACTION_DEFINEFUNCTION: // declare function
+ doActionDefineFunction(env, with_stack, pc,
&next_pc);
+ break;
+
+ case SWF::ACTION_BRANCHIFTRUE: // branch if
true
+ {
+ Sint16 offset = m_buffer[pc + 3] |
(m_buffer[pc + 4] << 8);
- bool test = env->top(0).to_bool();
- env->drop(1);
- if (test)
- {
- next_pc += offset;
-
- if (next_pc > stop_pc)
- {
- log_error("branch to
offset %d -- this section only runs to %d\n",
- next_pc,
- stop_pc);
- }
- }
- break;
- }
- case SWF::ACTION_CALLFRAME: // call frame
- {
- // Note: no extra data in this
instruction!
- assert(env->m_target);
-
env->m_target->call_frame_actions(env->top(0));
- env->drop(1);
-
- break;
- }
-
- case SWF::ACTION_GOTOEXPRESSION: // goto
frame expression, goto_frame_exp
- {
- // From Alexi's SWF ref:
- //
- // Pop a value or a string and jump to
the specified
- // frame. When a string is specified,
it can include a
- // path to a sprite as in:
- //
- // /Test:55
- //
- // When f_play is ON, the action is to
play as soon as
- // that frame is reached. Otherwise, the
- // frame is shown in stop mode.
-
- unsigned char play_flag = m_buffer[pc
+ 3];
- movie::play_state state =
play_flag ? movie::PLAY : movie::STOP;
-
- movie* target = env->get_target();
- bool success = false;
-
- if (env->top(0).get_type() ==
as_value::UNDEFINED)
- {
- // No-op.
- }
- else if (env->top(0).get_type() ==
as_value::STRING)
- {
- // @@ TODO: parse possible
sprite path...
+ bool test = env->top(0).to_bool();
+ env->drop(1);
+ if (test)
+ {
+ next_pc += offset;
+
+ if (next_pc > stop_pc)
+ {
+ log_error("branch to offset %d --
this section only runs to %d\n",
+ next_pc,
+ stop_pc);
+ }
+ }
+ break;
+ }
+ case SWF::ACTION_CALLFRAME: // call frame
+ {
+ // Note: no extra data in this instruction!
+ assert(env->m_target);
+ env->m_target->call_frame_actions(env->top(0));
+ env->drop(1);
+
+ break;
+ }
+
+ case SWF::ACTION_GOTOEXPRESSION: // goto frame
expression, goto_frame_exp
+ {
+ // From Alexi's SWF ref:
+ //
+ // Pop a value or a string and jump to the
specified
+ // frame. When a string is specified, it can
include a
+ // path to a sprite as in:
+ //
+ // /Test:55
+ //
+ // When f_play is ON, the action is to play as
soon as
+ // that frame is reached. Otherwise, the
+ // frame is shown in stop mode.
+
+ unsigned char play_flag = m_buffer[pc + 3];
+ movie::play_state state = play_flag ? movie::PLAY
: movie::STOP;
+
+ movie* target = env->get_target();
+ bool success = false;
+
+ if (env->top(0).get_type() == as_value::UNDEFINED)
+ {
+ // No-op.
+ }
+ else if (env->top(0).get_type() ==
as_value::STRING)
+ {
+ // @@ TODO: parse possible sprite path...
- // Also, if the frame spec is
actually a number (not a label), then
- // we need to do the
conversion...
-
- const char* frame_label =
env->top(0).to_string();
- if
(target->goto_labeled_frame(frame_label))
- {
- success = true;
- }
- else
- {
- // Couldn't find the
label. Try converting to a number.
- double num;
- if
(string_to_number(&num, env->top(0).to_string()))
- {
- int
frame_number = int(num);
-
target->goto_frame(frame_number);
- success = true;
- }
- // else no-op.
- }
- }
- else if (env->top(0).get_type() ==
as_value::OBJECT)
- {
- // This is a no-op; see
test_goto_frame.swf
- }
- else if (env->top(0).get_type() ==
as_value::NUMBER)
- {
- // Frame numbers appear to be
0-based! @@ Verify.
- int frame_number =
int(env->top(0).to_number());
-
target->goto_frame(frame_number);
- success = true;
- }
+ // Also, if the frame spec is actually a
number (not a label), then
+ // we need to do the conversion...
- if (success)
- {
- target->set_play_state(state);
- }
+ const char* frame_label =
env->top(0).to_string();
+ if
(target->goto_labeled_frame(frame_label))
+ {
+ success = true;
+ }
+ else
+ {
+ // Couldn't find the label. Try
converting to a number.
+ double num;
+ if (string_to_number(&num,
env->top(0).to_string()))
+ {
+ int frame_number =
int(num);
+
target->goto_frame(frame_number);
+ success = true;
+ }
+ // else no-op.
+ }
+ }
+ else if (env->top(0).get_type() ==
as_value::OBJECT)
+ {
+ // This is a no-op; see
test_goto_frame.swf
+ }
+ else if (env->top(0).get_type() ==
as_value::NUMBER)
+ {
+ // Frame numbers appear to be 0-based!
@@ Verify.
+ int frame_number =
int(env->top(0).to_number());
+ target->goto_frame(frame_number);
+ success = true;
+ }
+
+ if (success)
+ {
+ target->set_play_state(state);
+ }
- env->drop(1);
+ env->drop(1);
- break;
- }
+ break;
+ }
- }
- pc = next_pc;
}
+ pc = next_pc;
}
-
- env->set_target(original_target);
}
+ env->set_target(original_target);
+}
+
- //
- // as_value -- ActionScript value type
- //
+//
+// as_value -- ActionScript value type
+//
- as_value::as_value(as_object* obj)
- :
- m_type(OBJECT),
- m_object_value(obj)
+as_value::as_value(as_object* obj)
+ :
+ m_type(OBJECT),
+ m_object_value(obj)
+{
+ if (m_object_value)
{
- if (m_object_value)
- {
- m_object_value->add_ref();
- }
+ m_object_value->add_ref();
}
+}
- as_value::as_value(function_as_object* func)
- :
- m_type(AS_FUNCTION),
- m_as_function_value(func)
+as_value::as_value(function_as_object* func)
+ :
+ m_type(AS_FUNCTION),
+ m_as_function_value(func)
+{
+ if (m_as_function_value)
{
- if (m_as_function_value)
- {
- m_as_function_value->add_ref();
- }
+ m_as_function_value->add_ref();
}
+}
- const char* as_value::to_string() const
- // Conversion to string.
- {
- return to_tu_string().c_str();
- }
+const char* as_value::to_string() const
+ // Conversion to string.
+{
+ return to_tu_string().c_str();
+}
- const tu_stringi& as_value::to_tu_stringi() const
- {
- return reinterpret_cast<const tu_stringi&>(to_tu_string());
- }
+const tu_stringi& as_value::to_tu_stringi() const
+{
+ return reinterpret_cast<const tu_stringi&>(to_tu_string());
+}
- const tu_string& as_value::to_tu_string() const
- // Conversion to const tu_string&.
+const tu_string& as_value::to_tu_string() const
+ // Conversion to const tu_string&.
+{
+ if (m_type == STRING) { /* don't need to do anything */ }
+ else if (m_type == NUMBER)
{
- if (m_type == STRING) { /* don't need to do anything */ }
- else if (m_type == NUMBER)
+ // @@ Moock says if value is a NAN, then result is "NaN"
+ // INF goes to "Infinity"
+ // -INF goes to "-Infinity"
+ if (isnan(m_number_value)) m_string_value = "NaN";
+ else if (isinf(m_number_value))
{
- // @@ Moock says if value is a NAN, then result is "NaN"
- // INF goes to "Infinity"
- // -INF goes to "-Infinity"
- if (isnan(m_number_value)) m_string_value = "NaN";
- else if (isinf(m_number_value))
- {
- if (m_number_value > 0.0) m_string_value =
"+Infinity";
- else m_string_value = "-Infinity";
- }
- else
- {
- char buffer[50];
- snprintf(buffer, 50, "%.14g", m_number_value);
- m_string_value = buffer;
- }
+ if (m_number_value > 0.0) m_string_value = "+Infinity";
+ else m_string_value = "-Infinity";
}
- else if (m_type == UNDEFINED)
+ else
{
- // Behavior depends on file version. In
- // version 7+, it's "undefined", in versions
- // 6-, it's "".
- //
- // We'll go with the v7 behavior by default,
- // and conditionalize via _versioned()
- // functions.
- m_string_value = "undefined";
- }
- else if (m_type == NULLTYPE)
- {
- m_string_value = "null";
- }
- else if (m_type == BOOLEAN)
- {
- m_string_value = this->m_boolean_value ? "true" :
"false";
- }
- else if (m_type == OBJECT)
- {
- // @@ Moock says, "the value that results from
- // calling toString() on the object".
- //
- // The default toString() returns "[object
- // Object]" but may be customized.
- //
- // A Movieclip returns the absolute path of the object.
-
- const char* val = NULL;
- if (m_object_value)
- {
- val = m_object_value->get_text_value();
- }
-
- if (val)
- {
- m_string_value = val;
- }
- else
- {
- // Do we have a "toString" method?
- //
- // TODO: we need an environment in order to
call toString()!
-
- // This is the default.
- //m_string_value = "[object Object]";
- char buffer[50];
- snprintf(buffer, 50, "<as_object %p>", (void *)
m_object_value);
- m_string_value = buffer;
- }
+ char buffer[50];
+ snprintf(buffer, 50, "%.14g", m_number_value);
+ m_string_value = buffer;
}
- else if (m_type == C_FUNCTION)
+ }
+ else if (m_type == UNDEFINED)
+ {
+ // Behavior depends on file version. In
+ // version 7+, it's "undefined", in versions
+ // 6-, it's "".
+ //
+ // We'll go with the v7 behavior by default,
+ // and conditionalize via _versioned()
+ // functions.
+ m_string_value = "undefined";
+ }
+ else if (m_type == NULLTYPE)
+ {
+ m_string_value = "null";
+ }
+ else if (m_type == BOOLEAN)
+ {
+ m_string_value = this->m_boolean_value ? "true" : "false";
+ }
+ else if (m_type == OBJECT)
+ {
+ // @@ Moock says, "the value that results from
+ // calling toString() on the object".
+ //
+ // The default toString() returns "[object
+ // Object]" but may be customized.
+ //
+ // A Movieclip returns the absolute path of the object.
+
+ const char* val = NULL;
+ if (m_object_value)
{
- char buffer[50];
- snprintf(buffer, 50, "<c_function %p>", (void *)
m_c_function_value);
- m_string_value = buffer;
+ val = m_object_value->get_text_value();
}
- else if (m_type == AS_FUNCTION)
+
+ if (val)
{
- char buffer[50];
- snprintf(buffer, 50, "<as_function %p>", (void *)
m_as_function_value);
- m_string_value = buffer;
+ m_string_value = val;
}
- else
+ else
{
- m_string_value = "<bad type> "+m_type;
- assert(0);
+ // Do we have a "toString" method?
+ //
+ // TODO: we need an environment in order to call toString()!
+
+ // This is the default.
+ //m_string_value = "[object Object]";
+ char buffer[50];
+ snprintf(buffer, 50, "<as_object %p>", (void *)
m_object_value);
+ m_string_value = buffer;
}
-
- return m_string_value;
}
-
- const tu_string& as_value::to_tu_string_versioned(int version)
const
- // Conversion to const tu_string&.
+ else if (m_type == C_FUNCTION)
{
- if (m_type == UNDEFINED)
- {
- // Version-dependent behavior.
- if (version <= 6)
- {
- m_string_value = "";
- }
- else
- {
- m_string_value = "undefined";
- }
- return m_string_value;
- }
-
- return to_tu_string();
+ char buffer[50];
+ snprintf(buffer, 50, "<c_function %p>", (void *)
m_c_function_value);
+ m_string_value = buffer;
+ }
+ else if (m_type == AS_FUNCTION)
+ {
+ char buffer[50];
+ snprintf(buffer, 50, "<as_function %p>", (void *)
m_as_function_value);
+ m_string_value = buffer;
+ }
+ else
+ {
+ m_string_value = "<bad type> "+m_type;
+ assert(0);
}
+
+ return m_string_value;
+}
- double as_value::to_number() const
- // Conversion to double.
+const tu_string& as_value::to_tu_string_versioned(int version) const
+ // Conversion to const tu_string&.
+{
+ if (m_type == UNDEFINED)
{
- if (m_type == STRING)
- {
- // @@ Moock says the rule here is: if the
- // string is a valid float literal, then it
- // gets converted; otherwise it is set to NaN.
- //
- // Also, "Infinity", "-Infinity", and "NaN"
- // are recognized.
- if (! string_to_number(&m_number_value,
m_string_value.c_str()))
- {
- // Failed conversion to Number.
- double temp = 0.0; // avoid divide by zero
compiler warning by using a variable
- m_number_value = temp / temp; // this
division by zero creates a NaN value in the double
- }
- return m_number_value;
- }
- else if (m_type == NULLTYPE)
+ // Version-dependent behavior.
+ if (version <= 6)
{
- // Evan: from my tests
- return 0;
+ m_string_value = "";
}
- else if (m_type == BOOLEAN)
+ else
{
- // Evan: from my tests
- return (this->m_boolean_value) ? 1 : 0;
+ m_string_value = "undefined";
}
- else if (m_type == NUMBER)
+ return m_string_value;
+ }
+
+ return to_tu_string();
+}
+
+double as_value::to_number() const
+ // Conversion to double.
+{
+ if (m_type == STRING)
+ {
+ // @@ Moock says the rule here is: if the
+ // string is a valid float literal, then it
+ // gets converted; otherwise it is set to NaN.
+ //
+ // Also, "Infinity", "-Infinity", and "NaN"
+ // are recognized.
+ if (! string_to_number(&m_number_value, m_string_value.c_str()))
{
- return m_number_value;
+ // Failed conversion to Number.
+ double temp = 0.0; // avoid divide by zero compiler warning
by using a variable
+ m_number_value = temp / temp; // this division by
zero creates a NaN value in the double
}
- else if (m_type == OBJECT && m_object_value != NULL)
- {
- // @@ Moock says the result here should be
- // "the return value of the object's valueOf()
- // method".
- //
- // Arrays and Movieclips should return NaN.
-
- // Text characters with var names could get in
- // here.
- const char* textval = m_object_value->get_text_value();
- if (textval)
- {
- return atof(textval);
- }
+ return m_number_value;
+ }
+ else if (m_type == NULLTYPE)
+ {
+ // Evan: from my tests
+ return 0;
+ }
+ else if (m_type == BOOLEAN)
+ {
+ // Evan: from my tests
+ return (this->m_boolean_value) ? 1 : 0;
+ }
+ else if (m_type == NUMBER)
+ {
+ return m_number_value;
+ }
+ else if (m_type == OBJECT && m_object_value != NULL)
+ {
+ // @@ Moock says the result here should be
+ // "the return value of the object's valueOf()
+ // method".
+ //
+ // Arrays and Movieclips should return NaN.
- return 0.0;
- }
- else
+ // Text characters with var names could get in
+ // here.
+ const char* textval = m_object_value->get_text_value();
+ if (textval)
{
- return 0.0;
+ return atof(textval);
}
+
+ return 0.0;
+ }
+ else
+ {
+ return 0.0;
}
+}
- bool as_value::to_bool() const
- // Conversion to boolean.
+bool as_value::to_bool() const
+ // Conversion to boolean.
+{
+ // From Moock
+ if (m_type == STRING)
{
- // From Moock
- if (m_type == STRING)
- {
- if (m_string_value == "false")
- {
- return false;
- }
- else if (m_string_value == "true")
- {
- return true;
- }
- else
- {
- // @@ Moock: "true if the string can
- // be converted to a valid nonzero
- // number".
- //
- // Empty string --> false
- return to_number() != 0.0;
- }
- }
- else if (m_type == NUMBER)
- {
- // If m_number_value is NaN, comparison will
automatically be false, as it should
- return m_number_value != 0.0;
- }
- else if (m_type == BOOLEAN)
- {
- return this->m_boolean_value;
- }
- else if (m_type == OBJECT)
+ if (m_string_value == "false")
{
- return m_object_value != NULL;
+ return false;
}
- else if (m_type == C_FUNCTION)
+ else if (m_string_value == "true")
{
- return m_c_function_value != NULL;
+ return true;
}
- else if (m_type == AS_FUNCTION)
+ else
{
- return m_as_function_value != NULL;
- }
- else
- {
- assert(m_type == UNDEFINED || m_type == NULLTYPE);
- return false;
+ // @@ Moock: "true if the string can
+ // be converted to a valid nonzero
+ // number".
+ //
+ // Empty string --> false
+ return to_number() != 0.0;
}
}
-
-
- as_object* as_value::to_object() const
- // Return value as an object.
+ else if (m_type == NUMBER)
{
- if (m_type == OBJECT)
- {
- // OK.
- return m_object_value;
- }
- else if (m_type == AS_FUNCTION)
- {
- // An AS_FUNCTION *is* an object
- return m_as_function_value;
- }
- else
- {
- return NULL;
- }
+ // If m_number_value is NaN, comparison will automatically be
false, as it should
+ return m_number_value != 0.0;
}
-
-
- as_c_function_ptr as_value::to_c_function() const
- // Return value as a C function ptr. Returns NULL if value is
- // not a C function.
+ else if (m_type == BOOLEAN)
{
- if (m_type == C_FUNCTION)
- {
- // OK.
- return m_c_function_value;
- }
- else
- {
- return NULL;
- }
+ return this->m_boolean_value;
}
-
- function_as_object* as_value::to_as_function() const
- // Return value as an ActionScript function. Returns NULL if value is
- // not an ActionScript function.
+ else if (m_type == OBJECT)
{
- if (m_type == AS_FUNCTION)
- {
- // OK.
- return m_as_function_value;
- }
- else
- {
- return NULL;
- }
+ return m_object_value != NULL;
}
-
-
- void as_value::convert_to_number()
- // Force type to number.
+ else if (m_type == C_FUNCTION)
{
- set_double(to_number());
+ return m_c_function_value != NULL;
}
-
-
- void as_value::convert_to_string()
- // Force type to string.
+ else if (m_type == AS_FUNCTION)
{
- to_tu_string(); // init our string data.
- m_type = STRING; // force type.
+ return m_as_function_value != NULL;
}
-
-
- void as_value::convert_to_string_versioned(int version)
- // Force type to string.
+ else
{
- to_tu_string_versioned(version); // init our string data.
- m_type = STRING; // force type.
+ assert(m_type == UNDEFINED || m_type == NULLTYPE);
+ return false;
}
+}
-
- void as_value::set_as_object(as_object* obj)
+
+as_object* as_value::to_object() const
+ // Return value as an object.
+{
+ if (m_type == OBJECT)
{
- if (m_type != OBJECT || m_object_value != obj)
- {
- drop_refs();
- m_type = OBJECT;
- m_object_value = obj;
- if (m_object_value)
- {
- m_object_value->add_ref();
- }
- }
+ // OK.
+ return m_object_value;
}
-
- void as_value::set_function_as_object(function_as_object* func)
+ else if (m_type == AS_FUNCTION)
{
- if (m_type != AS_FUNCTION || m_as_function_value != func)
- {
- drop_refs();
- m_type = AS_FUNCTION;
- m_as_function_value = func;
- if (m_as_function_value)
- {
- m_as_function_value->add_ref();
- }
- }
+ // An AS_FUNCTION *is* an object
+ return m_as_function_value;
+ }
+ else
+ {
+ return NULL;
}
+}
- bool as_value::operator==(const as_value& v) const
- // Return true if operands are equal.
+as_c_function_ptr as_value::to_c_function() const
+ // Return value as a C function ptr. Returns NULL if value is
+ // not a C function.
+{
+ if (m_type == C_FUNCTION)
{
- bool this_nulltype = (m_type == UNDEFINED || m_type ==
NULLTYPE);
- bool v_nulltype = (v.get_type() == UNDEFINED || v.get_type() ==
NULLTYPE);
- if (this_nulltype || v_nulltype)
- {
- return this_nulltype == v_nulltype;
- }
- else if (m_type == STRING)
- {
- return m_string_value == v.to_tu_string();
- }
- else if (m_type == NUMBER)
- {
- return m_number_value == v.to_number();
- }
- else if (m_type == BOOLEAN)
- {
- return m_boolean_value == v.to_bool();
- }
- else if (m_type == OBJECT)
- {
- return m_object_value == v.to_object();
- }
- else if (m_type == AS_FUNCTION)
- {
- return m_as_function_value == v.to_object();
- }
- else if (m_type == C_FUNCTION)
- {
- return m_c_function_value == v.to_c_function();
- }
- else
- {
- // Evan: what about objects???
- // TODO
- return m_type == v.m_type;
- }
+ // OK.
+ return m_c_function_value;
+ }
+ else
+ {
+ return NULL;
}
+}
-
- bool as_value::operator!=(const as_value& v) const
- // Return true if operands are not equal.
+function_as_object* as_value::to_as_function() const
+ // Return value as an ActionScript function. Returns NULL if value is
+ // not an ActionScript function.
+{
+ if (m_type == AS_FUNCTION)
{
- return ! (*this == v);
+ // OK.
+ return m_as_function_value;
}
-
-
- void as_value::string_concat(const tu_string& str)
- // Sets *this to this string plus the given string.
+ else
{
- to_tu_string(); // make sure our m_string_value is initialized
- m_type = STRING;
- m_string_value += str;
+ return NULL;
}
+}
+
+
+void as_value::convert_to_number()
+ // Force type to number.
+{
+ set_double(to_number());
+}
+
+
+void as_value::convert_to_string()
+ // Force type to string.
+{
+ to_tu_string(); // init our string data.
+ m_type = STRING; // force type.
+}
+
+
+void as_value::convert_to_string_versioned(int version)
+ // Force type to string.
+{
+ to_tu_string_versioned(version); // init our string data.
+ m_type = STRING; // force type.
+}
- void as_value::drop_refs()
- // Drop any ref counts we have; this happens prior to changing our
value.
+
+void as_value::set_as_object(as_object* obj)
+{
+ if (m_type != OBJECT || m_object_value != obj)
{
- if (m_type == AS_FUNCTION)
- {
- if (m_as_function_value)
- {
- m_as_function_value->drop_ref();
- m_as_function_value = 0;
- }
- }
- else if (m_type == OBJECT)
+ drop_refs();
+ m_type = OBJECT;
+ m_object_value = obj;
+ if (m_object_value)
{
- if (m_object_value)
- {
- m_object_value->drop_ref();
- m_object_value = 0;
- }
+ m_object_value->add_ref();
}
}
+}
-
- //
- // as_environment
- //
-
-
- as_value as_environment::get_variable(const tu_string& varname,
const std::vector<with_stack_entry>& with_stack) const
- // Return the value of the given var, if it's defined.
- {
- // Path lookup rigamarole.
- movie* target = m_target;
- tu_string path;
- tu_string var;
- if (parse_path(varname, &path, &var))
- {
- target = find_target(path); // @@ Use with_stack
here too??? Need to test.
- if (target)
- {
- as_value val;
- target->get_member(var, &val);
- return val;
- }
- else
- {
- log_error("find_target(\"%s\") failed\n",
path.c_str());
- return as_value();
- }
- }
- else
+void as_value::set_function_as_object(function_as_object* func)
+{
+ if (m_type != AS_FUNCTION || m_as_function_value != func)
+ {
+ drop_refs();
+ m_type = AS_FUNCTION;
+ m_as_function_value = func;
+ if (m_as_function_value)
{
- return this->get_variable_raw(varname, with_stack);
+ m_as_function_value->add_ref();
}
}
+}
- as_value as_environment::get_variable_raw(
- const tu_string& varname,
- const std::vector<with_stack_entry>& with_stack) const
- // varname must be a plain variable name; no path parsing.
+bool as_value::operator==(const as_value& v) const
+ // Return true if operands are equal.
+{
+ bool this_nulltype = (m_type == UNDEFINED || m_type == NULLTYPE);
+ bool v_nulltype = (v.get_type() == UNDEFINED || v.get_type() == NULLTYPE);
+ if (this_nulltype || v_nulltype)
+ {
+ return this_nulltype == v_nulltype;
+ }
+ else if (m_type == STRING)
+ {
+ return m_string_value == v.to_tu_string();
+ }
+ else if (m_type == NUMBER)
+ {
+ return m_number_value == v.to_number();
+ }
+ else if (m_type == BOOLEAN)
+ {
+ return m_boolean_value == v.to_bool();
+ }
+ else if (m_type == OBJECT)
+ {
+ return m_object_value == v.to_object();
+ }
+ else if (m_type == AS_FUNCTION)
+ {
+ return m_as_function_value == v.to_object();
+ }
+ else if (m_type == C_FUNCTION)
+ {
+ return m_c_function_value == v.to_c_function();
+ }
+ else
{
- assert(strchr(varname.c_str(), ':') == NULL);
- assert(strchr(varname.c_str(), '/') == NULL);
- assert(strchr(varname.c_str(), '.') == NULL);
+ // Evan: what about objects???
+ // TODO
+ return m_type == v.m_type;
+ }
+}
- as_value val;
+
+bool as_value::operator!=(const as_value& v) const
+ // Return true if operands are not equal.
+{
+ return ! (*this == v);
+}
- // Check the with-stack.
- for (int i = with_stack.size() - 1; i >= 0; i--)
- {
- as_object* obj = with_stack[i].m_object.get_ptr();
- if (obj && obj->get_member(varname, &val))
- {
- // Found the var in this context.
- return val;
- }
- }
+
+void as_value::string_concat(const tu_string& str)
+ // Sets *this to this string plus the given string.
+{
+ to_tu_string(); // make sure our m_string_value is initialized
+ m_type = STRING;
+ m_string_value += str;
+}
- // Check locals.
- int local_index = find_local(varname);
- if (local_index >= 0)
+void as_value::drop_refs()
+ // Drop any ref counts we have; this happens prior to changing our value.
+{
+ if (m_type == AS_FUNCTION)
+ {
+ if (m_as_function_value)
{
- // Get local var.
- return m_local_frames[local_index].m_value;
+ m_as_function_value->drop_ref();
+ m_as_function_value = 0;
}
-
- // Looking for "this"?
- if (varname == "this")
+ }
+ else if (m_type == OBJECT)
+ {
+ if (m_object_value)
{
- val.set_as_object(m_target);
- return val;
+ m_object_value->drop_ref();
+ m_object_value = 0;
}
+ }
+}
- // Check movie members.
- if (m_target->get_member(varname, &val))
- {
- return val;
- }
- // Check built-in constants.
- if (varname == "_root" || varname == "_level0")
- {
- return as_value(m_target->get_root_movie());
- }
- if (varname == "_global")
- {
- return as_value(s_global.get_ptr());
- }
- if (s_global->get_member(varname, &val))
- {
- return val;
- }
-
- // Fallback.
- IF_VERBOSE_ACTION(log_msg("get_variable_raw(\"%s\") failed,
returning UNDEFINED.\n", varname.c_str()));
- return as_value();
- }
+//
+// as_environment
+//
- void as_environment::set_variable(
- const tu_string& varname,
- const as_value& val,
- const std::vector<with_stack_entry>& with_stack)
- // Given a path to variable, set its value.
+as_value as_environment::get_variable(const tu_string& varname, const
std::vector<with_stack_entry>& with_stack) const
+ // Return the value of the given var, if it's defined.
+{
+ // Path lookup rigamarole.
+ movie* target = m_target;
+ tu_string path;
+ tu_string var;
+ if (parse_path(varname, &path, &var))
{
- IF_VERBOSE_ACTION(log_msg("-------------- %s = %s\n",
varname.c_str(), val.to_string()));//xxxxxxxxxx
-
- // Path lookup rigamarole.
- movie* target = m_target;
- tu_string path;
- tu_string var;
- if (parse_path(varname, &path, &var))
+ target = find_target(path); // @@ Use with_stack here too??? Need
to test.
+ if (target)
{
- target = find_target(path);
- if (target)
- {
- target->set_member(var, val);
- }
+ as_value val;
+ target->get_member(var, &val);
+ return val;
}
- else
+ else
{
- this->set_variable_raw(varname, val, with_stack);
+ log_error("find_target(\"%s\") failed\n", path.c_str());
+ return as_value();
}
}
+ else
+ {
+ return this->get_variable_raw(varname, with_stack);
+ }
+}
- void as_environment::set_variable_raw(
- const tu_string& varname,
- const as_value& val,
- const std::vector<with_stack_entry>& with_stack)
- // No path rigamarole.
- {
- // Check the with-stack.
- for (int i = with_stack.size() - 1; i >= 0; i--)
- {
- as_object* obj = with_stack[i].m_object.get_ptr();
- as_value dummy;
- if (obj && obj->get_member(varname, &dummy))
- {
- // This object has the member; so set it here.
- obj->set_member(varname, val);
- return;
- }
- }
+as_value as_environment::get_variable_raw(
+ const tu_string& varname,
+ const std::vector<with_stack_entry>& with_stack) const
+ // varname must be a plain variable name; no path parsing.
+{
+ assert(strchr(varname.c_str(), ':') == NULL);
+ assert(strchr(varname.c_str(), '/') == NULL);
+ assert(strchr(varname.c_str(), '.') == NULL);
- // Check locals.
- int local_index = find_local(varname);
- if (local_index >= 0)
+ as_value val;
+
+ // Check the with-stack.
+ for (int i = with_stack.size() - 1; i >= 0; i--)
+ {
+ as_object* obj = with_stack[i].m_object.get_ptr();
+ if (obj && obj->get_member(varname, &val))
{
- // Set local var.
- m_local_frames[local_index].m_value = val;
- return;
+ // Found the var in this context.
+ return val;
}
+ }
- assert(m_target);
+ // Check locals.
+ int local_index = find_local(varname);
+ if (local_index >= 0)
+ {
+ // Get local var.
+ return m_local_frames[local_index].m_value;
+ }
- m_target->set_member(varname, val);
+ // Looking for "this"?
+ if (varname == "this")
+ {
+ val.set_as_object(m_target);
+ return val;
}
+ // Check movie members.
+ if (m_target->get_member(varname, &val))
+ {
+ return val;
+ }
- void as_environment::set_local(const tu_string& varname, const
as_value& val)
- // Set/initialize the value of the local variable.
+ // Check built-in constants.
+ if (varname == "_root" || varname == "_level0")
{
- // Is it in the current frame already?
- int index = find_local(varname);
- if (index < 0)
- {
- // Not in frame; create a new local var.
+ return as_value(m_target->get_root_movie());
+ }
+ if (varname == "_global")
+ {
+ return as_value(s_global.get_ptr());
+ }
+ if (s_global->get_member(varname, &val))
+ {
+ return val;
+ }
+
+ // Fallback.
+ IF_VERBOSE_ACTION(log_msg("get_variable_raw(\"%s\") failed, returning
UNDEFINED.\n", varname.c_str()));
+ return as_value();
+}
- assert(varname.length() > 0); // null varnames are
invalid!
- m_local_frames.push_back(frame_slot(varname, val));
- }
- else
+
+void as_environment::set_variable(
+ const tu_string& varname,
+ const as_value& val,
+ const std::vector<with_stack_entry>& with_stack)
+ // Given a path to variable, set its value.
+{
+ IF_VERBOSE_ACTION(log_msg("-------------- %s = %s\n", varname.c_str(),
val.to_string()));//xxxxxxxxxx
+
+ // Path lookup rigamarole.
+ movie* target = m_target;
+ tu_string path;
+ tu_string var;
+ if (parse_path(varname, &path, &var))
+ {
+ target = find_target(path);
+ if (target)
{
- // In frame already; modify existing var.
- m_local_frames[index].m_value = val;
+ target->set_member(var, val);
}
}
-
-
- void as_environment::add_local(const tu_string& varname, const
as_value& val)
- // Add a local var with the given name and value to our
- // current local frame. Use this when you know the var
- // doesn't exist yet, since it's faster than set_local();
- // e.g. when setting up args for a function.
+ else
{
- assert(varname.length() > 0);
- m_local_frames.push_back(frame_slot(varname, val));
+ this->set_variable_raw(varname, val, with_stack);
}
+}
- void as_environment::declare_local(const tu_string& varname)
- // Create the specified local var if it doesn't exist already.
+void as_environment::set_variable_raw(
+ const tu_string& varname,
+ const as_value& val,
+ const std::vector<with_stack_entry>& with_stack)
+ // No path rigamarole.
+{
+ // Check the with-stack.
+ for (int i = with_stack.size() - 1; i >= 0; i--)
{
- // Is it in the current frame already?
- int index = find_local(varname);
- if (index < 0)
+ as_object* obj = with_stack[i].m_object.get_ptr();
+ as_value dummy;
+ if (obj && obj->get_member(varname, &dummy))
{
- // Not in frame; create a new local var.
- assert(varname.length() > 0); // null varnames are
invalid!
- m_local_frames.push_back(frame_slot(varname,
as_value()));
- }
- else
- {
- // In frame already; don't mess with it.
+ // This object has the member; so set it here.
+ obj->set_member(varname, val);
+ return;
}
}
-
- bool as_environment::get_member(const tu_stringi& varname, as_value*
val) const
+ // Check locals.
+ int local_index = find_local(varname);
+ if (local_index >= 0)
{
- return m_variables.get(varname, val);
+ // Set local var.
+ m_local_frames[local_index].m_value = val;
+ return;
}
+ assert(m_target);
+
+ m_target->set_member(varname, val);
+}
+
+
+void as_environment::set_local(const tu_string& varname, const as_value& val)
+ // Set/initialize the value of the local variable.
+{
+ // Is it in the current frame already?
+ int index = find_local(varname);
+ if (index < 0)
+ {
+ // Not in frame; create a new local var.
- void as_environment::set_member(const tu_stringi& varname, const
as_value& val)
+ assert(varname.length() > 0); // null varnames are invalid!
+ m_local_frames.push_back(frame_slot(varname, val));
+ }
+ else
{
- m_variables[varname] = val;
+ // In frame already; modify existing var.
+ m_local_frames[index].m_value = val;
}
+}
- as_value* as_environment::local_register_ptr(int reg)
- // Return a pointer to the specified local register.
- // Local registers are numbered starting with 1.
- //
- // Return value will never be NULL. If reg is out of bounds,
- // we log an error, but still return a valid pointer (to
- // global reg[0]). So the behavior is a bit undefined, but
- // not dangerous.
+
+void as_environment::add_local(const tu_string& varname, const as_value& val)
+ // Add a local var with the given name and value to our
+ // current local frame. Use this when you know the var
+ // doesn't exist yet, since it's faster than set_local();
+ // e.g. when setting up args for a function.
+{
+ assert(varname.length() > 0);
+ m_local_frames.push_back(frame_slot(varname, val));
+}
+
+
+void as_environment::declare_local(const tu_string& varname)
+ // Create the specified local var if it doesn't exist already.
+{
+ // Is it in the current frame already?
+ int index = find_local(varname);
+ if (index < 0)
+ {
+ // Not in frame; create a new local var.
+ assert(varname.length() > 0); // null varnames are invalid!
+ m_local_frames.push_back(frame_slot(varname, as_value()));
+ }
+ else
{
- // We index the registers from the end of the register
- // array, so we don't have to keep base/frame
- // pointers.
+ // In frame already; don't mess with it.
+ }
+}
- if (reg <= 0 || reg > (int) m_local_register.size())
- {
- log_error("Invalid local register %d, stack only has %d
entries\n",
- reg, m_local_register.size());
+
+bool as_environment::get_member(const tu_stringi& varname, as_value* val)
const
+{
+ return m_variables.get(varname, val);
+}
- // Fallback: use global 0.
- return &m_global_register[0];
- }
- return &m_local_register[m_local_register.size() - reg];
- }
+void as_environment::set_member(const tu_stringi& varname, const as_value&
val)
+{
+ m_variables[varname] = val;
+}
+as_value* as_environment::local_register_ptr(int reg)
+ // Return a pointer to the specified local register.
+ // Local registers are numbered starting with 1.
+ //
+ // Return value will never be NULL. If reg is out of bounds,
+ // we log an error, but still return a valid pointer (to
+ // global reg[0]). So the behavior is a bit undefined, but
+ // not dangerous.
+{
+ // We index the registers from the end of the register
+ // array, so we don't have to keep base/frame
+ // pointers.
- int as_environment::find_local(const tu_string& varname) const
- // Search the active frame for the named var; return its index
- // in the m_local_frames stack if found.
- //
- // Otherwise return -1.
+ if (reg <= 0 || reg > (int) m_local_register.size())
{
- // Linear search sucks, but is probably fine for
- // typical use of local vars in script. There could
- // be pathological breakdowns if a function has tons
- // of locals though. The ActionScript bytecode does
- // not help us much by using strings to index locals.
+ log_error("Invalid local register %d, stack only has %d entries\n",
+ reg, m_local_register.size());
- for (int i = m_local_frames.size() - 1; i >= 0; i--)
- {
- const frame_slot& slot = m_local_frames[i];
- if (slot.m_name.length() == 0)
- {
- // End of local frame; stop looking.
- return -1;
- }
- else if (slot.m_name == varname)
- {
- // Found it.
- return i;
- }
- }
- return -1;
+ // Fallback: use global 0.
+ return &m_global_register[0];
}
+ return &m_local_register[m_local_register.size() - reg];
+}
- bool as_environment::parse_path(const tu_string& var_path,
tu_string* path, tu_string* var) const
- // See if the given variable name is actually a sprite path
- // followed by a variable name. These come in the format:
- //
- // /path/to/some/sprite/:varname
- //
- // (or same thing, without the last '/')
- //
- // or
- // path.to.some.var
- //
- // If that's the format, puts the path part (no colon or
- // trailing slash) in *path, and the varname part (no color)
- // in *var and returns true.
- //
- // If no colon, returns false and leaves *path & *var alone.
- {
- // Search for colon.
- int colon_index = 0;
- int var_path_length = var_path.length();
- for ( ; colon_index < var_path_length; colon_index++)
- {
- if (var_path[colon_index] == ':')
- {
- // Found it.
- break;
- }
- }
- if (colon_index >= var_path_length)
+int as_environment::find_local(const tu_string& varname) const
+ // Search the active frame for the named var; return its index
+ // in the m_local_frames stack if found.
+ //
+ // Otherwise return -1.
+{
+ // Linear search sucks, but is probably fine for
+ // typical use of local vars in script. There could
+ // be pathological breakdowns if a function has tons
+ // of locals though. The ActionScript bytecode does
+ // not help us much by using strings to index locals.
+
+ for (int i = m_local_frames.size() - 1; i >= 0; i--)
+ {
+ const frame_slot& slot = m_local_frames[i];
+ if (slot.m_name.length() == 0)
{
- // No colon. Is there a '.'? Find the last
- // one, if any.
- for (colon_index = var_path_length - 1; colon_index >=
0; colon_index--)
- {
- if (var_path[colon_index] == '.')
- {
- // Found it.
- break;
- }
- }
- if (colon_index < 0) return false;
+ // End of local frame; stop looking.
+ return -1;
}
-
- // Make the subparts.
-
- // Var.
- *var = &var_path[colon_index + 1];
-
- // Path.
- if (colon_index > 0)
+ else if (slot.m_name == varname)
{
- if (var_path[colon_index - 1] == '/')
- {
- // Trim off the extraneous trailing slash.
- colon_index--;
- }
+ // Found it.
+ return i;
}
- // @@ could be better. This whole usage of tu_string is very
flabby...
- *path = var_path;
- path->resize(colon_index);
-
- return true;
}
+ return -1;
+}
- movie* as_environment::find_target(const as_value& val) const
- // Find the sprite/movie represented by the given value. The
- // value might be a reference to the object itself, or a
- // string giving a relative path name to the object.
+bool as_environment::parse_path(const tu_string& var_path, tu_string* path,
tu_string* var) const
+ // See if the given variable name is actually a sprite path
+ // followed by a variable name. These come in the format:
+ //
+ // /path/to/some/sprite/:varname
+ //
+ // (or same thing, without the last '/')
+ //
+ // or
+ // path.to.some.var
+ //
+ // If that's the format, puts the path part (no colon or
+ // trailing slash) in *path, and the varname part (no color)
+ // in *var and returns true.
+ //
+ // If no colon, returns false and leaves *path & *var alone.
+{
+ // Search for colon.
+ int colon_index = 0;
+ int var_path_length = var_path.length();
+ for ( ; colon_index < var_path_length; colon_index++)
{
- if (val.get_type() == as_value::OBJECT)
- {
- if (val.to_object() != NULL)
- {
- return val.to_object()->to_movie();
- }
- else
- {
- return NULL;
- }
- }
- else if (val.get_type() == as_value::STRING)
- {
- return find_target(val.to_tu_string());
- }
- else
+ if (var_path[colon_index] == ':')
{
- log_error("error: invalid path; neither string nor
object");
- return NULL;
+ // Found it.
+ break;
}
}
-
- static const char* next_slash_or_dot(const char* word)
- // Search for next '.' or '/' character in this word. Return
- // a pointer to it, or to NULL if it wasn't found.
+ if (colon_index >= var_path_length)
{
- for (const char* p = word; *p; p++)
+ // No colon. Is there a '.'? Find the last
+ // one, if any.
+ for (colon_index = var_path_length - 1; colon_index >= 0;
colon_index--)
{
- if (*p == '.' && p[1] == '.')
- {
- p++;
- }
- else if (*p == '.' || *p == '/')
+ if (var_path[colon_index] == '.')
{
- return p;
+ // Found it.
+ break;
}
}
-
- return NULL;
+ if (colon_index < 0) return false;
}
+ // Make the subparts.
+
+ // Var.
+ *var = &var_path[colon_index + 1];
- movie* as_environment::find_target(const tu_string& path) const
- // Find the sprite/movie referenced by the given path.
+ // Path.
+ if (colon_index > 0)
{
- if (path.length() <= 0)
+ if (var_path[colon_index - 1] == '/')
{
- return m_target;
+ // Trim off the extraneous trailing slash.
+ colon_index--;
}
+ }
+ // @@ could be better. This whole usage of tu_string is very flabby...
+ *path = var_path;
+ path->resize(colon_index);
- assert(path.length() > 0);
+ return true;
+}
- movie* env = m_target;
- assert(env);
-
- const char* p = path.c_str();
- tu_string subpart;
- if (*p == '/')
+movie* as_environment::find_target(const as_value& val) const
+ // Find the sprite/movie represented by the given value. The
+ // value might be a reference to the object itself, or a
+ // string giving a relative path name to the object.
+{
+ if (val.get_type() == as_value::OBJECT)
+ {
+ if (val.to_object() != NULL)
{
- // Absolute path. Start at the root.
- env = env->get_relative_target("_level0");
- p++;
+ return val.to_object()->to_movie();
}
-
- if (*p == '\0')
- return env;
-
- for (;;)
+ else
{
- const char* next_slash = next_slash_or_dot(p);
- subpart = p;
- if (next_slash == p)
- {
- log_error("error: invalid path '%s'\n",
path.c_str());
- break;
- }
- else if (next_slash)
- {
- // Cut off the slash and everything after it.
- subpart.resize(next_slash - p);
- }
-
- env = env->get_relative_target(subpart);
- //@@ _level0 --> root, .. --> parent, . --> this,
other == character
+ return NULL;
+ }
+ }
+ else if (val.get_type() == as_value::STRING)
+ {
+ return find_target(val.to_tu_string());
+ }
+ else
+ {
+ log_error("error: invalid path; neither string nor object");
+ return NULL;
+ }
+}
- if (env == NULL || next_slash == NULL)
- {
- break;
- }
- p = next_slash + 1;
+static const char* next_slash_or_dot(const char* word)
+ // Search for next '.' or '/' character in this word. Return
+ // a pointer to it, or to NULL if it wasn't found.
+{
+ for (const char* p = word; *p; p++)
+ {
+ if (*p == '.' && p[1] == '.')
+ {
+ p++;
+ }
+ else if (*p == '.' || *p == '/')
+ {
+ return p;
}
- return env;
}
+ return NULL;
+}
- //
- // event_id
- //
-
- const tu_string& event_id::get_function_name() const
- {
- static tu_string s_function_names[EVENT_COUNT] =
- {
- "INVALID", // INVALID
- "onPress", // PRESS
- "onRelease", // RELEASE
- "onRelease_Outside", // RELEASE_OUTSIDE
- "onRoll_Over", // ROLL_OVER
- "onRoll_Out", // ROLL_OUT
- "onDrag_Over", // DRAG_OVER
- "onDrag_Out", // DRAG_OUT
- "onKeyPress", // KEY_PRESS
- "onInitialize", // INITIALIZE
-
- "onLoad", // LOAD
- "onUnload", // UNLOAD
- "onEnterFrame", // ENTER_FRAME
- "onMouseDown", // MOUSE_DOWN
- "onMouseUp", // MOUSE_UP
- "onMouseMove", // MOUSE_MOVE
- "onKeyDown", // KEY_DOWN
- "onKeyUp", // KEY_UP
- "onData", // DATA
- // These are for the MoveClipLoader ActionScript only
- "onLoadStart", // LOAD_START
- "onLoadError", // LOAD_ERROR
- "onLoadProgress", // LOAD_PROGRESS
- "onLoadInit", // LOAD_INIT
- // These are for the XMLSocket ActionScript only
- "onSockClose", // CLOSE
- "onSockConnect", // CONNECT
- "onSockXML", // XML
- // These are for the XML ActionScript only
- "onXMLLoad", // XML_LOAD
- "onXMLData", // XML_DATA
- "onTimer", // setInterval Timer expired
- };
-
- assert(m_id > INVALID && m_id < EVENT_COUNT);
- return s_function_names[m_id];
- }
-
-
- // Standard member lookup.
- as_standard_member get_standard_member(const tu_stringi& name)
- {
- static bool s_inited = false;
- static stringi_hash<as_standard_member> s_standard_member_map;
- if (!s_inited)
- {
- s_inited = true;
-
-
s_standard_member_map.resize(int(AS_STANDARD_MEMBER_COUNT));
-
- s_standard_member_map.add("_x", M_X);
- s_standard_member_map.add("_y", M_Y);
- s_standard_member_map.add("_xscale", M_XSCALE);
- s_standard_member_map.add("_yscale", M_YSCALE);
- s_standard_member_map.add("_currentframe",
M_CURRENTFRAME);
- s_standard_member_map.add("_totalframes",
M_TOTALFRAMES);
- s_standard_member_map.add("_alpha", M_ALPHA);
- s_standard_member_map.add("_visible", M_VISIBLE);
- s_standard_member_map.add("_width", M_WIDTH);
- s_standard_member_map.add("_height", M_HEIGHT);
- s_standard_member_map.add("_rotation", M_ROTATION);
- s_standard_member_map.add("_target", M_TARGET);
- s_standard_member_map.add("_framesloaded",
M_FRAMESLOADED);
- s_standard_member_map.add("_name", M_NAME);
- s_standard_member_map.add("_droptarget", M_DROPTARGET);
- s_standard_member_map.add("_url", M_URL);
- s_standard_member_map.add("_highquality",
M_HIGHQUALITY);
- s_standard_member_map.add("_focusrect", M_FOCUSRECT);
- s_standard_member_map.add("_soundbuftime",
M_SOUNDBUFTIME);
- s_standard_member_map.add("_xmouse", M_XMOUSE);
- s_standard_member_map.add("_ymouse", M_YMOUSE);
- s_standard_member_map.add("_parent", M_PARENT);
- s_standard_member_map.add("text", M_TEXT);
- s_standard_member_map.add("textWidth", M_TEXTWIDTH);
- s_standard_member_map.add("textColor", M_TEXTCOLOR);
- s_standard_member_map.add("onLoad", M_ONLOAD);
- }
-
- as_standard_member result = M_INVALID_MEMBER;
- s_standard_member_map.get(name, &result);
-
- return result;
- }
-
-
- //
- // Disassembler
- //
+movie* as_environment::find_target(const tu_string& path) const
+ // Find the sprite/movie referenced by the given path.
+{
+ if (path.length() <= 0)
+ {
+ return m_target;
+ }
-#define COMPILE_DISASM 1
+ assert(path.length() > 0);
-#ifndef COMPILE_DISASM
+ movie* env = m_target;
+ assert(env);
+
+ const char* p = path.c_str();
+ tu_string subpart;
- void log_disasm(const unsigned char* instruction_data)
- // No disassembler in this version...
+ if (*p == '/')
{
- log_msg("<no disasm>\n");
+ // Absolute path. Start at the root.
+ env = env->get_relative_target("_level0");
+ p++;
}
-#else // COMPILE_DISASM
+ if (*p == '\0')
+ return env;
- void log_disasm(const unsigned char* instruction_data)
- // Disassemble one instruction to the log.
+ for (;;)
{
- enum arg_format {
- ARG_NONE = 0,
- ARG_STR,
- ARG_HEX, // default hex dump, in case the format
is unknown or unsupported
- ARG_U8,
- ARG_U16,
- ARG_S16,
- ARG_PUSH_DATA,
- ARG_DECL_DICT,
- ARG_FUNCTION2
- };
- struct inst_info
- {
- int m_action_id;
- const char* m_instruction;
-
- arg_format m_arg_format;
- };
-
- static inst_info s_instruction_table[] = {
- { SWF::ACTION_NEXTFRAME, "next_frame",
ARG_NONE },
- { SWF::ACTION_PREVFRAME, "prev_frame",
ARG_NONE },
- { SWF::ACTION_PLAY, "play",
ARG_NONE },
- { SWF::ACTION_STOP, "stop",
ARG_NONE },
- { SWF::ACTION_TOGGLEQUALITY, "toggle_qlty",
ARG_NONE },
- { SWF::ACTION_STOPSOUNDS, "stop_sounds",
ARG_NONE },
- { SWF::ACTION_ADD, "add",
ARG_NONE },
- { SWF::ACTION_SUBTRACT, "sub",
ARG_NONE },
- { SWF::ACTION_MULTIPLY, "mul",
ARG_NONE },
- { SWF::ACTION_DIVIDE, "div",
ARG_NONE },
- { SWF::ACTION_EQUAL, "equ",
ARG_NONE },
- { SWF::ACTION_LESSTHAN, "lt",
ARG_NONE },
- { SWF::ACTION_LOGICALAND, "and",
ARG_NONE },
- { SWF::ACTION_LOGICALOR, "or",
ARG_NONE },
- { SWF::ACTION_LOGICALNOT, "not",
ARG_NONE },
- { SWF::ACTION_STRINGEQ, "str_eq",
ARG_NONE },
- { SWF::ACTION_STRINGLENGTH, "str_len",
ARG_NONE },
- { SWF::ACTION_SUBSTRING, "substr",
ARG_NONE },
- { SWF::ACTION_POP, "pop",
ARG_NONE },
- { SWF::ACTION_INT, "floor",
ARG_NONE },
- { SWF::ACTION_GETVARIABLE, "get_var",
ARG_NONE },
- { SWF::ACTION_SETVARIABLE, "set_var",
ARG_NONE },
- { SWF::ACTION_SETTARGETEXPRESSION, "set_target_exp",
ARG_NONE },
- { SWF::ACTION_STRINGCONCAT, "str_cat",
ARG_NONE },
- { SWF::ACTION_GETPROPERTY, "get_prop",
ARG_NONE },
- { SWF::ACTION_SETPROPERTY, "set_prop",
ARG_NONE },
- { SWF::ACTION_DUPLICATECLIP, "dup_sprite",
ARG_NONE },
- { SWF::ACTION_REMOVECLIP, "rem_sprite",
ARG_NONE },
- { SWF::ACTION_TRACE, "trace",
ARG_NONE },
- { SWF::ACTION_STARTDRAGMOVIE, "start_drag",
ARG_NONE },
- { SWF::ACTION_STOPDRAGMOVIE, "stop_drag",
ARG_NONE },
- { SWF::ACTION_STRINGCOMPARE, "str_lt",
ARG_NONE },
- { SWF::ACTION_THROW, "throw_fixme",
ARG_NONE },
- { SWF::ACTION_CASTOP, "cast_fixme",
ARG_NONE },
- { SWF::ACTION_IMPLEMENTSOP, "implements_fixme",
ARG_NONE },
- { SWF::ACTION_RANDOM, "random",
ARG_NONE },
- { SWF::ACTION_MBLENGTH, "mb_length_fixme",
ARG_NONE },
- { SWF::ACTION_ORD, "ord",
ARG_NONE },
- { SWF::ACTION_CHR, "chr",
ARG_NONE },
- { SWF::ACTION_GETTIMER, "get_timer",
ARG_NONE },
- { SWF::ACTION_MBSUBSTRING, "substr_mb_fixme",
ARG_NONE },
- { SWF::ACTION_MBORD, "ord_mb",
ARG_NONE },
- { SWF::ACTION_MBCHR, "chr_mb_fixme",
ARG_NONE },
- { SWF::ACTION_DELETEVAR, "delete_fixme",
ARG_NONE },
- { SWF::ACTION_DELETE, "delete_all_fixme",
ARG_NONE },
- { SWF::ACTION_VAREQUALS, "set_local",
ARG_NONE },
- { SWF::ACTION_CALLFUNCTION, "call_func",
ARG_NONE },
- { SWF::ACTION_RETURN, "return",
ARG_NONE },
- { SWF::ACTION_MODULO, "mod",
ARG_NONE },
- { SWF::ACTION_NEW, "new",
ARG_NONE },
- { SWF::ACTION_VAR, "decl_local",
ARG_NONE },
- { SWF::ACTION_INITARRAY, "decl_array",
ARG_NONE },
- { SWF::ACTION_INITOBJECT, "decl_obj_fixme",
ARG_NONE },
- { SWF::ACTION_TYPEOF, "type_of",
ARG_NONE },
- { SWF::ACTION_TARGETPATH, "get_target_fixme",
ARG_NONE },
- { SWF::ACTION_ENUMERATE, "enumerate",
ARG_NONE },
- { SWF::ACTION_NEWADD, "add_t",
ARG_NONE },
- { SWF::ACTION_NEWLESSTHAN, "lt_t",
ARG_NONE },
- { SWF::ACTION_NEWEQUALS, "eq_t",
ARG_NONE },
- { SWF::ACTION_TONUMBER, "number",
ARG_NONE },
- { SWF::ACTION_TOSTRING, "string",
ARG_NONE },
- { SWF::ACTION_DUP, "dup",
ARG_NONE },
- { SWF::ACTION_SWAP, "swap",
ARG_NONE },
- { SWF::ACTION_GETMEMBER, "get_member",
ARG_NONE },
- { SWF::ACTION_SETMEMBER, "set_member",
ARG_NONE },
- { SWF::ACTION_INCREMENT, "inc",
ARG_NONE },
- { SWF::ACTION_DECREMENT, "dec",
ARG_NONE },
- { SWF::ACTION_CALLMETHOD, "call_method",
ARG_NONE },
- { SWF::ACTION_NEWMETHOD, "new_method_fixme",
ARG_NONE },
- { SWF::ACTION_INSTANCEOF, "is_inst_of_fixme",
ARG_NONE },
- { SWF::ACTION_ENUM2, "enum_object_fixme",
ARG_NONE },
- { SWF::ACTION_BITWISEAND, "bit_and",
ARG_NONE },
- { SWF::ACTION_BITWISEOR, "bit_or",
ARG_NONE },
- { SWF::ACTION_BITWISEXOR, "bit_xor",
ARG_NONE },
- { SWF::ACTION_SHIFTLEFT, "shl",
ARG_NONE },
- { SWF::ACTION_SHIFTRIGHT, "asr",
ARG_NONE },
- { SWF::ACTION_SHIFTRIGHT2, "lsr",
ARG_NONE },
- { SWF::ACTION_STRICTEQ, "eq_strict",
ARG_NONE },
- { SWF::ACTION_GREATER, "gt_t",
ARG_NONE },
- { SWF::ACTION_EXTENDS, "extends_fixme",
ARG_NONE },
- { SWF::ACTION_GOTOFRAME, "goto_frame", ARG_U16
},
- { SWF::ACTION_GETURL, "get_url", ARG_STR
},
- { SWF::ACTION_SETREGISTER, "store_register",
ARG_U8 },
- { SWF::ACTION_CONSTANTPOOL, "decl_dict",
ARG_DECL_DICT },
- { SWF::ACTION_WAITFORFRAME, "wait_for_frame",
ARG_HEX },
- { SWF::ACTION_SETTARGET, "set_target", ARG_STR
},
- { SWF::ACTION_GOTOLABEL, "goto_frame_lbl",
ARG_STR },
- { SWF::ACTION_WAITFORFRAMEEXPRESSION,
"wait_for_fr_exp", ARG_HEX },
- { SWF::ACTION_DEFINEFUNCTION2, "function2",
ARG_FUNCTION2 },
- { SWF::ACTION_TRY, "try_fixme",
ARG_FUNCTION2 },
- { SWF::ACTION_WITH, "with", ARG_U16
},
- { SWF::ACTION_PUSHDATA, "push_data",
ARG_PUSH_DATA },
- { SWF::ACTION_BRANCHALWAYS, "goto", ARG_S16
},
- { SWF::ACTION_GETURL2, "get_url2", ARG_HEX
},
- { SWF::ACTION_DEFINEFUNCTION, "func", ARG_HEX
},
- { SWF::ACTION_BRANCHIFTRUE, "branch_if_true",
ARG_S16 },
- { SWF::ACTION_CALLFRAME, "call_frame", ARG_HEX
},
- { SWF::ACTION_GOTOEXPRESSION, "goto_frame_exp",
ARG_HEX },
- { SWF::ACTION_END, "<end>",
ARG_NONE }
- };
-
- int action_id = instruction_data[0];
- inst_info* info = NULL;
-
- for (int i = 0; ; i++)
+ const char* next_slash = next_slash_or_dot(p);
+ subpart = p;
+ if (next_slash == p)
{
- if (s_instruction_table[i].m_action_id == action_id)
- {
- info = &s_instruction_table[i];
- }
-
- if (s_instruction_table[i].m_action_id == 0)
- {
- // Stop at the end of the table and give up.
- break;
- }
+ log_error("error: invalid path '%s'\n", path.c_str());
+ break;
}
-
- arg_format fmt = ARG_HEX;
-
- // Show instruction.
- if (info == NULL)
+ else if (next_slash)
{
- log_msg("<unknown>[0x%02X]", action_id);
+ // Cut off the slash and everything after it.
+ subpart.resize(next_slash - p);
}
- else
+
+ env = env->get_relative_target(subpart);
+ //@@ _level0 --> root, .. --> parent, . --> this, other ==
character
+
+ if (env == NULL || next_slash == NULL)
{
- log_msg("%-15s", info->m_instruction);
- fmt = info->m_arg_format;
+ break;
}
- // Show instruction argument(s).
- if (action_id & 0x80)
- {
- assert(fmt != ARG_NONE);
+ p = next_slash + 1;
+ }
+ return env;
+}
- int length = instruction_data[1] |
(instruction_data[2] << 8);
- // log_msg(" [%d]", length);
+//
+// event_id
+//
- if (fmt == ARG_HEX)
- {
- for (int i = 0; i < length; i++)
- {
- log_msg(" 0x%02X", instruction_data[3 +
i]);
- }
- log_msg("\n");
- }
- else if (fmt == ARG_STR)
- {
- log_msg(" \"");
- for (int i = 0; i < length; i++)
- {
- log_msg("%c", instruction_data[3 + i]);
- }
- log_msg("\"\n");
- }
- else if (fmt == ARG_U8)
- {
- int val = instruction_data[3];
- log_msg(" %d\n", val);
- }
- else if (fmt == ARG_U16)
- {
- int val = instruction_data[3] |
(instruction_data[4] << 8);
- log_msg(" %d\n", val);
- }
- else if (fmt == ARG_S16)
- {
- int val = instruction_data[3] |
(instruction_data[4] << 8);
- if (val & 0x8000) val |= ~0x7FFF; //
sign-extend
- log_msg(" %d\n", val);
- }
- else if (fmt == ARG_PUSH_DATA)
- {
- log_msg("\n");
- int i = 0;
- while (i < length)
- {
- int type = instruction_data[3 + i];
- i++;
- log_msg("\t\t"); // indent
- if (type == 0)
- {
- // string
- log_msg("\"");
- while (instruction_data[3 + i])
- {
- log_msg("%c",
instruction_data[3 + i]);
- i++;
- }
- i++;
- log_msg("\"\n");
- }
- else if (type == 1)
- {
- // float (little-endian)
- union {
- float f;
- Uint32 i;
- } u;
- compiler_assert(sizeof(u) ==
sizeof(u.i));
-
- memcpy(&u.i, instruction_data +
3 + i, 4);
- u.i = swap_le32(u.i);
- i += 4;
+const tu_string& event_id::get_function_name() const
+{
+ static tu_string s_function_names[EVENT_COUNT] =
+ {
+ "INVALID", // INVALID
+ "onPress", // PRESS
+ "onRelease", // RELEASE
+ "onRelease_Outside", // RELEASE_OUTSIDE
+ "onRoll_Over", // ROLL_OVER
+ "onRoll_Out", // ROLL_OUT
+ "onDrag_Over", // DRAG_OVER
+ "onDrag_Out", // DRAG_OUT
+ "onKeyPress", // KEY_PRESS
+ "onInitialize", // INITIALIZE
+
+ "onLoad", // LOAD
+ "onUnload", // UNLOAD
+ "onEnterFrame", // ENTER_FRAME
+ "onMouseDown", // MOUSE_DOWN
+ "onMouseUp", // MOUSE_UP
+ "onMouseMove", // MOUSE_MOVE
+ "onKeyDown", // KEY_DOWN
+ "onKeyUp", // KEY_UP
+ "onData", // DATA
+ // These are for the MoveClipLoader ActionScript only
+ "onLoadStart", // LOAD_START
+ "onLoadError", // LOAD_ERROR
+ "onLoadProgress", // LOAD_PROGRESS
+ "onLoadInit", // LOAD_INIT
+ // These are for the XMLSocket ActionScript only
+ "onSockClose", // CLOSE
+ "onSockConnect", // CONNECT
+ "onSockXML", // XML
+ // These are for the XML ActionScript only
+ "onXMLLoad", // XML_LOAD
+ "onXMLData", // XML_DATA
+ "onTimer", // setInterval Timer expired
+ };
- log_msg("(float) %f\n", u.f);
- }
- else if (type == 2)
- {
- log_msg("NULL\n");
- }
- else if (type == 3)
- {
- log_msg("undef\n");
- }
- else if (type == 4)
- {
- // contents of register
- int reg =
instruction_data[3 + i];
- i++;
- log_msg("reg[%d]\n", reg);
- }
- else if (type == 5)
- {
- int bool_val =
instruction_data[3 + i];
- i++;
- log_msg("bool(%d)\n", bool_val);
- }
- else if (type == 6)
- {
- // double
- // wacky format: 45670123
- union {
- double d;
- Uint64 i;
- struct {
- Uint32 lo;
- Uint32 hi;
- } sub;
- } u;
- compiler_assert(sizeof(u) ==
sizeof(u.i));
-
- memcpy(&u.sub.hi,
instruction_data + 3 + i, 4);
- memcpy(&u.sub.lo,
instruction_data + 3 + i + 4, 4);
- u.i = swap_le64(u.i);
- i += 8;
+ assert(m_id > INVALID && m_id < EVENT_COUNT);
+ return s_function_names[m_id];
+}
+
+
+// Standard member lookup.
+as_standard_member get_standard_member(const tu_stringi& name)
+{
+ static bool s_inited = false;
+ static stringi_hash<as_standard_member> s_standard_member_map;
+ if (!s_inited)
+ {
+ s_inited = true;
+
+ s_standard_member_map.resize(int(AS_STANDARD_MEMBER_COUNT));
+
+ s_standard_member_map.add("_x", M_X);
+ s_standard_member_map.add("_y", M_Y);
+ s_standard_member_map.add("_xscale", M_XSCALE);
+ s_standard_member_map.add("_yscale", M_YSCALE);
+ s_standard_member_map.add("_currentframe", M_CURRENTFRAME);
+ s_standard_member_map.add("_totalframes", M_TOTALFRAMES);
+ s_standard_member_map.add("_alpha", M_ALPHA);
+ s_standard_member_map.add("_visible", M_VISIBLE);
+ s_standard_member_map.add("_width", M_WIDTH);
+ s_standard_member_map.add("_height", M_HEIGHT);
+ s_standard_member_map.add("_rotation", M_ROTATION);
+ s_standard_member_map.add("_target", M_TARGET);
+ s_standard_member_map.add("_framesloaded", M_FRAMESLOADED);
+ s_standard_member_map.add("_name", M_NAME);
+ s_standard_member_map.add("_droptarget", M_DROPTARGET);
+ s_standard_member_map.add("_url", M_URL);
+ s_standard_member_map.add("_highquality", M_HIGHQUALITY);
+ s_standard_member_map.add("_focusrect", M_FOCUSRECT);
+ s_standard_member_map.add("_soundbuftime", M_SOUNDBUFTIME);
+ s_standard_member_map.add("_xmouse", M_XMOUSE);
+ s_standard_member_map.add("_ymouse", M_YMOUSE);
+ s_standard_member_map.add("_parent", M_PARENT);
+ s_standard_member_map.add("text", M_TEXT);
+ s_standard_member_map.add("textWidth", M_TEXTWIDTH);
+ s_standard_member_map.add("textColor", M_TEXTCOLOR);
+ s_standard_member_map.add("onLoad", M_ONLOAD);
+ }
- log_msg("(double) %f\n", u.d);
- }
- else if (type == 7)
- {
- // int32
- Sint32 val =
instruction_data[3 + i]
- | (instruction_data[3 +
i + 1] << 8)
- | (instruction_data[3 +
i + 2] << 16)
- | (instruction_data[3 +
i + 3] << 24);
- i += 4;
- log_msg("(int) %d\n", val);
- }
- else if (type == 8)
- {
- int id = instruction_data[3
+ i];
- i++;
- log_msg("dict_lookup[%d]\n",
id);
- }
- else if (type == 9)
- {
- int id = instruction_data[3
+ i] | (instruction_data[3 + i + 1] << 8);
- i += 2;
- log_msg("dict_lookup_lg[%d]\n",
id);
- }
- }
- }
- else if (fmt == ARG_DECL_DICT)
- {
- int i = 0;
- int count = instruction_data[3 + i] |
(instruction_data[3 + i + 1] << 8);
- i += 2;
+ as_standard_member result = M_INVALID_MEMBER;
+ s_standard_member_map.get(name, &result);
- log_msg(" [%d]\n", count);
+ return result;
+}
- // Print strings.
- for (int ct = 0; ct < count; ct++)
- {
- log_msg("\t\t"); // indent
- log_msg("\"");
- while (instruction_data[3 + i])
- {
- // safety check.
- if (i >= length)
- {
- log_msg("<disasm error
-- length exceeded>\n");
- break;
- }
+//
+// Disassembler
+//
- log_msg("%c",
instruction_data[3 + i]);
- i++;
- }
- log_msg("\"\n");
- i++;
- }
- }
- else if (fmt == ARG_FUNCTION2)
- {
- // Signature info for a function2 opcode.
- int i = 0;
- const char* function_name = (const char*)
&instruction_data[3 + i];
- i += strlen(function_name) + 1;
-
- int arg_count = instruction_data[3 + i] |
(instruction_data[3 + i + 1] << 8);
- i += 2;
-
- int reg_count = instruction_data[3 + i];
- i++;
-
- log_msg("\n\t\tname = '%s', arg_count = %d,
reg_count = %d\n",
- function_name, arg_count, reg_count);
-
- uint16 flags = (instruction_data[3 + i]) |
(instruction_data[3 + i + 1] << 8);
- i += 2;
-
- // @@ What is the difference between "super"
and "_parent"?
-
- bool preload_global = (flags & 0x100) != 0;
- bool preload_parent = (flags & 0x80) != 0;
- bool preload_root = (flags & 0x40) != 0;
- bool suppress_super = (flags & 0x20) != 0;
- bool preload_super = (flags & 0x10) != 0;
- bool suppress_args = (flags & 0x08) != 0;
- bool preload_args = (flags & 0x04) != 0;
- bool suppress_this = (flags & 0x02) != 0;
- bool preload_this = (flags & 0x01) != 0;
-
- log_msg("\t\t pg = %d\n"
- "\t\t pp = %d\n"
- "\t\t pr = %d\n"
- "\t\tss = %d, ps = %d\n"
- "\t\tsa = %d, pa = %d\n"
- "\t\tst = %d, pt = %d\n",
- int(preload_global),
- int(preload_parent),
- int(preload_root),
- int(suppress_super),
- int(preload_super),
- int(suppress_args),
- int(preload_args),
- int(suppress_this),
- int(preload_this));
- for (int argi = 0; argi < arg_count; argi++)
- {
- int arg_register =
instruction_data[3 + i];
- i++;
- const char* arg_name = (const
char*) &instruction_data[3 + i];
- i += strlen(arg_name) + 1;
+#define COMPILE_DISASM 1
- log_msg("\t\targ[%d] - reg[%d] -
'%s'\n", argi, arg_register, arg_name);
- }
+#ifndef COMPILE_DISASM
- int function_length = instruction_data[3 +
i] | (instruction_data[3 + i + 1] << 8);
- i += 2;
+void log_disasm(const unsigned char* instruction_data)
+ // No disassembler in this version...
+{
+ log_msg("<no disasm>\n");
+}
- log_msg("\t\tfunction length = %d\n",
function_length);
- }
- }
- else
- {
- log_msg("\n");
+#else // COMPILE_DISASM
+
+void log_disasm(const unsigned char* instruction_data)
+ // Disassemble one instruction to the log.
+{
+ enum arg_format {
+ ARG_NONE = 0,
+ ARG_STR,
+ ARG_HEX, // default hex dump, in case the format is unknown or
unsupported
+ ARG_U8,
+ ARG_U16,
+ ARG_S16,
+ ARG_PUSH_DATA,
+ ARG_DECL_DICT,
+ ARG_FUNCTION2
+ };
+ struct inst_info
+ {
+ int m_action_id;
+ const char* m_instruction;
+
+ arg_format m_arg_format;
+ };
+
+ static inst_info s_instruction_table[] = {
+ { SWF::ACTION_NEXTFRAME, "next_frame", ARG_NONE },
+ { SWF::ACTION_PREVFRAME, "prev_frame", ARG_NONE },
+ { SWF::ACTION_PLAY, "play", ARG_NONE },
+ { SWF::ACTION_STOP, "stop", ARG_NONE },
+ { SWF::ACTION_TOGGLEQUALITY, "toggle_qlty", ARG_NONE },
+ { SWF::ACTION_STOPSOUNDS, "stop_sounds", ARG_NONE },
+ { SWF::ACTION_ADD, "add", ARG_NONE },
+ { SWF::ACTION_SUBTRACT, "sub", ARG_NONE },
+ { SWF::ACTION_MULTIPLY, "mul", ARG_NONE },
+ { SWF::ACTION_DIVIDE, "div", ARG_NONE },
+ { SWF::ACTION_EQUAL, "equ", ARG_NONE },
+ { SWF::ACTION_LESSTHAN, "lt", ARG_NONE },
+ { SWF::ACTION_LOGICALAND, "and", ARG_NONE },
+ { SWF::ACTION_LOGICALOR, "or", ARG_NONE },
+ { SWF::ACTION_LOGICALNOT, "not", ARG_NONE },
+ { SWF::ACTION_STRINGEQ, "str_eq", ARG_NONE },
+ { SWF::ACTION_STRINGLENGTH, "str_len", ARG_NONE },
+ { SWF::ACTION_SUBSTRING, "substr", ARG_NONE },
+ { SWF::ACTION_POP, "pop", ARG_NONE },
+ { SWF::ACTION_INT, "floor", ARG_NONE },
+ { SWF::ACTION_GETVARIABLE, "get_var", ARG_NONE },
+ { SWF::ACTION_SETVARIABLE, "set_var", ARG_NONE },
+ { SWF::ACTION_SETTARGETEXPRESSION, "set_target_exp", ARG_NONE },
+ { SWF::ACTION_STRINGCONCAT, "str_cat", ARG_NONE },
+ { SWF::ACTION_GETPROPERTY, "get_prop", ARG_NONE },
+ { SWF::ACTION_SETPROPERTY, "set_prop", ARG_NONE },
+ { SWF::ACTION_DUPLICATECLIP, "dup_sprite", ARG_NONE },
+ { SWF::ACTION_REMOVECLIP, "rem_sprite", ARG_NONE },
+ { SWF::ACTION_TRACE, "trace", ARG_NONE },
+ { SWF::ACTION_STARTDRAGMOVIE, "start_drag", ARG_NONE },
+ { SWF::ACTION_STOPDRAGMOVIE, "stop_drag", ARG_NONE },
+ { SWF::ACTION_STRINGCOMPARE, "str_lt", ARG_NONE },
+ { SWF::ACTION_THROW, "throw_fixme", ARG_NONE },
+ { SWF::ACTION_CASTOP, "cast_fixme", ARG_NONE },
+ { SWF::ACTION_IMPLEMENTSOP, "implements_fixme", ARG_NONE },
+ { SWF::ACTION_RANDOM, "random", ARG_NONE },
+ { SWF::ACTION_MBLENGTH, "mb_length_fixme", ARG_NONE },
+ { SWF::ACTION_ORD, "ord", ARG_NONE },
+ { SWF::ACTION_CHR, "chr", ARG_NONE },
+ { SWF::ACTION_GETTIMER, "get_timer", ARG_NONE },
+ { SWF::ACTION_MBSUBSTRING, "substr_mb_fixme", ARG_NONE },
+ { SWF::ACTION_MBORD, "ord_mb", ARG_NONE },
+ { SWF::ACTION_MBCHR, "chr_mb_fixme", ARG_NONE },
+ { SWF::ACTION_DELETEVAR, "delete_fixme", ARG_NONE },
+ { SWF::ACTION_DELETE, "delete_all_fixme", ARG_NONE },
+ { SWF::ACTION_VAREQUALS, "set_local", ARG_NONE },
+ { SWF::ACTION_CALLFUNCTION, "call_func", ARG_NONE },
+ { SWF::ACTION_RETURN, "return", ARG_NONE },
+ { SWF::ACTION_MODULO, "mod", ARG_NONE },
+ { SWF::ACTION_NEW, "new", ARG_NONE },
+ { SWF::ACTION_VAR, "decl_local", ARG_NONE },
+ { SWF::ACTION_INITARRAY, "decl_array", ARG_NONE },
+ { SWF::ACTION_INITOBJECT, "decl_obj_fixme", ARG_NONE },
+ { SWF::ACTION_TYPEOF, "type_of", ARG_NONE },
+ { SWF::ACTION_TARGETPATH, "get_target_fixme", ARG_NONE },
+ { SWF::ACTION_ENUMERATE, "enumerate", ARG_NONE },
+ { SWF::ACTION_NEWADD, "add_t", ARG_NONE },
+ { SWF::ACTION_NEWLESSTHAN, "lt_t", ARG_NONE },
+ { SWF::ACTION_NEWEQUALS, "eq_t", ARG_NONE },
+ { SWF::ACTION_TONUMBER, "number", ARG_NONE },
+ { SWF::ACTION_TOSTRING, "string", ARG_NONE },
+ { SWF::ACTION_DUP, "dup", ARG_NONE },
+ { SWF::ACTION_SWAP, "swap", ARG_NONE },
+ { SWF::ACTION_GETMEMBER, "get_member", ARG_NONE },
+ { SWF::ACTION_SETMEMBER, "set_member", ARG_NONE },
+ { SWF::ACTION_INCREMENT, "inc", ARG_NONE },
+ { SWF::ACTION_DECREMENT, "dec", ARG_NONE },
+ { SWF::ACTION_CALLMETHOD, "call_method", ARG_NONE },
+ { SWF::ACTION_NEWMETHOD, "new_method_fixme", ARG_NONE },
+ { SWF::ACTION_INSTANCEOF, "is_inst_of_fixme", ARG_NONE },
+ { SWF::ACTION_ENUM2, "enum_object_fixme", ARG_NONE },
+ { SWF::ACTION_BITWISEAND, "bit_and", ARG_NONE },
+ { SWF::ACTION_BITWISEOR, "bit_or", ARG_NONE },
+ { SWF::ACTION_BITWISEXOR, "bit_xor", ARG_NONE },
+ { SWF::ACTION_SHIFTLEFT, "shl", ARG_NONE },
+ { SWF::ACTION_SHIFTRIGHT, "asr", ARG_NONE },
+ { SWF::ACTION_SHIFTRIGHT2, "lsr", ARG_NONE },
+ { SWF::ACTION_STRICTEQ, "eq_strict", ARG_NONE },
+ { SWF::ACTION_GREATER, "gt_t", ARG_NONE },
+ { SWF::ACTION_EXTENDS, "extends_fixme", ARG_NONE },
+ { SWF::ACTION_GOTOFRAME, "goto_frame", ARG_U16 },
+ { SWF::ACTION_GETURL, "get_url", ARG_STR },
+ { SWF::ACTION_SETREGISTER, "store_register", ARG_U8 },
+ { SWF::ACTION_CONSTANTPOOL, "decl_dict", ARG_DECL_DICT },
+ { SWF::ACTION_WAITFORFRAME, "wait_for_frame", ARG_HEX },
+ { SWF::ACTION_SETTARGET, "set_target", ARG_STR },
+ { SWF::ACTION_GOTOLABEL, "goto_frame_lbl", ARG_STR },
+ { SWF::ACTION_WAITFORFRAMEEXPRESSION, "wait_for_fr_exp", ARG_HEX },
+ { SWF::ACTION_DEFINEFUNCTION2, "function2", ARG_FUNCTION2 },
+ { SWF::ACTION_TRY, "try_fixme", ARG_FUNCTION2 },
+ { SWF::ACTION_WITH, "with", ARG_U16 },
+ { SWF::ACTION_PUSHDATA, "push_data", ARG_PUSH_DATA },
+ { SWF::ACTION_BRANCHALWAYS, "goto", ARG_S16 },
+ { SWF::ACTION_GETURL2, "get_url2", ARG_HEX },
+ { SWF::ACTION_DEFINEFUNCTION, "func", ARG_HEX },
+ { SWF::ACTION_BRANCHIFTRUE, "branch_if_true", ARG_S16 },
+ { SWF::ACTION_CALLFRAME, "call_frame", ARG_HEX },
+ { SWF::ACTION_GOTOEXPRESSION, "goto_frame_exp", ARG_HEX },
+ { SWF::ACTION_END, "<end>", ARG_NONE }
+ };
+
+ int action_id = instruction_data[0];
+ inst_info* info = NULL;
+
+ for (int i = 0; ; i++)
+ {
+ if (s_instruction_table[i].m_action_id == action_id)
+ {
+ info = &s_instruction_table[i];
+ }
+
+ if (s_instruction_table[i].m_action_id == 0)
+ {
+ // Stop at the end of the table and give up.
+ break;
+ }
+ }
+
+ arg_format fmt = ARG_HEX;
+
+ // Show instruction.
+ if (info == NULL)
+ {
+ log_msg("<unknown>[0x%02X]", action_id);
+ }
+ else
+ {
+ log_msg("%-15s", info->m_instruction);
+ fmt = info->m_arg_format;
+ }
+
+ // Show instruction argument(s).
+ if (action_id & 0x80)
+ {
+ assert(fmt != ARG_NONE);
+
+ int length = instruction_data[1] | (instruction_data[2] << 8);
+
+ // log_msg(" [%d]", length);
+
+ if (fmt == ARG_HEX)
+ {
+ for (int i = 0; i < length; i++)
+ {
+ log_msg(" 0x%02X", instruction_data[3 + i]);
+ }
+ log_msg("\n");
+ }
+ else if (fmt == ARG_STR)
+ {
+ log_msg(" \"");
+ for (int i = 0; i < length; i++)
+ {
+ log_msg("%c", instruction_data[3 + i]);
+ }
+ log_msg("\"\n");
+ }
+ else if (fmt == ARG_U8)
+ {
+ int val = instruction_data[3];
+ log_msg(" %d\n", val);
+ }
+ else if (fmt == ARG_U16)
+ {
+ int val = instruction_data[3] | (instruction_data[4] << 8);
+ log_msg(" %d\n", val);
+ }
+ else if (fmt == ARG_S16)
+ {
+ int val = instruction_data[3] | (instruction_data[4] << 8);
+ if (val & 0x8000) val |= ~0x7FFF; // sign-extend
+ log_msg(" %d\n", val);
+ }
+ else if (fmt == ARG_PUSH_DATA)
+ {
+ log_msg("\n");
+ int i = 0;
+ while (i < length)
+ {
+ int type = instruction_data[3 + i];
+ i++;
+ log_msg("\t\t"); // indent
+ if (type == 0)
+ {
+ // string
+ log_msg("\"");
+ while (instruction_data[3 + i])
+ {
+ log_msg("%c", instruction_data[3 +
i]);
+ i++;
+ }
+ i++;
+ log_msg("\"\n");
+ }
+ else if (type == 1)
+ {
+ // float (little-endian)
+ union {
+ float f;
+ Uint32 i;
+ } u;
+ compiler_assert(sizeof(u) == sizeof(u.i));
+
+ memcpy(&u.i, instruction_data + 3 + i, 4);
+ u.i = swap_le32(u.i);
+ i += 4;
+
+ log_msg("(float) %f\n", u.f);
+ }
+ else if (type == 2)
+ {
+ log_msg("NULL\n");
+ }
+ else if (type == 3)
+ {
+ log_msg("undef\n");
+ }
+ else if (type == 4)
+ {
+ // contents of register
+ int reg = instruction_data[3 + i];
+ i++;
+ log_msg("reg[%d]\n", reg);
+ }
+ else if (type == 5)
+ {
+ int bool_val = instruction_data[3 + i];
+ i++;
+ log_msg("bool(%d)\n", bool_val);
+ }
+ else if (type == 6)
+ {
+ // double
+ // wacky format: 45670123
+ union {
+ double d;
+ Uint64 i;
+ struct {
+ Uint32 lo;
+ Uint32 hi;
+ } sub;
+ } u;
+ compiler_assert(sizeof(u) == sizeof(u.i));
+
+ memcpy(&u.sub.hi, instruction_data + 3 + i,
4);
+ memcpy(&u.sub.lo, instruction_data + 3 + i
+ 4, 4);
+ u.i = swap_le64(u.i);
+ i += 8;
+
+ log_msg("(double) %f\n", u.d);
+ }
+ else if (type == 7)
+ {
+ // int32
+ Sint32 val = instruction_data[3 + i]
+ | (instruction_data[3 + i + 1] << 8)
+ | (instruction_data[3 + i + 2] << 16)
+ | (instruction_data[3 + i + 3] << 24);
+ i += 4;
+ log_msg("(int) %d\n", val);
+ }
+ else if (type == 8)
+ {
+ int id = instruction_data[3 + i];
+ i++;
+ log_msg("dict_lookup[%d]\n", id);
+ }
+ else if (type == 9)
+ {
+ int id = instruction_data[3 + i] |
(instruction_data[3 + i + 1] << 8);
+ i += 2;
+ log_msg("dict_lookup_lg[%d]\n", id);
+ }
+ }
+ }
+ else if (fmt == ARG_DECL_DICT)
+ {
+ int i = 0;
+ int count = instruction_data[3 + i] | (instruction_data[3 +
i + 1] << 8);
+ i += 2;
+
+ log_msg(" [%d]\n", count);
+
+ // Print strings.
+ for (int ct = 0; ct < count; ct++)
+ {
+ log_msg("\t\t"); // indent
+
+ log_msg("\"");
+ while (instruction_data[3 + i])
+ {
+ // safety check.
+ if (i >= length)
+ {
+ log_msg("<disasm error -- length
exceeded>\n");
+ break;
+ }
+
+ log_msg("%c", instruction_data[3 + i]);
+ i++;
+ }
+ log_msg("\"\n");
+ i++;
+ }
+ }
+ else if (fmt == ARG_FUNCTION2)
+ {
+ // Signature info for a function2 opcode.
+ int i = 0;
+ const char* function_name = (const char*)
&instruction_data[3 + i];
+ i += strlen(function_name) + 1;
+
+ int arg_count = instruction_data[3 + i] |
(instruction_data[3 + i + 1] << 8);
+ i += 2;
+
+ int reg_count = instruction_data[3 + i];
+ i++;
+
+ log_msg("\n\t\tname = '%s', arg_count = %d, reg_count =
%d\n",
+ function_name, arg_count, reg_count);
+
+ uint16 flags = (instruction_data[3 + i]) |
(instruction_data[3 + i + 1] << 8);
+ i += 2;
+
+ // @@ What is the difference between "super" and "_parent"?
+
+ bool preload_global = (flags & 0x100) != 0;
+ bool preload_parent = (flags & 0x80) != 0;
+ bool preload_root = (flags & 0x40) != 0;
+ bool suppress_super = (flags & 0x20) != 0;
+ bool preload_super = (flags & 0x10) != 0;
+ bool suppress_args = (flags & 0x08) != 0;
+ bool preload_args = (flags & 0x04) != 0;
+ bool suppress_this = (flags & 0x02) != 0;
+ bool preload_this = (flags & 0x01) != 0;
+
+ log_msg("\t\t pg = %d\n"
+ "\t\t pp = %d\n"
+ "\t\t pr = %d\n"
+ "\t\tss = %d, ps = %d\n"
+ "\t\tsa = %d, pa = %d\n"
+ "\t\tst = %d, pt = %d\n",
+ int(preload_global),
+ int(preload_parent),
+ int(preload_root),
+ int(suppress_super),
+ int(preload_super),
+ int(suppress_args),
+ int(preload_args),
+ int(suppress_this),
+ int(preload_this));
+
+ for (int argi = 0; argi < arg_count; argi++)
+ {
+ int arg_register = instruction_data[3 + i];
+ i++;
+ const char* arg_name = (const char*)
&instruction_data[3 + i];
+ i += strlen(arg_name) + 1;
+
+ log_msg("\t\targ[%d] - reg[%d] - '%s'\n", argi,
arg_register, arg_name);
+ }
+
+ int function_length = instruction_data[3 + i] |
(instruction_data[3 + i + 1] << 8);
+ i += 2;
+
+ log_msg("\t\tfunction length = %d\n", function_length);
}
}
+ else
+ {
+ log_msg("\n");
+ }
+}
#endif // COMPILE_DISASM
- [Gnash-commit] gnash/server action.cpp,
Rob Savoye <=