r3583 - in trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2: . src

abraxa at sita.openmoko.org abraxa at sita.openmoko.org
Thu Dec 6 02:05:24 CET 2007


Author: abraxa
Date: 2007-12-06 02:05:17 +0100 (Thu, 06 Dec 2007)
New Revision: 3583

Added:
   trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/framebuffer.c
   trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/framebuffer.h
   trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/mplayer_playback.c
   trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/mplayer_playback.h
   trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/mplayer_startup_img.h
   trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/utils.c
   trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/utils.h
Modified:
   trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/configure.ac
   trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/openmoko-mediaplayer.desktop
   trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/Makefile.am
   trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/editor_page.c
   trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/files_page.c
   trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/guitools.c
   trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/guitools.h
   trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/main.c
   trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/main_page.c
   trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/persistent.c
   trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/playback.c
   trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/playlist.c
   trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/playlist.h
   trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/playlist_page.c
Log:
Phase out guitools.c and move useful helper functions to utils.c
Fix cover view widget order for proper cover image use
Add mplayer video support (albeit still slightly buggy)
Fix bug that displays random track positions/lengths before a track loads



Modified: trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/configure.ac
===================================================================
--- trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/configure.ac	2007-12-05 22:18:41 UTC (rev 3582)
+++ trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/configure.ac	2007-12-06 01:05:17 UTC (rev 3583)
@@ -53,9 +53,12 @@
 LIBS="-lmokoui2 $LIBS"
 AC_CHECK_LIB(mokoui2, moko_stock_register, , [echo "Error: libmokoui2 not found"; exit -1])
 
+LIBS="-lts $LIBS"
+AC_CHECK_LIB(ts, ts_open, , [echo "Error: libts not found"; exit -1])
 
-CFLAGS="$OPENMOKO_CFLAGS $GSTREAMER_CFLAGS $GCONF_CFLAGS $CFLAGS"
 
+CFLAGS="$TS_CFLAGS $OPENMOKO_CFLAGS $GSTREAMER_CFLAGS $GCONF_CFLAGS $CFLAGS"
+
 localedir=$datadir/locale
 AC_SUBST(localedir)
 

Modified: trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/openmoko-mediaplayer.desktop
===================================================================
--- trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/openmoko-mediaplayer.desktop	2007-12-05 22:18:41 UTC (rev 3582)
+++ trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/openmoko-mediaplayer.desktop	2007-12-06 01:05:17 UTC (rev 3583)
@@ -4,9 +4,7 @@
 Exec=openmoko-mediaplayer
 Icon=openmoko-mediaplayer
 MimeType=audio/x-scpls;audio/mp3;audio/x-mp3;audio/mpeg;audio/x-mpeg;audio/x-wav;application/x-ogg;
-Categories=Application;AudioVideo;Player;Multimedia
+Categories=Application;AudioVideo;Player;Multimedia;
 Terminal=false
 Type=Application
-Encoding=UTF-8
-SingleInstance=true
 StartupNotify=true

Modified: trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/Makefile.am
===================================================================
--- trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/Makefile.am	2007-12-05 22:18:41 UTC (rev 3582)
+++ trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/Makefile.am	2007-12-06 01:05:17 UTC (rev 3583)
@@ -20,11 +20,16 @@
         main.c main.h \
 	playlist.c playlist.h \
 	playback.c playback.h \
+	mplayer_playback.c mplayer_playback.h \
+	framebuffer.c framebuffer.h \
 	guitools.c guitools.h \
+	utils.c utils.h \
 	persistent.c persistent.h \
 	main_page.c main_page.h \
 	playlist_page.c playlist_page.h \
 	editor_page.c editor_page.h \
 	files_page.c files_page.h \
 	\
-	omp_spiff_c.cpp omp_spiff_c.h
+	omp_spiff_c.cpp omp_spiff_c.h \
+	\
+	mplayer_startup_img.h

Modified: trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/editor_page.c
===================================================================
--- trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/editor_page.c	2007-12-05 22:18:41 UTC (rev 3582)
+++ trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/editor_page.c	2007-12-06 01:05:17 UTC (rev 3583)
@@ -30,8 +30,9 @@
 #include <string.h>
 
 #include "editor_page.h"
+#include "guitools.h"
 #include "playlist.h"
-#include "guitools.h"
+#include "utils.h"
 
 /// Enumeration of the track list columns
 enum

Modified: trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/files_page.c
===================================================================
--- trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/files_page.c	2007-12-05 22:18:41 UTC (rev 3582)
+++ trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/files_page.c	2007-12-06 01:05:17 UTC (rev 3583)
@@ -34,6 +34,7 @@
 #include "guitools.h"
 #include "persistent.h"
 #include "playlist.h"
+#include "utils.h"
 
 /// Enumeration of the file list columns
 enum

Added: trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/framebuffer.c
===================================================================
--- trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/framebuffer.c	2007-12-05 22:18:41 UTC (rev 3582)
+++ trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/framebuffer.c	2007-12-06 01:05:17 UTC (rev 3583)
@@ -0,0 +1,271 @@
+/*
+ *  pslash - a lightweight framebuffer splashscreen for embedded devices.
+ *
+ *  Copyright (c) 2006 Matthew Allum <mallum at o-hand.com>
+ *
+ *  Adapted for the OpenMoko Media Player by Soeren Apel (abraxa at dar-clan.de)
+ *
+ *  This program 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 2, or (at your option)
+ *  any later version.
+ *
+ *  This program 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.
+ *
+ */
+
+#include <fcntl.h>
+#include <glib.h>
+#include <linux/fb.h>
+#include <linux/kd.h>
+#include <linux/vt.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <bits/types.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "framebuffer.h"
+
+/**
+ * Opens and returns a new handle to the default framebuffer device
+ * @param angle Angle of screen rotation (0, 90, 180 or 270)
+ * @return The framebuffer handle
+ */
+framebuffer*
+fb_new(gint angle)
+{
+	struct fb_var_screeninfo fb_var;
+	struct fb_fix_screeninfo fb_fix;
+
+	gint off;
+	gchar *fbdev = NULL;
+	framebuffer *fb = NULL;
+
+	fbdev = getenv("FBDEV");
+	if (!fbdev) fbdev = "/dev/fb0";
+
+	if (!(fb = malloc(sizeof(framebuffer))))
+	{
+		g_printerr("Out of memory while trying to allocate framebuffer handle\n");
+		goto fail;
+	}
+
+	memset (fb, 0, sizeof(framebuffer));
+	fb->fd = -1;
+
+	if ((fb->fd = open(fbdev, O_RDWR)) < 0)
+	{
+		g_printerr("Error opening %s\n", fbdev);
+		goto fail;
+	}
+
+	if ( (ioctl(fb->fd, FBIOGET_FSCREENINFO, &fb_fix) == -1) ||
+	     (ioctl(fb->fd, FBIOGET_VSCREENINFO, &fb_var) == -1) )
+	{
+		g_printerr("Error obtaining framebuffer info\n");
+		goto fail;
+	}
+
+	if (fb_var.bits_per_pixel < 16)
+	{
+		g_printerr("Error: %d bpp framebuffers are not supported\n", fb_var.bits_per_pixel);
+		goto fail;
+	}
+
+	fb->real_width  = fb->width  = fb_var.xres;
+	fb->real_height = fb->height = fb_var.yres;
+	fb->bpp    = fb_var.bits_per_pixel;
+	fb->stride = fb_fix.line_length;
+	fb->type   = fb_fix.type;
+	fb->visual = fb_fix.visual;
+
+	fb->base = (gchar*) mmap((__caddr_t) NULL,
+		/*fb_fix.smem_len */
+		fb->stride * fb->height,
+		PROT_READ | PROT_WRITE,
+		MAP_SHARED,
+		fb->fd, 0);
+
+	if (fb->base == (char*)-1)
+	{
+		g_printerr("Error: cannot mmap() the framebuffer\n");
+		goto fail;
+	}
+
+	off = (unsigned long) fb_fix.smem_start % (unsigned long) sysconf(_SC_PAGESIZE);
+	fb->data = fb->base + off;
+
+	fb->angle = angle;
+
+	if ( (fb->angle == 90) || (fb->angle == 270) )
+	{
+			fb->width  = fb->real_height;
+			fb->height = fb->real_width;
+	} else {
+			fb->width  = fb->real_width;
+			fb->height = fb->real_height;
+	}
+
+	return fb;
+
+fail:
+
+	if (fb) fb_free(fb);
+	return NULL;
+}
+
+/**
+ * Closes a framebuffer handle
+ */
+void
+fb_free(framebuffer *fb)
+{
+	if (!fb) return;
+
+	if (fb->fd >= 0) close (fb->fd);
+
+	free(fb);
+}
+
+/**
+ * Sets one pixel of the framebuffer to a specific color
+ * @param fb Framebuffer handle
+ * @param x X coordinate
+ * @param y Y coordinate
+ * @param red R component (0..255)
+ * @param green G component (0..255)
+ * @param blue B component (0..255)
+ */
+inline void
+psplash_fb_plot_pixel(framebuffer *fb,
+	gint x, gint y,
+	guint8 red, guint8 green, guint8 blue)
+{
+	#define OFFSET(fb,x,y) (((y) * (fb)->stride) + ((x) * ((fb)->bpp >> 3)))
+
+	gint off;
+
+	if ( (x < 0) || (x > fb->width-1) ||
+	     (y < 0) || (y > fb->height-1) ) return;
+
+	switch (fb->angle)
+	{
+		case 270:
+			off = OFFSET(fb, fb->height-y-1, x);
+			break;
+
+		case 180:
+			off = OFFSET(fb, fb->width-x-1, fb->height-y-1);
+			break;
+
+		case 90:
+			off = OFFSET(fb, y, fb->width-x-1);
+			break;
+
+		case 0:
+		default:
+			off = OFFSET(fb, x, y);
+	}
+
+	/* FIXME: handle no RGB orderings */
+	switch (fb->bpp)
+	{
+		case 24:
+		case 32:
+			*(fb->data + off)     = red;
+			*(fb->data + off + 1) = green;
+			*(fb->data + off + 2) = blue;
+			break;
+
+		case 16:
+			*(volatile guint16*) (fb->data + off) = ((red >> 3) << 11) | ((green >> 2) << 5) | (blue >> 3);
+			break;
+
+		default:
+			/* depth not supported yet */
+			break;
+	}
+}
+
+/**
+ * Fills a rectangular area with one color
+ * @param fb Framebuffer handle
+ * @param x X coordinate
+ * @param y Y coordinate
+ * @param width Rectangle width
+ * @param height Rectangle height
+ * @param red R component (0..255)
+ * @param green G component (0..255)
+ * @param blue B component (0..255)
+ */
+void
+fb_draw_rect(framebuffer *fb,
+	gint x, gint y,
+	gint width, gint height,
+	guint8 red, guint8 green, guint8 blue)
+{
+	gint dx, dy;
+
+	for (dy=0; dy<height; dy++)
+		for (dx=0; dx<width; dx++)
+			psplash_fb_plot_pixel(fb, x+dx, y+dy, red, green, blue);
+}
+
+void
+fb_draw_image(framebuffer *fb,
+	gint x, gint y,
+	gint img_width, gint img_height,
+	gint img_bytes_per_pixel,
+	guint8 *rle_data)
+{
+	guint8 *p = rle_data;
+	gint dx = 0, dy = 0, total_len;
+	guint len;
+
+	total_len = img_width * img_height * img_bytes_per_pixel;
+
+	/* FIXME: Optimise, check for over runs ... */
+	while ((p-rle_data) < total_len)
+	{
+		len = *(p++);
+
+		if (len & 128)
+		{
+			len -= 128;
+
+			if (!len) break;
+
+			do
+			{
+				psplash_fb_plot_pixel(fb, x+dx, y+dy, *p, *(p+1), *(p+2));
+				if (++dx >= img_width) { dx=0; dy++; }
+			}
+			while (--len && (p-rle_data) < total_len);
+
+			p += img_bytes_per_pixel;
+
+		} else {
+
+			if (!len) break;
+
+			do
+			{
+				psplash_fb_plot_pixel (fb, x+dx, y+dy, *p, *(p+1), *(p+2));
+				if (++dx >= img_width) { dx=0; dy++; }
+				p += img_bytes_per_pixel;
+			}
+			while (--len && (p - rle_data) < total_len);
+		}
+	}
+}

Added: trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/framebuffer.h
===================================================================
--- trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/framebuffer.h	2007-12-05 22:18:41 UTC (rev 3582)
+++ trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/framebuffer.h	2007-12-06 01:05:17 UTC (rev 3583)
@@ -0,0 +1,64 @@
+/*
+ *  pslash - a lightweight framebuffer splashscreen for embedded devices.
+ *
+ *  Copyright (c) 2006 Matthew Allum <mallum at o-hand.com>
+ *
+ *  Adapted for the OpenMoko Media Player by Soeren Apel (abraxa at dar-clan.de)
+ *
+ *  This program 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 2, or (at your option)
+ *  any later version.
+ *
+ *  This program 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.
+ *
+ */
+
+#ifndef _HAVE_FRAMEBUFFER_H
+#define _HAVE_FRAMEBUFFER_H
+
+#include <glib.h>
+#include <termios.h>
+
+typedef struct
+{
+  gint fd;
+  struct termios save_termios;
+  gint type;
+  gint visual;
+  gint width, height;
+  gint bpp;
+  gint stride;
+  gchar *data;
+  gchar *base;
+
+  gint angle;
+  gint real_width, real_height;
+}  framebuffer;
+
+framebuffer* fb_new(int angle);
+
+void fb_free(framebuffer *fb);
+
+inline void
+psplash_fb_plot_pixel(framebuffer *fb,
+	gint x, gint y,
+	guint8 red, guint8 green, guint8 blue);
+
+void
+fb_draw_rect(framebuffer *fb,
+	gint x, gint y,
+	gint width, gint height,
+	guint8 red, guint8 green, guint8 blue);
+
+void
+fb_draw_image(framebuffer *fb,
+	gint x, gint y,
+	gint img_width, gint img_height,
+	gint img_bytes_per_pixel,
+	guint8 *rle_data);
+
+#endif

Modified: trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/guitools.c
===================================================================
--- trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/guitools.c	2007-12-05 22:18:41 UTC (rev 3582)
+++ trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/guitools.c	2007-12-06 01:05:17 UTC (rev 3583)
@@ -27,12 +27,10 @@
 #include <gtk/gtk.h>
 
 #include "guitools.h"
+#include "utils.h"
 
-/// Absolute path to the UI pixmaps
-gchar *omp_ui_image_path = NULL;
 
 
-
 /**
  * Creates a label with default properties, wraps it up in a GtkAlignment and returns the latter for direct use
  * @param label Will be filled with a GtkLabel
@@ -104,91 +102,6 @@
 }
 
 /**
- * Creates a button containing a stock image
- * @param widget_name Name to set for the button and image widgets
- * @param image_name Name of the stock image to use
- * @param image Destination for the image's handle (can be NULL)
- * @param callback Callback to set
- * @return The button
- */
-GtkWidget *
-button_create_with_image(gchar *widget_name, gchar *image_name, GtkWidget **image, GCallback callback)
-{
-	GtkWidget *btn_image, *button;
-
-	button = gtk_button_new();
-	gtk_widget_set_name(GTK_WIDGET(button), widget_name);
-	g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(callback), NULL);
-	GTK_WIDGET_UNSET_FLAGS(GTK_WIDGET(button), GTK_CAN_FOCUS);
-
-	btn_image = gtk_image_new();
-	gtk_widget_set_name(GTK_WIDGET(btn_image), widget_name);
-	gtk_image_set_from_stock(GTK_IMAGE(btn_image), image_name, -1);
-	gtk_container_add(GTK_CONTAINER(button), GTK_WIDGET(btn_image));
-
-	if (image) *image = btn_image;
-
-	return button;
-}
-
-/**
- * Wraps a widget in an invisible GtkFrame so the widget can be padded using x/ythickness in the frame's style
- * @param widget Widget to be put inside the frame
- * @param name Name to assign to the frame, uses the widget's name if NULL
- * @return Frame containing the widget
- */
-GtkWidget *
-widget_wrap(GtkWidget *widget, gchar *name)
-{
-	GtkWidget *frame;
-
-	frame = gtk_frame_new(NULL);
-	gtk_widget_set_name(GTK_WIDGET(frame), (name) ? name : gtk_widget_get_name(widget));
-	gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_NONE);
-	gtk_container_add(GTK_CONTAINER(frame), GTK_WIDGET(widget));
-
-	return frame;
-}
-
-/**
- * Presents a simple non-modal error dialog to the user
- */
-void
-error_dialog(gchar *message)
-{
-	GtkWidget *dialog;
-
-	dialog = gtk_message_dialog_new(0,
-		GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
-		"%s", message);
-
-	// We don't want a title of "<unnamed>"
-	gtk_window_set_title(GTK_WINDOW(dialog), " ");
-
-	g_signal_connect_swapped(dialog, "response", G_CALLBACK (gtk_widget_destroy), dialog);
-	gtk_widget_show_all(dialog);
-}
-
-/**
- * Presents a simple modal error dialog to the user
- */
-void
-error_dialog_modal(gchar *message)
-{
-	GtkWidget *dialog;
-
-	dialog = gtk_message_dialog_new(0,
-		GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
-		"%s", message);
-
-	// We don't want a title of "<unnamed>"
-	gtk_window_set_title(GTK_WINDOW(dialog), " ");
-
-	gtk_dialog_run(GTK_DIALOG(dialog));
-	gtk_widget_destroy(dialog);
-}
-
-/**
  * Loads an image from disk and adds it to a given container, returning a reference to the image as well
  */
 void
@@ -213,25 +126,6 @@
 }
 
 /**
- * Adds a child to a GtkNotebook, filling the page handle with a stock icon
- */
-void
-notebook_add_page_with_stock(GtkWidget *notebook, GtkWidget *child, const gchar *icon_name, int padding)
-{
-	GtkWidget *icon, *alignment;
-
-	icon = gtk_image_new_from_icon_name(icon_name, GTK_ICON_SIZE_LARGE_TOOLBAR);
-
-	alignment = gtk_alignment_new(0.5, 0.5, 1, 1);
-	gtk_alignment_set_padding(GTK_ALIGNMENT(alignment), padding, padding, padding, padding);
-	gtk_container_add(GTK_CONTAINER(alignment), icon);
-	gtk_widget_show_all(GTK_WIDGET(alignment));
-
-	gtk_notebook_append_page(GTK_NOTEBOOK(notebook), child, alignment);
-	gtk_container_child_set(GTK_CONTAINER(notebook), child, "tab-expand", TRUE, NULL);
-}
-
-/**
  * Adds a child to a GtkNotebook, filling the page handle with an application-specific image
  */
 void

Modified: trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/guitools.h
===================================================================
--- trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/guitools.h	2007-12-05 22:18:41 UTC (rev 3582)
+++ trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/guitools.h	2007-12-06 01:05:17 UTC (rev 3583)
@@ -31,8 +31,6 @@
 
 #include "main.h"
 
-extern gchar *omp_ui_image_path;
-
 GdkPixbuf *pixbuf_new_from_file(const gchar* file_name);
 
 GtkWidget *label_create(GtkWidget **label, gchar *font_info, gchar *color_desc,
@@ -40,15 +38,9 @@
 
 GtkWidget *button_create_with_image(gchar *widget_name, gchar *image_name, GtkWidget **image, GCallback callback);
 
-GtkWidget *widget_wrap(GtkWidget *widget, gchar *name);
-
-void error_dialog(gchar *message);
-void error_dialog_modal(gchar *message);
-
 void container_add_image_with_ref(GtkContainer *container, gchar *image_name, GtkWidget **image);
 void container_add_image(GtkContainer *container, gchar *image_name);
 
-void notebook_add_page_with_stock(GtkWidget *notebook, GtkWidget *child, const gchar *icon_name, int padding);
 void notebook_add_page_with_image(GtkWidget *notebook, GtkWidget *child, const gchar *image_name, int padding);
 
 #endif

Modified: trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/main.c
===================================================================
--- trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/main.c	2007-12-05 22:18:41 UTC (rev 3582)
+++ trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/main.c	2007-12-06 01:05:17 UTC (rev 3583)
@@ -47,12 +47,12 @@
 #include "main.h"
 #include "editor_page.h"
 #include "files_page.h"
-#include "guitools.h"
 #include "main_page.h"
 #include "persistent.h"
 #include "playlist_page.h"
 #include "playlist.h"
 #include "playback.h"
+#include "utils.h"
 
 // Determines how the segfault handler terminates the program
 //define HANDLE_SIGSEGV

Modified: trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/main_page.c
===================================================================
--- trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/main_page.c	2007-12-05 22:18:41 UTC (rev 3582)
+++ trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/main_page.c	2007-12-06 01:05:17 UTC (rev 3583)
@@ -42,6 +42,7 @@
 #include "playlist.h"
 #include "playback.h"
 #include "persistent.h"
+#include "utils.h"
 
 // This is the amount the cursor must have moved in either direction to be considered moving
 // If we don't do this then it will constantly trigger gesture recognition due to jitter on the touchscreen
@@ -289,28 +290,20 @@
 	switch (main_gesture_data.gesture)
 	{
 		case OMP_MAIN_GESTURE_LEFT:
-		{
 			omp_main_rewind_clicked(NULL, NULL);
 			break;
-		}
 
 		case OMP_MAIN_GESTURE_RIGHT:
-		{
 			omp_main_fast_forward_clicked(NULL, NULL);
 			break;
-		}
 
 		case OMP_MAIN_GESTURE_UP:
-		{
 			omp_playback_set_volume(min(100, omp_playback_get_volume()+10));
 			break;
-		}
 
 		case OMP_MAIN_GESTURE_DOWN:
-		{
 			omp_playback_set_volume(max(0, omp_playback_get_volume()-10));
 			break;
-		}
 
 		default: break;
 	}
@@ -732,8 +725,8 @@
 	g_signal_connect(G_OBJECT(omp_window), OMP_EVENT_PLAYLIST_TRACK_CHANGED,
 		G_CALLBACK(omp_main_update_track_change), NULL);
 
-	g_signal_connect(G_OBJECT(omp_window), OMP_EVENT_PLAYLIST_TRACK_INFO_CHANGED,
-		G_CALLBACK(omp_main_update_track_info_changed), NULL);
+//	g_signal_connect(G_OBJECT(omp_window), OMP_EVENT_PLAYLIST_TRACK_INFO_CHANGED,
+//		G_CALLBACK(omp_main_update_track_info_changed), NULL);
 
 	g_signal_connect(G_OBJECT(omp_window), OMP_EVENT_PLAYLIST_TRACK_COUNT_CHANGED,
 		G_CALLBACK(omp_main_update_track_change), NULL);
@@ -817,7 +810,7 @@
 omp_main_get_video_window()
 {
 	if (GTK_WIDGET_NO_WINDOW(omp_main_widgets.cover_eventbox))
-		g_error(_("Video display widget has no window!\n"));
+		g_error("Video display widget has no window!\n");
 
 	return GDK_WINDOW_XWINDOW(omp_main_widgets.cover_eventbox->window);
 }
@@ -844,7 +837,8 @@
 	static gint old_track_id = 0;
 	static gulong old_track_length = 0;
 
-	gulong track_length, track_position;
+	gulong track_length = 0;
+	gulong track_position = 0;
 	gchar *artist = NULL;
 	gchar *title = NULL;
 	gchar *text;
@@ -854,7 +848,7 @@
 	if (omp_config_get_main_ui_show_cover())
 	{
 		gtk_image_set_from_stock(GTK_IMAGE(omp_main_widgets.cover_image), "no_cover", -1);
-		gtk_widget_queue_draw(omp_main_widgets.cover_image);	// Re-draw the default cover
+		gtk_widget_queue_draw(omp_main_widgets.cover_frame);	// Re-draw the default cover
 	}
 
 	// Set preliminary artist/title strings (updated on incoming metadata)
@@ -950,10 +944,10 @@
 void
 omp_main_update_show_cover_art(gpointer instance, gboolean flag, gpointer user_data)
 {
-/*	if (flag)
+	if (flag)
 		gtk_widget_show(omp_main_widgets.cover_frame);
 	else
-		gtk_widget_hide(omp_main_widgets.cover_frame); */
+		gtk_widget_hide(omp_main_widgets.cover_frame);
 }
 
 /**
@@ -964,11 +958,9 @@
 {
 	// Update Play/Pause button pixmap
 	if (omp_playback_get_state() == OMP_PLAYBACK_STATE_PAUSED)
-	{
 		gtk_image_set_from_stock(GTK_IMAGE(omp_main_widgets.play_pause_button_image), "play", -1);
-	} else {
+	else
 		gtk_image_set_from_stock(GTK_IMAGE(omp_main_widgets.play_pause_button_image), "pause", -1);
-	}
 }
 
 /**
@@ -1097,4 +1089,3 @@
 	if ( (frame) && (new_type != OMP_MAIN_LABEL_HIDDEN) )
 		gtk_widget_show(frame);
 }
-

Added: trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/mplayer_playback.c
===================================================================
--- trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/mplayer_playback.c	2007-12-05 22:18:41 UTC (rev 3582)
+++ trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/mplayer_playback.c	2007-12-06 01:05:17 UTC (rev 3583)
@@ -0,0 +1,1091 @@
+/*
+ *  OpenMoko Media Player
+ *   http://openmoko.org/
+ *
+ *  Copyright (C) 2007 by Soeren Apel (abraxa at dar-clan.de)
+ *
+ *  This program 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 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program 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 this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/**
+ * @file mplayer_playback.c
+ * Playback engine interface for utilizing mplayer
+ */
+
+#include <fcntl.h>
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <glib/gprintf.h>
+#include <linux/vt.h>
+#include <linux/kd.h>
+#include <linux/input.h>
+#include <tslib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "framebuffer.h"
+#include "main.h"
+#include "main_page.h"
+#include "mplayer_playback.h"
+#include "mplayer_startup_img.h"
+#include "persistent.h"
+#include "playback.h"
+#include "utils.h"
+
+// Originally defined in linux/stat.h
+#define S_IFIFO  0010000
+
+// If set to 1 the media player will make sure we're using a sane fbset
+#define OMP_MPLAYBACK_FBSET_CHECK 0
+
+// ttyX that X11 uses
+#define OMP_MPLAYBACK_X_CONSOLE 3
+
+// ttyX that we will let mplayer use for framebuffer access
+#define OMP_MPLAYBACK_FB_CONSOLE 4
+
+// LCM driver state file
+#define OMP_MPLAYBACK_LCM_STATE_FILE "/sys/devices/platform/s3c24xx-spi-gpio.1/spi0.0/state"
+
+// Named pipe to use for mplayer interface
+#define OMP_MPLAYBACK_PIPE_NAME "/tmp/mplayer_pipe"
+
+// Command lines used to run mplayer in X11 and framebuffer modes
+#define OMP_MPLAYBACK_MPLAYER_BINARY "/usr/bin/mplayer"
+#define OMP_MPLAYBACK_X_CMDLINE "-slave -input file=%s -really-quiet -framedrop -noslices -lavdopts fast -pp 0 -sws 0 -wid %d -zoom %s"
+#define OMP_MPLAYBACK_FB_CMDLINE "-slave -input file=%s -really-quiet -framedrop -noslices -lavdopts fast -pp 0 -sws 0 -vo fbdev -vf rotate=2 %s"
+// -zoom -vf scale=320:-3,rotate=2
+// -idle
+
+// This is the amount the cursor must have moved in either direction to be considered moving
+// If we don't do this then it will constantly trigger gesture recognition due to jitter on the touchscreen
+#define OMP_MP_MIN_CURSOR_DELTA 3
+
+
+/// Is set if fbset is usable for our purposes
+gboolean omp_mplayback_fbset_usable = FALSE;
+
+/// URI of currently loaded resource
+gchar *omp_mplayback_uri = NULL;
+
+/// Status of mplayer playback engine
+gboolean omp_mplayback_playing = FALSE;
+
+/// Pipe handle used to communicate with the mplayer slave instance
+gint omp_mplayback_pipe = -1;
+
+/// Flag stating whether mplayer was already sent a "quit" command
+gboolean omp_mplayback_terminating;
+
+/// Flag stating whether we're windowed (X11) or in fullscreen (FB)
+gboolean omp_mplayback_windowed;
+
+/// File handle of the touchscreen input device
+GPollFD omp_mplayback_ts_fd;
+
+/// Touchscreen device handle for use with tslib
+struct tsdev *omp_mplayback_ts_dev = NULL;
+
+/// This source notifies us when the touchscreen received input
+GSource *omp_mplayback_ts_source = NULL;
+
+/// The overlay window's handle
+GdkWindow *omp_mplayback_overlay_window = NULL;
+
+
+/// Possible gestures
+typedef enum
+{
+	OMP_MP_GESTURE_NONE,
+	OMP_MP_GESTURE_LEFT,
+	OMP_MP_GESTURE_RIGHT,
+	OMP_MP_GESTURE_UP,
+	OMP_MP_GESTURE_DOWN,
+	OMP_MP_GESTURE_TAP,
+} omp_mp_gesture;
+
+/// Holds the necessary infos to record and identify the gestures
+struct
+{
+	gboolean pressed;
+	GTimeVal start_time;
+	guint x_origin, y_origin;
+	guint last_x, last_y;
+	gboolean cursor_idle;        // TRUE if cursor isn't moving
+
+	gint radius, angle;
+	omp_mp_gesture gesture;
+	gboolean repeating;
+	guint repeat_timeout;
+} mp_gesture_data;
+
+
+// Some forward declarations for internal use
+void omp_mplayback_mplayer_terminate();
+void omp_mplayback_pause();
+
+
+/*************************************************************************************************/
+/*************************************** Gesture recognition *************************************/
+/*************************************************************************************************/
+
+/**
+ * Tries to determine which gesture the user has performed and fills the gesture data struct accordingly
+ * @param x X coordinate of current pressure point
+ * @param y Y coordinate of current pressure point
+ */
+void
+omp_mplayback_gesture_identify(guint x, guint y)
+{
+	gint delta_x, delta_y, gamma;
+
+	// Perform rect->polar conversion of the differential cursor movement
+	delta_x = x - mp_gesture_data.x_origin;
+	delta_y = y - mp_gesture_data.y_origin;
+
+	mp_gesture_data.radius = approx_radius(delta_x, delta_y);
+
+	// angle = arccos(gamma) but arccos() is too slow to compute -> range comparison
+	// We shift the comma by 3 digits so we can use integer math
+	gamma = delta_x*1000 / mp_gesture_data.radius;
+
+	if (mp_gesture_data.radius > omp_config_get_min_gesture_radius())
+	{
+
+		// Determine direction of movement
+		if (gamma < -707)
+		{
+			mp_gesture_data.gesture = OMP_MP_GESTURE_LEFT;
+
+		} else {
+
+			if (gamma > 707)
+			{
+				mp_gesture_data.gesture = OMP_MP_GESTURE_RIGHT;
+			} else {
+				mp_gesture_data.gesture = (delta_y < 0) ? OMP_MP_GESTURE_UP : OMP_MP_GESTURE_DOWN;
+			}
+
+		}
+
+	} else {
+
+		// Radius too small
+		mp_gesture_data.gesture = OMP_MP_GESTURE_TAP;
+	}
+
+g_printf("G:%d\t", mp_gesture_data.gesture);
+}
+
+/**
+ * Performs the action the current gesture commands
+ */
+void
+omp_mplayback_gesture_trigger()
+{
+	if (mp_gesture_data.gesture == OMP_MP_GESTURE_NONE) return;
+
+	switch (mp_gesture_data.gesture)
+	{
+		// The gestures are rotated by 90 degrees!
+		case OMP_MP_GESTURE_DOWN:
+			write(omp_mplayback_pipe, "seek -10\n", 9);
+			break;
+
+		case OMP_MP_GESTURE_UP:
+			write(omp_mplayback_pipe, "seek 10\n", 8);
+			break;
+
+		case OMP_MP_GESTURE_LEFT:
+			omp_playback_set_volume(min(100, omp_playback_get_volume()+10));
+			break;
+
+		case OMP_MP_GESTURE_RIGHT:
+			omp_playback_set_volume(max(0, omp_playback_get_volume()-10));
+			break;
+
+		case OMP_MP_GESTURE_TAP:
+			omp_mplayback_mplayer_terminate();
+
+/*
+			if (omp_mplayback_playing)
+				omp_mplayback_pause();
+			else
+				omp_mplayback_play();
+*/
+			break;
+
+		default: break;
+	}
+}
+
+/**
+ * This callback repeatedly performs the action the current gesture commands
+ */
+static gboolean
+omp_mplayback_gesture_repeat_callback(gpointer data)
+{
+	if (!mp_gesture_data.repeating) return FALSE;
+
+	omp_mplayback_gesture_trigger();
+
+	return TRUE;
+}
+
+/**
+ * Sets up the repeat timeout if the touchscreen is being pressed for a certain amount of time
+ */
+void
+omp_mplayback_gesture_check_repeat()
+{
+	GTimeVal current_time, delta_t;
+
+	if (!mp_gesture_data.repeating)
+	{
+		// Calculate duration of touchscreen press
+		g_get_current_time(&current_time);
+		delta_t.tv_sec  = current_time.tv_sec  - mp_gesture_data.start_time.tv_sec;
+		delta_t.tv_usec = current_time.tv_usec - mp_gesture_data.start_time.tv_usec;
+
+		if (delta_t.tv_usec >= omp_config_get_gesture_repeat_tresh()*1000)
+		{
+			mp_gesture_data.repeating = TRUE;
+			g_timeout_add(omp_config_get_gesture_repeat_intv(), omp_mplayback_gesture_repeat_callback, NULL);
+		}
+
+	}
+}
+
+/**
+ * Gets called whenever the cursor has been moved, handling gesture recognition and triggering
+ */
+void
+omp_mplayback_pointer_moved(guint x, guint y)
+{
+	gint delta_last_x, delta_last_y;
+
+	if (mp_gesture_data.pressed)
+	{
+		delta_last_x = abs(x - mp_gesture_data.last_x);
+		delta_last_y = abs(y - mp_gesture_data.last_y);
+
+		// Did the cursor move a substantial amount?
+		if ( (delta_last_x > OMP_MP_MIN_CURSOR_DELTA) && (delta_last_y > OMP_MP_MIN_CURSOR_DELTA) )
+		{
+			// Yes it did, so it's most likely being moved
+			mp_gesture_data.cursor_idle = FALSE;
+			mp_gesture_data.last_x = x;
+			mp_gesture_data.last_y = y;
+
+			// Make sure we won't trigger anymore (if we were before, that is)
+			mp_gesture_data.repeating = FALSE;
+
+		} else {
+
+			// Cursor is idle, so lets update the gesture data if it wasn't idle before
+			if (!mp_gesture_data.cursor_idle)
+				omp_mplayback_gesture_identify(x, y);
+
+			omp_mplayback_gesture_check_repeat();
+
+			mp_gesture_data.cursor_idle = TRUE;
+		}
+	}
+}
+
+/**
+ * Gets called whenever the touchscreen has been pressed, handling gesture recognition and triggering
+ */
+void
+omp_mplayback_pointer_pressed(guint x, guint y)
+{
+	mp_gesture_data.pressed = TRUE;
+	g_get_current_time(&mp_gesture_data.start_time);
+	mp_gesture_data.x_origin = x;
+	mp_gesture_data.y_origin = y;
+	mp_gesture_data.last_x = x;
+	mp_gesture_data.last_y = y;
+	mp_gesture_data.cursor_idle = FALSE;
+	mp_gesture_data.gesture = OMP_MP_GESTURE_NONE;
+	mp_gesture_data.repeating = FALSE;
+}
+
+/**
+ * Gets called whenever the touchscreen has been released, handling gesture recognition and triggering
+ */
+void
+omp_mplayback_pointer_released()
+{
+	// Stop repeat trigger if necessary - or trigger action
+	if (mp_gesture_data.repeating)
+	{
+		mp_gesture_data.repeating = FALSE;
+	} else {
+		omp_mplayback_gesture_trigger();
+	}
+
+	mp_gesture_data.pressed = FALSE;
+}
+
+/*************************************************************************************************/
+/****************************************** QVGA handling ****************************************/
+/*************************************************************************************************/
+
+/**
+ * Determines the current display mode
+ * @return TRUE if display is in QVGA mode, FALSE otherwise
+ */
+gboolean
+omp_mplayback_is_qvga()
+{
+	gchar *state;
+	gboolean is_qvga;
+
+	// Fetch and determine display state
+	if (g_file_get_contents(OMP_MPLAYBACK_LCM_STATE_FILE, &state, NULL, NULL))
+	{
+		is_qvga = strstr(state, "qvga") ? TRUE : FALSE;
+		g_free(state);
+
+	} else {
+
+		g_printerr("Could not read LCM state file: %s\n", OMP_MPLAYBACK_LCM_STATE_FILE);
+		is_qvga = FALSE;  // If kernel doesn't support QVGA it obviously can't be enabled
+	}
+
+	return is_qvga;
+}
+
+/**
+ * Switches between VGA/QVGA display modes
+ * @param qvga_state TRUE if display should switch to QVGA mode, FALSE for VGA
+ * @return TRUE for success, FALSE otherwise
+ */
+gboolean
+omp_mplayback_set_qvga(gboolean qvga_state)
+{
+	gint fd, tty;
+	gchar *vt;
+	FILE *fh;
+	gboolean result;
+	GError *error;
+
+	if (!omp_mplayback_fbset_usable)
+	{
+		g_printerr("No usable fbset was detected earlier, not switching display mode.\n");
+		return FALSE;
+	}
+
+#if 0
+	// Mute the kernel so it doesn't spill messages onto our neat console
+	fh = fopen("/proc/sys/kernel/printk", "w");
+	fputs("0", fh);
+	fclose(fh);
+
+	tty = qvga_state ? OMP_MPLAYBACK_FB_CONSOLE : OMP_MPLAYBACK_X_CONSOLE;
+	vt = g_strdup_printf("/dev/tty%d", tty);
+	fd = open(vt, O_RDWR);
+
+	This code does not return to X as it should
+
+	// Restore text mode on our FB terminal before switching back to X
+	if (!qvga_state)
+	{
+		if (ioctl(fd, KDSETMODE, KD_TEXT))
+		{
+			g_printerr("Could not change console mode on tty%d\n", tty);
+			goto error;
+		}
+	}
+
+	// Switch to a different VT
+	if (ioctl(fd, VT_ACTIVATE, tty))
+	{
+		g_printerr("Could not change terminal to %s (VT_ACTIVATE)\n", vt);
+		return FALSE;
+	}
+
+	if (ioctl(fd, VT_WAITACTIVE, tty))
+	{
+		g_printerr("Could not change terminal to %s (VT_WAITACTIVE)\n", vt);
+		goto error;
+	}
+
+	// Enable graphics mode on the FB terminal but leave the X terminal alone
+	if (qvga_state)
+	{
+		if (ioctl(fd, KDSETMODE, KD_GRAPHICS))
+		{
+			g_printerr("Could not change console mode on tty%d\n", tty);
+			goto error;
+		}
+	}
+
+	// Disable console blanking
+	if (qvga_state)
+	{
+		fh = fopen(vt, "w");
+		fputs("\27[9;0]", fh);
+		fclose(fh);
+	}
+
+	close(fd);
+	g_free(vt);
+#endif
+
+	// Run fbset
+	error = NULL;
+	g_spawn_command_line_sync(qvga_state ? "fbset qvga" : "fbset vga", NULL, NULL, NULL, &error);
+
+	if (error)
+	{
+		g_printerr("Could not invoke fbset: %s\n", error->message);
+		g_error_free(error);
+		goto error;
+	}
+
+	// Set LCM mode
+	// Can't use g_file_set_contents here because it doesn't write to the file directly
+	fh = fopen(OMP_MPLAYBACK_LCM_STATE_FILE, "w");
+
+	result = FALSE;
+	if (fh)
+	{
+		fputs(qvga_state ? "qvga-normal\n" : "normal\n", fh);
+		result = (fclose(fh) == 0);
+	}
+
+	if (!result)
+	{
+		g_printerr("Could not write to LCM state file: %s\n", OMP_MPLAYBACK_LCM_STATE_FILE);
+		goto error;
+	}
+
+	return TRUE;
+
+error:
+
+	omp_mplayback_set_qvga(FALSE);  // Try to get back to X
+	return FALSE;
+}
+
+/*************************************************************************************************/
+/***************************************** Overlay Window ****************************************/
+/*************************************************************************************************/
+
+/**
+ * Creates a black overlay window that covers the entire screen so that X doesn't interfere with mplayer
+ */
+void
+omp_mplayback_create_overlay()
+{
+	GdkWindowAttr attr;
+
+	if (omp_mplayback_overlay_window) return;
+
+	attr.width = 10;
+	attr.height = 10;
+	attr.wclass = GDK_INPUT_OUTPUT;
+	attr.window_type = GDK_WINDOW_TOPLEVEL;
+
+	omp_mplayback_overlay_window = gdk_window_new(omp_window->window, &attr, 0);
+
+	if (!omp_mplayback_overlay_window)
+	{
+		g_printerr("Failed creating the overlay window to intercept X I/O\n");
+		return;
+	}
+}
+
+/**
+ * Closes the overlay window
+ */
+void
+omp_mplayback_destroy_overlay()
+{
+	if (!omp_mplayback_overlay_window) return;
+
+	gdk_window_destroy(omp_mplayback_overlay_window);
+	omp_mplayback_overlay_window = NULL;
+}
+
+/**
+ * Shows the overlay window
+ */
+void
+omp_mplayback_show_overlay()
+{
+	gdk_window_show(omp_mplayback_overlay_window);
+	gdk_window_fullscreen(omp_mplayback_overlay_window);
+	gdk_window_set_keep_above(omp_mplayback_overlay_window, TRUE);
+
+	// Give X time to update before switching to QVGA and messing up the screen
+//	usleep(50*1000);  // 50ms
+}
+
+/**
+ * Hides the overlay window
+ */
+void
+omp_mplayback_hide_overlay()
+{
+	gdk_window_hide(omp_mplayback_overlay_window);
+}
+
+/*************************************************************************************************/
+/****************************************** Splash screen ****************************************/
+/*************************************************************************************************/
+
+/**
+ * Shows a "video player loading" splash screen using DirectFB
+ */
+void
+omp_mplayback_show_splash()
+{
+	framebuffer *fb;
+
+	fb = fb_new(0);
+
+	fb_draw_image(fb,
+		(fb->width - MPLAYER_STARTUP_IMG_WIDTH) >> 1,
+		(fb->height - MPLAYER_STARTUP_IMG_HEIGHT) >> 1,
+		MPLAYER_STARTUP_IMG_WIDTH,
+		MPLAYER_STARTUP_IMG_HEIGHT,
+		MPLAYER_STARTUP_IMG_BYTES_PER_PIXEL,
+		MPLAYER_STARTUP_IMG_RLE_PIXEL_DATA);
+
+	if (fb) fb_free(fb);
+}
+
+/*************************************************************************************************/
+/************************************* Touchscreen handling **************************************/
+/*************************************************************************************************/
+
+/**
+ * Perform any necessary preparations on incoming source events
+ */
+gboolean
+omp_mplayback_input_handler_prepare(GSource *source, gint *timeout)
+{
+	return FALSE;
+}
+
+/**
+ * Verify if we actually have data to handle
+ * @return TRUE if we have data to handle, FALSE otherwise
+ */
+gboolean
+omp_mplayback_input_handler_check(GSource *source)
+{
+	return omp_mplayback_ts_fd.revents & G_IO_IN;
+}
+
+/**
+ * Process the touchscreen event that we were notified of
+ */
+gboolean
+omp_mplayback_input_handler_dispatch(GSource *source, GSourceFunc callback, gpointer data)
+{
+	struct ts_sample sample;
+	static gint8 prev_pressure;
+
+	// We *could* directly read touchscreen information from the input device,
+	// however it would not be filtered at all (jitter/calibration/etc.) and thus
+	// pretty much pointless.
+	// When we get here we however know that a new event is waiting to be processed
+	// and so we simply query tslib as it does all the filtering.
+
+	if (ts_read(omp_mplayback_ts_dev, &sample, 1))
+	{
+		// Determine if we have a trigger/release situation or a mere position movement
+		if (sample.pressure && prev_pressure)
+		{
+			omp_mplayback_pointer_moved(sample.x, sample.y);
+g_printf("MOVE ");
+		} else {
+
+			prev_pressure = sample.pressure;
+
+			if (sample.pressure)
+g_printf("DOWN ");
+else
+g_printf("UP ");
+
+			if (sample.pressure)
+				omp_mplayback_pointer_pressed(sample.x, sample.y);
+			else
+				omp_mplayback_pointer_released();
+		}
+
+//		g_printf("%ld.%06ld: %6d %6d %6d\n", sample.tv.tv_sec, sample.tv.tv_usec, sample.x, sample.y, sample.pressure);
+	}
+
+	return TRUE;
+}
+
+/**
+ * Attaches input event handlers to the touchscreen so we can process user input
+ * @return TRUE for success, FALSE for failure
+ * @note Inspired by neod's buttonactions.c, written by Michael Lauer
+ */
+gboolean
+omp_mplayback_input_handler_attach()
+{
+	gchar *ts_device = NULL;
+
+	static GSourceFuncs funcs =
+	{
+		omp_mplayback_input_handler_prepare,
+		omp_mplayback_input_handler_check,
+		omp_mplayback_input_handler_dispatch,
+		NULL,
+	};
+
+	// Find touchscreen input device...
+	ts_device = getenv("TSLIB_TSDEVICE");
+	if (ts_device)
+	{
+		// ...and try to open it
+		omp_mplayback_ts_fd.fd = open(ts_device, O_RDONLY);
+
+	} else {
+
+		g_printerr("TSLIB_TSDEVICE environment variable not set, trying /dev/input/touchscreen0.\n");
+		omp_mplayback_ts_fd.fd = open("/dev/input/touchscreen0", O_RDONLY);
+	}
+
+	if (omp_mplayback_ts_fd.fd < 0)
+	{
+		g_printerr("Failed opening %s\n", ts_device);
+		return FALSE;
+	}
+
+	// Set up input event handlers and attach them to the main loop
+	omp_mplayback_ts_source = g_source_new(&funcs, sizeof(GSource));
+
+	omp_mplayback_ts_fd.events = G_IO_IN | G_IO_HUP | G_IO_ERR;
+	omp_mplayback_ts_fd.revents = 0;
+
+	g_source_add_poll(omp_mplayback_ts_source, &omp_mplayback_ts_fd);
+	g_source_attach(omp_mplayback_ts_source, NULL);
+
+	// Open tslib for the touchscreen device as well
+	omp_mplayback_ts_dev = ts_open(ts_device, 0);
+
+	if (omp_mplayback_ts_dev)
+	{
+		if (ts_config(omp_mplayback_ts_dev)) goto ts_error;
+
+	} else {
+
+		goto ts_error;
+	}
+
+	return TRUE;
+
+ts_error:
+
+	g_printerr("Failed opening tslib for %s!\n", ts_device);
+
+	// Clean up and return
+	if (omp_mplayback_ts_dev) ts_close(omp_mplayback_ts_dev);
+	omp_mplayback_ts_dev = NULL;
+
+	g_source_destroy(omp_mplayback_ts_source);
+	close(omp_mplayback_ts_fd.fd);
+
+	return FALSE;
+}
+
+/*************************************************************************************************/
+/**************************************** mplayer interface **************************************/
+/*************************************************************************************************/
+
+/**
+ * Gets called after mplayer was terminated
+ */
+void
+omp_mplayback_termination_callback(GPid pid, gint status, gpointer data)
+{
+	g_print("%s called\n", __FUNCTION__);
+
+	g_spawn_close_pid(pid);
+
+	// Stop listening for touchscreen input events
+	close(omp_mplayback_ts_fd.fd);
+	g_source_destroy(omp_mplayback_ts_source);
+
+	// Clean up the pipe
+	close(omp_mplayback_pipe);
+	omp_mplayback_pipe = -1;
+
+	// mplayer terminated so we want to get back into VGA mode
+	omp_mplayback_set_qvga(FALSE);
+
+	// Hide overlay window
+	omp_mplayback_hide_overlay();
+
+	g_free(omp_mplayback_uri);
+	omp_mplayback_uri = NULL;
+	omp_mplayback_playing = FALSE;
+
+	g_signal_emit_by_name(G_OBJECT(omp_window), OMP_EVENT_PLAYBACK_EOS);
+}
+
+/**
+ * Terminates mplayer
+ */
+void
+omp_mplayback_mplayer_terminate()
+{
+	if (omp_mplayback_pipe && !omp_mplayback_terminating)
+	{
+		omp_mplayback_terminating = TRUE;
+		write(omp_mplayback_pipe, "quit\n", 5);
+
+		g_printf("Terminating mplayer\n");
+	}
+}
+
+/**
+ * Initializes the mplayer playback interface
+ * @return TRUE on success, FALSE on failure
+ */
+gboolean
+omp_mplayback_init()
+{
+#if OMP_MPLAYBACK_FBSET_CHECK
+	gchar *fbset, *fbset_binary, *text;
+
+	// Check whether we'd be using busybox' fbset as that one simply does not work
+	omp_mplayback_fbset_usable = FALSE;
+	fbset = g_find_program_in_path("fbset");
+
+	if (fbset)
+	{
+		// We found fbset which is good...
+		omp_mplayback_fbset_usable = TRUE;
+
+		// ...but it might be a symlink to busybox
+		if (g_file_test(fbset, G_FILE_TEST_IS_SYMLINK))
+		{
+			fbset_binary = g_file_read_link(fbset, NULL);
+
+			// fbset is invalid if an error occured or it's the one busybox provides
+			if (!fbset_binary || strstr(fbset_binary, "busybox"))
+				omp_mplayback_fbset_usable = FALSE;
+
+			g_free(fbset_binary);
+		}
+
+		g_free(fbset);
+	}
+
+	if (!omp_mplayback_fbset_usable)
+	{
+		text = g_strdup(_("Could not find suitable fbset, please install the fbset package."));
+		g_printerr("%s\n", text);
+		error_dialog_modal(text);
+
+		g_free(text);
+		return FALSE;
+	}
+#else
+	omp_mplayback_fbset_usable = TRUE;
+#endif
+
+	// Prepare overlay window so we can be quicker when actually displaying it
+	omp_mplayback_create_overlay();
+
+	return TRUE;
+}
+
+/**
+ * Frees resources allocated for the mplayer playback interface
+ */
+void
+omp_mplayback_free()
+{
+	if (omp_mplayback_uri) g_free(omp_mplayback_uri);
+
+	// Shutdown mplayer interface if necessary
+	if (omp_mplayback_pipe != -1)
+	{
+		write(omp_mplayback_pipe, "quit\n", 5);
+		close(omp_mplayback_pipe);
+	}
+
+	// Make sure we leave the device in VGA mode on program termination
+	if (omp_mplayback_is_qvga()) omp_mplayback_set_qvga(FALSE);
+
+	// Remove overlay window
+	omp_mplayback_destroy_overlay();
+}
+
+
+/**
+ * Check whether URI is a movie file and loads it if it is
+ * @return TRUE if file was loaded, FALSE otherwise
+ */
+gboolean
+omp_mplayback_load_video_from_uri(gchar *uri)
+{
+	guint i, j;
+	gchar *extension, *cmd;
+	gboolean is_video;
+	g_print("%s: %s\n", __FUNCTION__, uri);
+
+	// Get file extension by finding last dot in the uri
+	for (i=0,j=0; uri[i]; i++)
+		if (uri[i] == '.') j = i;
+
+	extension = g_strdup(uri+j+1);
+	extension = g_ascii_strdown(extension, -1);
+
+	// Check extension to see if we have a playable file type
+	is_video = FALSE;
+
+	if ( (strcmp(extension, "mpg") == 0) ||
+	     (strcmp(extension, "mpeg") == 0) ||
+	     (strcmp(extension, "avi") == 0) ||
+	     (strcmp(extension, "3gp") == 0) ||
+	     (strcmp(extension, "wmv") == 0) )
+	{
+		is_video = TRUE;
+
+		// Copy URI
+		if (omp_mplayback_uri) g_free(omp_mplayback_uri);
+		omp_mplayback_uri = g_strdup(uri);
+
+		if (omp_mplayback_pipe != -1)
+		{
+			// mplayer was already running so we just set a new URI to play
+			cmd = g_strdup_printf("loadfile %s\n", uri);
+			write(omp_mplayback_pipe, cmd, strlen(cmd));
+			g_free(cmd);
+		}
+	}
+
+	// Scenario: video file loaded but not played, non-video file loaded afterwards
+	// In this case we need to empty the playback URI to make sure the old video won't
+	// be mistreated as the current file
+	if (!is_video && omp_mplayback_uri)
+	{
+		g_free(omp_mplayback_uri);
+		omp_mplayback_uri = NULL;
+	}
+
+	// Clean up
+	g_free(extension);
+
+	g_print("is_video = %d\n", is_video);
+
+	return is_video;
+}
+
+/**
+ * Tells whether the mplayer playback interface has a file loaded or not
+ * @return TRUE if a file is loaded, FALSE otherwise
+ */
+gboolean
+omp_mplayback_video_loaded()
+{
+	return omp_mplayback_uri != NULL;
+}
+
+/**
+ * Starts playback
+ */
+void
+omp_mplayback_play()
+{
+	#define MAX_ARGS 25
+	g_print("%s called\n", __FUNCTION__);
+
+	gchar *cmdline, *tmp, *msg;
+	gchar *argv[MAX_ARGS];
+	gint i, argc, cmdline_len;
+	GPid pid;
+	GError *error;
+
+	// play() should never be called without a valid URI set
+	g_return_if_fail(omp_mplayback_uri);
+
+	// Create mplayer instance if needed
+	if (omp_mplayback_pipe == -1)
+	{
+		omp_mplayback_terminating = FALSE;
+
+		// Reveal black overlay window to prevent X from receiving touchscreen events
+		omp_mplayback_show_overlay();
+
+		// Switch to QVGA mode
+		omp_mplayback_set_qvga(TRUE);
+
+		// Show splash screen
+		omp_mplayback_show_splash();
+
+		// Listen for input events, too
+		if (!omp_mplayback_input_handler_attach())
+		{
+			omp_mplayback_set_qvga(FALSE);
+			omp_mplayback_hide_overlay();
+			error_dialog_modal(_("Failed setting up touchscreen interface"));
+			return;
+		}
+
+		// Acquire pipe to talk to mplayer with
+		mkfifo(OMP_MPLAYBACK_PIPE_NAME, S_IFIFO|0666);
+		omp_mplayback_pipe = open(OMP_MPLAYBACK_PIPE_NAME, O_RDWR);
+
+		// Assemble command line
+		cmdline = g_strdup_printf(OMP_MPLAYBACK_FB_CMDLINE, OMP_MPLAYBACK_PIPE_NAME, omp_mplayback_uri);
+
+		#ifdef DEBUG
+			g_printf("Launching %s\n", cmdline);
+		#endif
+
+		// Split command line up
+		// To do this we replace all spaces by #0 and fill the argv array with the individual string chunks
+		argv[0] = OMP_MPLAYBACK_MPLAYER_BINARY;
+
+		cmdline_len = strlen(cmdline);
+		tmp = cmdline;
+		for (i=0,argc=1; (i<cmdline_len) && (argc<MAX_ARGS); argc++)
+		{
+			argv[argc] = tmp;
+			while ( (cmdline[i] != 32) && (cmdline[i] != 0) ) i++;
+			cmdline[i] = 0;
+			i++;
+			tmp = cmdline+i;
+		}
+		argv[argc] = NULL;
+
+		// Launch mplayer, making sure we can get notificed of its termination
+		error = NULL;
+		pid = 0;
+		g_spawn_async(NULL, argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL, &pid, &error);
+
+		if (error || !pid)
+		{
+			// ...back to VGA
+			omp_mplayback_set_qvga(FALSE);
+			omp_mplayback_hide_overlay();
+
+			msg = g_strdup_printf(_("Failed to launch mplayer (%s), falling back to gstreamer"), error->message);
+			g_printerr("%s\n", msg);
+			error_dialog_modal(msg);
+			g_free(msg);
+
+			g_error_free(error);
+			g_free(cmdline);
+			g_free(omp_mplayback_uri);
+			close(omp_mplayback_pipe);
+			omp_mplayback_pipe = -1;
+
+			return;
+		}
+
+		// Add child PID watcher
+		g_child_watch_add(pid, omp_mplayback_termination_callback, NULL);
+
+		// Clean up
+		g_free(cmdline);
+
+		omp_mplayback_windowed = FALSE;
+
+	} else {
+
+		// pipe is open, mplayer was already started
+		if (!omp_mplayback_playing)
+			write(omp_mplayback_pipe, "pause\n", 6);
+
+	}
+
+	omp_mplayback_playing = TRUE;
+}
+
+/**
+ * Pauses playback
+ */
+void
+omp_mplayback_pause()
+{
+	g_print("%s called\n", __FUNCTION__);
+
+	if ( (omp_mplayback_pipe != -1) && omp_mplayback_is_playing() )
+		write(omp_mplayback_pipe, "pause\n", 6);
+
+	omp_mplayback_playing = FALSE;
+}
+
+/**
+ * Tells the current status of the mplayer playback engine
+ * @return TRUE if playing, FALSE otherwise
+ */
+gboolean
+omp_mplayback_is_playing()
+{
+	g_print("%s: %d\n", __FUNCTION__, omp_mplayback_playing);
+
+	return omp_mplayback_playing;
+}
+
+/**
+ *
+ */
+gint
+omp_mplayback_get_state()
+{
+	g_print("%s: %d\n", __FUNCTION__, omp_mplayback_playing);
+
+	return omp_mplayback_playing ? OMP_PLAYBACK_STATE_PLAYING : OMP_PLAYBACK_STATE_PAUSED;
+}
+
+/**
+ *
+ */
+gulong
+omp_mplayback_get_video_position()
+{
+	return 0;
+}
+
+/**
+ *
+ */
+void
+omp_mplayback_set_video_position(gulong position)
+{
+}
+
+/**
+ * Sets mplayer volume
+ */
+void
+omp_mplayback_set_volume(guint volume)
+{
+	gchar *cmd;
+
+	if (!omp_mplayback_pipe) return;
+
+	cmd = g_strdup_printf("set_property volume %d\n", volume);
+g_printf(cmd);
+	write(omp_mplayback_pipe, cmd, strlen(cmd));
+	g_free(cmd);
+}

Added: trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/mplayer_playback.h
===================================================================
--- trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/mplayer_playback.h	2007-12-05 22:18:41 UTC (rev 3582)
+++ trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/mplayer_playback.h	2007-12-06 01:05:17 UTC (rev 3583)
@@ -0,0 +1,43 @@
+/*
+ *  OpenMoko Media Player
+ *   http://openmoko.org/
+ *
+ *  Copyright (C) 2007 by Soeren Apel (abraxa at dar-clan.de)
+ *
+ *  This program 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 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program 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 this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/**
+ * @file mplayer_playback.h
+ * Playback engine interface for utilizing mplayer
+ */
+
+#ifndef MPLAYER_PLAYBACK_H
+#define MPLAYER_PLAYBACK_H
+
+gboolean omp_mplayback_init();
+void omp_mplayback_free();
+
+gboolean omp_mplayback_load_video_from_uri(gchar *uri);
+gboolean omp_mplayback_video_loaded();
+
+void omp_mplayback_play();
+gboolean omp_mplayback_is_playing();
+gint omp_mplayback_get_state();
+gulong omp_mplayback_get_video_position();
+void omp_mplayback_set_video_position(gulong position);
+void omp_mplayback_set_volume(guint volume);
+
+#endif

Added: trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/mplayer_startup_img.h
===================================================================
--- trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/mplayer_startup_img.h	2007-12-05 22:18:41 UTC (rev 3582)
+++ trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/mplayer_startup_img.h	2007-12-06 01:05:17 UTC (rev 3583)
@@ -0,0 +1,1066 @@
+/* GdkPixbuf RGB C-Source image dump 1-byte-run-length-encoded */
+
+#define MPLAYER_STARTUP_IMG_ROWSTRIDE (720)
+#define MPLAYER_STARTUP_IMG_WIDTH (240)
+#define MPLAYER_STARTUP_IMG_HEIGHT (320)
+#define MPLAYER_STARTUP_IMG_BYTES_PER_PIXEL (3) /* 3:RGB, 4:RGBA */
+#define MPLAYER_STARTUP_IMG_RLE_PIXEL_DATA ((guint8*) \
+  "\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0" \
+  "\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0" \
+  "\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0" \
+  "\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" \
+  "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" \
+  "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" \
+  "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" \
+  "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" \
+  "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" \
+  "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" \
+  "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" \
+  "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" \
+  "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" \
+  "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" \
+  "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" \
+  "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" \
+  "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" \
+  "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" \
+  "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" \
+  "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\355\0\0\0\202" \
+  "\1\1\0\205\2\2\0\202\1\1\0\377\0\0\0\344\0\0\0\23\2\2\0\4\4\2\6\5\2\7" \
+  "\6\2\10\6\2\10\7\2\11\7\3\11\10\3\12\10\3\11\10\3\11\7\3\10\6\2\7\6\2" \
+  "\6\6\2\5\4\2\4\4\2\3\2\0\2\2\0\1\1\0\377\0\0\0\334\0\0\0\11\2\2\0\6\5" \
+  "\2\12\10\3\15\13\4\20\16\4\22\17\5\23\20\5\24\22\6\25\22\6\203\26\22" \
+  "\6\14\25\22\6\24\21\6\23\20\5\22\17\5\20\15\4\16\14\4\14\12\4\12\10\3" \
+  "\7\6\2\5\4\2\3\2\0\1\1\0\377\0\0\0\330\0\0\0\12\3\2\1\10\7\2\16\14\4" \
+  "\24\21\6\30\25\7\34\30\10\37\32\11\40\34\11\"\35\12\"\36\12\203#\36\12" \
+  "\17\"\36\12!\34\12\40\33\11\36\32\11\35\30\10\33\27\10\30\24\6\25\22" \
+  "\6\22\20\5\17\14\4\14\12\4\10\7\3\5\4\2\3\2\0\1\1\0\377\0\0\0\324\0\0" \
+  "\0\13\3\3\1\11\10\3\21\16\5\30\25\7\37\32\11%\40\12($\14*&\15,(\15.(" \
+  "\16.)\16\203.*\16\21.)\16-(\16,'\15*&\15)$\14'#\14%\40\13\"\35\12\37" \
+  "\32\11\33\27\10\27\24\6\23\20\6\20\15\4\13\12\3\7\6\2\4\3\1\1\1\0\377" \
+  "\0\0\0\321\0\0\0\11\3\2\1\11\10\3\22\17\5\32\27\10#\36\12*%\14""0*\16" \
+  "4.\20""5/\20\20260\20\1""71\20\20381\20\1""71\20\20260\20\20""5/\20""4" \
+  ".\20""2,\17""0*\16.(\16*&\15(#\14$\37\12\40\34\12\33\27\10\26\23\6\22" \
+  "\17\5\15\13\4\10\7\2\4\4\1\1\1\0\377\0\0\0\316\0\0\0$\2\2\0\10\7\2\21" \
+  "\16\5\32\26\7$\37\13,'\15""60\22E\77!OI,_Y<pjJ}wV\205\177[\212\204]\217" \
+  "\211`\215\206[\200xLxpCh`5SL%=6\24""82\20""81\20""71\20""5/\20""2-\17" \
+  "/*\16+&\15'\"\14#\36\12\36\32\10\30\24\6\23\20\5\15\13\4\10\7\2\4\3\1" \
+  "\377\0\0\0\315\0\0\0&\6\5\2\17\14\4$!\24FC2b_Mzvd\207\204q\210\204r\243" \
+  "\236\207\245\241\210\213\206r\203~l\202}j\200|h\206\202m\217\212s\233" \
+  "\227}\252\246\207\276\271\222\325\317\235\342\332\237\306\276\202\243" \
+  "\233c|tDNG!82\20""71\20""5/\20""2,\17.(\16)$\14$\40\12\36\32\10\30\25" \
+  "\6\22\17\5\14\12\4\6\6\2\2\2\0\377\0\0\0\312\0\0\0\11\16\16\14<;7fd]" \
+  "~|s\202\177r\206\202r\210\204r\225\221|\325\320\257\202\346\342\275\35" \
+  "\343\336\272\247\242\211~zf~ze|xczvbyu`xt_wr]vp\\\200{d\240\233|\304" \
+  "\276\222\341\332\240\343\333\230\305\274|\214\204PPI\"82\20""60\20""3" \
+  ".\17.)\16*%\14$\37\12\36\31\10\26\23\6\20\16\4\11\10\3\4\4\1\377\0\0" \
+  "\0\307\0\0\0\13\31\31\31MMMqpnvtp{xp\177|p\203\177p\206\202p\215\210" \
+  "u\334\330\266\346\342\277\203\346\342\276\36\241\234\203|xdzvawr_tp\\" \
+  "ro[so[rnYrnXqnWplVojTwrZ\243\236|\324\316\233\343\334\233\342\332\223" \
+  "\266\255old792\21""71\20""3-\17-(\16($\14\"\35\12\32\26\10\23\20\5\14" \
+  "\12\4\6\5\2\2\1\0\377\0\0\0\303\0\0\0\13\27\27\27SSSpppoonrqnutmzxn~" \
+  "|n\203\177m\204\177m\302\276\241\205\346\342\300\12\206\202lxt_tp]pm" \
+  "ZlhXjfWieVifTjfSjfR\202kfP\23kfNjeMjdL\223\215l\320\311\230\343\333\232" \
+  "\343\332\224\305\274{ph:82\21""60\20""1,\17+&\15%\40\13\36\32\10\26\23" \
+  "\6\16\14\4\10\6\2\2\2\1\377\0\0\0\300\0\0\0\2\11\11\11FFF\202nnn\11l" \
+  "lllljpokuskzxj~{k\202}k\220\213v\344\341\301\204\346\342\302\20\340\334" \
+  "\274xs^tp\\nkXifVd`S`]Q^[O^[N_\\M`]L`]Kb^Jc^Id`He`G\202d`F\17\226\220" \
+  "m\332\323\234\343\333\227\343\332\223\274\263s[S*82\20""4.\20.)\16(#" \
+  "\14\40\34\12\30\25\7\20\16\5\11\10\3\3\3\1\377\0\0\0\276\0\0\0\2$$$e" \
+  "ee\202lll\11jjjiiijjgnmgtrhyvh~{h\177{g\266\262\230\202\346\342\304\203" \
+  "\346\343\304\"\322\316\260up[plXhdTa^QZWNTRKRPJRPHOMCFC7B\77""0\77;)" \
+  "=9$@;#B>$FA$HC$LF)ZT5\255\246x\344\334\234\343\332\224\341\330\220\226" \
+  "\215V\77""8\25""60\20""0+\17*%\14\"\36\12\32\26\7\22\17\5\12\11\3\4\3" \
+  "\1\377\0\0\0\273\0\0\0\20\3\3\3AAAlllkkkjjjhhhfffffehhdmkespexue}ze}" \
+  "yd\330\325\270\346\343\304\203\346\343\305\24\346\342\305\307\303\247" \
+  "rnWlhTd`PZWLKIB00,\36\36\32\20\17\13\14\12\4\17\14\4\23\20\5\30\24\6" \
+  "\35\31\10\"\36\12(#\14-(\16""2,\17""60\20\20292\21\16rk\77\332\321\221" \
+  "\343\333\226\343\332\222\306\275{WO'81\20""2-\17,'\15$\37\12\34\30\10" \
+  "\23\20\6\12\11\3\4\3\1\377\0\0\0\271\0\0\0\17\12\12\12VVVkkkiiihhhff" \
+  "fdddbbbcbagfbljarobwsb{xb\212\205p\202\346\343\305\2\346\343\306\346" \
+  "\343\305\202\346\343\306$\276\272\237okTfbNIF3(%\27\15\13\4\3\2\0\0\0" \
+  "\0\1\1\0\2\2\0\5\4\2\10\7\2\14\12\4\21\16\5\26\23\6\35\31\10#\36\12(" \
+  "$\14.)\16""4.\20""82\20""92\21RK&\310\300\204\343\333\227\343\332\223" \
+  "\334\323\213tl<82\20""4.\20,(\16%\40\13\34\30\10\23\20\6\12\11\3\4\3" \
+  "\1\377\0\0\0\267\0\0\0\20\20\20\20\\\\\\iiihhhfffdddbbbaaa___``^dc^j" \
+  "h_qn`ur_xt_\242\235\205\202\346\343\306\204\346\343\307\7\263\257\224" \
+  "PJ.3.\22&!\13\30\25\7\14\12\4\3\2\0\204\0\0\0\32\1\1\0\3\2\1\7\6\2\14" \
+  "\12\4\21\17\5\30\24\6\36\32\10%\40\13+&\15""2-\17""71\20""92\21D<\32" \
+  "\271\260x\343\333\230\343\332\224\342\332\220\216\205N82\21""4/\17-(" \
+  "\16%!\13\34\30\10\22\20\5\11\10\3\2\2\1\377\0\0\0\265\0\0\0\21\21\21" \
+  "\21___hhhfffdddbbbaaa___^^^\\\\\\^][b`[if[ol\\tp\\vp\\\267\263\232\205" \
+  "\346\343\310\10\346\342\301\236\231v81\20""0*\16$\40\13\30\24\7\13\12" \
+  "\4\2\2\0\206\0\0\0\31\1\1\0\3\2\1\7\6\2\15\13\4\23\20\5\32\27\10\"\36" \
+  "\12)%\14""0+\17""60\20""92\21>7\25\260\250q\343\333\230\343\332\224\343" \
+  "\332\220\234\224Z:3\22""4/\17-(\16$\37\13\33\27\10\21\16\5\10\7\2\2\2" \
+  "\0\377\0\0\0\263\0\0\0\23\15\15\15]]]fffdddbbb```___^^^\\\\\\ZZZXXXZ" \
+  "ZW`^XgdXmjYrnYsoY\312\307\255\346\343\311\202\346\343\312\12\346\343" \
+  "\305\346\342\277\346\342\276\237\232w81\20""0*\16$\40\13\30\24\6\13\12" \
+  "\4\2\2\0\203\0\0\0\35\1\1\0\2\2\0\3\3\1\4\3\2\4\4\2\5\4\2\7\5\2\14\12" \
+  "\3\22\16\5\31\25\7!\34\11($\14""0*\16""60\20:2\21>7\25\263\253s\343\333" \
+  "\230\343\332\223\343\331\220\240\226[92\21""4.\20,'\16#\36\12\31\26\7" \
+  "\20\15\4\6\5\2\1\1\0\377\0\0\0\261\0\0\0\6\10\10\10VVVdddbbb```^^^\202" \
+  "\\\\\\\13ZZZXXXVVVUUUXVT^\\UebVkhVplVqmV\332\326\275\202\347\343\312" \
+  "+\346\342\302\346\342\300\346\342\301\346\342\302\237\232y81\20""0*\16" \
+  "$\40\13\30\24\6\13\12\4\2\2\0\0\0\0\2\2\0\4\4\1\11\7\2\14\11\3\17\14" \
+  "\4\21\14\4\21\15\4\20\15\4\20\14\4\17\14\4\21\16\5\25\21\6\33\26\7#\35" \
+  "\12+%\14""3,\17""91\20<4\21D<\27\302\271~\343\333\227\343\332\223\343" \
+  "\331\220\224\213S82\21""3-\17*%\15!\34\12\27\24\6\15\13\4\5\4\2\377\0" \
+  "\0\0\260\0\0\0\25\1\1\1HHHbbb```^^^\\\\\\ZZZYYYXXXVVVTTTRRRRRQUUQ\\Y" \
+  "Qb_RjeRniSrmV\346\342\312\346\343\311\202\346\342\302\20\346\342\303" \
+  "\346\342\304\346\343\304\242\236~81\20""0*\16$\40\13\30\24\7\13\12\4" \
+  "\4\3\1\5\4\1\13\10\3\21\15\4\26\22\6\33\26\7\36\30\10\203\40\32\10\2" \
+  "\36\30\10\34\25\6\202\30\22\6\24\32\24\6\37\30\10&\36\12.&\15""6-\17" \
+  "=3\21 at 5\21PF\37\322\312\214\343\333\226\343\332\222\343\331\216~vA92" \
+  "\20""2,\17($\14\37\32\11\24\21\6\12\11\3\2\2\1\377\0\0\0\257\0\0\0\25" \
+  "000```^^^\\\\\\ZZZXXXWWWVVVTTTRRRPPPNNNNNMRRMYWMa^PhdPlfPzt^\346\343" \
+  "\310\346\342\303\202\346\343\304\1\346\343\305\202\346\343\306)\247\242" \
+  "\20481\20""0+\16&!\13\31\26\7\17\14\4\14\11\3\21\15\4\30\23\6\37\31\10" \
+  "%\36\12*\"\13.&\14""0'\15""1(\15""1&\15/%\14+!\12&\35\11\"\31\7\37\27" \
+  "\6\40\30\7\"\32\10*\40\12""2(\15:/\20A5\22B6\22h^3\341\330\227\343\332" \
+  "\226\343\332\222\340\326\212bZ,:2\20""2+\16'\"\13\34\30\10\21\16\5\7" \
+  "\6\2\1\1\0\377\0\0\0\255\0\0\0\26\24\24\24]]]\\\\\\ZZZXXXWWWUUUTTTRR" \
+  "RPPPNNNMMMKKKKKJPNJWTK^[LeaLjeMojM\346\343\304\346\343\305\202\346\343" \
+  "\306\203\346\343\310\16\256\252\21692\20""3,\17)#\14\36\31\10\27\22\6" \
+  "\26\21\5\35\26\7&\36\12.$\14""4*\16:.\20=2\20\77""3\20\202 at 4\20\31=0" \
+  "\20:-\17""5(\16/$\14(\35\11%\32\10$\30\10(\34\11.#\14""7+\16>2\21D8\22" \
+  "E9\22\224\211V\344\333\231\343\332\224\343\332\220\321\307|J@\31;2\20" \
+  "1*\16&\40\13\30\24\6\15\13\4\4\4\1\377\0\0\0\254\0\0\0\31\2\2\2NNNZZ" \
+  "ZXXXWWWUUUTTTRRRPPPNNNMMMKKKJJJHHHHHFMLGTRG\\XHb^ILF)e_\77\346\343\306" \
+  "\346\343\307\346\343\310\346\343\311\202\346\343\312\17\346\343\313\267" \
+  "\263\230<4\21""6.\17-%\14%\36\12\"\32\10#\32\10*\40\12""3(\15;/\17A5" \
+  "\21F8\22G:\22H:\23\202J;\23\32I:\23F8\22B4\21<.\17""4'\14,\40\12(\34" \
+  "\10)\34\10+\37\12""3'\14</\17C6\21H;\23K=\26\310\275\202\343\333\227" \
+  "\343\332\222\343\331\216\254\240_A6\22:0\20""0(\15!\34\11\23\20\5\11" \
+  "\10\3\2\2\0\377\0\0\0\253\0\0\0\31***XXXWWWUUUTTTRRRPPPNNNMMMKKKJJJH" \
+  "HHFFFDDDDDBJICQODZVEHB(82\20nhH\346\343\310\346\343\311\346\343\312\346" \
+  "\343\313\202\346\343\314+\346\343\316\302\276\245 at 5\21:0\20""2)\16.$" \
+  "\14-#\13.$\14""6*\16\77""2\20F8\22J;\23fY4\250\241\201\320\314\256\327" \
+  "\322\264\312\304\244\240\226r]M%L<\23G7\21 at 0\20""7(\15/\40\12*\34\10" \
+  "*\35\10""0#\12""9+\16B3\20H9\22J<\24l^1\343\332\231\343\332\225\343\332" \
+  "\221\343\331\213sg4B6\22""9.\20,$\14\32\26\10\17\14\4\5\4\2\377\0\0\0" \
+  "\252\0\0\0\31\7\7\7SSSUUUTTTRRRPPPNNNLLLKKKJJJHHHFFFDDDBBB@@@AA\77FE" \
+  "@OL at D@,2-\17""82\20rmN\346\343\312\346\343\313\346\343\314\202\346\343" \
+  "\316-\347\344\316\347\344\317\316\313\264B6\22>3\21""9.\17""7+\16""6" \
+  "*\16""9,\16 at 2\20I:\22L<\24\224\212j\345\342\312\346\343\313\346\343\311" \
+  "\346\343\310\346\343\306\346\343\305\337\332\273\177qIQ>\24K9\22B0\20" \
+  "8'\14""0\40\12,\35\11/\40\11""8'\14\77""0\17F6\21M<\23N=\24\260\245m" \
+  "\343\333\230\343\332\223\343\331\216\317\304yK>\26 at 4\21""6+\16\"\35\12" \
+  "\24\22\6\12\10\3\2\2\0\377\0\0\0\251\0\0\0\32\37\37\37QQQRRRPPPNNNLL" \
+  "LJJJIIIHHHFFFDDDBBB@@@>>><<<==<CB<@=1(#\15""2-\17""82\20vpS\346\343\314" \
+  "\346\343\316\347\344\316\347\344\317\203\347\344\320\4\333\330\304E9" \
+  "\22C6\22 at 3\20\202 at 2\20%B4\20I9\22N=\24\214\200`\347\344\317\346\343\316" \
+  "\346\343\314\346\343\313\346\343\312\346\343\310\346\343\307\346\343" \
+  "\305\343\337\300ve<S\77\24L8\21B.\16""8%\13""0\36\11""0\36\10""5#\12" \
+  ">,\15F4\20N;\23P>\24dT'\342\332\231\343\332\226\343\332\221\343\331\213" \
+  "\220\203HG:\22>2\20-$\14\32\26\10\16\14\4\5\4\2\377\0\0\0\252\0\0\0\5" \
+  "\2\2\2\25\25\25...EEEHHH\202FFF\20DDDBBB@@@>>><<<:::888::8<:4\40\34\15" \
+  "(\"\14""2-\17""82\20vpS\347\343\316\347\344\317\202\347\344\320\202\347" \
+  "\344\322-\347\344\323\346\343\322K>\26H:\22H8\22G7\22H8\22J9\22P=\24" \
+  "\\K!\336\332\306\347\344\320\347\344\317\347\344\316\346\343\315\346" \
+  "\343\314\346\343\312\346\343\311\346\343\307\346\343\305\314\305\244" \
+  "YC\26R>\23J5\20>*\15""5\"\12""1\35\10""4\40\12<(\14D1\17L9\22R>\24S\77" \
+  "\24\261\244l\343\333\230\343\332\223\343\331\216\327\314~RC\31F7\22""8" \
+  ",\17\40\33\12\23\20\6\11\10\3\1\1\0\377\0\0\0\254\0\0\0\24\1\1\1\27\27" \
+  "\27""111BBB@@@>>><<<:::888777666765\36\33\24\33\30\10(\"\14""2-\17""9" \
+  "2\20uoR\347\344\317\347\344\320\202\347\344\322\202\347\344\324-\347" \
+  "\345\324\347\345\326\\N(L<\23L;\23M<\23O<\24R>\24T@\24\215\200^\347\344" \
+  "\323\347\344\322\347\344\321\347\344\320\347\344\317\346\343\316\346" \
+  "\343\314\346\343\312\346\343\310\346\343\307\346\343\305ub7YB\24P9\22" \
+  "F/\16;%\13""3\36\10""4\37\10;&\13E/\16L7\20S>\24VB\25sb1\344\333\232" \
+  "\343\332\225\343\332\221\343\331\212\222\204GL;\23C4\21&!\13\30\25\6" \
+  "\15\13\4\4\3\1\377\0\0\0\257\0\0\0\24\12\12\12&&&:::888777666444222$" \
+  "#\"\15\14\4\32\27\10'\"\14""3-\17;3\20tmO\347\344\321\347\344\322\347" \
+  "\344\323\347\345\324\347\345\325\202\347\345\326-\347\345\327m_;P>\24" \
+  "Q>\24R>\24T\77\24UA\25WB\25\250\236\202\347\345\324\347\344\324\347\344" \
+  "\323\347\344\321\347\344\320\347\344\317\346\343\316\346\343\314\346" \
+  "\343\312\346\343\310\346\343\306\222\201X]D\26V<\22K2\17@(\14""7\40\10" \
+  "5\36\10;$\12C-\16L5\20T=\22XB\25XC\25\310\276\201\343\333\227\343\332" \
+  "\222\343\331\215\322\306xR@\25I8\22""0(\15\36\31\10\21\17\5\7\6\2\1\1" \
+  "\0\377\0\0\0\260\0\0\0\22\7\7\7\"\"\"333222000,,,\11\11\7\15\14\4\32" \
+  "\26\7(\"\14""4.\17>5\21ogH\347\344\322\347\344\324\347\345\325\347\345" \
+  "\326\347\345\327\202\347\345\330\11\347\345\331~qPS\77\24T@\24VB\25X" \
+  "B\25YC\25ZD\26\255\241\206\202\347\345\326\"\347\345\324\347\344\323" \
+  "\347\344\322\347\344\320\347\344\317\346\343\316\346\343\314\346\343" \
+  "\312\346\343\310\227\205\\aF\26Y>\23O4\20E*\14<\"\12""6\35\10;\"\12C" \
+  "*\14K3\17T;\22ZA\24[D\26\227\206P\344\333\231\343\332\223\343\331\217" \
+  "\343\331\210}l4O=\24;/\17\"\36\12\25\22\6\12\11\3\2\2\0\377\0\0\0\262" \
+  "\0\0\0\17\12\12\12$$$,,,\27\27\27\4\3\1\15\13\4\32\27\7(#\13""7.\20@" \
+  "5\21jaA\347\344\324\347\345\325\347\345\327\347\345\330\202\347\345\331" \
+  ".\347\346\331\347\346\332\221\205fVB\25XC\25YC\25ZD\26\\D\26]E\26\231" \
+  "\212i\347\345\330\347\345\327\347\345\326\347\345\324\347\344\323\347" \
+  "\344\322\347\344\320\347\344\317\346\343\315\346\343\313\346\343\311" \
+  "\205oBcG\26\\@\24R7\20H,\15\77$\12:\36\10<\"\11C(\14L1\17T:\22ZA\24^" \
+  "E\26lU$\344\333\232\343\332\225\343\332\221\343\331\212\264\246^S@\24" \
+  "E5\21&\"\14\32\26\10\16\14\4\4\4\2\377\0\0\0\264\0\0\0\14\20\20\20\3" \
+  "\3\3\3\2\0\14\13\4\33\30\7+$\14""9/\20B6\22dX6\347\345\325\347\345\327" \
+  "\347\345\330\202\347\345\331/\347\346\332\347\346\333\347\346\334\243" \
+  "\231~YC\25ZC\26[C\26\\D\26_F\26`G\27rZ.\344\342\325\347\345\330\347\345" \
+  "\327\347\345\326\347\345\324\347\344\323\347\344\321\347\344\320\347" \
+  "\343\316\346\343\314\330\322\266kN\32fH\26^@\23T6\20K-\15B%\12>!\10>" \
+  "\"\11D(\13L0\16T9\21[@\24aF\26`G\27\313\277\202\343\333\227\343\332\222" \
+  "\343\331\214\334\322\177]I\32N<\23*%\14\36\31\10\21\17\5\7\6\2\377\0" \
+  "\0\0\266\0\0\0\30\3\2\0\16\14\4\35\30\10,$\14;0\20D8\22ZN*\347\345\327" \
+  "\347\345\330\347\345\331\347\346\332\347\346\333\323\317\300\266\256" \
+  "\230\232\215pr`8[C\26[B\25[A\24\\B\24`D\26cH\26eI\27\253\235~\202\347" \
+  "\345\331\"\347\345\327\347\345\326\347\344\324\347\344\322\347\344\320" \
+  "\347\344\317\346\343\316\227\202VlL\30gG\26_@\23U6\20M-\15E&\12B\"\11" \
+  "B#\11F)\14P2\16X:\21^B\24cF\26dH\27\252\232a\344\333\230\343\332\223" \
+  "\343\331\216\343\330\206\204q6VA\24.(\16\"\35\12\25\22\6\11\10\3\1\1" \
+  "\0\377\0\0\0\265\0\0\0<\5\3\1\20\14\4\36\30\7.$\14<0\20F9\22QC\34\340" \
+  "\336\320\276\271\244\235\224x~pM_L\"WB\25YC\25[D\26\\D\26ZB\24Y\77\24" \
+  "Y>\22Z\77\23^B\24cF\26hJ\27kN\32\273\260\224\347\345\331\347\345\330" \
+  "\347\345\327\347\345\325\347\344\323\347\344\322\346\343\316\251\226" \
+  "opN\31lK\30gE\25^=\22V4\20N-\14I(\12H(\13J*\13O/\15V6\20\\>\23aD\25e" \
+  "H\26fJ\27\216xA\344\333\232\343\332\224\343\332\220\343\331\210\253\232" \
+  "TZC\25""5-\17$\40\12\30\24\6\14\12\4\3\2\0\377\0\0\0\264\0\0\0=\3\1\0" \
+  "\7\4\2\21\14\4\37\27\7.#\13</\20G8\22L=\24P@\27Q\77\24S\77\24UA\25WB" \
+  "\24XB\25YB\24ZA\24Y@\24V<\22T9\21U:\21W:\22[=\22`B\24hI\27lM\30mM\30" \
+  "\231\203\\\316\305\260\346\344\330\347\345\326\344\342\320\307\273\237" \
+  "\220tFsP\31qN\30lI\26fC\24_<\21X6\20T1\16R/\15S1\16V4\16Y7\20^=\22bB" \
+  "\24gH\26jK\27jL\30w\\(\344\334\232\343\332\225\343\332\220\343\331\212" \
+  "\314\276n^E\26;1\20'#\14\33\27\10\16\14\4\4\4\2\377\0\0\0\263\0\0\0>" \
+  "\2\1\0\4\3\1\11\6\2\22\14\4\36\26\6,\40\12""9,\16D5\21L:\23O=\24Q=\24" \
+  "R=\24R=\23S<\22T<\22T;\22T:\22S8\21P5\20N2\17O2\16R4\17V7\20^>\22fE\25" \
+  "lK\27pN\30rP\30sP\31xW!\200`,xV\37uR\32uR\31tQ\30qL\27mH\26iD\24d\77" \
+  "\22`:\20\\7\20]8\20`<\21b>\22dA\23gE\24jH\26mL\30mM\30lM\30lN\30\340" \
+  "\327\227\343\332\226\343\332\221\343\331\213\342\327\201hN\34 at 4\21)$" \
+  "\14\35\31\10\20\16\4\6\5\2\377\0\0\0\262\0\0\0@\1\0\0\4\2\1\10\4\1\13" \
+  "\6\2\22\14\3\35\23\6(\34\10""4'\14>.\16E3\20I6\21J7\21L7\20L6\21M5\20" \
+  "M4\20M3\20M2\17L1\16L/\16J,\14J*\14L-\15S2\16[9\20b@\23hF\25nK\27rN\30" \
+  "tQ\30vR\31wR\31xS\32xS\31wR\31vP\30tN\30qK\26nG\26jD\24hA\23gA\22iC\24" \
+  "kF\24lH\25nJ\26pL\30rN\30rO\30qP\31\212o8\304\265|\344\334\234\343\333" \
+  "\227\343\332\222\343\331\214\343\330\204\200h-D7\22,'\15\40\33\11\22" \
+  "\20\5\7\6\2\1\1\0\377\0\0\0\261\0\0\0\21\3\2\0\6\4\1\12\6\2\16\7\2\22" \
+  "\12\3\32\20\5$\30\7.\40\12""6&\14<*\15 at .\16B.\16D.\16D-\16E,\16F,\15" \
+  "F+\14\202G*\14\24G(\14G(\13G&\12K(\13P.\15W4\17_:\21e@\22jE\24nI\26r" \
+  "L\27uO\30xR\31zT\32{T\32{T\31zR\31xP\30vN\30tK\26\202qI\26\27rJ\26tM" \
+  "\27uO\30wP\30wR\30wS\31xU\34\231\177H\311\274\206\344\335\246\344\334" \
+  "\241\344\334\234\343\333\230\343\332\222\343\331\214\343\330\204\226" \
+  "\200=G9\22.(\16\"\35\12\25\22\6\10\7\3\1\1\0\377\0\0\0\260\0\0\0\21\2" \
+  "\1\0\5\3\1\11\5\1\15\7\2\20\10\2\24\12\3\30\16\4\40\23\5)\32\7""0\36" \
+  "\11""5\"\12""9%\13;&\13<%\13=%\13\77$\12@%\12\202B%\12\17D$\12E%\12G" \
+  "%\12I&\12K(\12P,\14U0\15\\5\16`:\20f>\22jC\23qI\25vM\27zR\31|T\32\202" \
+  "}U\32\3}T\32|S\31{R\31\202zP\30\3zR\30|T\31}U\32\202}V\32\23\214j1\262" \
+  "\235j\332\321\242\345\337\256\344\336\252\344\335\246\344\334\242\344" \
+  "\334\234\344\333\230\343\332\222\343\331\215\343\330\204\246\222KH:\23" \
+  "/*\16#\36\12\26\22\6\12\10\3\2\2\0\377\0\0\0\257\0\0\0)\1\0\0\4\2\1\10" \
+  "\4\1\14\6\2\16\10\2\23\12\3\26\14\3\32\16\4\37\20\4%\25\5*\30\6/\33\10" \
+  "3\35\10""6\36\10""7\37\10""9\37\10:\40\10>!\10@\"\11D$\12F&\12J(\12L" \
+  ")\12N*\13P*\13R+\13U-\14Y1\15^5\16d9\20i\77\22pG\24wM\27|R\30\177V\32" \
+  "\201W\32\202X\32\202X\33\201W\32\200V\32\201V\32\202\202X\32\27\202Y" \
+  "\33\215h/\256\226d\320\304\232\345\340\270\345\340\265\345\340\262\345" \
+  "\337\256\344\336\252\344\336\246\344\334\242\344\334\235\344\333\230" \
+  "\343\332\223\343\331\216\343\330\204\265\241UH:\23""0*\16$\40\13\27\24" \
+  "\6\13\12\4\2\2\0\377\0\0\0\242\0\0\0\2\1\1\0\2\2\0\202\3\2\0\1\2\2\0" \
+  "\210\0\0\0\27\2\1\0\6\4\1\12\6\2\16\7\2\21\11\3\25\13\3\31\16\4\34\20" \
+  "\4\40\21\4%\23\5)\26\6.\31\6""0\32\7""4\34\10""9\36\10<!\11@$\12D&\12" \
+  "H(\14L+\14P.\15R.\15U0\16\202V0\15\13V.\14X.\14Z0\14]2\16c7\17k>\21r" \
+  "F\24yN\27~T\31\203X\32\205Z\33\202\206Z\32\34\207Z\33\207[\33\207[\34" \
+  "\230t<\264\234n\322\310\242\346\342\301\346\342\276\345\341\273\345\340" \
+  "\270\345\340\265\345\340\262\345\337\256\344\336\252\344\336\246\344" \
+  "\334\242\344\334\235\344\333\230\343\332\223\343\331\216\343\330\204" \
+  "\276\254\\F9\22""0+\17&!\13\30\24\7\14\12\4\3\2\0\377\0\0\0\241\0\0\0" \
+  "\3\4\3\1\7\6\2\12\10\3\202\13\11\4\6\11\10\3\7\6\2\6\5\2\4\4\2\3\3\1" \
+  "\3\2\0\202\2\2\0\27\3\2\0\6\4\1\13\6\2\17\11\2\22\13\3\26\15\4\33\20" \
+  "\5\37\22\5$\24\6(\26\6,\31\7""1\34\10""5\36\10:\"\11>$\12B'\13F*\14J" \
+  ",\15O0\16S2\17X6\20[8\20^9\20\202`:\20\13`8\20_5\16]3\16^2\15`3\15e7" \
+  "\17m\77\22vH\24}P\27\203W\32\207[\33\202\211\\\34\35\222h+\252\214Z\302" \
+  "\257\210\335\326\272\346\343\310\346\343\306\346\343\304\346\342\301" \
+  "\346\342\276\345\341\273\345\340\270\345\340\265\345\340\262\345\337" \
+  "\256\344\336\252\344\336\246\344\334\242\344\334\235\344\333\230\343" \
+  "\332\223\343\331\216\343\330\204\304\262`B7\22""1,\17&\"\13\31\26\7\14" \
+  "\12\4\3\2\0\377\0\0\0\240\0\0\0\4\5\4\2\13\11\3\20\16\4\24\22\6\202\26" \
+  "\22\6\5\25\22\6\23\20\5\20\16\4\16\14\4\15\13\4\202\14\12\4D\13\12\4" \
+  "\15\12\4\21\15\4\25\17\5\31\21\6\35\23\6\"\27\6&\31\7*\33\10.\36\11""4" \
+  "\40\12""8$\12<&\14A)\14E,\15J.\16N2\17R5\20W8\20[:\21^=\22c@\22fB\24" \
+  "jD\24kD\24jB\23j@\22g<\21d8\17d5\16e6\16j;\20rB\22zK\26\202T\31\211\\" \
+  "\33\215`\40\277\252\204\326\313\260\347\344\320\347\344\317\346\343\315" \
+  "\346\343\312\346\343\310\346\343\306\346\342\304\346\342\301\346\342" \
+  "\276\345\341\273\345\340\270\345\340\265\345\340\262\345\337\256\344" \
+  "\336\252\344\335\246\343\333\240\332\317\220\344\333\230\343\332\222" \
+  "\343\331\214\343\330\204\305\263`=4\21""2,\17&\"\13\31\26\7\14\12\4\3" \
+  "\2\0\377\0\0\0\237\0\0\0\5\4\3\1\13\11\3\23\20\5\32\27\10\40\34\12\202" \
+  "#\36\12K\"\35\12\40\33\11\35\31\10\34\30\10\32\26\7\31\26\7\30\24\7\31" \
+  "\25\7\33\27\10\37\30\10#\33\10&\34\11+\40\12.\"\13""3$\13""8'\14<*\15" \
+  "@,\16E/\16I1\17M4\20R7\20V:\21Z<\22^\77\23bB\24fD\25jH\26mJ\26qL\27t" \
+  "N\30vN\30tL\27rH\25oC\23k>\21i9\17i8\16o=\20wE\23\177N\26\207V\32\216" \
+  "^\34\217_\34\342\334\310\347\344\322\347\344\320\347\344\316\346\343" \
+  "\314\346\343\312\346\343\310\346\343\306\346\342\303\346\342\300\346" \
+  "\342\275\345\341\272\345\340\270\345\340\264\345\337\261\345\337\256" \
+  "\344\336\251\326\312\222\230v9\307\265v\343\333\230\343\332\222\343\331" \
+  "\214\343\330\204\305\263`82\20""2,\17&\"\13\31\26\7\14\12\4\3\2\0\377" \
+  "\0\0\0\236\0\0\0\6\2\2\0\10\7\2\22\17\5\34\30\10%\40\13,'\16\202/*\16" \
+  "K-(\16+&\15)$\14(#\14&\"\14&\"\13&!\13&\"\13)#\14-$\14""1'\15""5(\16" \
+  "8+\16<.\16A0\17D2\20I5\20M8\21Q:\22U<\22Y\77\23]B\24`C\25dF\26hH\26l" \
+  "J\27oM\27rO\30vQ\31xS\31{U\32~V\32}T\32zO\27vJ\25rB\22n>\21n:\20r\77" \
+  "\20zG\24\202P\27\212X\32\220_\34\222a\35\324\307\252\347\344\322\347" \
+  "\344\320\347\344\316\346\343\314\346\343\312\346\343\310\346\343\305" \
+  "\346\342\303\346\342\300\346\342\275\345\341\272\345\340\267\345\340" \
+  "\264\345\337\260\340\330\246\264\232`\211^\37\204Z\33\317\277\200\343" \
+  "\332\227\343\332\222\343\331\213\343\330\204\272\252Y82\20""1,\17&!\13" \
+  "\31\25\7\14\12\4\3\2\0\377\0\0\0\236\0\0\0S\4\3\1\15\13\4\30\24\6#\37" \
+  "\12-(\16""60\20""82\20""81\20""71\20""5/\20""4.\20""2-\17""2,\17""1," \
+  "\17""1+\16""4,\17""7.\20:0\20=1\20A4\20E6\21H7\22L:\22P<\23T>\24WA\24" \
+  "[B\24^D\26bF\26eH\27hJ\27kL\30nN\30qP\30zY$\216rD\240\210`\264\240\201" \
+  "\306\271\242\302\262\231\202X\33\200T\30|N\27xH\24tB\22r=\20u@\21}H\24" \
+  "\205P\27\215Z\32\223`\34\225c\35\310\262\215\347\344\322\347\344\317" \
+  "\346\343\316\346\343\314\346\343\312\346\343\306\346\343\305\346\342" \
+  "\302\346\342\277\346\341\274\345\341\272\345\340\267\342\332\255\277" \
+  "\247o\224i(\213^\34\211\\\34\207[\33\335\322\222\343\332\226\343\332" \
+  "\221\343\331\212\343\330\201\252\234Q82\20""0+\16%\40\13\30\24\7\14\12" \
+  "\4\3\2\0\377\0\0\0\235\0\0\0\14\1\1\0\7\6\2\22\17\5\36\31\10)$\14""3" \
+  "-\17""92\21\202{K\244\234hoh\77LE!92\21\20382\20\26""92\20<4\21 at 5\21" \
+  "B6\22F9\22I:\23L<\24O>\24R\77\24VB\25YC\25\\E\26mV*}h=\215yR\235\214" \
+  "h\255\236\177\275\261\227\314\305\261\336\333\317\347\346\336\347\346" \
+  "\337\203\350\347\340,\343\340\326\206[\35\204X\32\201R\30|K\25xE\23v" \
+  "@\21xA\22\200H\24\210R\27\216Z\32\225a\34\230d\36\275\237p\347\344\320" \
+  "\347\344\317\346\343\315\346\343\313\346\343\310\346\343\306\346\343" \
+  "\304\346\342\302\346\342\276\346\341\274\337\327\255\276\243k\232l)\222" \
+  "a\35\220_\34\216_\34\214^\34\220g&\344\334\232\343\332\225\343\332\220" \
+  "\343\331\211\343\330\200\225\212F81\20""0*\16$\37\12\27\23\6\12\11\3" \
+  "\2\2\0\377\0\0\0\235\0\0\0$\2\2\0\12\11\3\25\22\6\"\36\12.(\16""60\20" \
+  "92\21\276\266z\344\334\234\344\334\241\344\335\245\333\325\241\307\301" \
+  "\222\270\262\207\262\254\203\242\234u\242\234v\244\235x\246\236z\247" \
+  "\236|\256\246\205\270\261\221\275\266\226\311\302\246\321\314\262\335" \
+  "\331\302\347\344\321\347\344\322\347\345\324\347\345\326\347\345\330" \
+  "\347\345\331\347\346\331\347\346\333\347\346\334\347\346\335\203\347" \
+  "\346\336\202\350\346\337+\227r<\210Z\32\205U\31\202O\26}H\24zC\22|B\22" \
+  "\202J\24\212R\30\222Z\32\230b\34\234f\36\264\216V\347\344\320\347\343" \
+  "\316\346\343\314\346\343\312\346\343\310\346\343\305\346\342\303\346" \
+  "\342\301\325\306\232\267\224Z\234h\"\230d\36\226c\35\224b\35\222a\35" \
+  "\220`\34\217_\34\245\202@\344\333\232\343\332\224\343\332\217\343\331" \
+  "\210\343\327}\200v871\20.)\16#\36\12\26\22\6\12\10\3\2\2\0\377\0\0\0" \
+  "\235\0\0\0#\4\3\1\14\13\4\30\25\7%!\13""0+\17""82\20""92\21\306\277\200" \
+  "\344\334\233\344\334\240\344\335\244\344\336\250\345\336\254\345\337" \
+  "\260\345\340\263\345\340\266\345\341\271\346\341\274\346\342\276\346" \
+  "\342\301\346\343\304\346\343\306\346\343\310\346\343\312\346\343\314" \
+  "\347\344\316\347\344\320\347\344\322\347\344\323\347\345\325\347\345" \
+  "\326\347\345\330\347\345\331\347\346\331\347\346\332\202\347\346\334" \
+  "\1\347\346\335\203\347\346\336+\254\217b\213]\33\211X\32\205R\27\202" \
+  "L\25\177F\22\177D\22\205K\24\214S\27\224\\\32\232b\34\237h\36\254~>\347" \
+  "\344\317\346\343\315\346\343\313\346\343\311\346\343\306\336\325\262" \
+  "\306\252w\254}:\237h\37\236g\37\234e\36\231d\35\227c\35\226b\35\225b" \
+  "\35\223a\35\221`\34\272\236\\\344\333\230\343\332\223\343\331\216\343" \
+  "\330\206\343\327zf])60\20-(\16!\34\12\24\21\6\10\7\2\1\1\0\377\0\0\0" \
+  "\235\0\0\0!\4\4\2\16\14\4\32\26\7&\"\14""2-\17""92\21F>\32\232\222]\344" \
+  "\333\232\344\334\236\344\335\243\344\336\247\344\336\252\345\337\256" \
+  "\345\340\262\345\340\265\345\340\270\345\341\273\346\342\275\346\342" \
+  "\300\346\342\303\346\343\305\346\343\307\346\343\311\346\343\313\346" \
+  "\343\316\347\344\317\347\344\320\347\344\322\347\344\324\347\345\325" \
+  "\347\345\327\347\345\330\202\347\345\331\202\347\346\332\1\347\346\333" \
+  "\203\347\346\334\31\300\254\212\216^\34\214Z\32\212V\31\206N\26\203I" \
+  "\24\202F\22\210L\24\217S\27\226\\\32\236d\35\242h\37\246n&\346\343\315" \
+  "\346\343\314\336\325\267\312\256\177\265\210G\246l\40\244j\37\243i\37" \
+  "\240h\36\237f\36\234b\34\232b\34\202\230b\35\17\227b\35\225d\35\223a" \
+  "\35\320\275|\343\333\227\343\332\222\343\331\214\343\330\204\342\326" \
+  "vF\77\30""5/\20+&\15\37\32\11\22\20\5\7\6\2\377\0\0\0\236\0\0\0#\4\4" \
+  "\2\16\14\4\32\27\7'\"\14""3-\17""92\21RK$F>\33\314\303\206\344\334\235" \
+  "\344\334\242\344\335\246\344\336\252\345\337\255\345\337\260\345\340" \
+  "\263\345\340\267\345\341\272\346\341\274\346\342\277\346\342\302\346" \
+  "\343\304\346\343\306\346\343\310\346\343\312\346\343\314\346\343\316" \
+  "\347\344\317\347\344\321\347\344\322\347\344\324\347\345\325\347\345" \
+  "\326\347\345\327\347\345\330\202\347\345\331\204\347\346\332\17\326\314" \
+  "\265\220_\34\217^\34\215X\31\212R\27\210L\25\206H\23\212L\24\222S\27" \
+  "\231\\\32\240d\34\245j\37\247l\40\303\240k\264\202\77\204\250l\40\27" \
+  "\246j\37\244g\36\240d\35\235a\34\234`\34\232`\33\232a\34\231b\35\230" \
+  "d\35\230d\36\232k%\342\331\230\343\332\225\343\332\221\343\331\212\343" \
+  "\330\200\314\300f92\21""4.\20)$\14\34\30\10\20\15\4\6\5\2\377\0\0\0\236" \
+  "\0\0\0#\3\3\1\14\13\4\31\25\7&!\13""1,\17""82\20NG!KD\37IB\36\265\255" \
+  "v\344\334\240\344\335\244\344\336\250\345\336\254\345\337\260\345\340" \
+  "\262\345\340\266\345\341\270\345\341\273\346\342\276\346\342\300\346" \
+  "\342\303\346\343\305\346\343\306\346\343\311\346\343\313\346\343\315" \
+  "\347\343\316\347\344\317\347\344\321\347\344\322\347\344\323\347\345" \
+  "\324\347\345\326\347\345\327\203\347\345\330\203\347\345\331*\346\343" \
+  "\326\227h'\223`\34\221[\32\216U\30\214O\26\212J\24\215M\24\223T\27\232" \
+  "[\31\241b\34\246i\37\252m\40\252n\40\253n\40\252m\40\252l\40\251j\37" \
+  "\250h\36\246f\35\244c\34\240`\33\234\\\32\233]\32\233^\33\233a\34\233" \
+  "b\34\234d\36\232f\36\265\222M\344\333\230\343\332\223\343\331\217\343" \
+  "\331\207\343\327|\243\230K82\20""2,\17&\"\14\32\26\7\15\13\4\4\3\1\377" \
+  "\0\0\0\236\0\0\0\10\2\2\0\12\11\3\26\23\6$\37\12.)\16""71\20""92\21q" \
+  "i;\20292\21\31jb9\265\256{\343\334\245\344\336\252\345\337\256\345\337" \
+  "\261\345\340\264\345\340\267\345\341\272\346\341\274\346\342\277\346" \
+  "\342\301\346\343\304\346\343\305\346\343\307\346\343\311\346\343\313" \
+  "\346\343\315\347\343\316\347\344\317\347\344\321\347\344\322\347\344" \
+  "\323\347\344\324\347\345\325\202\347\345\326\1\347\345\327\204\347\345" \
+  "\330)\252\205N\226b\35\225^\33\222X\31\220R\27\217N\25\220N\24\225R\26" \
+  "\234Z\31\242a\34\246f\36\252k\37\254l\40\254l\37\253j\37\252h\36\251" \
+  "g\35\247d\34\246b\33\242^\32\236[\31\234Y\30\234Z\32\234]\32\235`\33" \
+  "\236c\34\236f\36\234f\37\321\275{\343\333\227\343\332\222\343\331\215" \
+  "\343\330\204\342\326xwm171\20/*\16$\37\12\26\23\6\13\12\4\2\2\0\377\0" \
+  "\0\0\236\0\0\0\11\1\1\0\10\7\2\24\21\6\40\34\12,'\15""60\20""92\21\202" \
+  "xF:3\22\20392\21\32IA\37}vL\250\240t\316\306\231\345\340\262\345\340" \
+  "\266\345\341\270\345\341\273\346\342\275\346\342\300\346\342\302\346" \
+  "\343\304\346\343\306\346\343\310\346\343\311\346\343\313\346\343\315" \
+  "\347\343\316\347\344\317\347\344\320\347\344\321\347\344\322\347\344" \
+  "\323\347\344\324\347\345\324\347\345\325\204\347\345\326)\277\244y\230" \
+  "d\36\230`\34\226\\\32\223V\30\222P\26\222O\25\227R\26\234X\30\241^\32" \
+  "\246b\33\251f\34\253g\36\254g\35\254f\34\252d\34\251b\33\247`\32\244" \
+  "]\31\243Z\31\237X\27\235W\30\235Z\30\236]\32\240b\34\240e\36\240h\37" \
+  "\250x1\343\332\230\343\332\224\343\332\220\343\331\211\343\330\200\337" \
+  "\322pF\77\27""60\20,'\15\40\34\12\24\21\6\10\7\3\1\1\0\377\0\0\0\237" \
+  "\0\0\0\10\6\5\2\20\16\4\35\30\10($\14""3.\17""92\21`X-jb6\20492\21\25" \
+  ";4\21\77""6\21B6\22L@\31fZ2~rI\220\204[\236\222j\254\242z\262\246\200" \
+  "\274\263\217\276\264\220\276\264\222\300\265\223\300\266\224\300\264" \
+  "\223\267\250\205\270\250\205\256\233t\250\222h\264\240z\203\347\344\322" \
+  "\1\347\344\323\204\347\344\324(\323\304\246\233e\36\232b\34\231^\32\227" \
+  "X\30\226S\27\225P\25\230Q\26\235V\27\242[\30\246_\32\251b\33\252b\33" \
+  "\254c\33\253b\33\253`\32\252^\32\250\\\31\245Y\30\244W\27\241V\27\240" \
+  "Y\30\241]\31\243a\33\244f\35\244h\37\243i\37\310\252f\343\333\230\343" \
+  "\332\223\343\331\216\343\331\207\343\327|\262\247S92\21""3.\17($\14\35" \
+  "\30\10\20\16\4\6\5\2\377\0\0\0\240\0\0\0\10\3\3\1\15\13\4\30\25\6%!\13" \
+  "0*\16""81\20:2\22\242\232_\20492\21\26;3\21>4\21A5\22D8\22H:\22L<\24" \
+  "P>\24S\77\24VB\25ZC\25]E\26`F\27dH\27gJ\27jL\30mM\30pO\30tP\31vS\31z" \
+  "T\32\200Z\40\342\336\310\202\347\344\320\1\347\344\321\204\347\344\322" \
+  "\15\345\340\314\237j$\236e\36\234a\34\233\\\31\232W\30\231S\26\232R\25" \
+  "\237U\26\243X\27\246[\30\252]\31\254_\31\202\254^\31\31\254]\31\254[" \
+  "\30\252Z\30\250X\27\246W\26\245Y\30\245\\\31\246b\33\247f\35\250j\36" \
+  "\250l\40\253t*\341\326\225\343\332\225\343\332\221\343\331\212\343\330" \
+  "\202\342\326utk.81\20""0*\16%\40\13\30\25\6\15\13\4\3\3\1\377\0\0\0\240" \
+  "\0\0\0\11\2\2\0\11\10\3\24\22\6!\34\12,'\16""60\20""92\21\210\200Jia" \
+  "5\20292\21\1""82\20\202:1\20\25<1\21>2\20C5\20G8\22L:\22P<\23S>\24WA" \
+  "\24[B\24^D\26bF\26dH\26hJ\30kK\27nL\27sP\30vR\31{U\32~V\32\307\270\226" \
+  "\347\344\316\202\347\344\317\204\347\344\320\17\347\344\317\261\210M" \
+  "\240g\36\237d\34\236^\33\234Y\30\234U\27\236T\26\240T\26\244V\26\250" \
+  "X\27\253[\30\254\\\30\256]\30\257\\\30\202\256\\\30\27\256[\30\254Z\27" \
+  "\252[\30\252^\32\253b\33\253f\35\253j\36\253m\40\251m\40\312\253f\343" \
+  "\333\230\343\332\223\343\331\216\343\331\207\343\327}\325\310f=6\23""6" \
+  "0\20,'\15!\34\12\24\21\6\11\10\3\1\1\0\377\0\0\0\241\0\0\0#\6\5\2\20" \
+  "\16\5\34\30\10(#\14""2-\17""82\20E>\32\274\264r<5\23""82\20""81\20""8" \
+  "0\20""8/\17""7,\16""6+\16""8,\16>/\17C1\17G4\20K7\21P9\22T;\22W=\22Z" \
+  "\77\23^A\24bB\24eD\24jH\26oK\27uP\30{T\32~V\32\251\217`\346\343\314\346" \
+  "\343\315\206\346\343\316&\304\250z\241h\37\242f\35\241a\34\240\\\32\237" \
+  "X\27\240V\26\243V\26\246W\27\252Z\27\254[\30\260\\\30\262]\30\262^\30" \
+  "\263^\30\262^\30\262]\30\260]\30\260`\31\257c\33\257f\34\257j\36\257" \
+  "n\40\256p!\263}2\342\330\226\343\332\224\343\332\220\343\331\212\343" \
+  "\330\203\342\326v\217\204<82\20""2,\17(#\14\34\30\10\20\16\5\6\5\2\377" \
+  "\0\0\0\242\0\0\0#\3\2\1\14\12\4\27\24\6#\37\12.(\16""70\20""92\21\226" \
+  "\216U\205}J82\20""81\20""6/\20""5,\16""4)\16""1%\14/#\13""2$\13""7'\14" \
+  "<)\16A-\16F/\16J2\17N3\17Q6\20U8\20Y9\21];\22b@\23jF\25rL\27yR\30~V\32" \
+  "\212d)\345\342\310\346\343\312\202\346\343\313\203\346\343\314'\346\343" \
+  "\313\330\314\252\243i\37\244h\36\244d\34\243_\32\243Z\31\244W\27\246" \
+  "W\26\251Y\27\254[\30\260\\\30\263^\30\265`\30\267`\31\270`\32\267`\31" \
+  "\265`\31\264a\32\264d\34\264h\35\263l\36\262n\40\262q!\260q\"\325\276" \
+  "z\343\333\226\343\332\222\343\331\215\343\331\206\343\327}\332\315hG" \
+  "@\30""60\20.(\16#\37\12\27\23\6\14\12\4\3\2\1\377\0\0\0\242\0\0\0\"\1" \
+  "\1\0\10\6\2\22\20\5\36\32\10)$\14""3.\17""92\21H@\34\324\312\202QJ#8" \
+  "1\20""6.\20""4+\17""1'\14-\"\13*\36\11(\33\10-\35\10""2\40\12""6\"\12" \
+  "<%\13@'\14D*\14I-\15L.\15P0\16U4\17\\9\21e@\22mH\25vO\30~V\32\203Y\32" \
+  "\315\300\233\203\346\343\310\203\346\343\311\16\346\343\310\346\342\307" \
+  "\252t-\246j\37\246f\35\246b\33\244]\31\245Y\30\250X\27\254Z\30\260\\" \
+  "\30\263^\30\266`\31\271a\31\202\274b\32\27\274c\32\273d\32\271g\33\270" \
+  "j\35\267l\36\267p\40\266r!\264r!\304\232P\343\333\230\343\332\224\343" \
+  "\331\220\343\331\212\343\330\202\342\326t\222\206<92\21""3-\17($\14\36" \
+  "\32\10\22\17\5\10\6\2\1\1\0\377\0\0\0\243\0\0\0!\4\4\1\15\13\4\30\25" \
+  "\6$\40\12.)\16""60\20""92\21\213\202L\302\270v\77""7\25""91\20""6,\16" \
+  "2'\15-\"\12)\35\10&\31\6&\26\6*\30\6.\32\6""3\35\10""8\40\10<\"\11@$" \
+  "\12D&\12H)\13M+\14U2\16_9\20jC\24tL\27~U\32\204Z\33\254\220^\210\346" \
+  "\343\306$\275\227\\\250l\40\250h\36\247d\34\247`\32\247\\\31\252Z\30" \
+  "\256[\30\262]\30\265`\31\271a\31\274c\32\300d\32\301f\32\301h\33\277" \
+  "j\34\276l\36\275o\37\273r\40\272t!\270u\"\273\2022\342\326\223\343\332" \
+  "\224\343\332\221\343\331\214\343\330\205\343\327{\323\305`C;\25""60\20" \
+  ".)\16$\37\12\30\24\6\15\13\4\4\3\1\377\0\0\0\244\0\0\0\"\1\1\0\10\7\2" \
+  "\23\20\5\36\32\10($\14""2-\17""82\20>7\25\311\277y\236\226]>5\21<2\21" \
+  "7,\17""3&\15.\"\12*\34\10(\30\6'\25\6*\26\5/\31\6""2\32\6""8\35\7<\40" \
+  "\10@\"\10D$\11I&\12P,\14[5\17f\77\22rJ\26|R\30\203Y\33\215d&\344\337" \
+  "\277\207\346\343\304$\320\272\214\250l\40\250i\37\250f\35\251b\33\252" \
+  "]\31\253[\30\256\\\30\263^\30\267`\31\274b\32\300d\32\303f\32\306j\34" \
+  "\306l\35\303n\36\301q\40\300s!\276v\"\274v\"\273z(\333\310\203\343\333" \
+  "\226\343\332\222\343\331\215\343\331\207\343\330~\342\325pxn-82\20""2" \
+  "-\17($\14\36\32\10\22\20\5\10\7\2\1\1\0\377\0\0\0\245\0\0\0!\4\4\1\15" \
+  "\13\4\30\24\6#\36\12-(\16""60\20""92\21`X,\342\330\214\203zF at 5\21>3\20" \
+  ";.\17""6)\15""2#\13/\37\11-\32\10,\30\6/\31\6""2\32\6""8\34\7;\40\10" \
+  "@\"\10D$\11I&\12N*\13X2\16c<\21pG\25zP\30\202X\33\207[\34\306\264\210" \
+  "\206\346\342\302$\346\342\301\342\334\270\252p%\252k\40\252h\36\252d" \
+  "\33\253`\32\254\\\31\256\\\30\263^\30\270a\32\274b\32\301f\32\306j\34" \
+  "\312o\36\310r\37\306s\40\303u\40\301v\"\277x#\274w#\326\271r\343\333" \
+  "\227\343\332\222\343\331\216\343\331\211\343\330\202\342\326u\264\250" \
+  "L:2\21""60\20,(\16#\36\12\30\24\6\15\13\4\4\3\1\377\0\0\0\246\0\0\0!" \
+  "\1\1\0\7\6\2\21\17\5\34\30\10'\"\14""0+\17""81\20""92\21\227\216S\342" \
+  "\330\214wl<C7\22B5\21>0\17:,\16""8'\14""4!\12""3\36\10""2\34\10""3\33" \
+  "\7""8\34\7;\40\10@\"\10D$\11I&\12M)\12V0\15a9\20mC\23wM\27\200V\32\207" \
+  "[\34\243\200G\202\346\342\276\202\346\342\277\203\346\342\276#\346\342" \
+  "\275\272\216N\252l\40\253j\37\254f\34\254b\33\254^\31\257\\\30\263^\30" \
+  "\267`\31\274d\32\301h\34\306n\35\310r\40\310u!\306v\"\305x\"\302z$\300" \
+  "y#\324\260g\343\333\227\343\332\223\343\331\220\343\331\212\343\330\203" \
+  "\343\326y\326\310`LD\30""81\20""0+\17'\"\14\34\30\10\21\17\5\7\6\2\1" \
+  "\1\0\377\0\0\0\247\0\0\0!\3\2\1\13\12\3\25\22\6\40\34\12*%\14""3-\17" \
+  "82\21>6\23\274\262m\337\325\213vj9G9\23F7\22B3\20 at .\17=*\15;'\13:#\12" \
+  "8\40\10:\37\10;\37\10@\"\10C#\11H&\12L(\12T-\14^6\17j@\22tK\26~T\31\206" \
+  "Z\33\212^\35\330\315\244\205\346\341\274#\345\341\273\345\341\272\316" \
+  "\265\200\253n\40\254l\37\254h\35\254d\34\254_\31\256]\30\262^\30\267" \
+  "b\32\274g\34\300l\35\304p\36\307t!\306w\"\306z#\304z#\302z$\323\256c" \
+  "\343\333\227\343\332\223\343\331\220\343\331\212\343\330\204\343\327" \
+  "{\341\323jnd'82\21""3-\17*%\14\40\34\12\25\22\6\13\12\3\3\2\1\377\0\0" \
+  "\0\251\0\0\0\40\6\5\2\17\14\4\31\25\7#\37\12-(\16""50\20:2\21MD\34\323" \
+  "\311~\341\327\214\202vBI;\23I9\22H6\21F2\20C.\16A+\15@(\14\77%\12@$\12" \
+  "A#\11C#\11H&\12L(\12R,\14[4\16f<\21rG\25|P\30\204Y\32\211\\\34\262\226" \
+  "^\205\345\341\271\202\345\340\270\40\341\331\256\254q%\254l\40\254i\36" \
+  "\254e\34\255a\32\257`\32\262b\32\267e\33\274j\35\277o\37\302s\40\304" \
+  "w\"\306y#\305{#\303|%\326\262h\343\333\227\343\332\223\343\331\220\343" \
+  "\331\212\343\331\205\343\327|\342\325n\220\204692\21""50\20-(\16#\37" \
+  "\12\30\25\7\16\14\4\6\5\2\377\0\0\0\252\0\0\0!\2\2\0\10\7\2\22\17\5\34" \
+  "\30\10&!\13/*\16""80\20=4\21`V(\332\320\203\342\330\215\227\212RO>\25" \
+  "N<\23M9\22K7\20I2\20H0\17G,\15F+\14F(\13H(\13J'\12L(\12Q+\13X0\15b:\20" \
+  "mC\23xM\26\202V\31\211\\\34\220d#\336\326\252\203\345\340\266\202\345" \
+  "\340\265\202\345\340\264\37\275\222O\253m\40\254k\37\254h\35\256e\34" \
+  "\257e\34\262f\34\267j\35\272n\36\276r\40\300u\"\302y#\302z$\304\177*" \
+  "\332\275u\343\333\226\343\332\222\343\331\220\343\331\212\343\331\205" \
+  "\343\327}\342\325n\247\232B:3\22""60\20/*\16&!\13\34\30\10\22\17\5\10" \
+  "\7\2\2\1\0\377\0\0\0\253\0\0\0\40\3\3\1\12\11\3\24\22\6\36\32\11(#\14" \
+  "1+\17;3\20 at 6\22j^.\335\323\204\343\331\215\270\254l\\J\35R>\24R>\23Q" \
+  ";\22Q8\21O5\20N2\17N0\16N/\16N.\15P.\14S-\14W/\15_6\20j@\22tJ\26~R\30" \
+  "\206Z\33\214^\34\272\237g\205\345\340\262\40\345\340\261\345\337\260" \
+  "\322\274\204\252n\40\254l\37\255k\37\256i\36\260i\36\263j\36\267n\37" \
+  "\271q\40\275u\"\277x#\300y$\306\2128\335\312\205\343\332\225\343\332" \
+  "\222\343\331\217\343\331\212\343\330\204\343\327}\342\325n\260\244H=" \
+  "6\22""71\20""0+\17(#\14\36\32\11\24\21\6\12\11\3\3\3\1\377\0\0\0\255" \
+  "\0\0\0\40\4\4\1\14\12\4\26\23\6\40\33\11*%\14""5.\17>4\21C7\22qd1\334" \
+  "\321\202\343\331\214\324\311\203\177o;WB\25XB\24X\77\24W>\23W;\22V:\21" \
+  "V7\20V6\20W4\17Y4\17\\5\16a:\20j@\22sH\25|Q\30\204X\32\212]\34\224g&" \
+  "\337\326\245\203\345\337\257\202\345\337\256\36\345\336\255\344\334\252" \
+  "\257x.\253n\40\255l\37\257l\37\261m\37\263n\40\266p\40\271t\"\274v\"" \
+  "\274x$\316\243V\342\330\223\343\332\224\343\332\221\343\331\216\343\331" \
+  "\211\343\330\203\343\327|\342\325n\261\244H>7\23""82\20""2,\17)%\14\40" \
+  "\33\11\26\23\6\14\12\4\4\4\1\377\0\0\0\257\0\0\0\37\6\5\2\16\14\4\30" \
+  "\24\7\"\35\12-&\15""8/\20A6\22F9\22i[*\326\314}\343\331\212\343\331\217" \
+  "\264\247gjT#\\D\26]C\25]B\24^A\24^@\23^>\22^=\22`<\21b<\21e>\22mD\24" \
+  "uK\26|R\30\203X\32\211[\33\216^\34\267\232^\202\344\336\253\203\344\336" \
+  "\252\36\344\336\251\344\336\250\306\244b\252n\40\255n\40\256n\40\260" \
+  "o\40\262p\40\266s\"\270t\"\301\212:\333\306\200\343\332\225\343\332\222" \
+  "\343\332\220\343\331\214\343\331\207\343\330\202\343\326y\342\324k\245" \
+  "\230\77<5\22""82\20""2-\17*%\15\"\35\12\30\24\7\16\14\4\6\5\2\1\1\0\377" \
+  "\0\0\0\257\0\0\0\40\1\1\0\7\6\2\17\15\4\31\25\7#\36\12""0(\15;0\20D7" \
+  "\22H:\23aR$\310\274p\343\331\211\343\331\214\337\325\214\246\227ZjR\40" \
+  "bG\26cG\26dF\26eF\25fE\25gD\25iD\24kD\24qH\26vM\27}R\31\203X\32\210[" \
+  "\33\214^\34\222b\40\330\314\223\203\344\336\246\36\344\335\246\344\335" \
+  "\245\344\335\244\336\322\226\252p#\252n!\255o!\257p!\261q\"\272\2024" \
+  "\324\270p\343\332\225\343\332\223\343\332\221\343\331\216\343\331\212" \
+  "\343\331\206\343\330\200\342\326v\341\322g\214\2004:2\21""82\20""2-\17" \
+  "*%\15\"\35\12\31\25\7\17\15\4\7\6\2\2\1\0\377\0\0\0\261\0\0\0\40\2\1" \
+  "\0\7\6\2\17\15\4\31\25\7$\36\12""2)\16=2\20F8\22J<\24TC\30\254\237Y\343" \
+  "\331\206\343\331\212\343\331\216\335\323\213\255\234^y`*hJ\27kL\30lK" \
+  "\27mK\27pL\27rL\27tM\30yP\30~U\32\203X\32\207[\33\212]\34\216^\34\247" \
+  "\202A\344\334\242\202\344\335\242\35\344\334\242\344\334\241\344\334" \
+  "\240\344\334\236\300\231R\251m\40\253o\"\273\213@\323\272s\343\332\224" \
+  "\343\332\223\343\332\221\343\331\217\343\331\213\343\331\207\343\330" \
+  "\202\343\327|\342\325p\324\306Zi`$92\21""71\20""1,\17*%\14\"\35\12\31" \
+  "\25\7\20\15\4\7\6\2\2\1\0\377\0\0\0\263\0\0\0\40\2\1\0\7\6\2\17\15\4" \
+  "\30\25\7\"\35\12""2(\16>2\20G9\22M<\24Q>\24\204r8\326\313x\343\331\207" \
+  "\343\331\212\343\331\216\343\332\220\310\272v\237\212M|_(pO\30tP\31u" \
+  "Q\30xR\31{T\32~V\32\202Y\33\206Z\33\211\\\34\214^\34\217_\34\300\246" \
+  "f\344\334\236\202\344\334\235\33\344\334\234\344\334\233\344\334\232" \
+  "\335\320\214\310\252b\335\320\212\343\332\224\343\332\223\343\332\221" \
+  "\343\331\217\343\331\214\343\331\210\343\330\204\343\330~\342\326v\342" \
+  "\324h\257\242FI@\26""92\21""60\20""0*\16($\14!\34\11\30\25\7\17\15\4" \
+  "\7\6\2\2\1\0\377\0\0\0\265\0\0\0;\2\1\0\7\6\2\17\15\4\27\24\6\37\32\11" \
+  ",$\14=1\20H8\22O>\24S\77\24`L\34\256\236V\342\327\202\343\331\207\343" \
+  "\331\211\343\331\215\343\331\217\343\332\221\322\305\200\267\244b\241" \
+  "\210I\221r6\202^\"~V\32\201X\33\204Z\33\207[\34\212]\34\216^\34\222c" \
+  "\40\325\305\203\344\333\231\344\333\230\343\333\227\343\332\226\343\332" \
+  "\225\343\332\224\343\332\223\343\332\221\343\332\220\343\331\216\343" \
+  "\331\212\343\331\210\343\330\204\343\330\177\342\326x\342\324n\316\300" \
+  "Vpf'92\21""82\20""4.\20.)\16'\"\14\37\32\11\27\24\6\17\15\4\7\6\2\2\1" \
+  "\0\377\0\0\0\267\0\0\0\30\2\1\0\6\5\2\15\13\4\25\22\6\35\31\10%\40\12" \
+  "4*\16E6\21P=\24UA\24XC\25s^'\272\253^\343\330\200\343\330\205\343\331" \
+  "\210\343\331\212\343\331\215\343\331\216\343\332\220\343\332\221\343" \
+  "\332\222\336\324\215\327\313\204\202\320\300{\3\321\301{\323\303}\333" \
+  "\316\207\203\343\332\223\31\343\332\222\343\332\221\343\332\220\343\331" \
+  "\217\343\331\215\343\331\213\343\331\211\343\330\206\343\330\202\343" \
+  "\327}\342\326x\342\324n\324\306]\210}3\77""8\23""92\21""60\20""1,\17" \
+  "+&\15$\40\13\35\31\10\25\22\6\15\13\4\6\5\2\2\1\0\377\0\0\0\271\0\0\0" \
+  "\26\1\1\0\5\4\2\13\12\3\22\20\5\32\26\7\"\35\12($\14""6,\17H8\22V@\25" \
+  "ZD\26^E\26zd+\267\246X\340\324}\343\330\202\343\330\204\343\331\207\343" \
+  "\331\211\343\331\212\343\331\214\343\331\215\203\343\331\216\202\343" \
+  "\332\217\1\343\331\217\202\343\331\216\31\343\331\215\343\331\214\343" \
+  "\331\213\343\331\211\343\331\210\343\330\205\343\330\202\343\330\177" \
+  "\343\327z\342\326u\342\324k\313\275W\202x0A:\24""92\21""81\20""4.\17" \
+  ".)\16($\14\"\35\12\32\26\7\22\20\5\13\12\3\5\4\2\1\1\0\377\0\0\0\274" \
+  "\0\0\0\22\4\3\1\11\10\3\20\15\4\27\24\6\36\32\10$\40\12*%\14""3,\16C" \
+  "6\22S\77\24_F\26cH\27pV\37\237\211B\312\272e\343\327|\343\330\177\343" \
+  "\330\201\202\343\330\204\202\343\330\206\1\343\331\207\203\343\331\210" \
+  "\202\343\330\206\27\343\330\205\343\330\204\343\330\202\343\330\200\343" \
+  "\327|\342\327y\342\326u\342\324n\326\310`\243\227Cd[#:3\22""92\21""8" \
+  "1\20""4.\20""0*\16*%\14$\40\12\36\32\10\27\24\6\20\16\4\11\10\3\4\3\1" \
+  "\377\0\0\0\277\0\0\0\24\2\2\0\6\5\2\14\12\4\22\20\5\31\25\7\40\33\11" \
+  "%\40\13*&\14""0*\16""6/\20D8\22Q>\24^F\26hK\30z\\\"\234\204=\272\246" \
+  "T\324\305j\342\326x\342\326y\202\343\327{\202\343\327|\202\343\327{\27" \
+  "\342\327y\342\326x\342\326v\342\325r\342\324n\332\315f\270\253O\220\205" \
+  ";cZ\"<5\22""92\21""82\20""71\20""4.\17""0*\16*&\15%\40\13\40\33\11\31" \
+  "\26\7\23\20\5\14\12\4\6\6\2\2\2\0\377\0\0\0\302\0\0\0\35\4\3\1\10\7\2" \
+  "\16\14\4\24\21\6\32\26\10\40\33\12%\40\13*%\14.)\16""2,\17""5/\20""8" \
+  "1\20\77""6\22G9\23N>\24T@\24]I\31sb(\204t2\214\177<\222\210B\236\224" \
+  "H\236\224G\236\223F\230\214B\216\203<}s2g^&PH\32\20392\21\16""82\20""7" \
+  "1\20""5/\20""2,\17.)\16*%\14%\40\13\40\34\12\32\26\10\24\21\6\16\14\4" \
+  "\10\7\3\4\3\1\1\1\0\377\0\0\0\304\0\0\0\20\2\2\0\5\4\2\11\10\3\16\14" \
+  "\4\24\21\6\31\26\7\36\32\10#\37\12(#\14*&\15.)\16""2,\17""4.\20""60\20" \
+  "71\20""81\20\20282\20\20792\21\20282\20\20""81\20""71\20""60\20""4.\20" \
+  "1,\16.)\16*&\15(#\14#\37\12\36\32\10\32\26\10\24\21\6\17\15\4\12\10\3" \
+  "\5\4\2\2\2\0\377\0\0\0\310\0\0\0\20\2\2\0\5\4\2\11\10\3\16\14\4\22\20" \
+  "\5\27\23\6\33\27\10\40\33\11$\37\12'\"\14)$\14,'\15.)\16""0+\16""2,\17" \
+  "3.\17\2074.\20\20""3.\17""2,\17""0*\16.)\16,'\15)$\14&\"\14$\37\12\40" \
+  "\33\11\33\27\10\27\23\6\22\20\5\16\14\4\11\10\3\5\4\2\2\2\0\377\0\0\0" \
+  "\314\0\0\0\16\2\2\0\4\4\2\10\6\2\14\12\4\20\15\4\23\20\5\26\23\6\32\26" \
+  "\10\36\31\10\40\34\12#\36\12%\40\13&\"\14(#\14\202)$\14\203*&\15\202" \
+  ")$\14\16(#\14&\"\14%\40\13\"\36\12\40\34\11\35\31\10\32\26\10\26\23\6" \
+  "\23\20\5\20\15\4\14\12\4\10\6\2\4\4\2\2\2\0\377\0\0\0\244\0\0\0\1\1\1" \
+  "\1\202\2\2\2\1\1\1\1\250\0\0\0\13\1\1\1\3\3\1\6\5\2\10\7\3\14\12\4\16" \
+  "\14\4\21\17\5\24\21\6\26\23\6\30\24\7\32\26\7\202\34\30\10\1\36\31\10" \
+  "\203\36\32\11\16\36\31\10\35\30\10\34\30\10\32\26\10\30\24\7\26\23\6" \
+  "\24\21\6\21\17\5\16\14\4\14\12\4\10\7\3\6\5\2\3\3\1\1\1\1\213\0\0\0\3" \
+  "\1\1\1\2\2\2\1\1\1\233\0\0\0\3\1\1\1\2\2\2\1\1\1\363\0\0\0\1\1\1\1\203" \
+  "\2\2\2\1\3\3\3\206\4\4\4\1\3\3\3\207\2\2\2\1\3\3\3\205\4\4\4\1\3\3\3" \
+  "\204\2\2\2\3\1\1\1\0\0\0\1\1\1\204\2\2\2\204\4\4\4\1\3\3\3\210\2\2\2" \
+  "\1\3\3\3\203\4\4\4\12\6\6\4\10\7\5\12\10\5\12\11\5\14\12\6\16\14\6\17" \
+  "\15\6\20\16\6\22\17\6\22\20\7\202\24\21\7\13\24\22\10\24\21\10\23\20" \
+  "\10\22\17\10\20\17\7\17\16\7\15\14\6\13\12\6\12\11\5\10\7\5\6\6\4\202" \
+  "\4\4\4\1\3\3\3\204\2\2\2\2\1\1\1\0\0\0\204\2\2\2\206\4\4\4\1\3\3\3\220" \
+  "\2\2\2\207\3\3\3\206\4\4\4\1\3\3\3\203\2\2\2\1\1\1\1\353\0\0\0\4\1\1" \
+  "\1\2\2\2\3\3\3\4\4\4\202\5\5\5\202\6\6\6\204\7\7\7\203\6\6\6\206\5\5" \
+  "\5\202\6\6\6\204\7\7\7\202\6\6\6\1\5\5\5\206\4\4\4\202\5\5\5\203\6\6" \
+  "\6\1\7\7\7\213\6\6\6\205\7\7\7\1\6\6\6\202\7\7\6\5\10\7\6\10\10\6\12" \
+  "\11\6\12\12\6\13\12\7\203\14\13\10\202\14\14\10\5\14\13\10\12\12\10\12" \
+  "\11\10\11\10\10\10\10\7\204\7\7\7\203\6\6\6\1\5\5\5\205\4\4\4\202\5\5" \
+  "\5\202\6\6\6\205\7\7\7\224\6\6\6\212\7\7\7\202\6\6\6\1\5\5\5\202\4\4" \
+  "\4\202\2\2\2\347\0\0\0\10\1\1\1\2\2\2\3\3\3\4\4\4\6\6\6\7\7\7\10\10\10" \
+  "\11\11\11\202\12\12\12\204\13\13\13\213\12\12\12\204\13\13\13\202\12" \
+  "\12\12\202\11\11\11\204\10\10\10\203\11\11\11\203\12\12\12\1\13\13\13" \
+  "\213\12\12\12\205\13\13\13\212\12\12\12\1\13\13\12\202\13\13\13\210\14" \
+  "\14\14\202\13\13\13\202\12\12\12\210\11\11\11\202\12\12\12\211\13\13" \
+  "\13\217\12\12\12\202\13\13\13\206\14\14\14\203\13\13\13\202\12\12\12" \
+  "\202\10\10\10\5\6\6\6\5\5\5\4\4\4\2\2\2\1\1\1\344\0\0\0\12\1\1\1\3\3" \
+  "\3\4\4\4\6\6\6\10\10\10\11\11\11\12\12\12\14\14\14\15\15\15\16\16\16" \
+  "\202\17\17\17\202\20\20\20\206\17\17\17\205\16\16\16\202\17\17\17\202" \
+  "\20\20\20\202\17\17\17\214\16\16\16\215\17\17\17\204\20\20\20\214\17" \
+  "\17\17\204\20\20\20\203\21\21\21\204\20\20\20\1\17\17\17\213\16\16\16" \
+  "\202\17\17\17\211\20\20\20\202\17\17\17\204\16\16\16\202\17\17\17\1\20" \
+  "\20\20\202\17\17\17\202\16\16\16\1\17\17\17\212\20\20\20\1\17\17\17\202" \
+  "\16\16\16\202\14\14\14\7\12\12\12\10\10\10\6\6\6\5\5\5\3\3\3\2\2\2\1" \
+  "\1\1\341\0\0\0\13\1\1\1\3\3\3\5\5\5\6\6\6\10\10\10\12\12\12\14\14\14" \
+  "\16\16\16\20\20\20\22\22\22\23\23\23\225\24\24\24\202\23\23\23\207\22" \
+  "\22\22\203\23\23\23\216\24\24\24\203\25\25\25\214\24\24\24\202\25\25" \
+  "\25\207\26\26\26\202\25\25\25\202\24\24\24\211\23\23\23\205\24\24\24" \
+  "\207\25\25\25\217\24\24\24\1\25\25\25\206\26\26\26\1\25\25\25\202\24" \
+  "\24\24\14\23\23\23\22\22\22\21\21\21\20\20\20\16\16\16\14\14\14\12\12" \
+  "\12\10\10\10\6\6\6\4\4\4\2\2\2\1\1\1\337\0\0\0\14\1\1\1\3\3\3\5\5\5\7" \
+  "\7\7\12\12\12\14\14\14\16\16\16\20\20\20\22\22\22\24\24\24\26\26\26\30" \
+  "\30\30\202\31\31\31\221\32\32\32\202\31\31\31\214\30\30\30\203\31\31" \
+  "\31\235\32\32\32\205\33\33\33\203\32\32\32\202\31\31\31\212\30\30\30" \
+  "\202\31\31\31\212\32\32\32\205\31\31\31\213\32\32\32\203\33\33\33\204" \
+  "\32\32\32\15\31\31\31\30\30\30\27\27\27\26\26\26\24\24\24\22\22\22\20" \
+  "\20\20\15\15\15\12\12\12\10\10\10\6\6\6\4\4\4\2\2\2\336\0\0\0\16\1\1" \
+  "\1\3\3\3\5\5\5\7\7\7\12\12\12\15\15\15\20\20\20\22\22\22\24\24\24\27" \
+  "\27\27\32\32\32\33\33\33\35\35\35\36\36\36\203\37\37\37\207\40\40\40" \
+  "\211\37\37\37\217\36\36\36\202\37\37\37\244\40\40\40\1\37\37\37\215\36" \
+  "\36\36\1\37\37\37\210\40\40\40\202\37\37\37\204\36\36\36\1\37\37\37\203" \
+  "\40\40\40\204\37\37\37\211\40\40\40\17\37\37\37\36\36\36\35\35\35\34" \
+  "\34\34\32\32\32\30\30\30\26\26\26\24\24\24\20\20\20\16\16\16\13\13\13" \
+  "\10\10\10\6\6\6\4\4\4\2\2\2\335\0\0\0\16\2\2\2\4\4\4\6\6\6\12\12\12\15" \
+  "\15\15\20\20\20\23\23\23\26\26\26\31\31\31\34\34\34\36\36\36\40\40\40" \
+  "\"\"\"###\257$$$\1%%%\215$$$\1%%%\211&&&\1%%%\220$$$\1%%%\206&&&\210" \
+  "$$$\1%%%\205$$$\1%%%\210&&&\202$$$\16\"\"\"!!!\37\37\37\35\35\35\32\32" \
+  "\32\27\27\27\24\24\24\21\21\21\16\16\16\13\13\13\10\10\10\5\5\5\3\3\3" \
+  "\1\1\1\333\0\0\0\17\1\1\1\3\3\3\6\6\6\10\10\10\14\14\14\20\20\20\23\23" \
+  "\23\26\26\26\32\32\32\35\35\35\40\40\40###%%%''')))\225***\1)))\207(" \
+  "((\202)))\237***\1+++\207,,,\202+++\204***\203)))\211***\203+++\1,,," \
+  "\203+++\217***\1+++\206,,,\21+++***)))(((&&&$$$\"\"\"\36\36\36\33\33" \
+  "\33\30\30\30\24\24\24\21\21\21\16\16\16\12\12\12\7\7\7\4\4\4\2\2\2\333" \
+  "\0\0\0\20\2\2\2\4\4\4\10\10\10\12\12\12\16\16\16\22\22\22\26\26\26\32" \
+  "\32\32\36\36\36!!!$$$(((***,,,...///\221000\202///\215...\1///\23500" \
+  "0\207111\202000\202///\211...\203///\212000\205///\213000\204111\203" \
+  "000\20///---,,,)))&&&###\37\37\37\34\34\34\30\30\30\24\24\24\20\20\20" \
+  "\14\14\14\11\11\11\6\6\6\3\3\3\1\1\1\331\0\0\0\21\1\1\1\3\3\3\6\6\6\11" \
+  "\11\11\14\14\14\20\20\20\25\25\25\31\31\31\35\35\35!!!%%%))),,,JJJ\227" \
+  "\227\227\303\303\303\334\334\334\202\352\352\352\4\343\343\343\314\314" \
+  "\314\254\254\254kkk\204555\3""444iii\271\271\271\206\302\302\302\3\262" \
+  "\262\262\201\201\201;;;\205333\12""444YYY\232\232\232\272\272\272\320" \
+  "\320\320\326\326\326\324\324\324\300\300\300\240\240\240hhh\204555\2" \
+  "GGG\251\251\251\207\302\302\302\3\261\261\261\177\177\177<<<\204555\2" \
+  "ttt\273\273\273\216\302\302\302\3\252\252\252rrr666\205444\5;;;\177\177" \
+  "\177\267\267\267\321\321\321\351\351\351\202\352\352\352\5\324\324\324" \
+  "\272\272\272\206\206\206@@@666\202555\2""444\276\276\276\202\302\302" \
+  "\302\1RRR\204555\2""777\247\247\247\202\302\302\302\2\252\252\252:::" \
+  "\202666\4lll\254\254\254\314\314\314\343\343\343\202\352\352\352\20\334" \
+  "\334\334\302\302\302\227\227\227III+++'''###\37\37\37\33\33\33\27\27" \
+  "\27\22\22\22\16\16\16\13\13\13\10\10\10\4\4\4\2\2\2\331\0\0\0\16\2\2" \
+  "\2\4\4\4\7\7\7\12\12\12\17\17\17\23\23\23\27\27\27\34\34\34\40\40\40" \
+  "%%%)))---\220\220\220\374\374\374\211\377\377\377\2\311\311\311DDD\202" \
+  "999\2SSS\373\373\373\211\377\377\377\2\351\351\351bbb\203888\2===\266" \
+  "\266\266\211\377\377\377\2\305\305\305BBB\202999\1\275\275\275\212\377" \
+  "\377\377\2\340\340\340PPP\202999\2ccc\376\376\376\221\377\377\377\2\317" \
+  "\317\317\77\77\77\203888\2^^^\346\346\346\211\377\377\377\3\357\357\357" \
+  "jjj:::\202999\1\371\371\371\202\377\377\377\1bbb\204999\1\260\260\260" \
+  "\202\377\377\377\5\357\357\357XXX:::DDD\312\312\312\211\377\377\377\15" \
+  "\373\373\373\216\216\216+++'''\"\"\"\36\36\36\32\32\32\25\25\25\21\21" \
+  "\21\14\14\14\11\11\11\5\5\5\2\2\2\331\0\0\0\14\2\2\2\5\5\5\10\10\10\14" \
+  "\14\14\20\20\20\24\24\24\32\32\32\36\36\36###(((,,,vvv\203\377\377\377" \
+  "\2\372\372\372\314\314\314\202\262\262\262\2\276\276\276\356\356\356" \
+  "\203\377\377\377\1\301\301\301\202===\1\177\177\177\203\377\377\377\1" \
+  "\320\320\320\203\306\306\306\1\344\344\344\203\377\377\377\2\360\360" \
+  "\360LLL\202;;;\1\257\257\257\203\377\377\377\5\355\355\355\311\311\311" \
+  "\306\306\306\314\314\314\364\364\364\203\377\377\377\1\267\267\267\202" \
+  "===\1\335\335\335\202\377\377\377\1\350\350\350\203\306\306\306\2\314" \
+  "\314\314\352\352\352\203\377\377\377\1\320\320\320\202===\1\223\223\223" \
+  "\202\377\377\377\2\374\374\374\312\312\312\204\306\306\306\1\363\363" \
+  "\363\202\377\377\377\1\335\335\335\202\306\306\306\2\310\310\310\353" \
+  "\353\353\203\377\377\377\1\240\240\240\202<<<\2HHH\354\354\354\203\377" \
+  "\377\377\5\336\336\336\271\271\271\262\262\262\266\266\266\330\330\330" \
+  "\203\377\377\377\2\364\364\364RRR\202===\1\372\372\372\202\377\377\377" \
+  "\1eee\203===\1\240\240\240\202\377\377\377\2\365\365\365ddd\202>>>\1" \
+  "\302\302\302\203\377\377\377\2\356\356\356\276\276\276\202\262\262\262" \
+  "\2\314\314\314\373\373\373\203\377\377\377\14ttt***%%%!!!\34\34\34\27" \
+  "\27\27\22\22\22\16\16\16\12\12\12\6\6\6\4\4\4\1\1\1\330\0\0\0\14\2\2" \
+  "\2\6\6\6\11\11\11\16\16\16\22\22\22\26\26\26\33\33\33\40\40\40%%%***" \
+  "000\341\341\341\202\377\377\377\2\337\337\337XXX\204@@@\2FFF\264\264" \
+  "\264\203\377\377\377\3fff@@@\203\203\203\202\377\377\377\2\341\341\341" \
+  "AAA\202BBB\4AAA@@@\200\200\200\374\374\374\202\377\377\377\4\246\246" \
+  "\246>>>RRR\374\374\374\202\377\377\377\7\257\257\257DDDAAABBBAAAJJJ\301" \
+  "\301\301\202\377\377\377\4\375\375\375OOO@@@\335\335\335\202\377\377" \
+  "\377\1\206\206\206\203BBB\3AAABBB\252\252\252\203\377\377\377\3]]]@@" \
+  "@\232\232\232\202\377\377\377\2\317\317\317AAA\204BBB\1\330\330\330\202" \
+  "\377\377\377\2\220\220\220BBB\202AAA\2CCC\300\300\300\202\377\377\377" \
+  "\1\350\350\350\202>>>\1\233\233\233\202\377\377\377\2\374\374\374\201" \
+  "\201\201\205@@@\2xxx\371\371\371\202\377\377\377\1\255\255\255\202@@" \
+  "@\1\372\372\372\202\377\377\377\1hhh\202@@@\1\220\220\220\202\377\377" \
+  "\377\5\370\370\370oooAAA@@@fff\203\377\377\377\2\261\261\261EEE\204@" \
+  "@@\2XXX\342\342\342\202\377\377\377\14\341\341\341---(((###\36\36\36" \
+  "\31\31\31\24\24\24\20\20\20\13\13\13\10\10\10\4\4\4\2\2\2\330\0\0\0\13" \
+  "\3\3\3\6\6\6\12\12\12\16\16\16\23\23\23\30\30\30\35\35\35\"\"\"(((,," \
+  ",VVV\203\377\377\377\1nnn\206BBB\2DDD\346\346\346\202\377\377\377\3\245" \
+  "\245\245BBB\204\204\204\202\377\377\377\1\336\336\336\206BBB\1\262\262" \
+  "\262\202\377\377\377\3\336\336\336@@@\201\201\201\202\377\377\377\2\361" \
+  "\361\361FFF\205BBB\2PPP\375\375\375\202\377\377\377\3vvvBBB\336\336\336" \
+  "\202\377\377\377\1\204\204\204\205BBB\2KKK\372\372\372\202\377\377\377" \
+  "\3\177\177\177BBB\233\233\233\202\377\377\377\1\315\315\315\205BBB\1" \
+  "\330\330\330\202\377\377\377\1\220\220\220\204BBB\1jjj\203\377\377\377" \
+  "\3HHH@@@\331\331\331\202\377\377\377\1\266\266\266\207BBB\1\242\242\242" \
+  "\202\377\377\377\1\352\352\352\202BBB\1\372\372\372\202\377\377\377\7" \
+  "iiiBBB\202\202\202\375\375\375\377\377\377\374\374\374{{{\203BBB\1\242" \
+  "\242\242\202\377\377\377\2\350\350\350DDD\206BBB\1kkk\203\377\377\377" \
+  "\13TTT***%%%\40\40\40\32\32\32\25\25\25\20\20\20\14\14\14\10\10\10\4" \
+  "\4\4\2\2\2\327\0\0\0\14\1\1\1\4\4\4\7\7\7\13\13\13\17\17\17\24\24\24" \
+  "\31\31\31\36\36\36$$$)))...}}}\202\377\377\377\2\364\364\364DDD\207B" \
+  "BB\1\255\255\255\202\377\377\377\3\310\310\310BBB\204\204\204\202\377" \
+  "\377\377\1\336\336\336\206BBB\1\204\204\204\202\377\377\377\3\370\370" \
+  "\370CCC\232\232\232\202\377\377\377\1\326\326\326\206BBB\2GGG\367\367" \
+  "\367\202\377\377\377\3\204\204\204BBB\336\336\336\202\377\377\377\1\204" \
+  "\204\204\206BBB\1\342\342\342\202\377\377\377\3\204\204\204BBB\233\233" \
+  "\233\202\377\377\377\1\315\315\315\205BBB\1\330\330\330\202\377\377\377" \
+  "\1\220\220\220\204BBB\1XXX\203\377\377\377\3RRRDDD\373\373\373\202\377" \
+  "\377\377\1\200\200\200\207BBB\1ggg\203\377\377\377\3RRRBBB\372\372\372" \
+  "\202\377\377\377\6iiijjj\372\372\372\377\377\377\376\376\376\206\206" \
+  "\206\204BBB\1\310\310\310\202\377\377\377\1\264\264\264\210BBB\1\356" \
+  "\356\356\202\377\377\377\13|||,,,&&&!!!\33\33\33\26\26\26\21\21\21\15" \
+  "\15\15\11\11\11\5\5\5\2\2\2\327\0\0\0\14\2\2\2\4\4\4\7\7\7\13\13\13\20" \
+  "\20\20\24\24\24\32\32\32\37\37\37$$$***///\223\223\223\202\377\377\377" \
+  "\1\335\335\335\210BBB\1\232\232\232\202\377\377\377\3\333\333\333BBB" \
+  "\204\204\204\202\377\377\377\1\336\336\336\206BBB\1sss\203\377\377\377" \
+  "\2III\244\244\244\202\377\377\377\1\372\372\372\206\354\354\354\1\364" \
+  "\364\364\203\377\377\377\3~~~BBB\336\336\336\202\377\377\377\1\204\204" \
+  "\204\206BBB\1\336\336\336\202\377\377\377\3\204\204\204BBB\233\233\233" \
+  "\202\377\377\377\1\315\315\315\205BBB\1\330\330\330\202\377\377\377\1" \
+  "\220\220\220\204BBB\1XXX\203\377\377\377\2RRRSSS\203\377\377\377\1hh" \
+  "h\207BBB\1TTT\203\377\377\377\3eeeBBB\372\372\372\202\377\377\377\2i" \
+  "ii\311\311\311\202\377\377\377\1\251\251\251\205BBB\1\332\332\332\202" \
+  "\377\377\377\1\234\234\234\210BBB\1\333\333\333\202\377\377\377\13\221" \
+  "\221\221,,,'''\"\"\"\34\34\34\27\27\27\22\22\22\15\15\15\11\11\11\5\5" \
+  "\5\2\2\2\327\0\0\0\14\2\2\2\4\4\4\7\7\7\14\14\14\20\20\20\25\25\25\32" \
+  "\32\32\37\37\37$$$***000\232\232\232\202\377\377\377\1\330\330\330\210" \
+  "BBB\1\226\226\226\202\377\377\377\3\342\342\342BBB\204\204\204\202\377" \
+  "\377\377\1\336\336\336\206BBB\1nnn\203\377\377\377\2KKK\246\246\246\214" \
+  "\377\377\377\4\363\363\363PPPBBB\336\336\336\202\377\377\377\1\204\204" \
+  "\204\206BBB\1\336\336\336\202\377\377\377\3\204\204\204BBB\233\233\233" \
+  "\202\377\377\377\1\315\315\315\205BBB\1\330\330\330\202\377\377\377\1" \
+  "\220\220\220\204BBB\1XXX\203\377\377\377\2RRRXXX\203\377\377\377\1dd" \
+  "d\207BBB\1OOO\203\377\377\377\3iiiBBB\372\372\372\202\377\377\377\2i" \
+  "ii\332\332\332\202\377\377\377\1\224\224\224\205BBB\1\342\342\342\202" \
+  "\377\377\377\1\226\226\226\210BBB\1\330\330\330\202\377\377\377\13\230" \
+  "\230\230,,,(((\"\"\"\34\34\34\30\30\30\22\22\22\16\16\16\11\11\11\5\5" \
+  "\5\2\2\2\327\0\0\0\14\2\2\2\4\4\4\7\7\7\13\13\13\20\20\20\25\25\25\32" \
+  "\32\32\37\37\37$$$***000\224\224\224\202\377\377\377\1\335\335\335\210" \
+  "BBB\1\234\234\234\202\377\377\377\3\332\332\332BBB\204\204\204\202\377" \
+  "\377\377\1\336\336\336\206BBB\1uuu\203\377\377\377\2FFF\243\243\243\202" \
+  "\377\377\377\1\351\351\351\210\240\240\240\2\237\237\237bbb\202BBB\1" \
+  "\336\336\336\202\377\377\377\1\204\204\204\206BBB\1\336\336\336\202\377" \
+  "\377\377\3\204\204\204BBB\233\233\233\202\377\377\377\1\315\315\315\205" \
+  "BBB\1\330\330\330\202\377\377\377\1\220\220\220\204BBB\1XXX\203\377\377" \
+  "\377\2RRRTTT\203\377\377\377\1ggg\207BBB\1UUU\203\377\377\377\3cccBB" \
+  "B\372\372\372\202\377\377\377\2iii\242\242\242\202\377\377\377\2\361" \
+  "\361\361]]]\204BBB\1\333\333\333\202\377\377\377\1\236\236\236\210BB" \
+  "B\1\334\334\334\202\377\377\377\13\217\217\217,,,'''\"\"\"\34\34\34\30" \
+  "\30\30\22\22\22\16\16\16\11\11\11\5\5\5\2\2\2\327\0\0\0\14\2\2\2\4\4" \
+  "\4\7\7\7\13\13\13\17\17\17\24\24\24\31\31\31\36\36\36$$$***///~~~\202" \
+  "\377\377\377\2\364\364\364DDD\207BBB\1\264\264\264\202\377\377\377\3" \
+  "\305\305\305BBB\204\204\204\202\377\377\377\1\336\336\336\206BBB\1\211" \
+  "\211\211\202\377\377\377\3\362\362\362BBB\232\232\232\202\377\377\377" \
+  "\1\334\334\334\214BBB\1\336\336\336\202\377\377\377\1\204\204\204\206" \
+  "BBB\1\336\336\336\202\377\377\377\3\204\204\204BBB\233\233\233\202\377" \
+  "\377\377\1\315\315\315\205BBB\1\330\330\330\202\377\377\377\1\220\220" \
+  "\220\204BBB\1XXX\203\377\377\377\3RRREEE\373\373\373\202\377\377\377" \
+  "\1\200\200\200\207BBB\1nnn\203\377\377\377\3OOOBBB\372\372\372\202\377" \
+  "\377\377\3iiiGGG\320\320\320\202\377\377\377\2\350\350\350XXX\203BBB" \
+  "\1\312\312\312\202\377\377\377\1\266\266\266\207BBB\2CCC\363\363\363" \
+  "\202\377\377\377\13xxx,,,&&&!!!\34\34\34\26\26\26\22\22\22\15\15\15\11" \
+  "\11\11\5\5\5\2\2\2\330\0\0\0\13\4\4\4\6\6\6\12\12\12\17\17\17\23\23\23" \
+  "\30\30\30\36\36\36###(((...XXX\203\377\377\377\1vvv\206BBB\2III\355\355" \
+  "\355\202\377\377\377\3\240\240\240BBB\204\204\204\202\377\377\377\1\336" \
+  "\336\336\206BBB\1\273\273\273\202\377\377\377\3\326\326\326BBB\204\204" \
+  "\204\202\377\377\377\2\364\364\364FFF\213BBB\1\336\336\336\202\377\377" \
+  "\377\1\204\204\204\206BBB\1\336\336\336\202\377\377\377\3\204\204\204" \
+  "BBB\233\233\233\202\377\377\377\1\315\315\315\205BBB\1\330\330\330\202" \
+  "\377\377\377\1\220\220\220\204BBB\1XXX\203\377\377\377\3RRRBBB\332\332" \
+  "\332\202\377\377\377\1\276\276\276\207BBB\1\255\255\255\202\377\377\377" \
+  "\1\345\345\345\202BBB\1\372\372\372\202\377\377\377\4iiiBBBLLL\330\330" \
+  "\330\202\377\377\377\2\343\343\343SSS\202BBB\1\246\246\246\202\377\377" \
+  "\377\2\355\355\355HHH\206BBB\1www\203\377\377\377\13OOO+++%%%\40\40\40" \
+  "\33\33\33\26\26\26\21\21\21\14\14\14\10\10\10\5\5\5\2\2\2\330\0\0\0\14" \
+  "\3\3\3\6\6\6\12\12\12\16\16\16\22\22\22\27\27\27\34\34\34!!!&&&,,,11" \
+  "1\340\340\340\202\377\377\377\2\351\351\351ddd\204BBB\2MMM\304\304\304" \
+  "\202\377\377\377\4\376\376\376bbbBBB\204\204\204\202\377\377\377\1\336" \
+  "\336\336\204BBB\3CCC\217\217\217\376\376\376\202\377\377\377\4\235\235" \
+  "\235@@@YYY\376\376\376\202\377\377\377\2\244\244\244DDD\212BBB\1\336" \
+  "\336\336\202\377\377\377\1\204\204\204\206@@@\1\336\336\336\202\377\377" \
+  "\377\3\204\204\204BBB\233\233\233\202\377\377\377\1\315\315\315\205B" \
+  "BB\1\330\330\330\202\377\377\377\2\220\220\220BBB\202AAA\2BBBXXX\203" \
+  "\377\377\377\3RRRAAA\234\234\234\202\377\377\377\2\376\376\376\223\223" \
+  "\223\204BBB\3AAA\212\212\212\375\375\375\202\377\377\377\1\250\250\250" \
+  "\202BBB\1\372\372\372\202\377\377\377\1hhh\202@@@\2OOO\336\336\336\202" \
+  "\377\377\377\4\335\335\335OOOBBBggg\203\377\377\377\2\300\300\300KKK" \
+  "\204BBB\2fff\354\354\354\202\377\377\377\14\332\332\332...)))$$$\36\36" \
+  "\36\32\32\32\24\24\24\20\20\20\14\14\14\10\10\10\4\4\4\2\2\2\330\0\0" \
+  "\0\15\2\2\2\5\5\5\11\11\11\14\14\14\21\21\21\26\26\26\32\32\32\40\40" \
+  "\40$$$)))...qqq\376\376\376\202\377\377\377\6\375\375\375\324\324\324" \
+  "\265\265\265\262\262\262\306\306\306\364\364\364\203\377\377\377\1\273" \
+  "\273\273\202BBB\1\204\204\204\202\377\377\377\1\370\370\370\204\331\331" \
+  "\331\1\354\354\354\203\377\377\377\2\355\355\355LLL\202>>>\1\305\305" \
+  "\305\203\377\377\377\1\355\355\355\207\331\331\331\1\313\313\313\202" \
+  "@@@\1\335\335\335\202\377\377\377\1\202\202\202\206>>>\1\335\335\335" \
+  "\202\377\377\377\3\203\203\203@@@\232\232\232\202\377\377\377\1\314\314" \
+  "\314\205@@@\1\330\330\330\202\377\377\377\1\216\216\216\204@@@\1VVV\203" \
+  "\377\377\377\4PPP\77\77\77III\347\347\347\203\377\377\377\5\350\350\350" \
+  "\274\274\274\262\262\262\272\272\272\343\343\343\203\377\377\377\2\361" \
+  "\361\361PPP\202\77\77\77\1\372\372\372\202\377\377\377\1fff\203>>>\2" \
+  "RRR\344\344\344\202\377\377\377\4\326\326\326JJJ@@@\276\276\276\203\377" \
+  "\377\377\6\364\364\364\306\306\306\262\262\262\265\265\265\325\325\325" \
+  "\375\375\375\202\377\377\377\15\376\376\376nnn,,,&&&\"\"\"\34\34\34\30" \
+  "\30\30\23\23\23\17\17\17\12\12\12\7\7\7\4\4\4\2\2\2\330\0\0\0\16\2\2" \
+  "\2\4\4\4\10\10\10\14\14\14\20\20\20\24\24\24\30\30\30\35\35\35\"\"\"" \
+  "&&&***///\204\204\204\367\367\367\211\377\377\377\5\300\300\300HHH@@" \
+  "@AAA\204\204\204\212\377\377\377\2\346\346\346^^^\203:::\2KKK\334\334" \
+  "\334\212\377\377\377\1\356\356\356\202<<<\1\334\334\334\202\377\377\377" \
+  "\1\200\200\200\206;;;\1\334\334\334\202\377\377\377\3\201\201\201<<<" \
+  "\230\230\230\202\377\377\377\1\313\313\313\204<<<\2;;;\327\327\327\202" \
+  "\377\377\377\1\214\214\214\204<<<\1RRR\203\377\377\377\1LLL\202;;;\2" \
+  "YYY\337\337\337\211\377\377\377\2\346\346\346ddd\203;;;\1\371\371\371" \
+  "\202\377\377\377\1ccc\204;;;\2TTT\352\352\352\202\377\377\377\4\316\316" \
+  "\316CCCDDD\276\276\276\211\377\377\377\15\367\367\367\204\204\204---" \
+  "((($$$\37\37\37\32\32\32\26\26\26\22\22\22\16\16\16\12\12\12\6\6\6\3" \
+  "\3\3\331\0\0\0\34\1\1\1\4\4\4\6\6\6\12\12\12\16\16\16\22\22\22\26\26" \
+  "\26\32\32\32\36\36\36###'''+++...DDD\214\214\214\272\272\272\323\323" \
+  "\323\342\342\342\351\351\351\327\327\327\304\304\304\242\242\242eee=" \
+  "==>>>\77\77\77@@@\204\204\204\202\377\377\377\2\365\365\365\306\306\306" \
+  "\202\305\305\305\202\304\304\304\3\254\254\254\177\177\177>>>\205666" \
+  "\4;;;\177\177\177\260\260\260\302\302\302\207\303\303\303\1\267\267\267" \
+  "\202888\1\252\252\252\202\302\302\302\1hhh\206777\1\252\252\252\202\302" \
+  "\302\302\3iii888yyy\202\303\303\303\1\236\236\236\204888\2""777\246\246" \
+  "\246\202\302\302\302\1ppp\204888\1HHH\203\302\302\302\2CCC777\202888" \
+  "\14;;;www\255\255\255\313\313\313\332\332\332\353\353\353\334\334\334" \
+  "\315\315\315\260\260\260}}}===888\203777\1\276\276\276\202\302\302\302" \
+  "\1SSS\205777\1VVV\203\302\302\302\1\212\212\212\202888\26bbb\241\241" \
+  "\241\305\305\305\327\327\327\350\350\350\342\342\342\322\322\322\272" \
+  "\272\272\214\214\214BBB,,,)))%%%!!!\34\34\34\30\30\30\24\24\24\20\20" \
+  "\20\14\14\14\10\10\10\5\5\5\2\2\2\332\0\0\0\33\2\2\2\5\5\5\10\10\10\14" \
+  "\14\14\20\20\20\24\24\24\27\27\27\33\33\33\37\37\37###&&&***,,,...00" \
+  "0111222444555666888999:::<<<===>>>\203\203\203\202\377\377\377\12\335" \
+  "\335\335>>><<<:::888666555444333222\207111\202222\1""333\207444\2702" \
+  "22\204111\212222\205444\1""333\202222\20""111000...+++(((%%%!!!\35\35" \
+  "\35\31\31\31\25\25\25\22\22\22\16\16\16\12\12\12\6\6\6\4\4\4\2\2\2\332" \
+  "\0\0\0\25\2\2\2\4\4\4\6\6\6\12\12\12\15\15\15\20\20\20\24\24\24\30\30" \
+  "\30\33\33\33\37\37\37\"\"\"%%%(((***+++,,,...///000222444\202666\4""8" \
+  "88:::<<<\202\202\202\202\377\377\377\6\335\335\335<<<:::888555333\202" \
+  "000\2...---\210,,,\1---\210...\1---\216,,,\210...\1---\205,,,\1---\202" \
+  "...\1---\205,,,\1---\206...\3---,,,---\206...\217,,,\210...\202,,,\16" \
+  "***)))&&&$$$\40\40\40\35\35\35\32\32\32\26\26\26\22\22\22\17\17\17\14" \
+  "\14\14\10\10\10\5\5\5\2\2\2\334\0\0\0\32\2\2\2\5\5\5\10\10\10\12\12\12" \
+  "\16\16\16\21\21\21\24\24\24\30\30\30\32\32\32\36\36\36\40\40\40\"\"\"" \
+  "$$$&&&''')))***,,,...///000333444666888~~~\202\377\377\377\13\334\334" \
+  "\334888666333000...,,,***)))((('''\206&&&\1'''\204(((\3)))***)))\244" \
+  "(((\203'''\222(((\1'''\204&&&\1'''\204(((\202'''\205(((\202)))\203((" \
+  "(\20'''&&&%%%$$$\"\"\"\37\37\37\34\34\34\31\31\31\26\26\26\23\23\23\20" \
+  "\20\20\14\14\14\11\11\11\6\6\6\4\4\4\2\2\2\334\0\0\0\32\1\1\1\3\3\3\6" \
+  "\6\6\10\10\10\13\13\13\16\16\16\21\21\21\24\24\24\26\26\26\31\31\31\34" \
+  "\34\34\35\35\35\37\37\37\40\40\40\"\"\"###%%%''')))***,,,...00011122" \
+  "2|||\202\377\377\377\14\333\333\333222000...,,,***(((&&&$$$###\"\"\"" \
+  "!!!\203\40\40\40\1!!!\204\"\"\"\1###\205$$$\1###\270\"\"\"\3!!!\40\40" \
+  "\40!!!\213\"\"\"\1###\204$$$\1###\202\"\"\"\16!!!\40\40\40\36\36\36\34" \
+  "\34\34\32\32\32\30\30\30\25\25\25\22\22\22\20\20\20\15\15\15\12\12\12" \
+  "\7\7\7\4\4\4\2\2\2\336\0\0\0\31\2\2\2\4\4\4\6\6\6\10\10\10\13\13\13\16" \
+  "\16\16\20\20\20\22\22\22\24\24\24\27\27\27\30\30\30\32\32\32\33\33\33" \
+  "\34\34\34\36\36\36\40\40\40\"\"\"$$$&&&''')))***,,,---DDD\202lll\12a" \
+  "aa---,,,)))'''%%%###!!!\37\37\37\36\36\36\202\34\34\34\204\33\33\33\203" \
+  "\34\34\34\1\35\35\35\207\36\36\36\1\35\35\35\220\34\34\34\205\35\35\35" \
+  "\222\34\34\34\2\35\35\35\36\36\36\202\35\35\35\205\34\34\34\204\35\35" \
+  "\35\204\34\34\34\1\33\33\33\213\34\34\34\1\35\35\35\206\36\36\36\3\35" \
+  "\35\35\34\34\34\33\33\33\202\32\32\32\13\30\30\30\26\26\26\24\24\24\21" \
+  "\21\21\17\17\17\14\14\14\12\12\12\7\7\7\5\5\5\3\3\3\1\1\1\337\0\0\0\26" \
+  "\2\2\2\4\4\4\6\6\6\10\10\10\12\12\12\14\14\14\16\16\16\20\20\20\22\22" \
+  "\22\24\24\24\25\25\25\26\26\26\27\27\27\31\31\31\32\32\32\34\34\34\36" \
+  "\36\36\40\40\40\"\"\"$$$%%%'''\206(((\11&&&$$$\"\"\"\40\40\40\36\36\36" \
+  "\34\34\34\32\32\32\30\30\30\27\27\27\207\26\26\26\1\27\27\27\211\30\30" \
+  "\30\215\26\26\26\212\30\30\30\205\26\26\26\203\30\30\30\1\27\27\27\205" \
+  "\26\26\26\210\30\30\30\1\27\27\27\207\30\30\30\1\27\27\27\215\26\26\26" \
+  "\1\27\27\27\210\30\30\30\16\27\27\27\26\26\26\25\25\25\24\24\24\23\23" \
+  "\23\22\22\22\20\20\20\16\16\16\13\13\13\11\11\11\6\6\6\5\5\5\3\3\3\1" \
+  "\1\1\340\0\0\0\26\1\1\1\2\2\2\3\3\3\5\5\5\7\7\7\11\11\11\12\12\12\14" \
+  "\14\14\16\16\16\17\17\17\20\20\20\21\21\21\22\22\22\24\24\24\25\25\25" \
+  "\27\27\27\31\31\31\33\33\33\35\35\35\36\36\36\40\40\40!!!\203\"\"\"\202" \
+  "###\13\"\"\"!!!\37\37\37\35\35\35\32\32\32\30\30\30\26\26\26\25\25\25" \
+  "\23\23\23\22\22\22\21\21\21\203\20\20\20\203\21\21\21\203\22\22\22\205" \
+  "\23\23\23\204\22\22\22\204\21\21\21\1\22\22\22\204\21\21\21\226\22\22" \
+  "\22\203\21\21\21\222\22\22\22\202\21\21\21\1\20\20\20\203\21\21\21\204" \
+  "\22\22\22\203\21\21\21\203\22\22\22\204\23\23\23\203\22\22\22\202\21" \
+  "\21\21\1\20\20\20\202\16\16\16\7\14\14\14\12\12\12\10\10\10\6\6\6\4\4" \
+  "\4\3\3\3\1\1\1\343\0\0\0\10\2\2\2\3\3\3\4\4\4\6\6\6\7\7\7\10\10\10\12" \
+  "\12\12\13\13\13\202\14\14\14\13\15\15\15\17\17\17\20\20\20\22\22\22\24" \
+  "\24\24\26\26\26\27\27\27\31\31\31\32\32\32\34\34\34\35\35\35\204\36\36" \
+  "\36\11\35\35\35\34\34\34\32\32\32\30\30\30\26\26\26\24\24\24\22\22\22" \
+  "\20\20\20\16\16\16\207\14\14\14\202\15\15\15\211\16\16\16\1\15\15\15" \
+  "\213\14\14\14\1\15\15\15\202\16\16\16\211\15\15\15\204\14\14\14\204\15" \
+  "\15\15\205\14\14\14\220\15\15\15\216\14\14\14\1\15\15\15\210\16\16\16" \
+  "\202\15\15\15\12\14\14\14\13\13\13\12\12\12\11\11\11\10\10\10\6\6\6\5" \
+  "\5\5\4\4\4\2\2\2\1\1\1\345\0\0\0\7\1\1\1\2\2\2\3\3\3\4\4\4\5\5\5\6\6" \
+  "\6\7\7\7\202\10\10\10\13\11\11\11\12\12\12\13\13\13\15\15\15\17\17\17" \
+  "\20\20\20\22\22\22\24\24\24\25\25\25\27\27\27\30\30\30\204\31\31\31\12" \
+  "\30\30\30\27\27\27\25\25\25\23\23\23\21\21\21\17\17\17\14\14\14\12\12" \
+  "\12\11\11\11\7\7\7\203\6\6\6\202\7\7\7\202\10\10\10\213\11\11\11\213" \
+  "\10\10\10\205\11\11\11\232\10\10\10\205\11\11\11\210\10\10\10\202\7\7" \
+  "\7\212\10\10\10\211\11\11\11\203\10\10\10\202\6\6\6\202\4\4\4\202\2\2" \
+  "\2\351\0\0\0\1\1\1\1\202\2\2\2\1\3\3\3\203\4\4\4\202\5\5\5\12\6\6\6\10" \
+  "\10\10\12\12\12\14\14\14\16\16\16\17\17\17\20\20\20\22\22\22\23\23\23" \
+  "\24\24\24\202\25\25\25\13\24\24\24\23\23\23\22\22\22\20\20\20\16\16\16" \
+  "\14\14\14\12\12\12\10\10\10\6\6\6\4\4\4\3\3\3\202\2\2\2\1\3\3\3\204\4" \
+  "\4\4\213\5\5\5\213\4\4\4\204\5\5\5\233\4\4\4\205\5\5\5\224\4\4\4\212" \
+  "\5\5\5\203\4\4\4\1\3\3\3\202\2\2\2\357\0\0\0\205\2\2\2\12\3\3\3\4\4\4" \
+  "\6\6\6\7\7\7\10\10\10\12\12\12\14\14\14\16\16\16\17\17\17\20\20\20\202" \
+  "\21\21\21\12\20\20\20\17\17\17\16\16\16\14\14\14\12\12\12\10\10\10\6" \
+  "\6\6\4\4\4\3\3\3\1\1\1\204\0\0\0\1\1\1\1\221\2\2\2\1\1\1\1\203\0\0\0" \
+  "\1\1\1\1\213\2\2\2\202\1\1\1\217\2\2\2\3\1\1\1\0\0\0\1\1\1\212\2\2\2" \
+  "\1\1\1\1\202\0\0\0\206\2\2\2\1\1\1\1\203\0\0\0\1\1\1\1\220\2\2\2\1\1" \
+  "\1\1\370\0\0\0\7\1\1\1\2\2\2\4\4\4\5\5\5\7\7\7\11\11\11\12\12\12\202" \
+  "\14\14\14\202\15\15\15\202\14\14\14\7\12\12\12\11\11\11\7\7\7\5\5\5\4" \
+  "\4\4\2\2\2\1\1\1\377\0\0\0\336\0\0\0\7\1\1\1\2\2\2\3\3\3\4\4\4\6\6\6" \
+  "\7\7\7\10\10\10\204\11\11\11\7\10\10\10\7\7\7\6\6\6\4\4\4\3\3\3\2\2\2" \
+  "\1\1\1\377\0\0\0\340\0\0\0\1\1\1\1\202\2\2\2\202\4\4\4\1\5\5\5\204\6" \
+  "\6\6\1\5\5\5\202\4\4\4\202\2\2\2\1\1\1\1\377\0\0\0\343\0\0\0\1\1\1\1" \
+  "\204\2\2\2\202\4\4\4\204\2\2\2\1\1\1\1\377\0\0\0\377\0\0\0\377\0\0\0" \
+  "\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0" \
+  "\0\377\0\0\0\223\0\0\0\1\2\2\2\202\20\20\20\1\5\5\5\202\0\0\0\2""888" \
+  "\15\15\15\245\0\0\0\2\15\15\15""888\212\0\0\0\2""888\15\15\15\217\0\0" \
+  "\0\4FFF\0\0\0\15\15\15""888\225\0\0\0\2""888\15\15\15\377\0\0\0\214\0" \
+  "\0\0\10\36\36\36\354\354\354\254\254\254\312\312\312\210\210\210\0\0" \
+  "\0\246\246\246(((\245\0\0\0\5\32\32\32sss\0\0\0\213\213\213\37\37\37" \
+  "\207\0\0\0\2\246\246\246(((\217\0\0\0\4\320\320\320\0\0\0\32\32\32ss" \
+  "s\225\0\0\0\2\246\246\246(((\377\0\0\0\214\0\0\0\34\36\36\36\306\306" \
+  "\306\0\0\0\3\3\3\342\342\342\22\22\22\246\246\246(((\0\0\0%%%}}}qqq\16" \
+  "\16\16\0\0\0...{{{~~~%%%\0\0\0\2\2\2```\206\206\206fff\0\0\0\2\2\2\\" \
+  "\\\\\203\203\203>>>\204\0\0\0\23>>>\32\32\32\0\0\0\\\\\\\27\27\27\0\0" \
+  "\0XXX\0\0\0...{{{~~~%%%\0\0\0\20\20\20GGG\34\34\34\316\316\316\202\202" \
+  "\202XXX\206\0\0\0\14\246\246\246(((\0\0\0""000\201\201\201ggg\7\7\7\0" \
+  "\0\0...{{{~~~%%%\202\0\0\0\14""888\202\202\202@@@\320\320\320\0\0\0\20" \
+  "\20\20GGG\0\0\0III000\177\177\177XXX\202\0\0\0\4;;;\202\202\202\77\77" \
+  "\77XXX\204\0\0\0\4III666\200\200\200SSS\202\0\0\0\11\246\246\246(((\6" \
+  "\6\6aaa\205\205\205^^^\2\2\2\20\20\20LLL\202\0\0\0\14)))222\0\0\0%%%" \
+  "}}}qqq\16\16\16\0\0\0III333\177\177\177\32\32\32\366\0\0\0\35\36\36\36" \
+  "\306\306\306\0\0\0\21\21\21\347\347\347\14\14\14\246\246\246(((\36\36" \
+  "\36\305\305\305999SSS\262\262\262\0\0\0@@@444<<<\311\311\311\10\10\10" \
+  "bbb\213\213\213&&&LLL\0\0\0\217\217\217xxx...\262\262\262:::\203\0\0" \
+  "\0\23bbbmmm\16\16\16\350\350\350fff\23\23\23\274\274\274\0\0\0@@@444" \
+  "<<<\311\311\311\10\10\10&&&\250\250\250\15\15\15\273\273\273RRR***\206" \
+  "\0\0\0\40\246\246\246(((%%%\314\314\314666vvv\237\237\237\0\0\0@@@44" \
+  "4<<<\311\311\311\10\10\10&&&\307\307\307000yyy\340\340\340\0\0\0&&&\250" \
+  "\250\250\0\0\0\256\256\256\230\230\230222\265\265\265@@@***\304\304\304" \
+  "///vvv\340\340\340\204\0\0\0\35\256\256\256\240\240\240+++\247\247\247" \
+  "VVV\0\0\0\246\246\246(((\16\16\16PPP+++\206\206\206qqq\1\1\1\314\314" \
+  "\314\14\14\14\0\0\0\252\252\252///\36\36\36\305\305\305999SSS\262\262" \
+  "\262\0\0\0\256\256\256\237\237\237666\17\17\17\366\0\0\0\12\36\36\36" \
+  "\360\360\360\272\272\272\300\300\300\\\\\\\0\0\0\246\246\246(((ppp\250" \
+  "\250\250\202fff\15\335\335\335\7\7\7$$$\207\207\207\233\233\233\331\331" \
+  "\331---NNN\277\277\277ZZZ\23\23\23\0\0\0\344\344\344\202fff\2\222\222" \
+  "\222\205\205\205\203\0\0\0\22\37\37\37\260\260\260MMMxxx\246\246\246" \
+  "TTT{{{\0\0\0$$$\207\207\207\233\233\233\331\331\331---&&&\250\250\250" \
+  "\0\0\0\252\252\252&&&\207\0\0\0\4\246\246\246(((rrrjjj\202\0\0\0\32\335" \
+  "\335\335\0\0\0$$$\207\207\207\233\233\233\331\331\331---sssccc\0\0\0" \
+  "\3\3\3\334\334\334\0\0\0&&&\250\250\250\0\0\0\256\256\256***\0\0\0aa" \
+  "annnttt```\0\0\0\2\2\2\333\333\333\204\0\0\0\25\256\256\256111\0\0\0" \
+  "---\252\252\252\0\0\0\246\246\246(((\1\1\1```\226\226\226\250\250\250" \
+  "\255\255\255\0\0\0rrrddd\26\26\26\302\302\302\0\0\0ppp\250\250\250\202" \
+  "fff\4\335\335\335\7\7\7\256\256\256---\370\0\0\0\2\36\36\36\306\306\306" \
+  "\204\0\0\0\4\246\246\246(((uuu\213\213\213\203<<<\14\3\3\3\312\312\312" \
+  "---\10\10\10\247\247\247222\0\0\0(((ooo\326\326\326\32\32\32\342\342" \
+  "\342\203<<<\1!!!\204\0\0\0\21\314\314\314\224\224\224$$$\264\264\264" \
+  "\230\230\230888\0\0\0\312\312\312---\10\10\10\247\247\247222&&&\250\250" \
+  "\250\0\0\0\251\251\251&&&\207\0\0\0\4\246\246\246(((vvvfff\202\0\0\0" \
+  "\32\334\334\334\0\0\0\312\312\312---\10\10\10\247\247\247222vvv___\0" \
+  "\0\0\1\1\1\332\332\332\0\0\0&&&\250\250\250\0\0\0\256\256\256\"\"\"\0" \
+  "\0\0^^^ppptttbbb\0\0\0\3\3\3\334\334\334\204\0\0\0\25\256\256\256---" \
+  "\0\0\0***\255\255\255\0\0\0\246\246\246(((XXX\224\224\224\20\20\20**" \
+  "*\262\262\262\0\0\0\22\22\22\301\301\301wwwbbb\0\0\0uuu\213\213\213\203" \
+  "<<<\3\3\3\3\256\256\256\"\"\"\370\0\0\0\2\36\36\36\306\306\306\204\0" \
+  "\0\0\27\246\246\246(((&&&\310\310\310(((\30\30\30III\0\0\0\323\323\323" \
+  "&&&666\350\350\350222444&&&\24\24\24\272\272\272***\236\236\236nnn\23" \
+  "\23\23""000(((\204\0\0\0\24\231\231\231\324\324\324\0\0\0\205\205\205" \
+  "\344\344\344\4\4\4\0\0\0\323\323\323&&&666\350\350\350222&&&\250\250" \
+  "\250\0\0\0\226\226\226ddd%%%\0\0\0\223\223\223\204\0\0\0\40\246\246\246" \
+  "(((...\305\305\305\35\35\35^^^\254\254\254\0\0\0\323\323\323&&&666\350" \
+  "\350\350222///\300\300\300\31\31\31ggg\342\342\342\0\0\0&&&\250\250\250" \
+  "\0\0\0\256\256\256\"\"\"\0\0\0^^^ppp&&&\310\310\310:::~~~\336\336\336" \
+  "\204\0\0\0\15\256\256\256\224\224\224\25\25\25\227\227\227bbb\0\0\0\246" \
+  "\246\246(((aaa\217\217\217\24\24\24\222\222\222\262\262\262\202\0\0\0" \
+  "\24\237\237\237\336\336\336\12\12\12\0\0\0&&&\310\310\310(((\30\30\30" \
+  "III\0\0\0\256\256\256\"\"\"\0\0\0UUU>>>\0\0\0UUU>>>\6\6\6\214\214\214" \
+  "\360\0\0\0\2\17\17\17ccc\204\0\0\0\27SSS\24\24\24\0\0\0""111\220\220" \
+  "\220\226\226\226YYY\0\0\0""666\230\230\230uuuSSS\31\31\31""444\216\216" \
+  "\216\231\231\231PPP\0\0\0\6\6\6mmm\233\233\233\203\203\203\36\36\36\204" \
+  "\0\0\0\5""444WWW\0\0\0***aaa\202\0\0\0\15""666\230\230\230uuuSSS\31\31" \
+  "\31\23\23\23TTT\0\0\0\27\27\27ttthhh\16\16\16\307\307\307\204\0\0\0\40" \
+  "SSS\24\24\24\0\0\0BBB\227\227\227~~~\16\16\16\0\0\0""666\230\230\230" \
+  "uuuSSS\31\31\31\0\0\0JJJ\231\231\231TTThhh\0\0\0\23\23\23TTT\0\0\0WW" \
+  "W\21\21\21\0\0\0///888\0\0\0""333yyy<<<\314\314\314\204\0\0\0\4\256\256" \
+  "\256XXX\226\226\226iii\202\0\0\0\7SSS\24\24\24\5\5\5zzz\225\225\225B" \
+  "BBYYY\202\0\0\0\2XXX\224\224\224\203\0\0\0\17""111\220\220\220\226\226" \
+  "\226YYY\0\0\0WWW\21\21\21\0\0\0EEE222\0\0\0EEE222\5\5\5rrr\377\0\0\0" \
+  "\244\0\0\0\2!!!777\240\0\0\0\4oooPPP\236\236\236{{{\204\0\0\0\2\256\256" \
+  "\256\"\"\"\214\0\0\0\3\77\77\77\302\302\302(((\377\0\0\0\330\0\0\0\3" \
+  "888\\\\\\<<<\205\0\0\0\2AAA\15\15\15\214\0\0\0\2LLL555\377\0\0\0\377" \
+  "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" \
+  "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" \
+  "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" \
+  "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" \
+  "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" \
+  "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" \
+  "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" \
+  "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" \
+  "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" \
+  "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" \
+  "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" \
+  "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\337\0\0\0\377\4\2\0\361" \
+  "\4\2\0\377\10\4\0\361\10\4\0\377\14\6\0\361\14\6\0\377\17\10\0\361\17" \
+  "\10\0\377\23\12\0\361\23\12\0\377\26\12\0\361\26\12\0\377\32\14\0\361" \
+  "\32\14\0\377\36\16\0\361\36\16\0\377!\20\0\361!\20\0\377%\22\0\361%\22" \
+  "\0\377(\24\0\361(\24\0\377,\26\0\361,\26\0\3770\30\0\3610\30\0\3773\32" \
+  "\0\3613\32\0\3777\34\0\3617\34\0\377:\35\0\361:\35\0\377>\36\0\361>\36" \
+  "\0\377B\40\0\361B\40\0\377E\"\0\361E\"\0\377I$\0\361I$\0\377L&\0\361" \
+  "L&\0\377P(\0\361P(\0\377T*\0\361T*\0\377W,\0\361W,\0\377[.\0\361[.\0" \
+  "\377^/\0\361^/\0\377b0\0\361b0\0\377f2\0\361f2\0\377j4\0\361j4\0\377" \
+  "m6\0\361m6\0\377q8\0\361q8\0\377t:\0\361t:\0\377x<\0\361x<\0\377|>\0" \
+  "\361|>\0\377\177@\0\361\177@\0\377\203B\0\361\203B\0\377\206B\0\361\206" \
+  "B\0\377\212D\0\361\212D\0\377\216F\0\361\216F\0\377\221H\0\361\221H\0" \
+  "\377\225J\0\361\225J\0\377\230L\0\361\230L\0\377\234N\0\361\234N\0\377" \
+  "\240P\0\361\240P\0\377\243R\0\361\243R\0\377\247T\0\361\247T\0\377\252" \
+  "U\0\361\252U\0\377\256V\0\361\256V\0\377\262X\0\361\262X\0\377\265Z\0" \
+  "\361\265Z\0\377\271\\\0\361\271\\\0\377\275^\0\361\275^\0\377\300`\0" \
+  "\361\300`\0\377\304b\0\361\304b\0\377\310d\0\361\310d\0\377\313f\0\361" \
+  "\313f\0\377\317h\0\361\317h\0\377\322h\0\361\322h\0\377\326j\0\361\326" \
+  "j\0\377\332l\0\361\332l\0\377\335n\0\361\335n\0\377\341p\0\361\341p\0" \
+  "\377\344r\0\361\344r\0\377\350t\0\361\350t\0\377\354v\0\361\354v\0")
+
+

Modified: trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/persistent.c
===================================================================
--- trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/persistent.c	2007-12-05 22:18:41 UTC (rev 3582)
+++ trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/persistent.c	2007-12-06 01:05:17 UTC (rev 3583)
@@ -226,6 +226,13 @@
 
 	// Set up GConf, fetch default values and attach notification handler
 	omp_gconf_client = gconf_client_get_default();
+
+	if (!omp_gconf_client)
+	{
+		g_printerr("GConf error: could not connect to gconfd\nWill continue execution, though program will have limited functionality.\n");
+		return;
+	}
+
 	gconf_client_add_dir(omp_gconf_client, OMP_GCONF_PATH, GCONF_CLIENT_PRELOAD_ONELEVEL, NULL);
 
 	omp_config->shuffle =

Modified: trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/playback.c
===================================================================
--- trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/playback.c	2007-12-05 22:18:41 UTC (rev 3582)
+++ trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/playback.c	2007-12-06 01:05:17 UTC (rev 3583)
@@ -29,11 +29,12 @@
 #include <gst/interfaces/xoverlay.h>
 #include <uriparser/Uri.h>
 
-#include "guitools.h"
 #include "main.h"
 #include "main_page.h"
+#include "mplayer_playback.h"
 #include "persistent.h"
 #include "playback.h"
+#include "utils.h"
 
 /// Our ticket to the gstreamer world
 GstElement *omp_gst_playbin = NULL;
@@ -151,7 +152,8 @@
 
 	gst_object_unref(bus);
 
-	return TRUE;
+	// Initialize mplayer interface and return its result as we would return TRUE now
+	return omp_mplayback_init();
 }
 
 /**
@@ -164,6 +166,10 @@
 
 	if (!omp_gst_playbin) return;
 
+	// Free resources used by the mplayer interface
+	omp_mplayback_free();
+
+	// Free resources used by gstreamer
 	bus = gst_pipeline_get_bus(GST_PIPELINE(omp_gst_playbin));
 	gst_bus_remove_signal_watch(bus);
 	gst_object_unref(bus);
@@ -189,6 +195,7 @@
 void
 omp_playback_reset()
 {
+	omp_mplayback_pause();
 	gst_element_set_state(omp_gst_playbin, GST_STATE_NULL);
 
 	g_signal_emit_by_name(G_OBJECT(omp_window), OMP_EVENT_PLAYBACK_RESET);
@@ -209,12 +216,25 @@
 		g_printf("Loading track: %s\n", uri);
 	#endif
 
-	// Update gstreamer pipe
-	gst_element_set_state(omp_gst_playbin, GST_STATE_NULL);
-	g_object_set(G_OBJECT(omp_gst_playbin), "uri", uri, NULL);
-	gst_element_set_state(omp_gst_playbin, GST_STATE_PAUSED);
+	// Make sure mplayer stays quiet
+	omp_mplayback_pause();
 
-	return (gst_element_set_state(omp_gst_playbin, GST_STATE_PAUSED) != GST_STATE_CHANGE_FAILURE);
+	if (omp_mplayback_load_video_from_uri(uri))
+	{
+		// As gstreamer won't be fed a new track we need to make sure it's silent
+		gst_element_set_state(omp_gst_playbin, GST_STATE_NULL);
+
+		return TRUE;
+
+	} else {
+
+		// Update gstreamer pipe
+		gst_element_set_state(omp_gst_playbin, GST_STATE_NULL);
+		g_object_set(G_OBJECT(omp_gst_playbin), "uri", uri, NULL);
+		gst_element_set_state(omp_gst_playbin, GST_STATE_PAUSED);
+
+		return (gst_element_set_state(omp_gst_playbin, GST_STATE_PAUSED) != GST_STATE_CHANGE_FAILURE);
+	}
 }
 
 /**
@@ -272,6 +292,14 @@
 		g_print("Starting playback\n");
 	#endif
 
+	// Start mplayer playback if a video was loaded
+	if (omp_mplayback_video_loaded())
+	{
+		omp_mplayback_play();
+		return;
+	}
+
+	// No video loaded, let's try playing with gstreamer
 	g_object_get(G_OBJECT(omp_gst_playbin), "uri", &track_uri, NULL);
 	if (!track_uri)
 	{
@@ -306,8 +334,10 @@
 		g_print("Suspending playback\n");
 	#endif
 
-	// Set state
-	gst_element_set_state(omp_gst_playbin, GST_STATE_PAUSED);
+	if (omp_mplayback_video_loaded())
+		omp_mplayback_pause();
+	else
+		gst_element_set_state(omp_gst_playbin, GST_STATE_PAUSED);
 
 	// Stop timer
 	omp_playback_ui_timeout_halted = TRUE;
@@ -321,7 +351,13 @@
 {
 	GstState state;
 
-	// Poll state with an immediate timeout
+	// Query mplayer interface first
+	if (omp_mplayback_is_playing())
+	{
+		return omp_mplayback_get_state();
+	}
+
+	// Poll gstreamer state with an immediate timeout
 	state = GST_STATE(omp_gst_playbin);
 
 	// The NULL and READY element states are no different from PAUSED for more abstract layers
@@ -342,6 +378,13 @@
 	GstFormat format = GST_FORMAT_TIME;
 	gint64 position = 0;
 
+	// Query mplayer interface first
+	if (omp_mplayback_is_playing())
+	{
+		return omp_mplayback_get_video_position();
+	}
+
+	// Let's see if gstreamer has anything to say
 	if (!omp_gst_playbin) return 0;
 
 	// Return 0 if function returns FALSE, position otherwise
@@ -358,6 +401,14 @@
 	GstState pipe_state;
 	gint64 pos;
 
+	// Use the mplayer interface if a video had been loaded
+	if (omp_mplayback_video_loaded())
+	{
+		omp_mplayback_set_video_position(position);
+		omp_playback_save_state();
+		return;
+	}
+
 	if (!omp_gst_playbin) return;
 
 	// If we don't clamp it to values >= 0 we trigger EOS messages which make the playlist mess up
@@ -431,6 +482,9 @@
 
 		g_signal_emit_by_name(G_OBJECT(omp_window), OMP_EVENT_PLAYBACK_VOLUME_CHANGED);
 	}
+
+	// Update mplayer interface, too
+	omp_mplayback_set_volume(volume);
 }
 
 /**
@@ -443,6 +497,9 @@
 	gdouble volume;
 	g_object_get(G_OBJECT(omp_gst_playbin), "volume", &volume, NULL);
 
+	// We don't care about the mplayer interface here because
+	// gstreamer's and mplayer's volume are always kept in sync
+
 	return volume*100;
 }
 
@@ -586,10 +643,7 @@
 omp_gst_message_element(GstBus *bus, GstMessage *message, gpointer data)
 {
 	if (gst_structure_has_name(message->structure, "prepare-xwindow-id"))
-	{
 		gst_x_overlay_set_xwindow_id(GST_X_OVERLAY(GST_MESSAGE_SRC(message)), omp_main_get_video_window());
-	}
 
 	return TRUE;
 }
-

Modified: trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/playlist.c
===================================================================
--- trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/playlist.c	2007-12-05 22:18:41 UTC (rev 3582)
+++ trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/playlist.c	2007-12-06 01:05:17 UTC (rev 3583)
@@ -34,12 +34,13 @@
 #include <stdarg.h>
 #include <string.h>
 
-#include "playlist.h"
 #include "guitools.h"
-#include "omp_spiff_c.h"
 #include "main.h"
-#include "playback.h"
+#include "omp_spiff_c.h"
 #include "persistent.h"
+#include "playback.h"
+#include "playlist.h"
+#include "utils.h"
 
 /// Loaded playlist
 omp_spiff_list *omp_playlist = NULL;
@@ -1065,41 +1066,6 @@
 }
 
 /**
- * Utility function that removes the path and extension of a file name
- * @param file_name File name to extract title from, can contain a path
- * @return String holding the title, must be freed after use
- * @todo Make unicode safe
- * @note Yes, this is quick'n'dirty. It will be replaced.
- */
-gchar *
-get_base_file_name(gchar *file_name)
-{
-	gchar base[256];
-	guint i, j, last_delim, extension_pos;
-
-	// Find last directory delimiter
-	last_delim = 0;
-	for (i=0; file_name[i]; i++)
-	{
-		if (file_name[i] == '/') last_delim = i+1;
-	}
-
-	// Find file extension
-	for(extension_pos = strlen(file_name);
-		(extension_pos) && (file_name[extension_pos] != '.');
-		extension_pos--);
-
-	// Extract title
-	for (j=0, i=last_delim; i<extension_pos; i++)
-	{
-		base[j++] = file_name[i];
-	}
-	base[j] = 0;
-	
-	return g_strdup((gchar *)&base);
-}
-
-/**
  * Utility function that extracts a playlist's name from its file name
  * @param playlist_file File name to extract title from, can contain a path
  * @return String holding the title, must be freed after use

Modified: trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/playlist.h
===================================================================
--- trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/playlist.h	2007-12-05 22:18:41 UTC (rev 3582)
+++ trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/playlist.h	2007-12-06 01:05:17 UTC (rev 3583)
@@ -99,7 +99,6 @@
 gboolean omp_playlist_track_append_file(gchar *file_name);
 guint omp_playlist_track_append_directory(gchar *dir_name);
 
-gchar *get_base_file_name(gchar *file_name);
 gchar *get_playlist_title(gchar *playlist_file);
 
 // Taken from uriparser's UriCommon.h, which sadly is not installed with uriparser

Modified: trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/playlist_page.c
===================================================================
--- trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/playlist_page.c	2007-12-05 22:18:41 UTC (rev 3582)
+++ trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/playlist_page.c	2007-12-06 01:05:17 UTC (rev 3583)
@@ -37,6 +37,7 @@
 #include "persistent.h"
 #include "playlist.h"
 #include "playlist_page.h"
+#include "utils.h"
 
 /// Enumeration of the playlist list columns
 enum

Added: trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/utils.c
===================================================================
--- trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/utils.c	2007-12-05 22:18:41 UTC (rev 3582)
+++ trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/utils.c	2007-12-06 01:05:17 UTC (rev 3583)
@@ -0,0 +1,174 @@
+/*
+ *  OpenMoko Media Player
+ *   http://openmoko.org/
+ *
+ *  Copyright (C) 2007 by Soeren Apel (abraxa at dar-clan.de)
+ *
+ *  This program 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 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program 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 this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/**
+ * @file utils.c
+ * Various utility functions
+ */
+
+#include <gtk/gtk.h>
+
+#include "main.h"
+#include "utils.h"
+
+/// Absolute path to the UI pixmaps
+gchar *omp_ui_image_path = NULL;
+
+
+
+/**
+ * Creates a button containing a stock image
+ * @param widget_name Name to set for the button and image widgets
+ * @param image_name Name of the stock image to use
+ * @param image Destination for the image's handle (can be NULL)
+ * @param callback Callback to set
+ * @return The button
+ */
+GtkWidget *
+button_create_with_image(gchar *widget_name, gchar *image_name, GtkWidget **image, GCallback callback)
+{
+	GtkWidget *btn_image, *button;
+
+	button = gtk_button_new();
+	gtk_widget_set_name(GTK_WIDGET(button), widget_name);
+	g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(callback), NULL);
+	GTK_WIDGET_UNSET_FLAGS(GTK_WIDGET(button), GTK_CAN_FOCUS);
+
+	btn_image = gtk_image_new();
+	gtk_widget_set_name(GTK_WIDGET(btn_image), widget_name);
+	gtk_image_set_from_stock(GTK_IMAGE(btn_image), image_name, -1);
+	gtk_container_add(GTK_CONTAINER(button), GTK_WIDGET(btn_image));
+
+	if (image) *image = btn_image;
+
+	return button;
+}
+
+/**
+ * Wraps a widget in an invisible GtkFrame so the widget can be padded using x/ythickness in the frame's style
+ * @param widget Widget to be put inside the frame
+ * @param name Name to assign to the frame, uses the widget's name if NULL
+ * @return Frame containing the widget
+ */
+GtkWidget *
+widget_wrap(GtkWidget *widget, gchar *name)
+{
+	GtkWidget *frame;
+
+	frame = gtk_frame_new(NULL);
+	gtk_widget_set_name(GTK_WIDGET(frame), (name) ? name : gtk_widget_get_name(widget));
+	gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_NONE);
+	gtk_container_add(GTK_CONTAINER(frame), GTK_WIDGET(widget));
+
+	return frame;
+}
+
+/**
+ * Adds a child to a GtkNotebook, filling the page handle with a stock icon
+ */
+void
+notebook_add_page_with_stock(GtkWidget *notebook, GtkWidget *child, const gchar *icon_name, int padding)
+{
+	GtkWidget *icon, *alignment;
+
+	icon = gtk_image_new_from_icon_name(icon_name, -1);//GTK_ICON_SIZE_LARGE_TOOLBAR);
+
+	alignment = gtk_alignment_new(0.5, 0.5, 1, 1);
+	gtk_alignment_set_padding(GTK_ALIGNMENT(alignment), padding, padding, padding, padding);
+	gtk_container_add(GTK_CONTAINER(alignment), icon);
+	gtk_widget_show_all(GTK_WIDGET(alignment));
+
+	gtk_notebook_append_page(GTK_NOTEBOOK(notebook), child, alignment);
+	gtk_container_child_set(GTK_CONTAINER(notebook), child, "tab-expand", TRUE, NULL);
+}
+
+/**
+ * Presents a simple non-modal error dialog to the user
+ */
+void
+error_dialog(gchar *message)
+{
+	GtkWidget *dialog;
+
+	dialog = gtk_message_dialog_new(0,
+		GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
+		"%s", message);
+
+	// We don't want a title of "<unnamed>"
+	gtk_window_set_title(GTK_WINDOW(dialog), " ");
+
+	g_signal_connect_swapped(dialog, "response", G_CALLBACK (gtk_widget_destroy), dialog);
+	gtk_widget_show_all(dialog);
+}
+
+/**
+ * Presents a simple modal error dialog to the user
+ */
+void
+error_dialog_modal(gchar *message)
+{
+	GtkWidget *dialog;
+
+	dialog = gtk_message_dialog_new(0,
+		GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
+		"%s", message);
+
+	// We don't want a title of "<unnamed>"
+	gtk_window_set_title(GTK_WINDOW(dialog), " ");
+
+	gtk_dialog_run(GTK_DIALOG(dialog));
+	gtk_widget_destroy(dialog);
+}
+
+/**
+ * Utility function that removes the path and extension of a file name
+ * @param file_name File name to extract title from, can contain a path
+ * @return String holding the title, must be freed after use
+ * @todo Make unicode safe
+ * @note Yes, this is quick'n'dirty. It will be replaced.
+ */
+gchar *
+get_base_file_name(gchar *file_name)
+{
+	gchar base[256];
+	guint i, j, last_delim, extension_pos;
+
+	// Find last directory delimiter
+	last_delim = 0;
+	for (i=0; file_name[i]; i++)
+	{
+		if (file_name[i] == '/') last_delim = i+1;
+	}
+
+	// Find file extension
+	for(extension_pos = strlen(file_name);
+		(extension_pos) && (file_name[extension_pos] != '.');
+		extension_pos--);
+
+	// Extract title
+	for (j=0, i=last_delim; i<extension_pos; i++)
+	{
+		base[j++] = file_name[i];
+	}
+	base[j] = 0;
+	
+	return g_strdup((gchar *)&base);
+}

Added: trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/utils.h
===================================================================
--- trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/utils.h	2007-12-05 22:18:41 UTC (rev 3582)
+++ trunk/src/target/OM-2007.2/applications/openmoko-mediaplayer2/src/utils.h	2007-12-06 01:05:17 UTC (rev 3583)
@@ -0,0 +1,46 @@
+/*
+ *  OpenMoko Media Player
+ *   http://openmoko.org/
+ *
+ *  Copyright (C) 2007 by Soeren Apel (abraxa at dar-clan.de)
+ *
+ *  This program 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 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program 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 this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/**
+ * @file utils.h
+ * Various utility functions
+ */
+
+#ifndef UTILS_H
+#define UTILS_H
+
+#include <gtk/gtk.h>
+
+extern gchar *omp_ui_image_path;
+
+
+
+GtkWidget *button_create_with_image(gchar *widget_name, gchar *image_name, GtkWidget **image, GCallback callback);
+GtkWidget *widget_wrap(GtkWidget *widget, gchar *name);
+
+void notebook_add_page_with_stock(GtkWidget *notebook, GtkWidget *child, const gchar *icon_name, int padding);
+
+void error_dialog(gchar *message);
+void error_dialog_modal(gchar *message);
+
+gchar *get_base_file_name(gchar *file_name);
+
+#endif





More information about the commitlog mailing list