/* Parses command line arguments and stores them in a struct */ cc_error_t cc_parse_args(int argc, char* argv[], Options* options) { options->buffersize = DEFAULT_BUFFER_SIZE; options->outfile_index = -1; options->argc = argc; options->argv = argv; options->mode = DEFAULT_FILE_PERM; int c; while ((c = getopt(argc, argv, "+b:m:o:vh")) != -1) { switch (c) { case 'b': if ( !sscanf(optarg, "%u", &options->buffersize) ) cc_error(CC_USAGE); break; case 'm': if ( !sscanf(optarg, "%o", &options->mode) ) cc_error(CC_USAGE); break; case 'o': options->outfile_index = optind-1; break; case 'v': options->verbose = 1; break; case 'h': cc_error(CC_USAGE); break; case '?': cc_error(CC_USAGE); break; } } options->infiles_index = optind; return CC_NONE; }
int call_function(intptr_t addr, const RuntimeScriptValue *object, int numparm, const RuntimeScriptValue *parms) { if (!addr) { cc_error("null function pointer in call_function"); return -1; } if (numparm > 0 && !parms) { cc_error("invalid parameters array in call_function"); return -1; } intptr_t parm_value[9]; if (object) { parm_value[0] = (intptr_t)object->GetPtrWithOffset(); numparm++; } for (int ival = object ? 1 : 0, iparm = 0; ival < numparm; ++ival, ++iparm) { switch (parms[iparm].Type) { case kScValInteger: case kScValFloat: // AGS passes floats, copying their values into long variable case kScValPluginArg: parm_value[ival] = (intptr_t)parms[iparm].IValue; break; break; default: parm_value[ival] = (intptr_t)parms[iparm].GetPtrWithOffset(); break; } } // // AN IMPORTANT NOTE ON PARAM TYPE // of 2012-11-10 // //// NOTE of 2012-12-20: //// Everything said below is applicable only for calling //// exported plugin functions. // // Here we are sending parameters of type intptr_t to registered // function of unknown kind. Intptr_t is 32-bit for x32 build and // 64-bit for x64 build. // The exported functions usually have two types of parameters: // pointer and 'int' (32-bit). For x32 build those two have the // same size, but for x64 build first has 64-bit size while the // second remains 32-bit. // In formal case that would cause 'overflow' - function will // receive more data than needed (written to stack), with some // values shifted further by 32 bits. // // Upon testing, however, it was revealed that AMD64 processor, // the only platform we support x64 Linux AGS build on right now, // treats all the function parameters pushed to stack as 64-bit // values (few first parameters are sent via registers, and hence // are least concern anyway). Therefore, no 'overflow' occurs, // and 64-bit values are being effectively truncated to 32-bit // integers in the callee. // // Since this is still quite unreliable, this should be // reimplemented when there's enough free time available for // developers both for coding & testing. // // Most basic idea is to pass array of RuntimeScriptValue // objects (that hold type description) and get same RSV as a // return result. Keep in mind, though, that this solution will // require fixing ALL exported functions, so a good amount of // time and energy should be allocated for this task. // switch (numparm) { case 0: { int (*fparam) (); fparam = (int (*)())addr; return fparam(); } case 1: { int (*fparam) (intptr_t); fparam = (int (*)(intptr_t))addr; return fparam(parm_value[0]); } case 2: { int (*fparam) (intptr_t, intptr_t); fparam = (int (*)(intptr_t, intptr_t))addr; return fparam(parm_value[0], parm_value[1]); } case 3: { int (*fparam) (intptr_t, intptr_t, intptr_t); fparam = (int (*)(intptr_t, intptr_t, intptr_t))addr; return fparam(parm_value[0], parm_value[1], parm_value[2]); } case 4: { int (*fparam) (intptr_t, intptr_t, intptr_t, intptr_t); fparam = (int (*)(intptr_t, intptr_t, intptr_t, intptr_t))addr; return fparam(parm_value[0], parm_value[1], parm_value[2], parm_value[3]); } case 5: { int (*fparam) (intptr_t, intptr_t, intptr_t, intptr_t, intptr_t); fparam = (int (*)(intptr_t, intptr_t, intptr_t, intptr_t, intptr_t))addr; return fparam(parm_value[0], parm_value[1], parm_value[2], parm_value[3], parm_value[4]); } case 6: { int (*fparam) (intptr_t, intptr_t, intptr_t, intptr_t, intptr_t, intptr_t); fparam = (int (*)(intptr_t, intptr_t, intptr_t, intptr_t, intptr_t, intptr_t))addr; return fparam(parm_value[0], parm_value[1], parm_value[2], parm_value[3], parm_value[4], parm_value[5]); } case 7: { int (*fparam) (intptr_t, intptr_t, intptr_t, intptr_t, intptr_t, intptr_t, intptr_t); fparam = (int (*)(intptr_t, intptr_t, intptr_t, intptr_t, intptr_t, intptr_t, intptr_t))addr; return fparam(parm_value[0], parm_value[1], parm_value[2], parm_value[3], parm_value[4], parm_value[5], parm_value[6]); } case 8: { int (*fparam) (intptr_t, intptr_t, intptr_t, intptr_t, intptr_t, intptr_t, intptr_t, intptr_t); fparam = (int (*)(intptr_t, intptr_t, intptr_t, intptr_t, intptr_t, intptr_t, intptr_t, intptr_t))addr; return fparam(parm_value[0], parm_value[1], parm_value[2], parm_value[3], parm_value[4], parm_value[5], parm_value[6], parm_value[7]); } case 9: { int (*fparam) (intptr_t, intptr_t, intptr_t, intptr_t, intptr_t, intptr_t, intptr_t, intptr_t, intptr_t); fparam = (int (*)(intptr_t, intptr_t, intptr_t, intptr_t, intptr_t, intptr_t, intptr_t, intptr_t, intptr_t))addr; return fparam(parm_value[0], parm_value[1], parm_value[2], parm_value[3], parm_value[4], parm_value[5], parm_value[6], parm_value[7], parm_value[8]); } } cc_error("too many arguments in call to function"); return -1; }
const char *ScriptSprintf(char *buffer, size_t buf_length, const char *format, const RuntimeScriptValue *args, int32_t argc) { if (!buffer) { cc_error("internal error in ScriptSprintf: buffer is null"); return ""; } if (!format) { cc_error("internal error in ScriptSprintf: format string is null"); return ""; } if (argc > 0 && !args) { cc_error("internal error in ScriptSprintf: args pointer is null"); return ""; } // Expected format character count: // percent sign: 1 // flag: 1 // field width 10 (an uint32 number) // precision sign 1 // precision 10 (an uint32 number) // length modifier 2 // type 1 // NOTE: although width and precision will // not likely be defined by a 10-digit // number, such case is theoretically valid. const size_t fmtbuf_size = 27; char fmtbuf[fmtbuf_size]; char *fmt_bufptr; char *fmt_bufendptr = &fmtbuf[fmtbuf_size - 1]; char *out_ptr = buffer; const char *out_endptr = buffer + buf_length; const char *fmt_ptr = format; int32_t arg_idx = 0; ptrdiff_t avail_outbuf; int snprintf_res; FormatParseResult fmt_done; // Parse the format string, looking for argument placeholders while (*fmt_ptr && out_ptr != out_endptr) { // Try to put argument into placeholder if (*fmt_ptr == '%' && arg_idx < argc) { avail_outbuf = out_endptr - out_ptr; fmt_bufptr = fmtbuf; *(fmt_bufptr++) = '%'; snprintf_res = 0; fmt_done = kFormatParseNone; const RuntimeScriptValue &arg = args[arg_idx]; // Parse placeholder while (*(++fmt_ptr) && fmt_done == kFormatParseNone && fmt_bufptr != fmt_bufendptr) { *(fmt_bufptr++) = *fmt_ptr; switch (*fmt_ptr) { case 'd': case 'i': case 'o': case 'u': case 'x': case 'X': case 'c': // Print integer *fmt_bufptr = 0; snprintf_res = snprintf(out_ptr, avail_outbuf, fmtbuf, arg.IValue); fmt_done = kFormatParseArgument; break; case 'e': case 'E': case 'f': case 'F': case 'g': case 'G': case 'a': case 'A': // Print float *fmt_bufptr = 0; snprintf_res = snprintf(out_ptr, avail_outbuf, fmtbuf, arg.FValue); fmt_done = kFormatParseArgument; break; case 's': if (!arg.Ptr) { if (loaded_game_file_version < kGameVersion_320) { // print "(null)" into the placeholder; // NOTE: the behavior of printf("%s", 0) is undefined (MS version prints "(null)", // but there's no guarantee others will), therefore we shouldn't let snprintf do // all the job here. *fmt_bufptr = 0; strncpy(out_ptr, "(null)", avail_outbuf); snprintf_res = Math::Min<ptrdiff_t>(avail_outbuf, 6); fmt_done = kFormatParseArgument; break; } else { cc_error("ScriptSprintf: argument %d is expected to be a string, but it is null pointer", arg_idx); return ""; } } else if (arg.Ptr == buffer) { cc_error("ScriptSprintf: argument %d is a pointer to output buffer", arg_idx); return ""; } // fall through intended --- case 'p': // Print string, or pointer value *fmt_bufptr = 0; snprintf_res = snprintf(out_ptr, avail_outbuf, fmtbuf, arg.Ptr); fmt_done = kFormatParseArgument; break; case '%': // This may be a literal percent sign ('%%') if (fmt_bufptr - fmtbuf == 2) { fmt_done = kFormatParseLiteralPercent; } // ...Otherwise we reached the next placeholder else { fmt_ptr--; fmt_bufptr--; fmt_done = kFormatParseInvalid; } break; } } if (fmt_done == kFormatParseArgument) { out_ptr += snprintf_res >= 0 ? snprintf_res : avail_outbuf; arg_idx++; } else if (fmt_done == kFormatParseLiteralPercent) { *(out_ptr++) = '%'; } // If placeholder was not valid, just copy stored format buffer as it is else { size_t copy_len = Math::Min(Math::Min<ptrdiff_t>(fmt_bufptr - fmtbuf, fmtbuf_size - 1), avail_outbuf); memcpy(out_ptr, fmtbuf, copy_len); out_ptr += copy_len; } } // If there's no placeholder, simply copy the character to output buffer else { *(out_ptr++) = *(fmt_ptr++); } } // Terminate the string *out_ptr = 0; return buffer; }
const char *ScriptVSprintf(char *buffer, size_t buf_length, const char *format, va_list &arg_ptr) { if (!buffer) { cc_error("internal error in ScriptSprintf: buffer is null"); return ""; } if (!format) { cc_error("internal error in ScriptSprintf: format string is null"); return ""; } const size_t fmtbuf_size = 27; char fmtbuf[fmtbuf_size]; char *fmt_bufptr; char *fmt_bufendptr = &fmtbuf[fmtbuf_size - 1]; char *out_ptr = buffer; const char *out_endptr = buffer + buf_length; const char *fmt_ptr = format; int32_t arg_idx = 0; ptrdiff_t avail_outbuf; int snprintf_res; FormatParseResult fmt_done; union VAR_ARG { int32_t IValue; float FValue; const char *Ptr; } arg; // Parse the format string, looking for argument placeholders while (*fmt_ptr && out_ptr != out_endptr) { // Try to put argument into placeholder if (*fmt_ptr == '%') { avail_outbuf = out_endptr - out_ptr; fmt_bufptr = fmtbuf; *(fmt_bufptr++) = '%'; snprintf_res = 0; fmt_done = kFormatParseNone; // Parse placeholder while (*(++fmt_ptr) && fmt_done == kFormatParseNone && fmt_bufptr != fmt_bufendptr) { *(fmt_bufptr++) = *fmt_ptr; switch (*fmt_ptr) { case 'd': case 'i': case 'o': case 'u': case 'x': case 'X': case 'c': // Print integer arg = va_arg(arg_ptr, VAR_ARG); *fmt_bufptr = 0; snprintf_res = snprintf(out_ptr, avail_outbuf, fmtbuf, arg.IValue); fmt_done = kFormatParseArgument; break; case 'e': case 'E': case 'f': case 'F': case 'g': case 'G': case 'a': case 'A': // Print float arg = va_arg(arg_ptr, VAR_ARG); *fmt_bufptr = 0; snprintf_res = snprintf(out_ptr, avail_outbuf, fmtbuf, arg.FValue); fmt_done = kFormatParseArgument; break; case 's': arg = va_arg(arg_ptr, VAR_ARG); if (!arg.Ptr) { if (loaded_game_file_version < kGameVersion_320) { *fmt_bufptr = 0; strncpy(out_ptr, "(null)", avail_outbuf); snprintf_res = Math::Min<ptrdiff_t>(avail_outbuf, 6); fmt_done = kFormatParseArgument; break; } else { cc_error("ScriptSprintf: argument %d is expected to be a string, but it is null pointer", arg_idx); return ""; } } else if (arg.Ptr == buffer) { cc_error("ScriptSprintf: argument %d is a pointer to output buffer", arg_idx); return ""; } // fall through intended --- case 'p': // Print string, or pointer value *fmt_bufptr = 0; snprintf_res = snprintf(out_ptr, avail_outbuf, fmtbuf, arg.Ptr); fmt_done = kFormatParseArgument; break; case '%': // This may be a literal percent sign ('%%') if (fmt_bufptr - fmtbuf == 2) { fmt_done = kFormatParseLiteralPercent; } // ...Otherwise we reached the next placeholder else { fmt_ptr--; fmt_bufptr--; fmt_done = kFormatParseInvalid; } break; } } if (fmt_done == kFormatParseArgument) { out_ptr += snprintf_res >= 0 ? snprintf_res : avail_outbuf; arg_idx++; } else if (fmt_done == kFormatParseLiteralPercent) { *(out_ptr++) = '%'; } // If placeholder was not valid, just copy stored format buffer as it is else { size_t copy_len = Math::Min(Math::Min<ptrdiff_t>(fmt_bufptr - fmtbuf, fmtbuf_size - 1), avail_outbuf); memcpy(out_ptr, fmtbuf, copy_len); out_ptr += copy_len; } } // If there's no placeholder, simply copy the character to output buffer else { *(out_ptr++) = *(fmt_ptr++); } } // Terminate the string *out_ptr = 0; return buffer; }
int32_t StaticGame::ReadInt32(const char *address, intptr_t offset) { int index = offset / sizeof(int32_t); if (index >= 5 && index < 5 + MAXGLOBALVARS) return play.globalvars[index - 5]; switch (index) { case 0: return play.score; case 1: return play.usedmode; case 2: return play.disabled_user_interface; case 3: return play.gscript_timer; case 4: return play.debug_mode; // 5 -> 54: play.globalvars case 55: return play.messagetime; case 56: return play.usedinv; case 57: return play.inv_top; case 58: return play.inv_numdisp; case 59: return play.obsolete_inv_numorder; case 60: return play.inv_numinline; case 61: return play.text_speed; case 62: return play.sierra_inv_color; case 63: return play.talkanim_speed; case 64: return play.inv_item_wid; case 65: return play.inv_item_hit; case 66: return play.speech_text_shadow; case 67: return play.swap_portrait_side; case 68: return play.speech_textwindow_gui; case 69: return play.follow_change_room_timer; case 70: return play.totalscore; case 71: return play.skip_display; case 72: return play.no_multiloop_repeat; case 73: return play.roomscript_finished; case 74: return play.used_inv_on; case 75: return play.no_textbg_when_voice; case 76: return play.max_dialogoption_width; case 77: return play.no_hicolor_fadein; case 78: return play.bgspeech_game_speed; case 79: return play.bgspeech_stay_on_display; case 80: return play.unfactor_speech_from_textlength; case 81: return play.mp3_loop_before_end; case 82: return play.speech_music_drop; case 83: return play.in_cutscene; case 84: return play.fast_forward; case 85: return play.room_width; case 86: return play.room_height; case 87: return play.game_speed_modifier; case 88: return play.score_sound; case 89: return play.takeover_data; case 90: return play.replay_hotkey; case 91: return play.dialog_options_x; case 92: return play.dialog_options_y; case 93: return play.narrator_speech; case 94: return play.ambient_sounds_persist; case 95: return play.lipsync_speed; case 96: return play.close_mouth_speech_time; case 97: return play.disable_antialiasing; case 98: return play.text_speed_modifier; case 99: return play.text_align; case 100: return play.speech_bubble_width; case 101: return play.min_dialogoption_width; case 102: return play.disable_dialog_parser; case 103: return play.anim_background_speed; case 104: return play.top_bar_backcolor; case 105: return play.top_bar_textcolor; case 106: return play.top_bar_bordercolor; case 107: return play.top_bar_borderwidth; case 108: return play.top_bar_ypos; case 109: return play.screenshot_width; case 110: return play.screenshot_height; case 111: return play.top_bar_font; case 112: return play.speech_text_align; case 113: return play.auto_use_walkto_points; case 114: return play.inventory_greys_out; case 115: return play.skip_speech_specific_key; case 116: return play.abort_key; case 117: return play.fade_to_red; case 118: return play.fade_to_green; case 119: return play.fade_to_blue; case 120: return play.show_single_dialog_option; case 121: return play.keep_screen_during_instant_transition; case 122: return play.read_dialog_option_colour; case 123: return play.stop_dialog_at_end; case 124: return play.speech_portrait_placement; case 125: return play.speech_portrait_x; case 126: return play.speech_portrait_y; case 127: return play.speech_display_post_time_ms; case 128: return play.dialog_options_highlight_color; } cc_error("StaticGame: unsupported variable offset %d", offset); return 0; }
void StaticGame::WriteInt32(const char *address, intptr_t offset, int32_t val) { int index = offset / sizeof(int32_t); if (index >= 5 && index < 5 + MAXGLOBALVARS) { play.globalvars[index - 5] = val; return; } switch (index) { case 0: play.score = val; break; case 1: play.usedmode = val; break; case 2: play.disabled_user_interface = val; break; case 3: play.gscript_timer = val; break; case 4: set_debug_mode(val != 0); break; // play.debug_mode // 5 -> 54: play.globalvars case 55: play.messagetime = val; break; case 56: play.usedinv = val; break; case 57: play.inv_top = val; break; case 58: play.inv_numdisp = val; break; case 59: play.obsolete_inv_numorder = val; break; case 60: play.inv_numinline = val; break; case 61: play.text_speed = val; break; case 62: play.sierra_inv_color = val; break; case 63: play.talkanim_speed = val; break; case 64: play.inv_item_wid = val; break; case 65: play.inv_item_hit = val; break; case 66: play.speech_text_shadow = val; break; case 67: play.swap_portrait_side = val; break; case 68: play.speech_textwindow_gui = val; break; case 69: play.follow_change_room_timer = val; break; case 70: play.totalscore = val; break; case 71: play.skip_display = val; break; case 72: play.no_multiloop_repeat = val; break; case 73: play.roomscript_finished = val; break; case 74: play.used_inv_on = val; break; case 75: play.no_textbg_when_voice = val; break; case 76: play.max_dialogoption_width = val; break; case 77: play.no_hicolor_fadein = val; break; case 78: play.bgspeech_game_speed = val; break; case 79: play.bgspeech_stay_on_display = val; break; case 80: play.unfactor_speech_from_textlength = val; break; case 81: play.mp3_loop_before_end = val; break; case 82: play.speech_music_drop = val; break; case 83: case 84: case 85: case 86: cc_error("StaticGame: attempt to write readonly variable at offset %d", offset); break; case 87: play.game_speed_modifier = val; break; case 88: play.score_sound = val; break; case 89: play.takeover_data = val; break; case 90: play.replay_hotkey = val; break; case 91: play.dialog_options_x = val; break; case 92: play.dialog_options_y = val; break; case 93: play.narrator_speech = val; break; case 94: play.ambient_sounds_persist = val; break; case 95: play.lipsync_speed = val; break; case 96: play.close_mouth_speech_time = val; break; case 97: play.disable_antialiasing = val; break; case 98: play.text_speed_modifier = val; break; case 99: play.text_align = ReadScriptAlignment(val); break; case 100: play.speech_bubble_width = val; break; case 101: play.min_dialogoption_width = val; break; case 102: play.disable_dialog_parser = val; break; case 103: play.anim_background_speed = val; break; case 104: play.top_bar_backcolor = val; break; case 105: play.top_bar_textcolor = val; break; case 106: play.top_bar_bordercolor = val; break; case 107: play.top_bar_borderwidth = val; break; case 108: play.top_bar_ypos = val; break; case 109: play.screenshot_width = val; break; case 110: play.screenshot_height = val; break; case 111: play.top_bar_font = val; break; case 112: play.speech_text_align = ReadScriptAlignment(val); break; case 113: play.auto_use_walkto_points = val; break; case 114: play.inventory_greys_out = val; break; case 115: play.skip_speech_specific_key = val; break; case 116: play.abort_key = val; break; case 117: case 118: case 119: cc_error("StaticGame: attempt to write readonly variable at offset %d", offset); break; case 120: play.show_single_dialog_option = val; break; case 121: play.keep_screen_during_instant_transition = val; break; case 122: play.read_dialog_option_colour = val; break; case 123: play.stop_dialog_at_end = val; break; case 124: play.speech_portrait_placement = val; break; case 125: play.speech_portrait_x = val; break; case 126: play.speech_portrait_y = val; break; case 127: play.speech_display_post_time_ms = val; break; case 128: play.dialog_options_highlight_color = val; break; default: cc_error("StaticGame: unsupported variable offset %d", offset); } }