📄 gameswf_action.cpp
字号:
// <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 <= 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(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); // 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"); // Jam something into the remaining (invalid) entries. while (ct < count) { m_dictionary[ct] = "<invalid>"; ct++; } return; } i++; } i++; } } 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(); array<with_stack_entry> empty_with_stack; execute(env, 0, m_buffer.size(), NULL, empty_with_stack, false /* not function2 */); env->set_local_frame_top(local_stack_top); } void action_buffer::execute( as_environment* env, int start_pc, int exec_bytes, as_value* retval, const array<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); array<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 0x00: // end of actions. return; case 0x04: // next frame. env->get_target()->goto_frame(env->get_target()->get_current_frame() + 1); break; case 0x05: // prev frame. env->get_target()->goto_frame(env->get_target()->get_current_frame() - 1); break; case 0x06: // action play env->get_target()->set_play_state(movie::PLAY); break; case 0x07: // action stop env->get_target()->set_play_state(movie::STOP); break; case 0x08: // toggle quality case 0x09: // stop sounds break; case 0x0A: // add { env->top(1) += env->top(0); env->drop(1); break; } case 0x0B: // subtract { env->top(1) -= env->top(0); env->drop(1); break; } case 0x0C: // multiply { env->top(1) *= env->top(0); env->drop(1); break; } case 0x0D: // divide { env->top(1) /= env->top(0); env->drop(1); break; } case 0x0E: // equal { env->top(1).set_bool(env->top(1) == env->top(0)); env->drop(1); break; } case 0x0F: // less than { env->top(1).set_bool(env->top(1) < env->top(0)); env->drop(1); break; } case 0x10: // logical and { env->top(1).set_bool(env->top(1).to_bool() && env->top(0).to_bool()); env->drop(1); break; } case 0x11: // logical or { env->top(1).set_bool(env->top(1).to_bool() && env->top(0).to_bool()); env->drop(1); break; } case 0x12: // logical not { env->top(0).set_bool(! env->top(0).to_bool()); break; } case 0x13: // string equal { env->top(1).set_bool(env->top(1).to_tu_string() == env->top(0).to_tu_string()); env->drop(1); break; } case 0x14: // string length { env->top(0).set_int(env->top(0).to_tu_string_versioned(version).utf8_length()); break; } case 0x15: // 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 0x17: // pop { env->drop(1); break; } case 0x18: // int { env->top(0).set_int(int(floor(env->top(0).to_number()))); break; } case 0x1C: // 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 0x1D: // 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 0x20: // set target expression { as_object_interface* target_object = env->top(0).to_object(); IF_VERBOSE_ACTION(log_msg("-- ActionSetTarget2: %s (%d)", ((character *) target_object)->m_name.c_str(), ((character *) target_object)->m_id)); movie* target = env->find_target(target_object); env->set_target (target); break; } case 0x21: // 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 0x22: // 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 0x23: // 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 0x24: // 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 0x25: // remove clip env->get_target()->remove_display_object(env->top(0).to_tu_string()); env->drop(1); break; case 0x26: // 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 0x27: // 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 0x28: // stop drag movie { movie* root_movie = env->get_target()->get_root_movie(); assert(root_movie); root_movie->stop_drag(); break; } case 0x29: // string less than { env->top(1).set_bool(env->top(1).to_tu_string() < env->top(0).to_tu_string()); break; } case 0x2A: // throw { log_error("todo opcode: %02X\n", action_id); break; } case 0x2B: // cast_object { // TODO // // Pop o1, pop s2 // Make sure o1 is an instance of s2. // If the cast succeeds, push o1, else push NULL. // // The cast doesn't appear to coerce at all, it's more // like a dynamic_cast<> in C++ I think. log_error("todo opcode: %02X\n", action_id); break; } case 0x2C: // implements { // 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 0x30: // 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 0x31: // mb length { // @@ TODO log_error("todo opcode: %02X\n", action_id); break; } case 0x32: // ord { // ASCII code of first character env->top(0).set_int(env->top(0).to_string()[0]); break; } case 0x33: // chr { char buf[2]; buf[0] = int(env->top(0).to_number()); buf[1] = 0; env->top(0).set_string(buf); break; } case 0x34: // get timer // Push milliseconds since we started playing. env->push(floorf(env->m_target->get_timer() * 1000.0f)); break; case 0x35: // mb substring { // @@ TODO log_error("todo opcode: %02X\n", action_id); break; } case 0x37: // mb chr { // @@ TODO log_error("todo opcode: %02X\n", action_id); break; } case 0x3A: // delete { // @@ TODO // Apparently this can be used to remove properties from // an object? log_error("todo opcode: %02X\n", action_id); break; } case 0x3B: // delete2 { // @@ tulrich: delete is not valid here! Do we actually just want to // NULL out the object pointer in the environment (to drop the ref)? // Should at least check the ref count before deleting anything!!!// as_value obj_name = env->pop(); as_value obj_ptr = env->get_variable_raw(env->top(0).to_tu_string(), with_stack);///x delete obj_ptr.to_object();// log_error("%08X\n", obj_ptr.to_object()); log_error("todo opcode: %02X\n", action_id); break; } case 0x3C: // set local { as_value value = env->pop(); as_value varname = env->pop(); env->set_local(varname.to_tu_string(), value); break; } case 0x3D: // call function { 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::C_FUNCTION && function.get_type() != as_value::AS_FUNCTION) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -