# Bazaar merge directive format 2 (Bazaar 0.90) # revision_id: colin@gibibit.com-20080711163832-cutvrahspirds0fw # target_branch: http://grub.gibibit.com/bzr/trunk-clean # testament_sha1: 25c6cfc2b182753473e3e2647dd4a223f17a28ec # timestamp: 2008-07-11 10:10:07 -0700 # source_branch: http://grub.gibibit.com/bzr/trunk-clean # base_revision_id: colin@gibibit.com-20080711163747-7v43uh66u2lp4mb7 # # Begin patch === modified file 'commands/videotest.c' --- commands/videotest.c 2008-07-09 16:14:04 +0000 +++ commands/videotest.c 2008-07-09 16:52:26 +0000 @@ -49,19 +49,6 @@ int double_buffering; }; -/* A 2D rectangle type. - * This could be worth integrating into the video API if it proves useful.*/ -struct grub_video_rect -{ - /* These are signed because if there are unsigned it causes Bad Things - * to happen when arithmetic and comparisions involving signed types is - * done. Important signed types include offsets from absolute locations. */ - int x; - int y; - int width; - int height; -}; -typedef struct grub_video_rect grub_video_rect_t; static void basic_video_test (struct videotest_options *vt_opts) === modified file 'conf/common.rmk' --- conf/common.rmk 2008-07-03 14:12:08 +0000 +++ conf/common.rmk 2008-07-07 15:07:13 +0000 @@ -287,6 +287,7 @@ cmp.mod cat.mod help.mod font.mod search.mod \ loopback.mod fs_uuid.mod configfile.mod echo.mod \ terminfo.mod test.mod blocklist.mod hexdump.mod \ + protomenu.mod \ read.mod sleep.mod loadenv.mod # For hello.mod. @@ -374,6 +375,11 @@ hexdump_mod_CFLAGS = $(COMMON_CFLAGS) hexdump_mod_LDFLAGS = $(COMMON_LDFLAGS) +# For protomenu.mod. +protomenu_mod_SOURCES = protomenu/protomenu.c protomenu/widget-box.c +protomenu_mod_CFLAGS = $(COMMON_CFLAGS) +protomenu_mod_LDFLAGS = $(COMMON_LDFLAGS) + # For read.mod. read_mod_SOURCES = commands/read.c read_mod_CFLAGS = $(COMMON_CFLAGS) === modified file 'conf/i386-pc.rmk' --- conf/i386-pc.rmk 2008-07-03 14:27:43 +0000 +++ conf/i386-pc.rmk 2008-07-10 13:55:34 +0000 @@ -46,11 +46,13 @@ kern/i386/tsc.c \ kern/generic/millisleep.c \ kern/env.c \ + kern/menu_viewer.c \ term/i386/pc/console.c \ symlist.c kernel_img_HEADERS = arg.h boot.h cache.h device.h disk.h dl.h elf.h elfload.h \ env.h err.h file.h fs.h kernel.h loader.h misc.h mm.h net.h parser.h \ partition.h pc_partition.h rescue.h symbol.h term.h time.h types.h \ + menu_viewer.h \ machine/biosdisk.h machine/boot.h machine/console.h machine/init.h \ machine/memory.h machine/loader.h machine/vga.h machine/vbe.h machine/kernel.h kernel_img_CFLAGS = $(COMMON_CFLAGS) === added file 'include/grub/menu.h' --- include/grub/menu.h 1970-01-01 00:00:00 +0000 +++ include/grub/menu.h 2008-07-10 13:55:34 +0000 @@ -0,0 +1,51 @@ +/* menu.h - menu and menu entry model declarations. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2002,2003,2005,2006,2007,2008 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#ifndef GRUB_MENU_HEADER +#define GRUB_MENU_HEADER 1 + +/* The menu entry. */ +struct grub_menu_entry +{ + /* The title name. */ + const char *title; + + /* The commands associated with this menu entry. */ + struct grub_script *commands; + + /* The sourcecode of the menu entry, used by the editor. */ + const char *sourcecode; + + /* The next element. */ + struct grub_menu_entry *next; +}; +typedef struct grub_menu_entry *grub_menu_entry_t; + +/* The menu. */ +struct grub_menu +{ + /* The size of a menu. */ + int size; + + /* The list of menu entries. */ + grub_menu_entry_t entry_list; +}; +typedef struct grub_menu *grub_menu_t; + +#endif /* GRUB_MENU_HEADER */ === added file 'include/grub/menu_viewer.h' --- include/grub/menu_viewer.h 1970-01-01 00:00:00 +0000 +++ include/grub/menu_viewer.h 2008-07-11 14:56:59 +0000 @@ -0,0 +1,48 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2002,2003,2005,2007,2008 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#ifndef GRUB_MENU_VIEWER_HEADER +#define GRUB_MENU_VIEWER_HEADER 1 + +#include +#include +#include +#include + +struct grub_menu_viewer +{ + /* The menu viewer name. */ + const char *name; + + grub_err_t (*show_menu) (grub_menu_t menu, int nested); + + struct grub_menu_viewer *next; +}; +typedef struct grub_menu_viewer *grub_menu_viewer_t; + +void EXPORT_FUNC(grub_menu_viewer_init) (void); + +void EXPORT_FUNC(grub_menu_viewer_register) (grub_menu_viewer_t viewer); + +grub_err_t EXPORT_FUNC(grub_menu_viewer_show_menu) (grub_menu_t menu, int nested); + +/* Return nonzero iff the menu viewer should clean up and return ASAP. */ +int EXPORT_FUNC(grub_menu_viewer_should_return) (void); + +#endif /* GRUB_MENU_VIEWER_HEADER */ + === modified file 'include/grub/normal.h' --- include/grub/normal.h 2008-03-26 12:01:02 +0000 +++ include/grub/normal.h 2008-07-11 16:03:24 +0000 @@ -25,6 +25,7 @@ #include #include #include +#include /* The maximum size of a command-line. */ #define GRUB_MAX_CMDLINE 1600 @@ -84,34 +85,6 @@ }; typedef struct grub_command *grub_command_t; -/* The menu entry. */ -struct grub_menu_entry -{ - /* The title name. */ - const char *title; - - /* The commands associated with this menu entry. */ - struct grub_script *commands; - - /* The sourcecode of the menu entry, used by the editor. */ - const char *sourcecode; - - /* The next element. */ - struct grub_menu_entry *next; -}; -typedef struct grub_menu_entry *grub_menu_entry_t; - -/* The menu. */ -struct grub_menu -{ - /* The size of a menu. */ - int size; - - /* The list of menu entries. */ - grub_menu_entry_t entry_list; -}; -typedef struct grub_menu *grub_menu_t; - /* This is used to store the names of filesystem modules for auto-loading. */ struct grub_fs_module_list { @@ -123,10 +96,12 @@ /* To exit from the normal mode. */ extern grub_jmp_buf grub_exit_env; +extern struct grub_menu_viewer grub_normal_terminal_menu_viewer; + void grub_enter_normal_mode (const char *config); void grub_normal_execute (const char *config, int nested); -void grub_menu_run (grub_menu_t menu, int nested); void grub_menu_entry_run (grub_menu_entry_t entry); +void grub_menu_execute_entry(grub_menu_entry_t entry); void grub_cmdline_run (int nested); int grub_cmdline_get (const char *prompt, char cmdline[], unsigned max_len, int echo_char, int readline); === added file 'include/grub/proto_widgets.h' --- include/grub/proto_widgets.h 1970-01-01 00:00:00 +0000 +++ include/grub/proto_widgets.h 2008-07-08 13:59:48 +0000 @@ -0,0 +1,48 @@ +/* proto_widgets.h - Widgets for the graphical menu prototype. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2006,2007,2008 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#ifndef GRUB_PROTO_WIDGETS_HEADER +#define GRUB_PROTO_WIDGETS_HEADER 1 + +#include + +typedef struct grub_protomenu_widgets_box +{ + /* The size of the content. */ + int width; + int height; + + struct grub_video_bitmap **raw_pixmaps; + struct grub_video_bitmap **scaled_pixmaps; + + void (*draw) (struct grub_protomenu_widgets_box *self, int x, int y); + void (*set_content_size) (struct grub_protomenu_widgets_box *self, + int width, int height); + int (*get_left_pad) (struct grub_protomenu_widgets_box *self); + int (*get_top_pad) (struct grub_protomenu_widgets_box *self); + void (*destroy) (struct grub_protomenu_widgets_box *self); +} +*grub_protomenu_widgets_box_t; + +grub_protomenu_widgets_box_t +grub_protomenu_widgets_create_box (const char *pixmaps_prefix, + const char *pixmaps_suffix); + +#endif /* ! GRUB_PROTO_WIDGETS_HEADER */ + === modified file 'include/grub/video.h' --- include/grub/video.h 2008-07-03 14:12:08 +0000 +++ include/grub/video.h 2008-07-07 15:07:13 +0000 @@ -151,6 +151,16 @@ grub_uint8_t a; /* Reserved bits value (0-255). */ }; +/* A 2D rectangle type. */ +struct grub_video_rect +{ + int x; + int y; + int width; + int height; +}; +typedef struct grub_video_rect grub_video_rect_t; + struct grub_video_adapter { /* The video adapter name. */ === added file 'kern/menu_viewer.c' --- kern/menu_viewer.c 1970-01-01 00:00:00 +0000 +++ kern/menu_viewer.c 2008-07-11 14:56:59 +0000 @@ -0,0 +1,99 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2002,2003,2005,2007 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include + +/* The list of menu viewers. */ +static grub_menu_viewer_t menu_viewer_list; + +static int should_return; +static int menu_viewer_changed; + +void +grub_menu_viewer_register (grub_menu_viewer_t viewer) +{ + viewer->next = menu_viewer_list; + menu_viewer_list = viewer; +} + +static grub_menu_viewer_t get_current_menu_viewer (void) +{ + const char *selected_name = grub_env_get ("menuviewer"); + + /* If none selected, pick the last registered one. */ + if (selected_name == 0) + return menu_viewer_list; + + grub_menu_viewer_t cur; + for (cur = menu_viewer_list; cur; cur = cur->next) + { + if (grub_strcmp (cur->name, selected_name) == 0) + return cur; + } + + /* Fall back to the first entry (or null). */ + return menu_viewer_list; +} + +grub_err_t +grub_menu_viewer_show_menu (grub_menu_t menu, int nested) +{ + grub_err_t err; + int repeat = 0; + do + { + repeat = 0; + menu_viewer_changed = 0; + grub_menu_viewer_t cur = get_current_menu_viewer (); + if (!cur) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "No menu viewer available."); + + should_return = 0; + err = cur->show_menu (menu, nested); + if (menu_viewer_changed) + repeat = 1; + } + while (repeat); + return err; +} + +int +grub_menu_viewer_should_return (void) +{ + return should_return; +} + +static char * +menuviewer_write_hook (struct grub_env_var *var __attribute__ ((unused)), + const char *val) +{ + menu_viewer_changed = 1; + should_return = 1; + return grub_strdup (val); +} + +void +grub_menu_viewer_init (void) +{ + grub_register_variable_hook ("menuviewer", 0, menuviewer_write_hook); +} + === modified file 'normal/main.c' --- normal/main.c 2008-02-02 16:48:52 +0000 +++ normal/main.c 2008-07-11 14:56:59 +0000 @@ -28,6 +28,7 @@ #include #include #include +#include grub_jmp_buf grub_exit_env; @@ -476,7 +477,7 @@ if (menu && menu->size) { - grub_menu_run (menu, nested); + grub_menu_viewer_show_menu (menu, nested); if (nested) free_menu (menu); } @@ -519,6 +520,8 @@ if (mod) grub_dl_ref (mod); + grub_menu_viewer_register (&grub_normal_terminal_menu_viewer); + grub_set_history (GRUB_DEFAULT_HISTORY_SIZE); /* Register a command "normal" for the rescue mode. */ @@ -535,6 +538,8 @@ /* This registers some built-in commands. */ grub_command_init (); + + grub_menu_viewer_init (); } GRUB_MOD_FINI(normal) === modified file 'normal/menu.c' --- normal/menu.c 2008-02-09 11:00:19 +0000 +++ normal/menu.c 2008-07-11 16:03:24 +0000 @@ -24,6 +24,7 @@ #include #include #include +#include static grub_uint8_t grub_color_menu_normal; static grub_uint8_t grub_color_menu_highlight; @@ -364,7 +365,7 @@ if (timeout > 0) print_timeout (timeout, offset, 0); - while (1) + while (!grub_menu_viewer_should_return ()) { int c; timeout = get_timeout (); @@ -468,6 +469,10 @@ } goto refresh; + case 't': + grub_env_set ("menuviewer", "protomenu"); + goto refresh; + default: break; } @@ -476,13 +481,14 @@ } } - /* Never reach here. */ + /* Exit menu without activating an item. This occurs if the user presses + * 't', switching to the graphical menu viewer. */ return -1; } /* Run a menu entry. */ -static void -run_menu_entry (grub_menu_entry_t entry) +void +grub_menu_execute_entry(grub_menu_entry_t entry) { grub_script_execute (entry->commands); @@ -491,8 +497,8 @@ grub_command_execute ("boot", 0); } -void -grub_menu_run (grub_menu_t menu, int nested) +static grub_err_t +show_menu (grub_menu_t menu, int nested) { while (1) { @@ -513,7 +519,7 @@ grub_printf (" Booting \'%s\'\n\n", e->title); - run_menu_entry (e); + grub_menu_execute_entry (e); /* Deal with a fallback entry. */ /* FIXME: Multiple fallback entries like GRUB Legacy. */ @@ -526,7 +532,7 @@ e = get_entry (menu, fallback_entry); grub_env_unset ("fallback"); grub_printf ("\n Falling back to \'%s\'\n\n", e->title); - run_menu_entry (e); + grub_menu_execute_entry (e); } if (grub_errno != GRUB_ERR_NONE) @@ -537,4 +543,14 @@ grub_wait_after_message (); } } + + return GRUB_ERR_NONE; } + +struct grub_menu_viewer grub_normal_terminal_menu_viewer = +{ + .name = "terminal", + .show_menu = show_menu +}; + + === added directory 'protomenu' === added file 'protomenu/protomenu.c' --- protomenu/protomenu.c 1970-01-01 00:00:00 +0000 +++ protomenu/protomenu.c 2008-07-11 16:03:24 +0000 @@ -0,0 +1,417 @@ +/* protomenu.c - Prototype graphical menu: command implementation. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef struct menu_item +{ + const char *name; + struct grub_video_bitmap *icon; + grub_menu_entry_t entry; +} +menu_item_t; + +static const int icon_width = 48; +static const int icon_height = 48; +static grub_video_rect_t screen; +static grub_font_t title_font; +static grub_font_t item_font; +static grub_font_t status_font; +static grub_video_color_t title_color; +static grub_video_color_t item_color; +static grub_video_color_t status_color; +static grub_video_color_t status_bg_color; +static struct grub_video_bitmap *background_image; +static const char title_text[] = "GRUB 2 Boot Menu"; +static int num_items; +static menu_item_t *items; +static int selected_item_index; +static grub_protomenu_widgets_box_t menu_box; +static grub_protomenu_widgets_box_t selected_item_box; + +static grub_err_t +menu_init (grub_menu_t menu) +{ + num_items = menu->size; + if (num_items < 1) + return grub_error (GRUB_ERR_MENU, "Empty menu"); + + items = grub_malloc (num_items * sizeof (*items)); + if (!items) + return grub_errno; + + int i; + grub_menu_entry_t cur; + for (i = 0, cur = menu->entry_list; i < num_items; i++, cur = cur->next) + { + items[i].name = cur->title; + items[i].icon = 0; /* TODO match w/ patterns to choose an icon */ + items[i].entry = cur; + } + + /* + items[0].name = "Ubuntu 8.10"; + grub_video_bitmap_load (&img, "/boot/images/icon-ubuntu.tga"); + if (grub_errno == GRUB_ERR_NONE) + { + grub_video_bitmap_create_scaled (&items[0].icon, icon_width, + icon_height, img, + GRUB_VIDEO_BITMAP_SCALE_METHOD_BEST); + if (grub_errno != GRUB_ERR_NONE) + return; + grub_video_bitmap_destroy (img); + } + */ + + return GRUB_ERR_NONE; +} + +static void +menu_destroy (void) +{ + if (items) + { + int i; + for (i = 0; i < num_items; i++) + { + if (items[i].icon) + { + grub_video_bitmap_destroy (items[i].icon); + items[i].icon = 0; + } + } + grub_free (items); + items = 0; + } +} + +static grub_err_t +style_init (void) +{ + if (!(title_font = grub_font_get ("Helvetica Bold 24")) + || !(item_font = grub_font_get ("Helvetica Bold 14")) + || !(status_font = grub_font_get ("Helvetica 10"))) + return grub_errno; + + title_color = grub_video_map_rgb (0, 0, 0); + item_color = grub_video_map_rgb (0, 0, 0); + status_color = grub_video_map_rgb (255, 255, 255); + status_bg_color = grub_video_map_rgba (0, 0, 0, 112); + + struct grub_video_bitmap *img; + grub_video_bitmap_load (&img, "/boot/images/bg-protomenu1.tga"); + if (grub_errno != GRUB_ERR_NONE) + return grub_errno; + + grub_video_bitmap_create_scaled (&background_image, screen.width, + screen.height, img, + GRUB_VIDEO_BITMAP_SCALE_METHOD_BEST); + if (grub_errno != GRUB_ERR_NONE) + return grub_errno; + grub_video_bitmap_destroy (img); + + menu_box = grub_protomenu_widgets_create_box ("/boot/images/menubox_", ".tga"); + if (menu_box == 0) + return grub_error (GRUB_ERR_MENU, "Unable to create menu box"); + + selected_item_box = grub_protomenu_widgets_create_box ("/boot/images/select_blue_", ".tga"); + if (selected_item_box == 0) + return grub_error (GRUB_ERR_MENU, "Unable to create selection box"); + + return GRUB_ERR_NONE; +} + +static void +style_destroy (void) +{ + if (menu_box) + { + menu_box->destroy(menu_box); + menu_box = 0; + } + if (selected_item_box) + { + selected_item_box->destroy(selected_item_box); + selected_item_box = 0; + } +} + +static void +draw_background (void) +{ + grub_video_blit_bitmap (background_image, GRUB_VIDEO_BLIT_REPLACE, + 0, 0, 0, 0, + grub_video_bitmap_get_width (background_image), + grub_video_bitmap_get_height (background_image)); +} + +static void +draw_menu (void) +{ + int boxpad = 14; + int icon_text_space = 4; + int item_vspace = 16; + + int ascent = grub_font_get_ascent (item_font); + int descent = grub_font_get_descent (item_font); + int item_height = 52; + + grub_video_rect_t r; + r.width = screen.width * 2 / 3; + /* Set the menu box height to fit the items. */ + r.height = (item_height * num_items + + item_vspace * (num_items - 1) + + 2 * boxpad); + r.x = (screen.width - r.width) / 2; + r.y = (screen.height - r.height) / 2; + menu_box->set_content_size (menu_box, r.width, r.height); + + int menu_box_left_pad = menu_box->get_left_pad (menu_box); + int menu_box_top_pad = menu_box->get_top_pad (menu_box); + menu_box->draw (menu_box, r.x - menu_box_left_pad, r.y - menu_box_top_pad); + + int item_top = r.y + boxpad; + int item_left = r.x + boxpad; + int i; + + for (i = 0; i < num_items; i++) + { + if (i == selected_item_index) + { + selected_item_box->set_content_size (selected_item_box, + r.width - 2 * boxpad, + item_height); + int leftpad = selected_item_box->get_left_pad (selected_item_box); + int toppad = selected_item_box->get_top_pad (selected_item_box); + selected_item_box->draw (selected_item_box, + item_left - leftpad, + item_top - toppad); + } + + if (items[i].icon) + grub_video_blit_bitmap (items[i].icon, GRUB_VIDEO_BLIT_BLEND, + item_left, + item_top + (item_height - icon_height) / 2, + 0, 0, icon_width, icon_height); + + grub_video_draw_string (items[i].name, item_font, item_color, + item_left + icon_width + icon_text_space, + (item_top + (item_height - (ascent + descent)) + / 2 + ascent)); + + item_top += item_height + item_vspace; + } +} + +static void +draw_title (void) +{ + /* Center the title. */ + int title_width = grub_font_get_string_width (title_font, title_text); + int x = (screen.width - title_width) / 2; + int y = 40 + grub_font_get_ascent (title_font); + grub_video_draw_string (title_text, title_font, title_color, x, y); +} + +static void +draw_status (void) +{ + int descent = grub_font_get_descent (status_font); + int ascent = grub_font_get_ascent (status_font); + int vpad = 5; + int textheight = descent + ascent + 1; + int h = 2 * vpad + 2 * textheight; + + grub_video_fill_rect (status_bg_color, 0, screen.height - h, + screen.width, screen.height - 1); + + int texty = screen.height - h + vpad + ascent; + grub_video_draw_string ("Select an item with the arrow keys and " + "press Enter to boot.", + status_font, status_color, 30, texty); + texty += textheight; + grub_video_draw_string ("Press: 'c' for command line; 't' to switch to " + "non-graphical menu.", + status_font, status_color, 30, texty); + +} + +static grub_err_t +set_graphics_mode (void) +{ + const char *nodoublebuf_str = grub_env_get ("protomenu_nodoublebuf"); + int doublebuf_flags = + (nodoublebuf_str && nodoublebuf_str[0] == 'y') + ? 0 + : GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED; + + if (grub_video_setup (1024, 768, GRUB_VIDEO_MODE_TYPE_RGB | doublebuf_flags) + != GRUB_ERR_NONE) + return grub_errno; + + return GRUB_ERR_NONE; +} + +static grub_err_t +set_text_mode (void) +{ + return grub_video_restore (); +} + +/* TODO set the 'terminal' type to console for now, here. + (until gfxterm use is integrated). */ +static grub_err_t +show_menu (grub_menu_t menu, int nested __attribute__ ((unused))) +{ + set_graphics_mode (); + + grub_error_push (); + grub_video_get_viewport ((unsigned *) &screen.x, + (unsigned *) &screen.y, + (unsigned *) &screen.width, + (unsigned *) &screen.height); + + /* Clear the screen; there may be garbage left over in video memory, and + loading the menu style (particularly the background) can take a while. */ + grub_video_fill_rect (grub_video_map_rgb (100, 100, 100), + screen.x, screen.y, screen.width, screen.height); + grub_video_swap_buffers (); + + if (menu_init (menu) != GRUB_ERR_NONE) + goto menufail; + + if (style_init () != GRUB_ERR_NONE) + goto stylefail; + + selected_item_index = 0; + int done = 0; + while (!done && !grub_menu_viewer_should_return ()) + { + draw_background (); + draw_menu (); + draw_title (); + draw_status (); + grub_video_swap_buffers (); + + int c = GRUB_TERM_ASCII_CHAR (grub_getkey ()); + if (c == 'j' || c == 14) + { + selected_item_index++; + if (selected_item_index >= num_items) + selected_item_index = 0; + } + else if (c == 'k' || c == 16) + { + selected_item_index--; + if (selected_item_index < 0) + selected_item_index = num_items - 1; + } + else if (c == '\r' || c == '\n' || c == 6) + { + if (selected_item_index >=0 && selected_item_index < num_items) + { + menu_item_t *item = &items[selected_item_index]; + if (item->entry != 0) + { + set_text_mode (); + grub_menu_execute_entry (item->entry); + if (grub_errno != GRUB_ERR_NONE) + grub_wait_after_message (); + if (set_graphics_mode () != GRUB_ERR_NONE) + done = 1; + } + } + } + else if (c == 'c') + { + set_text_mode (); + grub_cmdline_run (1); + if (grub_errno != GRUB_ERR_NONE) + grub_wait_after_message (); + if (set_graphics_mode () != GRUB_ERR_NONE) + done = 1; + } + else if (c == 't') + { + /* The write hook for 'menuviewer' will cause + * grub_menu_viewer_should_return to return nonzero. */ + grub_env_set ("menuviewer", "terminal"); + } + else if (nested && c == GRUB_TERM_ESC) + { + done = 1; + } + } + + style_destroy (); +stylefail: + menu_destroy (); +menufail: + set_text_mode (); + + grub_print_error (); + + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_cmd_protomenu (struct grub_arg_list *state __attribute__ ((unused)), + int argc __attribute__ ((unused)), + char **args __attribute__ ((unused))) +{ + grub_menu_t menu = grub_env_get_data_slot ("menu"); + if (!menu) + return grub_error (GRUB_ERR_MENU, "No menu context"); + + show_menu (menu, 1); + + return 0; +} + +static struct grub_menu_viewer menu_viewer = +{ + .name = "protomenu", + .show_menu = show_menu +}; + +GRUB_MOD_INIT (protomenu) +{ + (void) mod; /* To stop warning. */ + grub_register_command ("protomenu", + grub_cmd_protomenu, GRUB_COMMAND_FLAG_BOTH, + "protomenu", "Show graphical menu prototype", 0); + grub_menu_viewer_register (&menu_viewer); +} + +GRUB_MOD_FINI (protomenu) +{ + grub_unregister_command ("protomenu"); +} === added file 'protomenu/widget-box.c' --- protomenu/widget-box.c 1970-01-01 00:00:00 +0000 +++ protomenu/widget-box.c 2008-07-08 13:59:48 +0000 @@ -0,0 +1,245 @@ +/* widget_box.c - Pixmap-stylized box widget. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +enum box_pixmaps +{ + BOX_PIXMAP_NW, BOX_PIXMAP_NE, BOX_PIXMAP_SE, BOX_PIXMAP_SW, + BOX_PIXMAP_N, BOX_PIXMAP_E, BOX_PIXMAP_S, BOX_PIXMAP_W, + BOX_PIXMAP_CENTER +}; + +static const char *box_pixmap_names[] = { + /* Corners: */ + "nw", "ne", "se", "sw", + /* Sides: */ + "n", "e", "s", "w", + /* Center: */ + "c" +}; + +#define BOX_NUM_PIXMAPS (sizeof(box_pixmap_names)/sizeof(*box_pixmap_names)) + +static void +draw (struct grub_protomenu_widgets_box *self, int x, int y) +{ + int height_n; + int height_s; + int height_e; + int height_w; + int width_n; + int width_s; + int width_e; + int width_w; + unsigned i; + + /* Don't try to draw if any pixmaps are null, except the center. */ + for (i = 0; i < BOX_NUM_PIXMAPS; i++) + { + if (i != BOX_PIXMAP_CENTER && self->scaled_pixmaps[i] == 0) + return; + } + + height_n = grub_video_bitmap_get_height (self->scaled_pixmaps[BOX_PIXMAP_N]); + height_s = grub_video_bitmap_get_height (self->scaled_pixmaps[BOX_PIXMAP_S]); + height_e = grub_video_bitmap_get_height (self->scaled_pixmaps[BOX_PIXMAP_E]); + height_w = grub_video_bitmap_get_height (self->scaled_pixmaps[BOX_PIXMAP_W]); + width_n = grub_video_bitmap_get_width (self->scaled_pixmaps[BOX_PIXMAP_N]); + width_s = grub_video_bitmap_get_width (self->scaled_pixmaps[BOX_PIXMAP_S]); + width_e = grub_video_bitmap_get_width (self->scaled_pixmaps[BOX_PIXMAP_E]); + width_w = grub_video_bitmap_get_width (self->scaled_pixmaps[BOX_PIXMAP_W]); + + /* Draw sides. */ + grub_video_blit_bitmap (self->scaled_pixmaps[BOX_PIXMAP_N], + GRUB_VIDEO_BLIT_BLEND, + x + width_w, y, 0, 0, width_n, height_n); + grub_video_blit_bitmap (self->scaled_pixmaps[BOX_PIXMAP_S], + GRUB_VIDEO_BLIT_BLEND, + x + width_w, y + height_n + height_w, + 0, 0, width_s, height_s); + grub_video_blit_bitmap (self->scaled_pixmaps[BOX_PIXMAP_E], + GRUB_VIDEO_BLIT_BLEND, + x + width_w + width_n, y + height_n, + 0, 0, width_e, height_e); + grub_video_blit_bitmap (self->scaled_pixmaps[BOX_PIXMAP_W], + GRUB_VIDEO_BLIT_BLEND, + x, y + height_n, 0, 0, width_w, height_w); + + /* Draw corners. */ + grub_video_blit_bitmap (self->scaled_pixmaps[BOX_PIXMAP_NW], + GRUB_VIDEO_BLIT_BLEND, + x, y, 0, 0, width_w, height_n); + grub_video_blit_bitmap (self->scaled_pixmaps[BOX_PIXMAP_NE], + GRUB_VIDEO_BLIT_BLEND, + x + width_w + width_n, y, + 0, 0, width_e, height_n); + grub_video_blit_bitmap (self->scaled_pixmaps[BOX_PIXMAP_SE], + GRUB_VIDEO_BLIT_BLEND, + x + width_w + width_n, y + height_n + height_e, + 0, 0, width_e, height_s); + grub_video_blit_bitmap (self->scaled_pixmaps[BOX_PIXMAP_SW], + GRUB_VIDEO_BLIT_BLEND, + x, y + height_n + height_w, + 0, 0, width_w, height_s); + + /* Draw center. */ + if (self->scaled_pixmaps[BOX_PIXMAP_CENTER]) + grub_video_blit_bitmap (self->scaled_pixmaps[BOX_PIXMAP_CENTER], + GRUB_VIDEO_BLIT_BLEND, + x + width_w, y + height_n, + 0, 0, self->width, self->height); +} + +static void +scale_pixmap (grub_protomenu_widgets_box_t self, int i, int w, int h) +{ + struct grub_video_bitmap **scaled = &self->scaled_pixmaps[i]; + struct grub_video_bitmap *raw = self->raw_pixmaps[i]; + + if (raw == 0) + return; + + if (w == -1) + w = grub_video_bitmap_get_width (raw); + if (h == -1) + h = grub_video_bitmap_get_height (raw); + + if (*scaled == 0 + || ((int) grub_video_bitmap_get_width (*scaled) != w) + || ((int) grub_video_bitmap_get_height (*scaled) != h)) + { + if (*scaled) + { + grub_video_bitmap_destroy (*scaled); + *scaled = 0; + } + grub_video_bitmap_create_scaled (scaled, w, h, raw, + GRUB_VIDEO_BITMAP_SCALE_METHOD_BEST); + } +} + +static void +set_content_size (grub_protomenu_widgets_box_t self, + int width, int height) +{ + self->width = width; + self->height = height; + /* Resize sides to match the width and height. */ + /* It is assumed that the corners width/height match the adjacent sides. */ + + /* Resize N and S sides to match width. */ + scale_pixmap(self, BOX_PIXMAP_N, width, -1); + scale_pixmap(self, BOX_PIXMAP_S, width, -1); + + /* Resize E and W sides to match height. */ + scale_pixmap(self, BOX_PIXMAP_E, -1, height); + scale_pixmap(self, BOX_PIXMAP_W, -1, height); + + /* Don't scale the corners--they are assumed to match the sides. */ + scale_pixmap(self, BOX_PIXMAP_NW, -1, -1); + scale_pixmap(self, BOX_PIXMAP_SW, -1, -1); + scale_pixmap(self, BOX_PIXMAP_NE, -1, -1); + scale_pixmap(self, BOX_PIXMAP_SE, -1, -1); + + /* Scale the center area. */ + scale_pixmap(self, BOX_PIXMAP_CENTER, width, height); +} + +static int +get_left_pad (struct grub_protomenu_widgets_box *self) +{ + return grub_video_bitmap_get_width (self->raw_pixmaps[BOX_PIXMAP_W]); +} + +static int +get_top_pad (struct grub_protomenu_widgets_box *self) +{ + return grub_video_bitmap_get_height (self->raw_pixmaps[BOX_PIXMAP_N]); +} + +static void +destroy (struct grub_protomenu_widgets_box *self) +{ + unsigned i; + for (i = 0; i < BOX_NUM_PIXMAPS; i++) + { + if (self->raw_pixmaps[i]) + grub_video_bitmap_destroy(self->raw_pixmaps[i]); + self->raw_pixmaps[i] = 0; + + if (self->scaled_pixmaps[i]) + grub_video_bitmap_destroy(self->scaled_pixmaps[i]); + self->scaled_pixmaps[i] = 0; + } + grub_free (self->raw_pixmaps); + self->raw_pixmaps = 0; + grub_free (self->scaled_pixmaps); + self->scaled_pixmaps = 0; + + grub_free (self); /* Free self: must be the last step! */ +} + + +grub_protomenu_widgets_box_t +grub_protomenu_widgets_create_box (const char *pixmaps_prefix, + const char *pixmaps_suffix) +{ + char path[200]; + unsigned i; + grub_protomenu_widgets_box_t box; + + box = (grub_protomenu_widgets_box_t) grub_malloc (sizeof (*box)); + if (!box) + return 0; + box->width = 0; + box->height = 0; + box->raw_pixmaps = + (struct grub_video_bitmap **) grub_malloc (BOX_NUM_PIXMAPS * + sizeof (struct grub_video_bitmap + *)); + box->scaled_pixmaps = + (struct grub_video_bitmap **) grub_malloc (BOX_NUM_PIXMAPS * + sizeof (struct grub_video_bitmap + *)); + + for (i = 0; i < BOX_NUM_PIXMAPS; i++) + { + /* TODO dynamically allocate PATH, making sure it's large enough */ + grub_sprintf (path, "%s%s%s", + pixmaps_prefix, box_pixmap_names[i], pixmaps_suffix); + grub_video_bitmap_load (&box->raw_pixmaps[i], path); + box->scaled_pixmaps[i] = 0; + } + + box->draw = draw; + box->set_content_size = set_content_size; + box->get_left_pad = get_left_pad; + box->get_top_pad = get_top_pad; + box->destroy = destroy; + + return box; +} # Begin bundle IyBCYXphYXIgcmV2aXNpb24gYnVuZGxlIHY0CiMKQlpoOTFBWSZTWUVQeOoAOif/gHX1z/z///// //ff/r////5gSD73qcs6CXpdNwLdBveyc9APbd9POhvvsdre3ns6Xtdh997WvvSnN73vs6G99oPa yfVKPshKCxe7kutrbAwutB2LuBxH0Mq7MqqAqSSrx59eu4FexJQKCgY2rUR7q9Oqi6G9sihizG6D r28e7r3VzWtPUzBDphLlUsLTKrLUCQxmy0poZSSJSyoo07ru6u3To5m5bYVQkHp7z3NsgxICNEyY ieQg0Iaap+inqHqGjaMT1QD1GgAAADQ/VBhKBGgQQJqamTUE2ptqnpPUMBAAAADQAANAaANNNCER NNEYmmUb1RoNpAeptTQNAAAAAAeo0AAEmkkIjQjREwU80nppT1HkwFPKD1NpPUaHpqeo09Ro9TIA ADICJJCaaBMTIAjJoAjQ0mTJPFPUemgU/EEnqMmm1G1A9QeTUCpJACAE0QCaZNA1NGQaTTU/Uh6j anpBpoNqDIAAHqHOhngIDAkD2e73ewLix6/6ylPPG0kJAjBTGFYe6/ChrRRSG9Pe1re0kZyY/LuX g83Ys/YdJswSjfPPNIERQtzYXNGMntOdhVxe2trjt2STs6L0vwZSpWHcfFdzEsS001xwOSgvifcm QVNw8OnKgfOhADIl7btDs1NGiyC/Z2QRAcDt8/3nPeG8OCPwczzjggfuChFA8AOBzxXgH71bSsf7 HhUkIrwCcHCUnzZgsN2WxRvy44V8XQEGd0IPeYShWKgieCADWSTOQ+vJC33VVEC49bEEHiL9TIZl g3bn/US2voZWReA7h70BCYaZeA7sKVzLRaVe/wdDWk2ydaHGtsNJJk5MU22l51tTLfZZ1banLkHC cuNG1HVDzYzBA+cheSQJFTPCuagKJIJ5z/6eftvNbo266M53eHM7LkKnCeB7d830qKYNxd0melxe TTU3tqm2156M23241mG22GYSpghiFoUUxCpG1adTeNjDjVHleWcsslTllmmC4nHF1trFk3thncyu 3tb6d7DYPGlNTSyrApvdWmsXWFdXxuaEWppelDwkPTY/zQkL5/IdQh5Rg0O4f6A5l1af+bB0LIaJ uopbSoFMNfE28pG/ttQZuOiaUyUHReAlKFSsWjCjAGQJvAECavr7U2+NXvS1fUz7Fhq6wFENIk2x il5YV1b44zE4WMCFOwQkNuqxd+uh0eznakOrinRA0MIpAnDJwMhiR2562pv2cGkkA9UjxrrEFVSU UYRmEPhX5gkZGGFOttr3VVN9Rld3nesWNlV62nsYmqLqBaRi1S7i9zIJjEk6GAETa5dpZMUeFqLb FZUasw9mw+B7k1Jrij2oPYs8vZ2i6maR1kLgoooLT5G3fv7vi/B7rezzxyHUFFEHzzOxTwnQQ8T3 8amZmE5auOyee6kNxEzl/m7DBwhP556JNOvzkPH1+vx7vDDmilojqN0A6InkqiQkbZHm8lydiogY h0ThNXgFRovQQE5UPkgoEIorjEZBEOmDcJAgxgnm8Y1aPbRBSg/cNlHNALEbwPMHhJQD+A4gUPdQ FA4KQYI6YKrQwc8XRFRsMUJ/U6vh3PsGSSH3Vu5UO4QRHxVVqC2BTz9eueh6O3rQDrC8kfz2E3Lr HanSPsf9tND5gdFPkNkwKFpGFMqgL4Jj8DAHA6w332C55Iisn6tnAJiXypoDhjaYW2P7zflCp85Q DIn4XkR8y2gO5MbEAnrXkPUlelvNpFCoC3Qj6zvQI+8QFlShb1nKFDwT9odO3tbFG+//c2O9McJQ YfIroKVq7rvEMjMyMzKzOmrdfv1Tg9+jiqaU2XKW26eeLGvE6XXur7XvsbcnyCBlRBApbHEfXCka d1Ng4Lip8RTlS4jtzn4fX+CUB1VRRFUUURYh9l6uvufH2XDQlNJMfWkHQRQcFcr2DQi3jucChc+a k7MzBhLUBLFLkmd4WV4VnxtYz1nHqPODQZPdOSNjqikIofAoz+rl3BJ4gkZIooKRYoIwFhBRYsEQ UUGMIxRFkBRYKBIAUP+M0CyA8tHRBajIB7wdbBEPm+E+abdPxVgHL6NhgpwIFFQjIEIQIs5PUbcf sbyfdj5+ZKc20qnKnJSSrZBrfkzRUtm8wXUq7Vs1KWxE4+h3mkTajWpZQ3zd42MMXhMSxCq00JrU kMTazjESg8h5NFnlxRqH3dvDE+ERhgBWjNiHbZnQmyLY+54+423PvA2ZFOnd+DqzDKy9FoKcSx2V lqqUOjMqRZkyo7wz90HIcJVO2mC8Nmb01RodxVC03cqr79FT+oyiZmRcwfEJ7xtZktsxwKcLwoQu lQjWKFqMrV5eFTF2EytnGJW0jCyy882Ry+SmJSiqLpcAoKMxZsvFDNIdBaqaMsmDFXMsEEo9bTm1 sPSMRRSpmhfDOpZZyKDVUoKGViivQxNTCiilpwt8NOKzSuCYGhknEu95tatTYs10uZnX1S8rMldb Na+3X4qiyI39wAIeewf8cD5x5t1XR2saTrcySbfKT2d2vE5xdmpCS0KgnwpgFoHV0tFSD11AoO46 VMBfOhq5i47u9j8ZSyxgd78j/WXY1JjWxWnPACjYZs1aDvwzPyhDcXvlPSPuHDho9T5vL5WoEgHP 9CD0vd2Tg+NZit0FRuweLW61/pT0pb9+ETSSjaA0A/06h5NohqT4ccaqq91XuB8O2jQP8hDPQFlu t1iGI8+A7rV7RrqH4jcIZs4c0xH46JkFEFyBemXU4HReANoNJA/xuzlMJwoN7vEgdBF6h5Y2O+Rd itBT+3Sr9/ya7361l3Vg9wON0RqGMR0ue9hPu0RQgeD3NNXE8mz+76HpzUXZxCrePVz3Byc8JMGs FlH1ge2NhOGWEqgceDY8kCDqWL7svXb28KTIAk19VlhKL3mZLC3XIe3O8LyG1BOD8cIB55OLrAa6 A33z0hF2mmUK+MsAZtsZIIoMsUC9pPJI0QBYL13ADp0eS+OGp3wkAPIAeD7FqsCnebkM6boEQEAn FeQzqS4eeBc2e0tMEsGeUMeeGB2FXBHDgpnO2QTlrSFahkk2BksNguUPB4U0vv7+Ybb5bSCY2K5G J1Iskzskmd3QT45F7QNkAvykkJNda8aM5KTp0DkMRkA38eN2vTiksM6YPDDDgZGJAYxZDTCRS1hl Qi1AIRhC/fnxNXQgXmfR9QOq3NKG7f5NQL4AqEYiGqKXIiyWTBgzFPmJMA+7IWxG8FQ3tITcejeo hGCeKKtAlTreMjuHaLUx2XDhasXz2iTgJVKfKqGAURqSZtBnO+EFtabK/HN/C+gOKBspVeS9A6VA 1G+BpcPHx1HGh0F/HdrnxaI461yxvHEgT84SE8ZE3E+Ai2enREAaF/PaJXDy/lZpzJ0w1ShK8aV8 5U9YOI33FmYTTkGTIRUQfSLQaWxHHcgX9Kw0+KQD7giiiiIiIiIiKKIiiiiim2QOnRVWZ0pLdaC4 EgplxCMGSp7nbqRp1hnxqkIV/OOxbukH2aUFuGa5RqgTZFSDHLIPSv0c4BkWM2EMHWyf0VujQHaA QBYOq/Za8VCkwdEDX0RsmDeCBHrHO0XpndNK1+LtkEsfVvZ8Mt5aHyqJeMEcP9J8dxAFOK4VVw3G TqSN8Vgwy8kIwRx5Msc/8zpcpOFYcDrIdXVt29Gq3KYLka1nkAWdSRDCxtEd5IcBs3rGUrWz7sgb rRQOH9mq85TJnoybZojs3CO7s6O6Mj6UT09beRPO3jqHYZ3TCDGBD1jB2enUHHa/xh98VpJGG73h 8C+Hv+Lv7/nd7u+SjKMw9wZVTaPxbrMrd9bCXLqXu93ouhc2M2Zu2ez3/P5O9AyQL3em3icx5ixa IXNoWNODcAIZmGzRTeUC+hwnkKIHJzOZlynhBKOhpPXMnyKpTTlsUlJbEXqKNS5uRZimbk/Y96yi NMRvhNVJ6nPdR1d3Zdl0XwbGzLXCp2+3JTcIzqkKoPo1KsY3Y71jGNeMNmVNjT27dd94twxeKQfC XFwlhevg7XN05cI6EubJREGyH2Q3M6nMVNm7SALyfGGBABF14uppSm/bt35vpV6vVl47ZcIMIOMH Oc5UpSWEqoEqhWEQp+XC2wuD+XCSYZCkUPpsGRCIhDIOIuKi+5Cx0id4cyHkGjnH8XzjsnCfGCoW Qqo08dFvbdayvW78vZ9bG+/PK9oWZigVNsQkUf9SIFQH9cVyIv/cIwnbGfSyf3ocIBugcJAqE+KA hUISLhBC4lR/0MZVIRA+r5vL9v2e7rr2+LdbZhXzGJ9/18vl/f8IWzRv8jg0e1ks0kH4nu0LvQuG 2/NNdISpNKrXPHzcsKC1VIOQZDHmfwDxjH6bn9UFUNTck6t3voVj80IJxBv8R8UeMscm6eoE1Evo kGtsWa/yjBWKwzATEt0piyHlvt+MVM/lr01ep1pvRWW3X449DiJmIOQq+hDW61yXW6ioqqkLVTSG If2R5MQPjzidateBXlA4OnNLcrUpp+q5xAq818rpKyujZQ8KdpGU2U9Gs/AK25qsHGwV5XFaU3aT zsZP0z6gqIqCIgqghIKw/ptRgoemftFgT/FCB8zND4BFQZ8bCwZDD7chcCUZDSSChP1JPqgDNg4H bcMQNq5V/h3l5+XcTHfEfbAFXC6qdkV08fE+zn+b37N9ijrdxP3PlTnv688s7tNGsf43wXWUL/gq jm1yE9POYQdhz0W5qQvj7D0Bce06j74aZphCfOuNecjIm/p9HiBPXz2O+HWjfM7EQE+5ByFN9xPp sNIrHTSGzK7UGrjJyF9ZbjlxAubVGQZRPhwC4cMM3Dn/DADq379VVRVV5H1vaT4an2P6B9Arm6r3 xKL3o9r8OqTlhKIpnF1LTaJJcUUh+RexT9SFoSj0FpISxZLCJ8IMjWQugP7lDcFlF3bgMTRQHecw VSySAkiQKLSrGZReRU7HmRyeANAReAvPSrKE9WYTF3oG18uQmd2IHfkpncnrEsb8QyEdRFYGtbYq XBzd+A0nWMt+8bcdnL31hPEaqHKZ5heA+8b2mkuyGl+QNXF5+fBTT6hGvH8aq2yLocNM0Nobd0aF t0XQjyj8Bss3ZYB6OK46w8ukEg9bXBmpsaM0JhqU2VAiGwaAAD2/KT0eiMr2QiCUod0AggOF3JCD y2wR1KD1XrQhI5dnDa9pKCp0Cc9ND9MSk0cMUdHoaOz1fZOOqIiIjFFBX5JCT/PmTvQ/XflIoyH9 DJUPrS6+jMFYY/3YH1k83ynwPwek9i268vk65CTaT6Q86Tx+96stttJbZaq0ttttslttW22Fttst 1D2Nw8snsE7Ng47cXA3+yC0nxS0hP3dL3X5Hxvwv+fb69r9YLfY2G2mv7E+zlzz4eZcPWHjlhcs/ KlalylCsBhV/as4AZpLAC+FlVBnV9BrRpgyuZUa13VDOpIuoyYGZVhwnCSVik3SLFJMScMDi7de+ U9Dn3+69m05QDqAUD6B+u38P+WpirsJPAIpFbOS/Thjeq0FxgvfcL571R/LQSHZgZMll3HnIk5tz GXqdL1sM5IllEUVKF5qWgoBtgFWWvcaiXRHShaosGkQRGYrMnV+Vq00ZVfYrM0knhLmU4LMmqaPa u2kIFBRnHewwPhEBHOB0FMh+YlbD7BYDNS+atU1VIk2ZMJL9lMmTTTJfmtN0iTScIEKkjCSKRbTR yWzXr2Tcz0QZFABAYRJE+X5u0EERgkd8FzTSFk5GZInVc4r9a2UbbWxIleCSfwTjvZZc2TM3OLJ1 Thc4Nng+qc241bLNzQuaL3Zg3tc1PBms7yJM2ciTZo4r2Lios8GDJkchR5A7ldAB8rtX5FC5V511 7csUuEDfAENX9TsRSzHCyzFGl0vDICMKLOOa3iKg6jy9aUSKS5iGMKVULLdQZbrd2W+L5AOz2vWm MkIeKhLnMot6i8GwmcM9eGctIwkNJorrI6Z3zkUQliOkhtOvyELidRMbiiTaZko2h4uneZEOo1mL u3YWTda9Q4kFZcYX6tXbfWgzraPOYomggl0RhF7CaDRcc+/McDC9zD1nIxqsMCDCcGhj0Uw/o3jP bfPaFiMydeO+e0UbD2VmFtTrRZ2rg67rXWxbffCAVLFjs/Q1uibFTaJfhvo2lscr8Fuo3CpcLfmW 9VrmuM5cgzNHGRdJrkEEqdjKI0nA3AXIRA3QK+Iyc7FIpYaSw51L04VKiF05REGELEWRJHENwNZi 4gspYViVEZFRNKN04HKm1ENrtpERFLYQSwri7dH1CJBvuwA5FBVRNEjHLZeBEQUuVZJ7jJ2NFEeR C4ydBeh2MHcPuw+UjDfvxd3JEcjXcawKraVRcRyLKaNp10Zjq6NnWTlttRvRutLPBabUzb1t9Q4S E5p/5ntb3gzYOilnF3Yu3jg5MDaXHV0U4PJS9e7rOvdcvfDkzZtFzB3ceOLZq+pH1yZC41v+wnlE 716mg5ggPjes6M+7RnX3pIF2fM5oBtRYbpQuOFV03HSLsxDNd6E40JF8nyMKSqs5Z64xRtaBFb2t ldYlMIFA0A6JTbNmLaTNXtBR/mlUVUVUreCHr9l2SvJRpzOTlIk2bkss+6TJfdpzlkXT5HXDc1nV wzZwYuA6mgoNJsFaU3UZOJPeWjKm40gjp8fxxAUPaVM5EOox2FjpfpyVowgKlM0mKjIIyVFEdZxU 4uNsW9weZG22NbrVbKXFzvBsumaoe4xzWe/ArmLzxlxoLE3YFA1RgSEcjsVOa2uMlSsHVZd1rzcV p4N+UiTQ4OLZtx0xVTMhhXLHcbIqJyMVNhRwaLQ3kZoHGDhdzgvLQsIOYUCIabiRP5KK9Rz9PSbT Uaz08CZz0FuSKcinQ8DmiBj0LFDoObgpUqeDV1Zs1zFTRm9ejViyUXL30atXdqm4vUGRt20YGoPA D6BPSJ6lSOhfT8WcTo27OF3ea9fxiertPdviPMOOOsR74omVGyt6LbArspjDGKVe4l7ZMeuumEsU DIiJoQ+CMIb7Tfe6IQILt9Fi3vEkcNj3kGYLGw+p4vxFhgWRxT3Fx9UkgPegiI5Qy10EoMlpOOST nO4puQOTfjjYZ/p5Ms+4ujocDGOddwYQThESlirWH6kEInUXgpwWFL5WeUrao1ygQ4oiIe/38lyB E7mDwkjbTC+VJg2udGFlGy560TNtpEYqkV3Ys1MZecjkttRnV7oxLrtiWjdyvcJEnN7JDHpv5vE5 mE6YLOaTYpUk7w/+z22dXZ06cXi1ZHRe6uju6PI0bni1ZNWjqwdma5oyEklWBSpQYyWPtI4yJoPP hPgIeY52dM9zQXdLViSYFoYKb6xOFQlWsrC+Fi4ajodqTvUvGxl70FREYLIqAhWEZsWzSjuHU0DI mbGWFWPLm3LyJq2b3Vq1b64KticWTNcYi5cBkRE9tDc5MuiMZ2QNFkpQ4ULGSCb77s9dihhERzRY 0LuYRA9qWyTsnsLBYuRQ0SQZFNypS2EF2bYG9DRQESSSDURbSkAWMFabHNzqiCJxSu6Mi4GsQjJk c7FTRUYmS1hueoiMDk22ixYOB5sjGSEJL1tWFKLsjQQuL4UXFFGc7xTiR7yHZvuLPoY8pjYnnUk9 rsdWDuROPF25ZPFbwcnwRL2zR5nm9frs8zs9Cz0NW7R7ZElOzI7G91YNmalm5SOjqvdXEpwYs2LR qcWCJfMkfMhZCPrdAbQ51YoYZtuVuZ2b5XIFMaG+/ZIg4NJEVT2K17dWpqILxCvVZVIoHGJfJ79F gC9PQzTNsIjprC6ZV1aFFFNsEbenQk2RQuIiHvU4PBCPsV5LEImyb9OMqxQtqTkACBCShByOaNqV AdAJJKdBDp0qQTUEseSNXwS6og4gsEigyLJ02YuWqPYQSTkWxudTjiLcYsyorsMKbm5ucJPJsTJy g2SBkwKoqComRCyiIFjoRkXoVZJ5nYo63Oeg5VkQH0XvFZZpVHC0w6dWzVfZgaPJFtHCze6dXRwb OO5owb2za+tJDsjBZ0OkOCS/5MXRrisauHXs0OzRwbnNZwXrtWzwdWEgyXsBzqSWGODB1HCQ0VRB EaLG+4xsQYPYkCGURD4xD7KInrnphsS7+TRon0VdN1pSiWs1Cpd8SPLSsWvduXYl4wZn0BOioqj2 rR7rdAttg2NoBRLySYGu2ecKghnIW0JqECDcELs4V7BdGxWUYwnQrbGaUfhEtAQcjF+j9CSiiz7C RlfyYOD2Ek7ComDIp/8iZ6JuUMkmCLFyDA/B26a662uGDgwOCdzRJJ1RAk1rtdNp1nkXodCeCD7a e5ApjnVu20LTVOo3Wx4DY3KijDEWwqr6b1bNzsx86ODqxm65ueXJwaWYuzZwbOcRM5Em5y3uZwar Oa9dq2YFzFZZg6Ljg4rlSpkgQRipgNhTJYhxU+XkuefNDRQuaNjo4OLexk+j4I+bCeUkfC0Oe7br fwtle3Yb73jnBC60iaVilLsIXJpa9bM17Xcm8k3KlKtUitJFqUsZExidrD2UYa+TOGZmmLjFNkKA lmZi1STFYDmwX3SRjSTVUJMmbr13PHTfnIhmqCUw33SRqqSMd7u0x33YcHldev0cD8dUQ5PJgsMT lSnI77iEdTa4UNxhii23PJG5uiKKKipwSdU1U4IME9sHQkqirX1Dpggi5gufPJJexc3Ju3exF2PH TdTPu9ndzaKb2bqKlMkWOXJPRTyaLng4myIIncqeS5377nGBqcWa52Rs0iDpOLBTu8DmxYMjs6Or oOUIOBTJ2LFzJyMclzt2qbGxo5JODKaF5GbexZsHFqc3Js5tXNGn1T8STlJlJJ8s5k9C11359DZW aXqsC2ni/allwhi2LrN3KYGekGKSwsVSINoYrlqyULogiWERxDCiCmMj2oFm2JREQTsqAIhXwdj2 bl6LjEtWI54UbAiZKdxUE4pQTwSUNk67zqkl7EYJOC5ck33MtxU6ihc2JNbgxal50G44HBYbXpo4 ODJuiLtbGD2Od7HHcY4KZyxUE7jHQqWIMKHcvkumFF6x2uWI7bdD6SaHMdJ8bFDsXO4KHUY7hyjE 2xtJay00uZL3GDCzvIk8Hdg4rN7U0UN9ZFYERNFDkgWw43PVzRgwdjB1HOpg3PYk0aOqmbVzauLo p0Y2b1nLlo4MlLG9oyaGrmydGZfJ70TpEvkNUc2lRVvqn1C/r1qjKo3e3QwhR1g5jKol3mJtM0NL RpFjK6RlFtZnZEDDDrj1EIG2STv36k9zUnC4Y5KXcEVI4uiw3qSTmyZTo0wL7I0XKeJzb1nzoxX4 GsNXd4tF8Y5b8mcOe+yT3psdjYvbKZHOxKyQbHjoMHchMK63GsVFF5HOCbHawt2gtzBc2GKBYLij DJVSulFvZxZqYqcWU7Nc2WDSfJKnyHbjwcHVqvXt6kAQ3pkZqjm3UuVHNyDwVOS4op0OfMmTg0eX fzYtV996zc+ETkyU0atXRg2WfTE9+WC9bn8yhT3A8olyp+axydnpo/JlOrRqLh6dQ6xpiBhBTgih ZXytl50DjcAQEYr2IgXcARsKBfc3qLZD5f4Q27Lrb5xmEOpDWrUBxQLhs8znh8KeOxu6UF7dUr5w jJj8xGpCVtznpL6yJrVa1qAXxoaICirQqQswWSdVaoIwuuiWSdbe9hfE7BhCs0mxi2fe+qhnnXZn Vy5HfwQ8TCwWBkJefsAkDWESRRtDlK3PhO4My7Wu7W0FeGc09vkZ14zmpByqFl5UdTN/GuGonhUK e/czxujw+dMo6EV70K1ZSNcYJzEOQeQtka7ZReQkH/ckkqcf+Q4CB7VP7fISJIQjBA8aBARshBCA fTAGBCgH4CDjCwP5qElVhBbJQQLRJIV+6yaSGhDHSeCAelB7xGJUPagDJWMUEZ0gDKLGwBhqQiCY VVjKS+KAMmQBw7oxYkYxCIgoLGRiyLGDFjGGGEZKiIiCKEYDAQE6+RJPX2HQr7hokILINEKfCANI HegcUCk2gHVAMJDFVVYsHpAElgHOaknwiMYKLKCfF7/qci0+spJZSJsAAM2cD9Y/07kPxFJ+4fv/ z/YNDiPhLlBuwH/I/cP7RwtXW+H+AJ/Qg7QLChFkkYpFQkGBzQpgkcx/hB/gMAMCA5fnIP2kVKKp UUVSilFy+Au8KRP6TqyFJnlHZSkq6r2qZHrgqGlQfe1RdhBMkP9A4FmhyLilwmJvj0S4Q6NdYkpY nOhy7TUJ0GKQT+xEuj1EGWH+hToRYdbkubJwTJPROs5GLlWXB6VJTgeORxeZncnDAlrDAnOApwgt CxCCwHeYNgHPAToGIvAg/6p/P9gH7bgesMiUIZ+k2LsQ95sDc8N3L9zz4D/sidjHNo7B6NV2A3Dc PSgaEUy2CHlfE3YJsHQZwdw5xpTOg28X9RXaMVxMRMBg9zBwOLePiMIMvLB3iZYiFxEyGtOaUqYG AxIPeIUFhgxsNGTynyJvNU6JZ4zxkizqnZHbrNHytEvhTYiaHZuX5grail/ENA2bhwGIpNvGlDMl 0HeNDVsw8SwGLeU8jcNhsiwcwcFFLGYjFGhxDM0XHIN1jOXuOBYahiMbAwdbmXTCQXjpHQPBTMwz 5ZDmHUMBzje3NhwHMFCYBMB7oMg3ilwkyVAdF/inJincyc0pLk5GSbhOlluQvqWHY2aGN0uthsRT AMUUvHcAehzL+D7j3x+GKnxxB/PgQ9f2s+kroh4a6OWttXWtUbU33XqYbJ71UVZ+qW8vMhd7fKeb QUPOPOJFO3adierb1915eUi6CnIyDw08ZKqqu0aLwzjkOoKMkuVPKNLpGlQudOYvUzkG5sERNDYz sDYh0odZ/uIHKh+U7X6i5SCLErMEYfyfiFVWIggoiqppsyAn3z2A6g433/brTBzOcNznJsHIlk45 4A2l3DXmRi5tERJ1SAaZLDYPvnLr4W2Kllu5DKfPKKqais7C+4mJ/FUpSjMXFUSESJEjQiwClIpx S5WtU0lVKuQUXkvSCkwPkqq4so/Cq2TY3SVzhkCAOZh+mMIqZjSHI3siboMCTY5BscTbFHtGqKr3 EO6HHRRFkUgqskkJk40xB0ajUEHkAbBcWL0NC5tejTWK6MJx4TkeA4iqKxWIgKoIgpHqAoURGQD6 mb9p4iUoZ0nI5B4Bh4RKzqgzfXeBtrRZSqlubZtrbvnbMDvnWYAWDNJNOzPSqmhQC7BNrUjBkuNS F6DcBv0GkiZzN933Q5lPRH6v1c1aF06ZeXnQYH6lFo+4+L79R75iZyZ6nAEfgPvLyyg6y8uNB+Uu XLmBzBQsSUDck96J444aoYOdzKZNyj/XZrrt7FxaNDB9zgjRsieaFtysyy9i5NAxBc6CmihwKbin qQfTMEHQyQUKEGizRexXNm5l7ZI8d3dmtk5uyyzZrvdH8+LBzZDUOrhw4gTFo1jaLxHDrBSSp7Gm QNIxXSDAgjtSNR0WN70MvszTjV25c8nvO7N+2RJe9S5w5Ln0vJy3fPFyDskFh3WMzyexL8JH0uUW 4sdW/OIcg3J1kEhIZEoDeqU03BJl+q1x4rKTBENYLrpQ8dULpir7UiUwhBgHoTM3jctyIbCKDYS+ gE4Ilg4O9tac/p0iaxSV3RB4rMP6mrC67BaLHkClzBPzRd0cxs1OoDAasIGMHhjuLy+CdF+/IdTm RTZdRijM9r9aFyvRe7DYOZBNfJ7wkXv48BiHnPFQCaO+4lecojebj5HJlfVnOT5WTJZe97aRJo9T c1kSZPgpJYYY2Pl0MSULlDB8gwpwZILkntRPEBgxZPWswcVl74/HcvaLNzm6rOqAeGTTTZ2ZtD0e lXtn+/3bPX5NVOr49ldXFz5+L0MmbJ0WW3s88GajFff6Wz4dYe6GTi999lat72L3Nq483rmnj/N5 pvAPjQBZIuUkKeqb0pQLA9adD4vE6MVmAJIMT9VHbEbQYQUZ6z+bJT3KnqBmD2U48LoyQQPVgD4P LIWl78zNcnWIxPX2e6YD+eR8a/GU/YBL3JYEyELvQUOaKNojyfHZAPa9/p9dh+tvEfUuTc+f0KLI dxnCB5jqNPFRfArDXWcuLHpc/rxatnxLN5cuZPS+o1XnF4fF7tWL4ubRcxdX1yYMXVuX3uLeydnR 0bKUxat7W9VMzVzU0YyScGizZsyXLkzZtmbJ0aN7NR0tGchpNRRqIajMajEfo51R7bEQ+7uK1zY1 dzu4Meyuqzgsv10jm2jqxaJMpPTUydXmybLdmxybNPle9vXqS/sknhBak5H2+Wrk5O70L3pHghu+ dtSv/agwV5YcuVqqCh9ULsNIJoZI7eSJvyf8oPafLT8ayVLFFBsSwElIlIyypSyiyllBSlglCpSg 2VlKDYlIMlkSg0qUspSg2JQGFBKDYlIylVlmiHzAFNyYJ8bQfoMVpzkGEJQ+o+8p/nAzrBekWFCd ipmDYviESEgqc3KtcSxUvxTT8RKEye3MvQGTlnF6Kvij1kQugwxDKFh8ZZVgMmjWMmHrJNkg4U2w mZBGTdKIsFWQ8G++RigJwbZhBEyIIZa+/JJxdE9sp7B5j52b/95pUkTJk+3fw+tqfK2YOz6XvbLp xWfQsxDkGMVPmODRc2Dcc+BcXEDMkGTJsYNi5xOsROTg3GrizbnFq5xByXs1N2S9xXM2LfBuYMlP GJvWbnB+tHLod/IB55Y3xhAfom6HQ83ieDezPT9tomC9wLPPGTpIk6uzyeDxcXzcsXVvwPMmTZq4 LnF/M3u49yCq2pJpIk0YOTNlYs9tnacZLv6DHqVF0jgPfVgOhzLBPFD9LD1XLirx2SdR1OT2uzyd ngycokfbN09ZE8E7f18/JKSuV+KPP1AHq8gBn+sErtXh/D0vebuVMPM3Mg7HnA6E4QXqekpiFVrR zYwZ3hXQohuAubvQJ63rF0/2j8BPk1evUrvPMC/MvUfu+R2EsdseaQDRPUJ1aGMu74y3V6ucIkDI kzpyhE+7o6+w5ZSW5OXZDJpiTVxpAG7Ez19HkTmk2ASNJvaAspDsMqCqI9mjRwWSBreBH1je0XUc RSbAWBroLdURF5ZCUAXF6DLr7PRbXK8EutGGA5Ry8stG7ONq2ZYWKM/JJvhdNqERD1GtRfUcBhWB A6x6Bll6K16nc3dl8/CpsOF3tsoPSPMMR6RrCExERGTBcspio8HJi7r1zNxayJNOffrVfky58+uG 7Xh8q1fG+u3F9zGVajVOm2+8bZ8WjHTX2HtY9D1PqB5PFVfd5Kti7PBkabL1mjeweCmrVe7l7NuE nqMOXMjlS5oqdjgYkgNHsA94nj6e/neIO4l3P4hCwgWBGwga9Su6COlGRgSD0UjkkSDCChgKBBM9 hpNXIGs93dg9piTuukb2TNTh4t6SzNNsOq5NKVQwBJ+Rddl2BM8k7BuO0b+BoHyX6b002GojajWv Qk0Jm5TZlMoDIMouPceT0PUtKCBILccy4j5oGQwE+gdxpIdukhzdh3Yimh4thKk87AydMOTG5gsX l8HCT2RKSXDcdConcG8do9mXXwyQTCIr61/Y6BRNeU7LKDqch5dQp1ofI/cIf3oGzDSqnjnJjTq1 JZ4yQMk2NbbH40evyd0gTp7PwsqfPaJjBodSoOhEftgsiKAe6L8965upAXzjEnfzDwYTy6EWCniT uQqYaInokSazRMYIkC8swYAgjARCCnnEDeZ7anJM7l0h90rN6+U4uRDJtupVVwJ/l85BvcrOX5r7 5j2eV3opnMUvkuvQYLIz7n7ZS8IR5uA2QNilxmBM4ZEO+pKRixYEVgRWwwMHU0XRQMyBRmBDKIHv au/gNwCJYBTQrZ4JnNcIEIip1QTp3TvkIbUDghoWJBWMQwiNES9ooh+C2VaBK6Li2TqpZd57fAS8 YG0FYv1PpfY/E3qPsfa+1cp9ji48dWqmj8b0MFz8jcvasm43rL3ysJEmDIwWasmDkucWTg9hTNc5 tnNgYOK5ovblyWbnFZq3ODk++TBwbNGrFTgwfo/R2Ts5ufJ4eMLzcye2Zb2NlLckznRJyMcjGRTg wOdjsSSng0bCh2cVm5urxPfVni1ciJc22XR5+Dt26rmazg4tXV0dWxE8Ic+Fyfgg9sg90g9xqWmM psW5wMz23ABcvrgg0EHDuWkx+QG5XaQ1KHhS8yBrxsj6xOkPUoenaPNTDcMiB9PoVsWPAe8Tz36R Zdgww4LrshvIMJAyYgj5xcElAcw+YIG/4PnyvjeDcXy+665fV998MCqIW6T11An9kA5j6Y3SSU6k 6nGLsonyflT4I/u/LXGZjvIcjfXM4nrfGBcfYLrECKLRzH/n67BsqguiSfUDCE0ypBE9HL+jam0m 1kzKW/uFzLvXAptfxJJsM1NUwZtqklhytD8+yZ9rIpNI240raOEDMRGEt9v283fzk1oHX1vh44J5 5pjoZRO5O1Jp+IVRZZDbBVgCykMQKk8HOJnsF4gQG+C2VBpMIoWApGBD8aQiMUFEGMSKKD976Nye uByYxZ7VDgucvnT099OWUbibZoMN1ZFH2lIVp0298S72q4DhgYSb/gac5jMKHWBfJ+Fg8y7yNAT5 k+uT9MT7EfFH5Xp+CSDgLQq6FQpNsx+hE+uR/Fh/LJJRkP88kff8Narv7uoB94BhEf2EMYJ/Ina3 7xL0w/j+X0QcfcT4Hwun7SpG83e4h7gAcoCvePk8qH74Kj0+KEhuHlH5UOriaxAegEh4J8CKoiRD 4fkwMYnmtNWVhQSpKusfOxICPSPM0Plef59IEReSILzI+PATPguZXwdvrGycBAkFA5wIFh9EcvXJ kg+iLo/LHycy4dINLCpFgD3o6+CDD7UnqSDhJPk5Gk9OjwOfW613q1v6eN/P4Ve3awOR0wP4/1t8 hcndFCQXuihId3+VQes32iqt9P2wawXocU8KgpIpKUAoQ+RnPn3hw5ujO8YjCMu7HEcAETGgn7bv xDEnnk0HzVspJd84B8rWR7DdPXl709AckBlgioh+bJJ/x9mv9FTQb5t5N9wN4Zafg4rOcRNDlZ+q UVT5/VdyLrtge2TutJ19Uh2A8fRBdknilkff65H76iJ7SHpYLOo7D5GqgSqlfvLOdWyrq8Sh+68u PykfuR/XMvD86NzP5tCfQjRGaMod5ud/1T7y68YFIHSou8v/GBeal1BT9aOy8Mmj5T/xzeTwfr5A NaHmFAz/es5xDmVPvE5HPeodoHNmyq4boqSdI1VQem+jexU8Si3q7EKcAc4XuIR7VSx4/ITHflAN a6HUXCtnF4c9VPByoQM12ovMeowvubyygXOS3pnImF6gWcBLk0/bgpvETbgImY53kikL9n2UizND +RL0/N0ggyd/DJNZeqS0jabnMdlFk/pq6CmYyTqn8OkbYpvQ8L3pn1yb07Lr9Oz2IH61zoFvKJ1I Dw19ehr7xMTX1iXXsyqm6vXYAAKbdz+Hr7W9EcQYg8qOtPaH6DpNCj2BpV7HnihAYAkCy0UANygX r774+YO1XlIKPIYbtrWlHlJUkIFEgCqsBHupPjTPh2EzCWWEp9UAax8UYziWQmX2VDCY/JeALZD3 dcyS7aqOiakmMRPX45CJ7yURDXPfm5EiEOUEw0vyPuuu5mTGD9cHpgweqwPy1HnRnSMp6oLQ7ypd OCQ9Q/dNt5KGaehFj0V2iaUDP8YIzWs/Z/niBsGKJ3uxV0nxfAE8pDy8jyvTxTtwwijg5AGHTVyw CzwMAdJTEdYTBNYRJmZmtHqfoyGIHBEWQIwOhgGYJFzIfJYaUiBh+dep9L0Il4wrVWVSriJoAVIJ aH9S5HpXBnn4v0EBG4OGjqXHlEIgPApIQQkWhQKFPxMGldTLgNaUk4xqIE9qS2CHM+HBHaDnanNl DRCkqbSVPsFdxxQOI69V6HIovNli/vdqBmQOY3bNu+Per6PYFx5vTnf96uK6ZsE2w1wuVbCUgbks sBCxJSQix+5YVFgRmBGRlZCopGBRQFPnjZsy+WFGddCAFgwGkBTiMQBPvcipykkXBCy4wCKDJyYr kdnl7QPPAxvlvlgO2Jb+DSfthfEuQvg4ewDy0j+QTJMyBgqpEPBHSLB3aOBGwfBUmkfY4LtP0tJN yKEsixSS39ETZPonxIczpCvyf6mP4Qc0ILZcYuADlFBgidQwiJ/0KaGMRicsaA3kqC1ESpKQikdY vpz+qDywh5Jtoi7SVB6AVI1bnOfIjzGFdbxrZMULxsCBRrHAd4FDlesQ1BYak8wxe7Hz2cElqDNE oUDTDkH2r07VHFQNbr3nxQ0Q0pBlJPPuq0lD1pUnsnEgYnzvWn7EtHpeqpGvLzBoPA7xPBGyexjJ 58nS6zKlbTRmMhGMJBBjFXAE/ufKecnGKVEOA1ONkKYlwniE0uoT8j3POOCBmBTECIbgN3rHwkfg qqoMeym1QtLMLwQ8kH3wfkiDdf81VT0w9M74ESkefmlJ4lSe2cscCC+PvkOyNsex0RcqFKkH3H4Q s82hfqEnmmc1GjDkPrXS5fHPihbZ0IHQIEg7wYYrlKYv03l6foedhjeTBaqtCoVD9dxnnfsVflCV HEJx8/NOuCY+6TwYJ7R1gUBziekX7A+ZXzLgob+QRxHYvgVREZFAcgopBPUPaTY2CiTZ2WKRCKYT K5UVGsihD5nQhqFrC1VDCQ3J+mfpgvwKF59L3KnqEdYlxoDLHQF1pgHaMA0hNELZWrEnvndhIB7g XCHtEMB3A4fAPp8tfCSSjXSeDfFoDDM7DZ6l8byeQLqMyPJChzWsyL2g6h7wYY3lD8Ye0WDgKYAN 2llB8MKzqBysVB+q35+HKdT/Cjzh+TiiBwzIVnIwKzRZqDrB9+Ae8gYKB7UCCLYB2vKusxWFkk+N QpriiXX1deB8i2DAmw5cPfZuWYGcVf0CUJ86H9jQ9SoSQkiN4Yc6Bc6TOnVyawgxFAPKp6hS5kRn Bom0tCWUVFDPouhaoIFEEDFVFMFE7UdFKL+NiGAhFkYgegGmkDEKH6TPq+PTp23FgHojKuws671N R5t9H5W468KoDKIvFnlvuOw9pOslMdrOMvB4eYh/r8twMOoO5nDF71O0Ya/2BKdFHWjX3hY3QHTQ oIq0D5xz0RE8UvXbTetaL8INUFQXQvItUKtIsrvL4JAgQewLm0d+QIa/7yz3aTyUhQ+tfR62kCd3 pKsRBBi7hs/hfqcMv/za2c+wnfy45XA9K3j2WGhMRi+4X1WPXNaFftLEJaf8VLKYP2+PWFJbZYFo yzdKMgW08Nm6ZT9L1Y3E7FTz5DT5Fhd/vP81j7IacE1vuCCVRB6h0LTtqiBk1RNh75yWuXaEgx1G mfjfIm6CkoSGUnBkqy1NqQdbilfxvJdbvFX6GLh2XaoOz5q2/OGZntCslT4QeRUPpj4O946heELF xeNIRA0nOimg174kf8SsJB4AfTJAMNwNBGFCG843UUWWQkCLRElVROhl7ylYz3L7GNnzYF4ZhOno u2kJF9h1IGx9EThucx04xDG6FwVYbWbFj2WLzcUPfeMhz56qQg3IT231hdmw09K6dI4JMjI7iI4O hh3QqItQRCgIQft4tgSSqAqw0KA5KsqJJlt60pGvsxdYUUFSjBPGbj63Hw8tZz3ku4yFIkOCdxsT VODbKb5CqCisNb5Dow1ToSmyvOOyuZroTmAAFL9Pu+ZQx05siiWptLFi0YZFij8SBsQcEGG5vKkc 7SGZoSxKkFJUwLpHEVFkF0lRC+2ClLSL4oas7J157kDA4UqmLFeK85iGjVoCQhAmhakmHbJlgGQK DDRSco4Tr6taRYYSBhhCChcVdUvS601gs4KXTPy8zxOXWUb8/MON8kiXggR2IFCW1L6F2fGiQZy0 UbYMihIg5gDxRQCQXUkvGHCFcCLhr/C12dFI5AhqxEC41QnWNC1DZyF55PVT5CSEjmwE+dXTqUNg wADkA6lXOQv7WtVUcqsAs4J56X0KbtLxC2Cg4BzD1F4tIQbokIqveotCYxDw4EsgqEtaILQflk2g 8PWlQwNWAC2V5A1KFul59CBiN1xZA1qcmuEk3LDtfKq3GdQ3kAoYIQYrJIBIILRA9AnW3Gv1XiKH aGaCOpVFMBfmENo4XnSYKJ1oD+We8pgRN4ZexufeE2M2knnxG0SobsmMHn7XRNmWDKE7pdIk9ntL kriVDs3XskaN6WSolLYJEuKV1F46UIJU3zXoHOv5qHfkUhEg+NVoMgPyhkuobTsE5XpVMXnvcF+I Np5uHGpy+T6x9Ty+8q5A9aOgdTiPgutQejSDMnLOJ4XvnQ1mvyvIqpYLGVgMCJHzJKIxQkQCimtG nUAic3s9C+Pe515GAR5lc9k7g0o8vED1DaNCIFL2Jq0MkEkFEmOzVa99sakEEzHGqfKJ8Q2zJ4K7 0ccxDEkQkCKFGg2qGIHBc+2N3FUTmEwA6dxRRRWSie/InzsMzu6KDIv0hhwhgEONwYA3+CQ+uBsG vKQ8ZO0m80QOnxB+RvUavVhgooxA8jV9ixEl2wwdiwybmO2iAe5ELopWQMUWFfdJJ8Z+HrkOWEB1 R+J0mo/MiXtjxxET3qKQMopzibwsPAecvyaIkhIw4bx7KKtdcdmHiq+ogd431AHcNssJ/HDE0/iM OsHpevOx1jjMo8MgcTykPfDAnwkPESdV2jWC0SdJOiOSNt1V9ZPzodioofnMlOP1ibK50NgP5kMF D1qGcDSe65V8fhuXnE2ifG27xQPEdDoQPG8onvCe1A+xD+0/r8Z40g/gLfgUXTHIWirS0hUZMry9 RUP/4u5IpwoSCKoPHUA=