/* Pebble libpebble.a stub resolver for radare2/r2js.
 *
 * Usage:
 *   r2 -i examples/pebble.r2.js hello-world/build/aplite/pebble-app.elf
 *
 * The per-API stubs linked from libpebble.a look like this in Thumb bytes:
 *   0f b4              push {r0, r1, r2, r3}
 *   01 49              ldr  r1, [pc, #4]
 *   ff f7 ?? ??        b.w  jump_to_pbl_function
 *   ?? ?? ?? ??        little-endian firmware jump-table byte offset
 *
 * Objdump prints the same pattern as halfwords:
 *   b40f 4901 f7ff ???? <offset32>
 *
 * The shared trampoline usually looks like this in file/memory bytes:
 *   03 a3 18 68 08 44 02 68 94 46 0f bc 60 47 00 bf ?? ?? ?? ??
 * where the last word is pbl_table_addr and is patched by the firmware loader.
 */

const STUB_PATTERN = "0fb40149fff7............";
const TRAMPOLINE_PATTERN = "03a318680844026894460fbc604700bf........";

const PEBBLE_API_BY_INDEX = {
	0: "accel_data_service_subscribe__deprecated",
	1: "accel_data_service_unsubscribe",
	2: "accel_service_peek",
	3: "accel_service_set_samples_per_update",
	4: "accel_service_set_sampling_rate",
	5: "accel_tap_service_subscribe",
	6: "accel_tap_service_unsubscribe",
	7: "action_bar_layer_legacy2_add_to_window",
	8: "action_bar_layer_legacy2_clear_icon",
	9: "action_bar_layer_legacy2_create",
	10: "action_bar_layer_legacy2_destroy",
	11: "action_bar_layer_legacy2_get_layer",
	12: "action_bar_layer_legacy2_remove_from_window",
	13: "action_bar_layer_legacy2_set_background_color_2bit",
	14: "action_bar_layer_legacy2_set_click_config_provider",
	15: "action_bar_layer_legacy2_set_context",
	16: "action_bar_layer_legacy2_set_icon",
	17: "animation_legacy2_create",
	18: "animation_legacy2_destroy",
	19: "animation_legacy2_get_context",
	20: "animation_legacy2_is_scheduled",
	21: "animation_legacy2_schedule",
	22: "animation_legacy2_set_curve",
	23: "animation_legacy2_set_delay",
	24: "animation_legacy2_set_duration",
	25: "animation_legacy2_set_handlers",
	26: "animation_legacy2_set_implementation",
	27: "animation_legacy2_unschedule",
	28: "animation_legacy2_unschedule_all",
	29: "app_comm_get_sniff_interval",
	30: "app_comm_set_sniff_interval",
	31: "app_event_loop",
	34: "app_log",
	35: "app_message_deregister_callbacks",
	36: "app_message_open",
	43: "app_sync_deinit",
	44: "app_sync_get",
	45: "app_sync_init",
	46: "app_sync_set",
	47: "app_timer_cancel",
	48: "app_timer_register",
	49: "app_timer_reschedule",
	50: "atan2_lookup",
	51: "atoi",
	52: "atol",
	53: "battery_state_service_peek",
	54: "battery_state_service_subscribe",
	55: "battery_state_service_unsubscribe",
	56: "bitmap_layer_create",
	57: "bitmap_layer_destroy",
	58: "bitmap_layer_get_layer",
	59: "bitmap_layer_set_alignment",
	60: "bitmap_layer_set_background_color_2bit",
	61: "bitmap_layer_set_bitmap",
	62: "bitmap_layer_set_compositing_mode",
	63: "bluetooth_connection_service_peek",
	64: "bluetooth_connection_service_subscribe",
	65: "bluetooth_connection_service_unsubscribe",
	66: "click_number_of_clicks_counted",
	67: "click_recognizer_get_button_id",
	68: "clock_copy_time_string",
	69: "clock_is_24h_style",
	70: "cos_lookup",
	71: "data_logging_create",
	72: "data_logging_finish",
	73: "data_logging_log",
	74: "dict_calc_buffer_size",
	75: "dict_calc_buffer_size_from_tuplets",
	76: "dict_find",
	77: "dict_merge",
	78: "dict_read_begin_from_buffer",
	79: "dict_read_first",
	80: "dict_read_next",
	81: "dict_serialize_tuplets",
	82: "dict_serialize_tuplets_to_buffer__deprecated",
	83: "dict_serialize_tuplets_to_buffer_with_iter",
	84: "dict_write_begin",
	85: "dict_write_cstring",
	86: "dict_write_data",
	87: "dict_write_end",
	88: "dict_write_int",
	89: "dict_write_int16",
	90: "dict_write_int32",
	91: "dict_write_int8",
	92: "dict_write_tuplet",
	93: "dict_write_uint16",
	94: "dict_write_uint32",
	95: "dict_write_uint8",
	96: "fonts_get_system_font",
	97: "fonts_load_custom_font",
	98: "fonts_unload_custom_font",
	99: "free",
	100: "gbitmap_create_as_sub_bitmap",
	101: "gbitmap_create_with_data",
	102: "gbitmap_create_with_resource",
	103: "gbitmap_destroy",
	104: "gmtime",
	105: "gpath_create",
	106: "gpath_destroy",
	107: "gpath_draw_filled_legacy",
	108: "gpath_draw_outline",
	109: "gpath_move_to",
	110: "gpath_rotate_to",
	111: "gpoint_equal",
	112: "graphics_context_set_compositing_mode",
	113: "graphics_context_set_fill_color_2bit",
	114: "graphics_context_set_stroke_color_2bit",
	115: "graphics_context_set_text_color_2bit",
	116: "graphics_draw_bitmap_in_rect",
	117: "graphics_draw_circle",
	118: "graphics_draw_line",
	119: "graphics_draw_pixel",
	120: "graphics_draw_rect",
	121: "graphics_draw_round_rect",
	122: "graphics_fill_circle",
	123: "graphics_fill_rect",
	125: "graphics_text_layout_get_max_used_size",
	126: "grect_align",
	127: "grect_center_point",
	128: "grect_clip",
	129: "grect_contains_point",
	130: "grect_crop",
	131: "grect_equal",
	132: "grect_is_empty",
	133: "grect_standardize",
	134: "gsize_equal",
	135: "inverter_layer_create",
	136: "inverter_layer_destroy",
	137: "inverter_layer_get_layer",
	138: "layer_add_child",
	139: "layer_create",
	140: "layer_create_with_data",
	141: "layer_destroy",
	142: "layer_get_bounds",
	143: "layer_get_clips",
	144: "layer_get_data",
	145: "layer_get_frame",
	146: "layer_get_hidden",
	147: "layer_get_window",
	148: "layer_insert_above_sibling",
	149: "layer_insert_below_sibling",
	150: "layer_mark_dirty",
	151: "layer_remove_child_layers",
	152: "layer_remove_from_parent",
	153: "layer_set_bounds",
	154: "layer_set_clips",
	155: "layer_set_frame",
	156: "layer_set_hidden",
	157: "layer_set_update_proc",
	158: "light_enable",
	159: "light_enable_interaction",
	160: "localtime__deprecated",
	161: "malloc",
	162: "memcpy",
	163: "memmove",
	164: "memset",
	165: "menu_cell_basic_draw",
	166: "menu_cell_basic_header_draw",
	167: "menu_cell_title_draw",
	168: "menu_index_compare",
	169: "menu_layer_legacy2_create",
	170: "menu_layer_destroy",
	171: "menu_layer_get_layer",
	172: "menu_layer_get_scroll_layer",
	173: "menu_layer_get_selected_index",
	174: "menu_layer_reload_data",
	175: "menu_layer_legacy2_set_callbacks__deprecated",
	176: "menu_layer_set_click_config_onto_window",
	177: "menu_layer_set_selected_index",
	178: "menu_layer_set_selected_next",
	179: "number_window_create",
	180: "number_window_destroy",
	181: "number_window_get_value",
	182: "number_window_set_label",
	183: "number_window_set_max",
	184: "number_window_set_min",
	185: "number_window_set_step_size",
	186: "number_window_set_value",
	187: "persist_delete",
	188: "persist_exists",
	189: "persist_get_size",
	190: "persist_read_bool",
	191: "persist_read_data__deprecated",
	192: "persist_read_int",
	193: "persist_read_string__deprecated",
	194: "persist_write_bool",
	195: "persist_write_data__deprecated",
	196: "persist_write_int",
	197: "persist_write_string",
	198: "property_animation_legacy2_create",
	199: "property_animation_legacy2_create_layer_frame",
	200: "property_animation_legacy2_destroy",
	201: "property_animation_legacy2_update_gpoint",
	202: "property_animation_legacy2_update_grect",
	203: "property_animation_legacy2_update_int16",
	204: "psleep",
	205: "rand",
	206: "resource_get_handle",
	207: "resource_load",
	208: "resource_load_byte_range",
	209: "resource_size",
	210: "rot_bitmap_layer_create",
	211: "rot_bitmap_layer_destroy",
	212: "rot_bitmap_layer_increment_angle",
	213: "rot_bitmap_layer_set_angle",
	214: "rot_bitmap_layer_set_corner_clip_color_2bit",
	215: "rot_bitmap_set_compositing_mode",
	216: "rot_bitmap_set_src_ic",
	217: "scroll_layer_add_child",
	218: "scroll_layer_create",
	219: "scroll_layer_destroy",
	220: "scroll_layer_get_content_offset",
	221: "scroll_layer_get_content_size",
	222: "scroll_layer_get_layer",
	223: "scroll_layer_get_shadow_hidden",
	224: "scroll_layer_scroll_down_click_handler",
	225: "scroll_layer_scroll_up_click_handler",
	226: "scroll_layer_set_callbacks",
	227: "scroll_layer_set_click_config_onto_window",
	228: "scroll_layer_set_content_offset",
	229: "scroll_layer_set_content_size",
	230: "scroll_layer_set_context",
	231: "scroll_layer_set_frame",
	232: "scroll_layer_set_shadow_hidden",
	233: "simple_menu_layer_create",
	234: "simple_menu_layer_destroy",
	235: "simple_menu_layer_get_layer",
	236: "simple_menu_layer_get_selected_index",
	237: "simple_menu_layer_set_selected_index",
	238: "sin_lookup",
	239: "snprintf",
	240: "srand",
	241: "strcat",
	242: "strcmp",
	243: "strcpy",
	244: "strftime",
	245: "strlen",
	246: "strncat",
	247: "strncmp",
	248: "strncpy",
	249: "text_layer_legacy2_create",
	250: "text_layer_legacy2_destroy",
	251: "text_layer_legacy2_get_content_size",
	252: "text_layer_legacy2_get_layer",
	253: "text_layer_legacy2_get_text",
	254: "text_layer_legacy2_set_background_color_2bit",
	255: "text_layer_legacy2_set_font",
	256: "text_layer_legacy2_set_overflow_mode",
	257: "text_layer_legacy2_set_size",
	258: "text_layer_legacy2_set_text",
	259: "text_layer_legacy2_set_text_alignment",
	260: "text_layer_legacy2_set_text_color_2bit",
	262: "tick_timer_service_subscribe",
	263: "tick_timer_service_unsubscribe",
	264: "time__deprecated",
	265: "time_ms_deprecated",
	266: "vibes_cancel",
	267: "vibes_double_pulse",
	268: "vibes_enqueue_custom_pattern",
	269: "vibes_long_pulse",
	270: "vibes_short_pulse",
	271: "window_create",
	272: "window_destroy",
	273: "window_get_click_config_provider",
	274: "window_get_fullscreen",
	275: "window_get_root_layer",
	276: "window_is_loaded",
	277: "window_set_background_color_2bit",
	278: "window_set_click_config_provider",
	279: "window_set_click_config_provider_with_context",
	280: "window_set_fullscreen",
	281: "window_set_status_bar_icon",
	282: "window_set_window_handlers",
	283: "window_stack_contains_window",
	284: "window_stack_get_top_window",
	285: "window_stack_pop",
	286: "window_stack_pop_all",
	287: "window_stack_push",
	288: "window_stack_remove",
	289: "app_focus_service_subscribe",
	290: "app_focus_service_unsubscribe",
	291: "window_get_user_data",
	292: "window_set_user_data",
	293: "app_message_get_context",
	294: "app_message_inbox_size_maximum",
	295: "app_message_outbox_begin",
	296: "app_message_outbox_send",
	297: "app_message_outbox_size_maximum",
	298: "app_message_register_inbox_dropped",
	299: "app_message_register_inbox_received",
	300: "app_message_register_outbox_failed",
	301: "app_message_register_outbox_sent",
	302: "app_message_set_context",
	303: "window_long_click_subscribe",
	304: "window_multi_click_subscribe",
	305: "window_raw_click_subscribe",
	306: "window_set_click_context",
	307: "window_single_click_subscribe",
	308: "window_single_repeating_click_subscribe",
	309: "graphics_draw_text",
	310: "dict_serialize_tuplets_to_buffer",
	311: "persist_read_data",
	312: "persist_read_string",
	313: "persist_write_data",
	314: "dict_size",
	315: "graphics_text_layout_get_content_size",
	316: "simple_menu_layer_get_menu_layer",
	317: "accel_data_service_subscribe",
	318: "calloc",
	319: "bitmap_layer_get_bitmap",
	320: "menu_layer_legacy2_set_callbacks",
	321: "window_get_click_config_context",
	322: "number_window_get_window",
	323: "realloc",
	324: "gbitmap_create_blank_2bit",
	325: "click_recognizer_is_repeating",
	326: "accel_raw_data_service_subscribe",
	327: "app_worker_is_running",
	328: "app_worker_kill",
	329: "app_worker_launch",
	330: "app_worker_message_subscribe",
	331: "app_worker_message_unsubscribe",
	332: "app_worker_send_message",
	333: "worker_event_loop",
	334: "worker_launch_app",
	335: "heap_bytes_free",
	336: "heap_bytes_used",
	337: "compass_service_peek",
	338: "compass_service_set_heading_filter",
	339: "compass_service_subscribe",
	340: "compass_service_unsubscribe",
	341: "uuid_equal",
	342: "uuid_to_string",
	343: "gpath_draw_filled",
	344: "animation_legacy2_set_custom_curve",
	345: "watch_info_get_color",
	346: "watch_info_get_firmware_version",
	347: "watch_info_get_model",
	348: "graphics_capture_frame_buffer_2bit",
	349: "graphics_frame_buffer_is_captured",
	350: "graphics_release_frame_buffer",
	351: "clock_to_timestamp",
	352: "launch_reason",
	353: "wakeup_cancel",
	354: "wakeup_cancel_all",
	355: "wakeup_get_launch_event",
	356: "wakeup_query",
	357: "wakeup_schedule",
	358: "wakeup_service_subscribe",
	359: "clock_is_timezone_set",
	360: "i18n_get_system_locale",
	361: "_localeconv_r",
	362: "setlocale",
	363: "mktime",
	364: "gcolor_equal",
	365: "__profiler_init",
	366: "__profiler_print_stats",
	367: "__profiler_start",
	368: "__profiler_stop",
	370: "bitmap_layer_set_background_color",
	371: "graphics_context_set_fill_color",
	372: "graphics_context_set_stroke_color",
	373: "graphics_context_set_text_color",
	374: "rot_bitmap_layer_set_corner_clip_color",
	377: "window_set_background_color",
	378: "clock_get_timezone",
	379: "localtime",
	380: "animation_create",
	381: "animation_destroy",
	382: "animation_get_context",
	383: "animation_is_scheduled",
	384: "animation_schedule",
	385: "animation_set_curve",
	386: "animation_set_custom_curve",
	387: "animation_set_delay",
	388: "animation_set_duration",
	389: "animation_set_handlers",
	390: "animation_set_implementation",
	391: "animation_unschedule",
	392: "animation_unschedule_all",
	393: "gbitmap_create_blank",
	394: "graphics_capture_frame_buffer",
	395: "graphics_capture_frame_buffer_format",
	396: "property_animation_create",
	397: "property_animation_create_layer_frame",
	398: "property_animation_destroy",
	399: "property_animation_from",
	400: "property_animation_get_animation",
	401: "property_animation_subject",
	402: "property_animation_to",
	403: "property_animation_update_gpoint",
	404: "property_animation_update_grect",
	405: "property_animation_update_int16",
	406: "gbitmap_create_blank_with_palette",
	407: "gbitmap_get_bounds",
	408: "gbitmap_get_bytes_per_row",
	409: "gbitmap_get_data",
	410: "gbitmap_get_format",
	411: "gbitmap_get_palette",
	412: "gbitmap_set_bounds",
	413: "gbitmap_set_data",
	414: "gbitmap_set_palette",
	415: "gbitmap_sequence_create_with_resource",
	416: "gbitmap_sequence_destroy",
	417: "gbitmap_sequence_get_bitmap_size",
	418: "gbitmap_sequence_get_current_frame_idx",
	419: "gbitmap_sequence_get_total_num_frames",
	420: "gbitmap_sequence_update_bitmap_next_frame",
	421: "gbitmap_create_from_png_data",
	422: "animation_clone",
	423: "animation_get_delay",
	424: "animation_get_duration",
	425: "animation_get_play_count",
	426: "animation_get_elapsed",
	427: "animation_get_reverse",
	428: "animation_sequence_create",
	429: "animation_sequence_create_from_array",
	430: "animation_set_play_count",
	431: "animation_set_elapsed",
	432: "animation_set_reverse",
	433: "animation_spawn_create",
	434: "animation_spawn_create_from_array",
	435: "animation_get_curve",
	436: "animation_get_custom_curve",
	437: "animation_get_implementation",
	438: "launch_get_args",
	439: "menu_layer_create",
	441: "gbitmap_sequence_get_play_count",
	442: "gbitmap_sequence_restart",
	443: "gbitmap_sequence_set_play_count",
	444: "graphics_context_set_antialiased",
	445: "graphics_context_set_stroke_width",
	446: "action_bar_layer_add_to_window",
	447: "action_bar_layer_clear_icon",
	448: "action_bar_layer_create",
	449: "action_bar_layer_destroy",
	450: "action_bar_layer_get_layer",
	451: "action_bar_layer_remove_from_window",
	452: "action_bar_layer_set_background_color",
	453: "action_bar_layer_set_click_config_provider",
	454: "action_bar_layer_set_context",
	455: "action_bar_layer_set_icon",
	456: "action_bar_layer_set_icon_animated",
	457: "gbitmap_sequence_update_bitmap_by_elapsed",
	458: "gbitmap_create_palettized_from_1bit",
	459: "menu_cell_layer_is_highlighted",
	460: "graphics_draw_rotated_bitmap",
	461: "action_bar_layer_set_icon_press_animation",
	462: "text_layer_create",
	463: "text_layer_destroy",
	464: "text_layer_get_content_size",
	465: "text_layer_get_layer",
	466: "text_layer_get_text",
	467: "text_layer_set_background_color",
	468: "text_layer_set_font",
	469: "text_layer_set_overflow_mode",
	470: "text_layer_set_size",
	471: "text_layer_set_text",
	472: "text_layer_set_text_alignment",
	473: "text_layer_set_text_color",
	474: "gdraw_command_draw",
	475: "gdraw_command_frame_draw",
	476: "gdraw_command_frame_get_duration",
	477: "gdraw_command_frame_set_duration",
	478: "gdraw_command_get_fill_color",
	479: "gdraw_command_get_hidden",
	480: "gdraw_command_get_num_points",
	481: "gdraw_command_get_path_open",
	482: "gdraw_command_get_point",
	483: "gdraw_command_get_radius",
	484: "gdraw_command_get_stroke_color",
	485: "gdraw_command_get_stroke_width",
	486: "gdraw_command_get_type",
	487: "gdraw_command_image_clone",
	488: "gdraw_command_image_create_with_resource",
	489: "gdraw_command_image_destroy",
	490: "gdraw_command_image_draw",
	491: "gdraw_command_image_get_bounds_size",
	492: "gdraw_command_image_get_command_list",
	493: "gdraw_command_image_set_bounds_size",
	494: "gdraw_command_list_draw",
	495: "gdraw_command_list_get_command",
	496: "gdraw_command_list_get_num_commands",
	497: "gdraw_command_list_iterate",
	498: "gdraw_command_sequence_clone",
	499: "gdraw_command_sequence_create_with_resource",
	500: "gdraw_command_sequence_destroy",
	501: "gdraw_command_sequence_get_bounds_size",
	502: "gdraw_command_sequence_get_frame_by_elapsed",
	503: "gdraw_command_sequence_get_frame_by_index",
	504: "gdraw_command_sequence_get_num_frames",
	505: "gdraw_command_sequence_get_play_count",
	506: "gdraw_command_sequence_get_total_duration",
	507: "gdraw_command_sequence_set_bounds_size",
	508: "gdraw_command_sequence_set_play_count",
	509: "gdraw_command_set_fill_color",
	510: "gdraw_command_set_hidden",
	511: "gdraw_command_set_path_open",
	512: "gdraw_command_set_point",
	513: "gdraw_command_set_radius",
	514: "gdraw_command_set_stroke_color",
	515: "gdraw_command_set_stroke_width",
	516: "property_animation_create_bounds_origin",
	517: "property_animation_update_uint32",
	518: "gpath_draw_outline_open",
	519: "time",
	520: "menu_layer_set_highlight_colors",
	521: "menu_layer_set_normal_colors",
	522: "menu_layer_set_callbacks",
	523: "menu_layer_pad_bottom_enable",
	524: "status_bar_layer_create",
	525: "status_bar_layer_destroy",
	526: "status_bar_layer_get_background_color",
	527: "status_bar_layer_get_foreground_color",
	528: "status_bar_layer_get_layer",
	529: "status_bar_layer_set_colors",
	530: "status_bar_layer_set_separator_mode",
	531: "difftime",
	532: "time_ms",
	533: "gcolor_legible_over",
	534: "property_animation_update_gcolor8",
	535: "app_focus_service_subscribe_handlers",
	536: "action_menu_close",
	537: "action_menu_freeze",
	538: "action_menu_get_context",
	539: "action_menu_get_root_level",
	540: "action_menu_hierarchy_destroy",
	541: "action_menu_item_get_action_data",
	542: "action_menu_item_get_label",
	543: "action_menu_level_add_action",
	544: "action_menu_level_add_child",
	545: "action_menu_level_create",
	546: "action_menu_level_set_display_mode",
	547: "action_menu_open",
	548: "action_menu_set_result_window",
	549: "action_menu_unfreeze",
	550: "dictation_session_create",
	551: "dictation_session_destroy",
	552: "dictation_session_enable_confirmation",
	553: "dictation_session_start",
	554: "dictation_session_stop",
	555: "smartstrap_attribute_begin_write",
	556: "smartstrap_attribute_create",
	557: "smartstrap_attribute_destroy",
	558: "smartstrap_attribute_end_write",
	559: "smartstrap_attribute_get_attribute_id",
	560: "smartstrap_attribute_get_service_id",
	561: "smartstrap_attribute_read",
	562: "smartstrap_service_is_available",
	563: "smartstrap_set_timeout",
	564: "smartstrap_subscribe",
	565: "smartstrap_unsubscribe",
	566: "connection_service_peek_pebble_app_connection",
	567: "connection_service_peek_pebblekit_connection",
	568: "connection_service_subscribe",
	569: "connection_service_unsubscribe",
	570: "dictation_session_enable_error_dialogs",
	571: "gbitmap_get_data_row_info",
	572: "content_indicator_configure_direction",
	573: "content_indicator_create",
	574: "content_indicator_destroy",
	575: "content_indicator_get_content_available",
	576: "content_indicator_set_content_available",
	577: "scroll_layer_get_content_indicator",
	578: "menu_layer_get_center_focused",
	579: "menu_layer_set_center_focused",
	580: "grect_inset",
	581: "gpoint_from_polar",
	582: "graphics_draw_arc",
	583: "graphics_fill_radial",
	584: "grect_centered_from_polar",
	585: "graphics_text_attributes_create",
	586: "graphics_text_attributes_destroy",
	587: "graphics_text_attributes_enable_paging",
	588: "graphics_text_attributes_enable_screen_text_flow",
	589: "graphics_text_attributes_restore_default_paging",
	590: "graphics_text_attributes_restore_default_text_flow",
	591: "graphics_text_layout_get_content_size_with_attributes",
	592: "layer_convert_point_to_screen",
	593: "layer_convert_rect_to_screen",
	594: "scroll_layer_get_paging",
	595: "scroll_layer_set_paging",
	596: "text_layer_enable_screen_text_flow_and_paging",
	597: "text_layer_restore_default_text_flow_and_paging",
	598: "menu_layer_is_index_selected",
	599: "health_service_activities_iterate",
	600: "health_service_any_activity_accessible",
	601: "health_service_events_subscribe",
	602: "health_service_events_unsubscribe",
	603: "health_service_get_minute_history",
	604: "health_service_metric_accessible",
	605: "health_service_peek_current_activities",
	606: "health_service_sum",
	607: "health_service_sum_today",
	608: "time_start_of_today",
	609: "health_service_metric_averaged_accessible",
	610: "health_service_sum_averaged",
	611: "health_service_get_measurement_system_for_display",
	612: "gdraw_command_frame_get_command_list",
};

function hex(addr) {
	return "0x" + (addr >>> 0).toString(16);
}

function cleanName(name) {
	return name.replace(/[^A-Za-z0-9_]/g, "_");
}

function u32leFromHex(hexstr, byteOffset) {
	const p = byteOffset * 2;
	return parseInt(hexstr.slice(p + 6, p + 8) + hexstr.slice(p + 4, p + 6) + hexstr.slice(p + 2, p + 4) + hexstr.slice(p, p + 2), 16) >>> 0;
}

function searchJson(pattern) {
	const out = r2.cmd("/xj " + pattern).trim();
	if (!out) {
		return [];
	}
	try {
		return JSON.parse(out);
	} catch (e) {
		console.log("Cannot parse /xj output for " + pattern + ": " + e);
		return [];
	}
}

function nameTrampoline() {
	const hits = searchJson(TRAMPOLINE_PATTERN);
	for (const hit of hits) {
		const addr = hit.addr >>> 0;
		r2.cmd("f sym.pebble.jump_to_pbl_function 16 @ " + hex(addr));
		r2.cmd("f sym.pebble.pbl_table_addr 4 @ " + hex(addr + 16));
		r2.cmd("CCu Pebble firmware API trampoline, pbl_table_addr is patched by loader @ " + hex(addr));
	}
	return hits.length;
}

function nameStubs() {
	r2.cmd("e search.align=4");
	const hits = searchJson(STUB_PATTERN);
	let named = 0;
	let unknown = 0;
	for (const hit of hits) {
		const addr = hit.addr >>> 0;
		const data = (hit.data || "").replace(/[^0-9a-fA-F]/g, "").toLowerCase();
		if (data.length < 24) {
			continue;
		}
		const byteOffset = u32leFromHex(data, 8);
		if ((byteOffset & 3) !== 0) {
			continue;
		}
		const index = byteOffset >>> 2;
		const apiName = PEBBLE_API_BY_INDEX[index] || ("unknown_" + index);
		const flagName = "sym.pebble." + cleanName(apiName);
		r2.cmd("f " + flagName + " 12 @ " + hex(addr));
		r2.cmd("af @ " + hex(addr));
		r2.cmd("afn " + flagName + " @ " + hex(addr));
		r2.cmd("CCu Pebble API stub " + apiName + " index=" + index + " offset=0x" + byteOffset.toString(16) + " @ " + hex(addr));
		if (PEBBLE_API_BY_INDEX[index]) {
			named++;
		} else {
			unknown++;
		}
	}
	console.log("Pebble stubs: named=" + named + " unknown=" + unknown + " total=" + hits.length);
	return named;
}

r2.cmd("e search.align=4");
const trampolines = nameTrampoline();
const stubs = nameStubs();
console.log("Pebble trampoline hits: " + trampolines);
console.log("Pebble stub resolver complete: " + stubs + " named stubs");
