diff options
| author | SND\weimingzhi_cp <SND\weimingzhi_cp@e17a0e51-4ae3-4d35-97c3-1a29b211df97> | 2009-04-16 06:22:51 +0000 |
|---|---|---|
| committer | SND\weimingzhi_cp <SND\weimingzhi_cp@e17a0e51-4ae3-4d35-97c3-1a29b211df97> | 2009-04-16 06:22:51 +0000 |
| commit | 8139fbf8204882663446bcb06f68789353597820 (patch) | |
| tree | 6ea1f39932b33faee84d603e956470e37f135804 /gui | |
| download | pcsxr-8139fbf8204882663446bcb06f68789353597820.tar.gz | |
git-svn-id: https://pcsxr.svn.codeplex.com/svn/pcsxr@23061 e17a0e51-4ae3-4d35-97c3-1a29b211df97
Diffstat (limited to 'gui')
| -rw-r--r-- | gui/Cheat.c | 516 | ||||
| -rw-r--r-- | gui/Cheat.h | 26 | ||||
| -rw-r--r-- | gui/Config.c | 162 | ||||
| -rw-r--r-- | gui/Gtk2Gui.c | 2255 | ||||
| -rw-r--r-- | gui/Linux.h | 83 | ||||
| -rw-r--r-- | gui/LnxMain.c | 487 | ||||
| -rw-r--r-- | gui/Makefile.am | 19 | ||||
| -rw-r--r-- | gui/Makefile.in | 520 | ||||
| -rw-r--r-- | gui/Plugin.c | 387 | ||||
| -rw-r--r-- | gui/hdebug.c | 386 | ||||
| -rw-r--r-- | gui/hdebug.h | 33 | ||||
| -rw-r--r-- | gui/nopic.h | 1345 |
12 files changed, 6219 insertions, 0 deletions
diff --git a/gui/Cheat.c b/gui/Cheat.c new file mode 100644 index 00000000..d17bfbbc --- /dev/null +++ b/gui/Cheat.c @@ -0,0 +1,516 @@ +/* Cheat Support for PCSX-Reloaded + * + * Copyright (C) 2009, Wei Mingzhi <whistler@openoffice.org>. + * + * 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., 51 Franklin Steet, Fifth Floor, Boston, MA 02111-1307 USA + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdint.h> + +#include <gdk/gdkkeysyms.h> +#include <gtk/gtk.h> +#include <glade/glade.h> + +#include "Linux.h" + +#include "../libpcsxcore/cheat.h" + +GtkWidget *CheatListDlg = NULL; +GtkWidget *CheatSearchDlg = NULL; + +static void LoadCheatListItems(int index) { + GtkListStore *store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_STRING); + GtkTreeIter iter; + GtkWidget *widget; + GladeXML *xml; + + int i; + + xml = glade_get_widget_tree(CheatListDlg); + widget = glade_xml_get_widget(xml, "GtkCList_Cheat"); + + for (i = 0; i < NumCheats; i++) { + gtk_list_store_append(store, &iter); + gtk_list_store_set(store, &iter, 0, Cheats[i].Enabled ? _(" Yes") : "", + 1, Cheats[i].Descr, -1); + } + + gtk_tree_view_set_model(GTK_TREE_VIEW(widget), GTK_TREE_MODEL(store)); + g_object_unref(G_OBJECT(store)); + gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(widget), TRUE); + gtk_widget_show(widget); + + if (index >= NumCheats) { + index = NumCheats - 1; + } + + if (index >= 0) { + GtkTreePath *path; + GtkTreeSelection *sel; + + path = gtk_tree_path_new_from_indices(index, -1); + sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget)); + + gtk_tree_selection_select_path(sel, path); + gtk_tree_path_free(path); + } +} + +static void CheatList_TreeSelectionChanged(GtkTreeSelection *selection, gpointer user_data) { + GladeXML *xml; + GtkTreeIter iter; + GtkTreeModel *model; + GtkTreePath *path; + + gboolean selected; + int i; + + selected = gtk_tree_selection_get_selected(selection, &model, &iter); + + if (selected) { + path = gtk_tree_model_get_path(model, &iter); + i = *gtk_tree_path_get_indices(path); + + // If a row was selected, and the row is not blank, we can now enable + // some of the disabled widgets + xml = glade_get_widget_tree(CheatListDlg); + + gtk_widget_set_sensitive(GTK_WIDGET(glade_xml_get_widget(xml, "editbutton1")), TRUE); + gtk_widget_set_sensitive(GTK_WIDGET(glade_xml_get_widget(xml, "delbutton1")), TRUE); + if (Cheats[i].Enabled) { + gtk_widget_set_sensitive(GTK_WIDGET(glade_xml_get_widget(xml, "enablebutton1")), FALSE); + gtk_widget_set_sensitive(GTK_WIDGET(glade_xml_get_widget(xml, "disablebutton1")), TRUE); + } else { + gtk_widget_set_sensitive(GTK_WIDGET(glade_xml_get_widget(xml, "enablebutton1")), TRUE); + gtk_widget_set_sensitive(GTK_WIDGET(glade_xml_get_widget(xml, "disablebutton1")), FALSE); + } + } else { + xml = glade_get_widget_tree(CheatListDlg); + + gtk_widget_set_sensitive(GTK_WIDGET(glade_xml_get_widget(xml, "editbutton1")), FALSE); + gtk_widget_set_sensitive(GTK_WIDGET(glade_xml_get_widget(xml, "delbutton1")), FALSE); + gtk_widget_set_sensitive(GTK_WIDGET(glade_xml_get_widget(xml, "enablebutton1")), FALSE); + gtk_widget_set_sensitive(GTK_WIDGET(glade_xml_get_widget(xml, "disablebutton1")), FALSE); + } + + gtk_widget_set_sensitive (GTK_WIDGET(glade_xml_get_widget(xml, "savebutton1")), NumCheats); +} + +static void OnCheatListDlg_AddClicked(GtkWidget *widget, gpointer user_data) { + GtkWidget *dlg; + GtkWidget *box, *scroll, *label, *descr_edit, *code_edit; + + dlg = gtk_dialog_new_with_buttons(_("Add New Cheat"), GTK_WINDOW(CheatListDlg), + GTK_DIALOG_MODAL, GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, NULL); + + gtk_window_set_default_size(GTK_WINDOW(dlg), 350, 350); + + box = GTK_WIDGET(GTK_DIALOG(dlg)->vbox); + + label = gtk_label_new(_("Cheat Description:")); + gtk_box_pack_start(GTK_BOX(box), label, FALSE, FALSE, 5); + gtk_widget_show(label); + + descr_edit = gtk_entry_new(); + gtk_box_pack_start(GTK_BOX(box), descr_edit, FALSE, FALSE, 5); + gtk_widget_show(descr_edit); + + label = gtk_label_new(_("Cheat Code:")); + gtk_box_pack_start(GTK_BOX(box), label, FALSE, FALSE, 5); + gtk_widget_show(label); + + code_edit = gtk_text_view_new(); + gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(code_edit), GTK_WRAP_CHAR); + + scroll = gtk_scrolled_window_new(NULL, NULL); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), + GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); + + gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), code_edit); + gtk_widget_show(code_edit); + + gtk_box_pack_start(GTK_BOX(box), scroll, TRUE, TRUE, 5); + gtk_widget_show(scroll); + + gtk_window_set_position(GTK_WINDOW(dlg), GTK_WIN_POS_CENTER); + + gtk_widget_show_all(dlg); + + if (gtk_dialog_run(GTK_DIALOG(dlg)) == GTK_RESPONSE_ACCEPT) { + GtkTextBuffer *b = gtk_text_view_get_buffer(GTK_TEXT_VIEW(code_edit)); + GtkTextIter s, e; + char *codetext; + + gtk_text_buffer_get_bounds(GTK_TEXT_BUFFER(b), &s, &e); + codetext = strdup(gtk_text_buffer_get_text(GTK_TEXT_BUFFER(b), &s, &e, FALSE)); + + if (AddCheat(gtk_entry_get_text(GTK_ENTRY(descr_edit)), codetext) != 0) { + SysErrorMessage(_("Error"), _("Invalid cheat code!")); + } + + LoadCheatListItems(NumCheats - 1); + + free(codetext); + } + + gtk_widget_destroy(dlg); +} + +static void OnCheatListDlg_EditClicked(GtkWidget *widget, gpointer user_data) { + GtkWidget *dlg; + GtkWidget *box, *scroll, *label, *descr_edit, *code_edit; + GladeXML *xml; + GtkTreeIter iter; + GtkTreeModel *model; + GtkTreePath *path; + + gboolean selected; + int index, i; + char buf[8192]; + char *p = buf; + + xml = glade_get_widget_tree(CheatListDlg); + widget = glade_xml_get_widget(xml, "GtkCList_Cheat"); + + selected = gtk_tree_selection_get_selected( + gtk_tree_view_get_selection(GTK_TREE_VIEW(widget)), + &model, &iter); + + if (!selected) { + return; + } + + path = gtk_tree_model_get_path(model, &iter); + index = *gtk_tree_path_get_indices(path); + + dlg = gtk_dialog_new_with_buttons(_("Edit Cheat"), GTK_WINDOW(CheatListDlg), + GTK_DIALOG_MODAL, GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, NULL); + + gtk_window_set_default_size(GTK_WINDOW(dlg), 350, 350); + + box = GTK_WIDGET(GTK_DIALOG(dlg)->vbox); + + label = gtk_label_new(_("Cheat Description:")); + gtk_box_pack_start(GTK_BOX(box), label, FALSE, FALSE, 5); + gtk_widget_show(label); + + descr_edit = gtk_entry_new(); + gtk_entry_set_text(GTK_ENTRY(descr_edit), Cheats[index].Descr); + gtk_box_pack_start(GTK_BOX(box), descr_edit, FALSE, FALSE, 5); + gtk_widget_show(descr_edit); + + label = gtk_label_new(_("Cheat Code:")); + gtk_box_pack_start(GTK_BOX(box), label, FALSE, FALSE, 5); + gtk_widget_show(label); + + code_edit = gtk_text_view_new(); + + for (i = Cheats[index].First; i < Cheats[index].First + Cheats[index].n; i++) { + sprintf(p, "%.8X %.4X\n", CheatCodes[i].Addr, CheatCodes[i].Val); + p += 14; + *p = '\0'; + } + + gtk_text_buffer_set_text(gtk_text_view_get_buffer(GTK_TEXT_VIEW(code_edit)), + buf, -1); + + gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(code_edit), GTK_WRAP_CHAR); + + scroll = gtk_scrolled_window_new(NULL, NULL); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), + GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); + + gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), code_edit); + gtk_widget_show(code_edit); + + gtk_box_pack_start(GTK_BOX(box), scroll, TRUE, TRUE, 5); + gtk_widget_show(scroll); + + gtk_window_set_position(GTK_WINDOW(dlg), GTK_WIN_POS_CENTER); + + gtk_widget_show_all(dlg); + + if (gtk_dialog_run(GTK_DIALOG(dlg)) == GTK_RESPONSE_ACCEPT) { + GtkTextBuffer *b = gtk_text_view_get_buffer(GTK_TEXT_VIEW(code_edit)); + GtkTextIter s, e; + char *codetext; + + gtk_text_buffer_get_bounds(GTK_TEXT_BUFFER(b), &s, &e); + codetext = strdup(gtk_text_buffer_get_text(GTK_TEXT_BUFFER(b), &s, &e, FALSE)); + + if (EditCheat(index, gtk_entry_get_text(GTK_ENTRY(descr_edit)), codetext) != 0) { + SysErrorMessage(_("Error"), _("Invalid cheat code!")); + } + + LoadCheatListItems(index); + + free(codetext); + } + + gtk_widget_destroy(dlg); +} + +static void OnCheatListDlg_DelClicked(GtkWidget *widget, gpointer user_data) { + GladeXML *xml; + GtkTreeIter iter; + GtkTreeModel *model; + GtkTreePath *path; + + gboolean selected; + int i = -1; + + xml = glade_get_widget_tree(CheatListDlg); + widget = glade_xml_get_widget(xml, "GtkCList_Cheat"); + + selected = gtk_tree_selection_get_selected( + gtk_tree_view_get_selection(GTK_TREE_VIEW(widget)), + &model, &iter); + + if (selected) { + path = gtk_tree_model_get_path(model, &iter); + i = *gtk_tree_path_get_indices(path); + + RemoveCheat(i); + } + + LoadCheatListItems(i); // FIXME: should remove it from the list directly + // rather than regenerating the whole list +} + +static void OnCheatListDlg_EnableClicked(GtkWidget *widget, gpointer user_data) { + GladeXML *xml; + GtkTreeIter iter; + GtkTreeModel *model; + GtkTreePath *path; + + gboolean selected; + int i = -1; + + xml = glade_get_widget_tree(CheatListDlg); + widget = glade_xml_get_widget(xml, "GtkCList_Cheat"); + + selected = gtk_tree_selection_get_selected( + gtk_tree_view_get_selection(GTK_TREE_VIEW(widget)), + &model, &iter); + + if (selected) { + path = gtk_tree_model_get_path(model, &iter); + i = *gtk_tree_path_get_indices(path); + + Cheats[i].Enabled = 1; + } + + LoadCheatListItems(i); // FIXME: should modify it in the list directly + // rather than regenerating the whole list +} + +static void OnCheatListDlg_DisableClicked(GtkWidget *widget, gpointer user_data) { + GladeXML *xml; + GtkTreeIter iter; + GtkTreeModel *model; + GtkTreePath *path; + + gboolean selected; + int i = -1; + + xml = glade_get_widget_tree(CheatListDlg); + widget = glade_xml_get_widget(xml, "GtkCList_Cheat"); + + selected = gtk_tree_selection_get_selected( + gtk_tree_view_get_selection(GTK_TREE_VIEW(widget)), + &model, &iter); + + if (selected) { + path = gtk_tree_model_get_path(model, &iter); + i = *gtk_tree_path_get_indices(path); + + Cheats[i].Enabled = 0; + } + + LoadCheatListItems(i); // FIXME: should modify it in the list directly + // rather than regenerating the whole list +} + +static void OnCheatListDlg_OpenClicked(GtkWidget *widget, gpointer user_data) { + GtkWidget *chooser; + gchar *filename; + + GtkFileFilter *filter; + + chooser = gtk_file_chooser_dialog_new (_("Open Cheat File"), + NULL, GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_OK, GTK_RESPONSE_OK, NULL); + + filename = g_build_filename(getenv("HOME"), CHEATS_DIR, NULL); + gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (chooser), filename); + g_free(filename); + + filter = gtk_file_filter_new (); + gtk_file_filter_add_pattern (filter, "*.cht"); + gtk_file_filter_set_name (filter, _("PCSX Cheat Code Files (*.cht)")); + gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), filter); + + filter = gtk_file_filter_new (); + gtk_file_filter_add_pattern (filter, "*"); + gtk_file_filter_set_name (filter, _("All Files")); + gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), filter); + + if (gtk_dialog_run (GTK_DIALOG (chooser)) == GTK_RESPONSE_OK) { + filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (chooser)); + gtk_widget_destroy (GTK_WIDGET (chooser)); + while (gtk_events_pending()) gtk_main_iteration(); + } else { + gtk_widget_destroy (GTK_WIDGET (chooser)); + while (gtk_events_pending()) gtk_main_iteration(); + return; + } + + LoadCheats(filename); + + g_free(filename); + + LoadCheatListItems(-1); +} + +static void OnCheatListDlg_SaveClicked(GtkWidget *widget, gpointer user_data) { + GtkWidget *chooser; + gchar *filename; + GtkFileFilter *filter; + + chooser = gtk_file_chooser_dialog_new(_("Save Cheat File"), + NULL, GTK_FILE_CHOOSER_ACTION_SAVE, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_OK, GTK_RESPONSE_OK, NULL); + + filename = g_build_filename(getenv("HOME"), CHEATS_DIR, NULL); + gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(chooser), filename); + g_free(filename); + + filter = gtk_file_filter_new(); + gtk_file_filter_add_pattern(filter, "*.cht"); + gtk_file_filter_set_name(filter, _("PCSX Cheat Code Files (*.cht)")); + gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(chooser), filter); + + filter = gtk_file_filter_new(); + gtk_file_filter_add_pattern(filter, "*"); + gtk_file_filter_set_name(filter, _("All Files (*.*)")); + gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(chooser), filter); + + if (gtk_dialog_run(GTK_DIALOG(chooser)) == GTK_RESPONSE_OK) { + filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (chooser)); + gtk_widget_destroy (GTK_WIDGET(chooser)); + while (gtk_events_pending()) gtk_main_iteration(); + } else { + gtk_widget_destroy (GTK_WIDGET(chooser)); + while (gtk_events_pending()) gtk_main_iteration(); + return; + } + + SaveCheats(filename); + + g_free(filename); +} + +static void OnCheatListDlg_Clicked() { + gtk_widget_destroy(CheatListDlg); + CheatListDlg = NULL; +} + +// run the cheat list dialog +void RunCheatListDialog() { + GladeXML *xml; + GtkWidget *widget; + GtkTreeSelection *treesel; + GtkTreeViewColumn *column; + GtkCellRenderer *renderer; + + xml = glade_xml_new(PACKAGE_DATA_DIR "pcsx.glade2", "CheatListDlg", NULL); + if (!xml) { + g_warning(_("Error: Glade interface could not be loaded!")); + return; + } + + CheatListDlg = glade_xml_get_widget(xml, "CheatListDlg"); + gtk_window_set_title(GTK_WINDOW(CheatListDlg), _("Cheat Codes")); + + widget = glade_xml_get_widget(xml, "GtkCList_Cheat"); + + // column for enable + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes (_("Enable"), + renderer, "text", 0, NULL); + gtk_tree_view_append_column (GTK_TREE_VIEW(widget), column); + + // column for description + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes (_("Description"), + renderer, "text", 1, NULL); + gtk_tree_view_append_column (GTK_TREE_VIEW(widget), column); + + LoadCheatListItems(-1); + + treesel = gtk_tree_view_get_selection (GTK_TREE_VIEW(widget)); + gtk_tree_selection_set_mode (treesel, GTK_SELECTION_SINGLE); + g_signal_connect_data (G_OBJECT (treesel), "changed", + G_CALLBACK (CheatList_TreeSelectionChanged), + NULL, NULL, G_CONNECT_AFTER); + + widget = glade_xml_get_widget(xml, "addbutton1"); + g_signal_connect_data(GTK_OBJECT(widget), "clicked", + GTK_SIGNAL_FUNC(OnCheatListDlg_AddClicked), xml, NULL, G_CONNECT_AFTER); + + widget = glade_xml_get_widget(xml, "editbutton1"); + g_signal_connect_data(GTK_OBJECT(widget), "clicked", + GTK_SIGNAL_FUNC(OnCheatListDlg_EditClicked), xml, NULL, G_CONNECT_AFTER); + + widget = glade_xml_get_widget(xml, "delbutton1"); + g_signal_connect_data(GTK_OBJECT(widget), "clicked", + GTK_SIGNAL_FUNC(OnCheatListDlg_DelClicked), xml, NULL, G_CONNECT_AFTER); + + widget = glade_xml_get_widget(xml, "enablebutton1"); + g_signal_connect_data(GTK_OBJECT(widget), "clicked", + GTK_SIGNAL_FUNC(OnCheatListDlg_EnableClicked), xml, NULL, G_CONNECT_AFTER); + + widget = glade_xml_get_widget(xml, "disablebutton1"); + g_signal_connect_data(GTK_OBJECT(widget), "clicked", + GTK_SIGNAL_FUNC(OnCheatListDlg_DisableClicked), xml, NULL, G_CONNECT_AFTER); + + widget = glade_xml_get_widget(xml, "loadbutton1"); + g_signal_connect_data(GTK_OBJECT(widget), "clicked", + GTK_SIGNAL_FUNC(OnCheatListDlg_OpenClicked), xml, NULL, G_CONNECT_AFTER); + + widget = glade_xml_get_widget(xml, "savebutton1"); + g_signal_connect_data(GTK_OBJECT(widget), "clicked", + GTK_SIGNAL_FUNC(OnCheatListDlg_SaveClicked), xml, NULL, G_CONNECT_AFTER); + + // Setup a handler for when Close or Cancel is clicked + g_signal_connect_data(GTK_OBJECT(CheatListDlg), "response", + GTK_SIGNAL_FUNC(OnCheatListDlg_Clicked), xml, (GClosureNotify)g_object_unref, G_CONNECT_AFTER); + + gtk_widget_set_sensitive (GTK_WIDGET(glade_xml_get_widget(xml, "savebutton1")), NumCheats); + gtk_widget_set_sensitive (GTK_WIDGET(glade_xml_get_widget(xml, "editbutton1")), FALSE); + gtk_widget_set_sensitive (GTK_WIDGET(glade_xml_get_widget(xml, "delbutton1")), FALSE); + gtk_widget_set_sensitive (GTK_WIDGET(glade_xml_get_widget(xml, "enablebutton1")), FALSE); + gtk_widget_set_sensitive (GTK_WIDGET(glade_xml_get_widget(xml, "disablebutton1")), FALSE); + gtk_widget_set_sensitive (GTK_WIDGET(glade_xml_get_widget(xml, "editbutton1")), FALSE); +} + +// run the cheat search dialog +void RunCheatSearchDialog() { +} diff --git a/gui/Cheat.h b/gui/Cheat.h new file mode 100644 index 00000000..62039118 --- /dev/null +++ b/gui/Cheat.h @@ -0,0 +1,26 @@ +/* Cheat Support for PCSX-Reloaded + * + * Copyright (C) 2009, Wei Mingzhi <whistler@openoffice.org>. + * + * 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., 51 Franklin Steet, Fifth Floor, Boston, MA 02111-1307 USA + */ + +#ifndef GUI_CHEAT_H +#define GUI_CHEAT_H + +void RunCheatListDialog(); +void RunCheatSearchDialog(); + +#endif diff --git a/gui/Config.c b/gui/Config.c new file mode 100644 index 00000000..dc8489b1 --- /dev/null +++ b/gui/Config.c @@ -0,0 +1,162 @@ +/* Pcsx - Pc Psx Emulator + * Copyright (C) 1999-2002 Pcsx Team + * + * 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., 51 Franklin Steet, Fifth Floor, Boston, MA 02111-1307 USA + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <unistd.h> + +#include "Linux.h" + +/* TODO escaping/unescaping would be nice, as would maxchars */ +void GetValue(char *src, char *name, char *outvar) { + char *tmp; + + *outvar = 0; + tmp = strstr(src, name); + if (tmp == NULL) return; + + tmp += strlen(name); + while ((*tmp == ' ') || (*tmp == '=')) tmp++; + + while (*tmp != '\n' && *tmp != 0) + *outvar++ = *tmp++; + + *outvar = 0; + return; +} + +void GetValuel(char *src, char *name, long *var) { + char *tmp = strstr(src, name); + if (tmp != NULL) { + tmp+=strlen(name); + while ((*tmp == ' ') || (*tmp == '=')) tmp++; + if (*tmp != '\n') sscanf(tmp, "%lx", var); + } + return; +} + +#define SetValue(name, var) \ + fprintf (f,"%s = %s\n", name, var); + +#define SetValuel(name, var) \ + fprintf (f,"%s = %lx\n", name, var); + +/* TODO Use glib functions for filename handling, use constants, glib-key-value-file-parser */ +int LoadConfig(PcsxConfig *Conf) { + struct stat buf; + FILE *f; + int size; + char *data; + + /* TODO local var called cfgfile */ + + // Ryan says: use dotdir, dotdir is GOOD + // No giant homedir names + strncpy(cfgfile, getenv("HOME"), 200); + strcat(cfgfile, PCSX_DOT_DIR); + + // proceed to load the cfg file + // append its name + strcat(cfgfile, cfgfile_basename); + + // file is now ~/.pcsx/pcsx.cfg (or whatever cfgfile_basename is) + if (stat(cfgfile, &buf) == -1) { + // the config file doesn't exist! + /* TODO Error checking? */ + printf("Configuration file %s couldn't be found\n", cfgfile); + return -1; + } + + size = buf.st_size; + + /* TODO Error checking for the next two lines, and at least log failures */ + f = fopen(cfgfile, "r"); + if (f == NULL) return -1; + + data = (char*)malloc(size); + if (data == NULL) return -1; + + fread(data, 1, buf.st_size, f); + fclose(f); + + GetValue(data, "Bios", Config.Bios); + GetValue(data, "Gpu", Config.Gpu); + GetValue(data, "Spu", Config.Spu); + GetValue(data, "Cdr", Config.Cdr); + GetValue(data, "Pad1", Config.Pad1); + GetValue(data, "Pad2", Config.Pad2); + GetValue(data, "Net", Config.Net); + GetValue(data, "Mcd1", Config.Mcd1); + GetValue(data, "Mcd2", Config.Mcd2); + GetValue(data, "BiosDir", Config.BiosDir); + GetValue(data, "PluginsDir", Config.PluginsDir); + GetValuel(data, "Xa", &Config.Xa); + GetValuel(data, "Sio", &Config.Sio); + GetValuel(data, "Mdec", &Config.Mdec); + GetValuel(data, "PsxAuto", &Config.PsxAuto); + GetValuel(data, "PsxType", &Config.PsxType); + GetValuel(data, "Cdda", &Config.Cdda); + GetValuel(data, "Cpu", &Config.Cpu); + GetValuel(data, "Dbg", &Config.Dbg); + GetValuel(data, "PsxOut", &Config.PsxOut); + GetValuel(data, "SpuIrq", &Config.SpuIrq); + GetValuel(data, "RCntFix", &Config.RCntFix); + GetValuel(data, "VSyncWA", &Config.VSyncWA); + + free(data); + + return 0; +} + +void SaveConfig() { + FILE *f; + + /* TODO Error checking for the next two lines, and at least log + failures - suggest a file dialog to specify a new file or + create a new file */ + f = fopen(cfgfile, "w"); + if (f == NULL) return; + + SetValue("Bios", Config.Bios); + SetValue("Gpu", Config.Gpu); + SetValue("Spu", Config.Spu); + SetValue("Cdr", Config.Cdr); + SetValue("Net", Config.Net); + SetValue("Pad1", Config.Pad1); + SetValue("Pad2", Config.Pad2); + SetValue("Mcd1", Config.Mcd1); + SetValue("Mcd2", Config.Mcd2); + SetValue("BiosDir", Config.BiosDir); + SetValue("PluginsDir", Config.PluginsDir); + SetValuel("Xa", Config.Xa); + SetValuel("Sio", Config.Sio); + SetValuel("Mdec", Config.Mdec); + SetValuel("PsxAuto", Config.PsxAuto); + SetValuel("PsxType", Config.PsxType); + SetValuel("Cdda", Config.Cdda); + SetValuel("Cpu", Config.Cpu); + SetValuel("Dbg", Config.Dbg); + SetValuel("PsxOut", Config.PsxOut); + SetValuel("SpuIrq", Config.SpuIrq); + SetValuel("RCntFix", Config.RCntFix); + SetValuel("VSyncWA", Config.VSyncWA); + + fclose(f); +} diff --git a/gui/Gtk2Gui.c b/gui/Gtk2Gui.c new file mode 100644 index 00000000..f423d99d --- /dev/null +++ b/gui/Gtk2Gui.c @@ -0,0 +1,2255 @@ +/* Pcsx - Pc Psx Emulator + * Copyright (C) 1999-2002 Pcsx Team + * + * 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., 51 Franklin Steet, Fifth Floor, Boston, MA 02111-1307 USA + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <dirent.h> +#include <dlfcn.h> +#include <sys/stat.h> +#include <gdk/gdkkeysyms.h> +#include <gtk/gtk.h> +#include <glade/glade.h> +#include <signal.h> +#include <sys/time.h> +#include <regex.h> + +#include "Linux.h" + +#include "hdebug.h" + +#include "../libpcsxcore/plugins.h" +#include "../libpcsxcore/sio.h" + +extern int UseGui; + +PSEgetLibType PSE_getLibType = NULL; +PSEgetLibVersion PSE_getLibVersion = NULL; +PSEgetLibName PSE_getLibName = NULL; + +// Helper Functions +void UpdatePluginsBIOS(); +void UpdatePluginsBIOS_UpdateGUI(GladeXML *xml); +void FindNetPlugin(GladeXML *xml); +void copy_memcard_data (char *from, char *to, gint *i, gchar *str); + +void OnNet_Conf(GtkWidget *widget, gpointer user_data); +void OnNet_About(GtkWidget *widget, gpointer user_data); + +// Functions Callbacks +/*make them getting the right params, will be used later*/ +void OnFile_RunCd(); +void OnFile_RunBios(); +void OnFile_RunExe(); +void OnFile_RunImage(); +void OnEmu_Run(); +void OnEmu_Reset(); +void OnEmu_SwitchImage(); +void OnConf_Mcds(); +void OnConf_Cpu(); +void OnConf_Net(); +void OnHelp_Help(); +void OnHelp_About(); +void OnDestroy(); +void OnFile_Exit(); + +void OnBiosPath_Changed(GtkWidget *wdg, gpointer data); +void OnConf_Clicked (GtkDialog *dialog, gint arg1, gpointer user_data); +void OnPluginPath_Changed(GtkWidget *wdg, gpointer data); +void OnConfConf_Pad1About(GtkWidget *widget, gpointer user_data); +void OnConfConf_Pad2About(GtkWidget *widget, gpointer user_data); +void OnConfConf_Pad1Conf(GtkWidget *widget, gpointer user_data); +void OnConfConf_Pad2Conf(GtkWidget *widget, gpointer user_data); + +void on_states_load(GtkWidget *widget, gpointer user_data); +void on_states_load_other(); +void on_states_save(GtkWidget *widget, gpointer user_data); +void on_states_save_other(); + +void on_configure_plugin(GtkWidget *widget, gpointer user_data); +void on_about_plugin(GtkWidget *widget, gpointer user_data); + +GtkWidget *Window = NULL; +GtkWidget *ConfDlg = NULL; + +//GtkAccelGroup *AccelGroup; + +GtkWidget *controlwidget; + +int destroy = 0; + +#define MAX_SLOTS 5 +#define MAX_MEMCARD_BLOCKS 15 + +#define FindComboText(combo, list, conf) \ + if (strlen(conf) > 0) { \ + int i; \ + for (i = 2; i < 255; i += 2) { \ + if (!strcmp(conf, list[i - 2])) { \ + gtk_combo_box_set_active (GTK_COMBO_BOX (combo), i / 2 - 1); \ + break; \ + } \ + } \ + } + +#define GetComboText(combo, list, conf) \ + { \ + int row; \ + row = gtk_combo_box_get_active(GTK_COMBO_BOX(combo)); \ + strcpy(conf, (char *)list[row * 2]); \ + } + +/* TODO - If MAX_SLOTS changes, need to find a way to automatically set all positions */ +int Slots[MAX_SLOTS] = { -1, -1, -1, -1, -1 }; + +void ResetMenuSlots(GladeXML *xml) { + GtkWidget *widget; + gchar *str; + int i; + + if (CdromId[0] == '\0') { + /* disable state saving/loading if no CD is loaded */ + for (i = 0; i < MAX_SLOTS; i++) { + str = g_strdup_printf("GtkMenuItem_SaveSlot%d", i+1); + widget = glade_xml_get_widget(xml, str); + g_free(str); + + gtk_widget_set_sensitive(widget, FALSE); + + str = g_strdup_printf("GtkMenuItem_LoadSlot%d", i+1); + widget = glade_xml_get_widget (xml, str); + g_free (str); + + gtk_widget_set_sensitive(widget, FALSE); + } + + /* also disable certain menu items */ + widget = glade_xml_get_widget (xml, "other1"); + gtk_widget_set_sensitive(widget, FALSE); + widget = glade_xml_get_widget (xml, "other2"); + gtk_widget_set_sensitive(widget, FALSE); + widget = glade_xml_get_widget (xml, "run1"); + gtk_widget_set_sensitive(widget, FALSE); + widget = glade_xml_get_widget (xml, "reset1"); + gtk_widget_set_sensitive(widget, FALSE); + widget = glade_xml_get_widget (xml, "search1"); + gtk_widget_set_sensitive(widget, FALSE); + widget = glade_xml_get_widget (xml, "SwitchImage"); + gtk_widget_set_sensitive(widget, FALSE); + } + else { + for (i = 0; i < MAX_SLOTS; i++) { + str = g_strdup_printf("GtkMenuItem_LoadSlot%d", i+1); + widget = glade_xml_get_widget (xml, str); + g_free (str); + + if (Slots[i] == -1) + gtk_widget_set_sensitive(widget, FALSE); + else + gtk_widget_set_sensitive(widget, TRUE); + } + + widget = glade_xml_get_widget (xml, "plugins_bios"); + gtk_widget_set_sensitive(widget, FALSE); + widget = glade_xml_get_widget (xml, "net1"); + gtk_widget_set_sensitive(widget, FALSE); + widget = glade_xml_get_widget (xml, "SwitchImage"); + gtk_widget_set_sensitive(widget, cdrfilename[0]); + } +} + +int match(const char *string, char *pattern) { + int status; + regex_t re; + + if (regcomp(&re, pattern, REG_EXTENDED|REG_NOSUB) != 0) { + return 0; + } + status = regexec(&re, string, (size_t) 0, NULL, 0); + regfree(&re); + if (status != 0) { + return 0; + } + + return 1; +} + +gchar* get_state_filename (int i) { + gchar *state_filename; + char SStateFile[64]; + char trimlabel[33]; + int j; + + strncpy(trimlabel, CdromLabel, 32); + trimlabel[32] = 0; + for (j = 31; j >= 0; j--) + if (trimlabel[j] == ' ') + trimlabel[j] = 0; + else + continue; + + sprintf(SStateFile, "%.32s-%.9s.%3.3d", trimlabel, CdromId, i); + state_filename = g_build_filename (getenv("HOME"), STATES_DIR, SStateFile, NULL); + + return state_filename; +} + +void UpdateMenuSlots() { + gchar *str; + int i; + + for (i = 0; i < MAX_SLOTS; i++) { + str = get_state_filename (i); + Slots[i] = CheckState(str); + g_free (str); + } +} + +void StartGui() { + GladeXML *xml; + GtkWidget *widget; + + /* If a plugin fails, the Window is not NULL, but is not initialised, + so the following causes a segfault + if (Window != NULL) { + gtk_window_present (GTK_WINDOW (Window)); + return; + }*/ + + xml = glade_xml_new(PACKAGE_DATA_DIR "pcsx.glade2", "MainWindow", NULL); + + if (!xml) { + g_warning("We could not load the interface!"); + return; + } + + Window = glade_xml_get_widget(xml, "MainWindow"); + gtk_window_set_title(GTK_WINDOW(Window), "PCSX"); + gtk_window_set_icon_from_file(GTK_WINDOW(Window), PIXMAPDIR "pcsx-icon.png", NULL); + gtk_window_set_default_icon_from_file(PIXMAPDIR "pcsx-icon.png", NULL); + ResetMenuSlots(xml); + + /* Set up callbacks */ + g_signal_connect_data(GTK_OBJECT(Window), "delete-event", + GTK_SIGNAL_FUNC(OnDestroy), xml, (GClosureNotify)g_object_unref, G_CONNECT_AFTER); + + /* File menu */ + widget = glade_xml_get_widget(xml, "RunCd"); + g_signal_connect_data(GTK_OBJECT(widget), "activate", + GTK_SIGNAL_FUNC(OnFile_RunCd), NULL, NULL, G_CONNECT_AFTER); + + widget = glade_xml_get_widget(xml, "RunBios"); + g_signal_connect_data(GTK_OBJECT(widget), "activate", + GTK_SIGNAL_FUNC(OnFile_RunBios), NULL, NULL, G_CONNECT_AFTER); + + widget = glade_xml_get_widget(xml, "RunExe"); + g_signal_connect_data(GTK_OBJECT(widget), "activate", + GTK_SIGNAL_FUNC(OnFile_RunExe), NULL, NULL, G_CONNECT_AFTER); + + widget = glade_xml_get_widget(xml, "RunImage"); + g_signal_connect_data(GTK_OBJECT(widget), "activate", + GTK_SIGNAL_FUNC(OnFile_RunImage), NULL, NULL, G_CONNECT_AFTER); + + widget = glade_xml_get_widget(xml, "SwitchImage"); + g_signal_connect_data(GTK_OBJECT(widget), "activate", + GTK_SIGNAL_FUNC(OnEmu_SwitchImage), NULL, NULL, G_CONNECT_AFTER); + + widget = glade_xml_get_widget(xml, "exit2"); + g_signal_connect_data(GTK_OBJECT(widget), "activate", + GTK_SIGNAL_FUNC(OnFile_Exit), NULL, NULL, G_CONNECT_AFTER); + + /* States */ + widget = glade_xml_get_widget(xml, "GtkMenuItem_LoadSlot1"); + g_signal_connect_data(GTK_OBJECT(widget), "activate", + GTK_SIGNAL_FUNC(on_states_load), (gpointer) 0, NULL, G_CONNECT_AFTER); + widget = glade_xml_get_widget(xml, "GtkMenuItem_LoadSlot2"); + g_signal_connect_data(GTK_OBJECT(widget), "activate", + GTK_SIGNAL_FUNC(on_states_load), (gpointer) 1, NULL, G_CONNECT_AFTER); + widget = glade_xml_get_widget(xml, "GtkMenuItem_LoadSlot3"); + g_signal_connect_data(GTK_OBJECT(widget), "activate", + GTK_SIGNAL_FUNC(on_states_load), (gpointer) 2, NULL, G_CONNECT_AFTER); + widget = glade_xml_get_widget(xml, "GtkMenuItem_LoadSlot4"); + g_signal_connect_data(GTK_OBJECT(widget), "activate", + GTK_SIGNAL_FUNC(on_states_load), (gpointer) 3, NULL, G_CONNECT_AFTER); + widget = glade_xml_get_widget(xml, "GtkMenuItem_LoadSlot5"); + g_signal_connect_data(GTK_OBJECT(widget), "activate", + GTK_SIGNAL_FUNC(on_states_load), (gpointer) 4, NULL, G_CONNECT_AFTER); + widget = glade_xml_get_widget(xml, "other1"); + g_signal_connect_data(GTK_OBJECT(widget), "activate", + GTK_SIGNAL_FUNC(on_states_load_other), NULL, NULL, G_CONNECT_AFTER); + + widget = glade_xml_get_widget(xml, "GtkMenuItem_SaveSlot1"); + g_signal_connect_data(GTK_OBJECT(widget), "activate", + GTK_SIGNAL_FUNC(on_states_save), (gpointer) 0, NULL, G_CONNECT_AFTER); + widget = glade_xml_get_widget(xml, "GtkMenuItem_SaveSlot2"); + g_signal_connect_data(GTK_OBJECT(widget), "activate", + GTK_SIGNAL_FUNC(on_states_save), (gpointer) 1, NULL, G_CONNECT_AFTER); + widget = glade_xml_get_widget(xml, "GtkMenuItem_SaveSlot3"); + g_signal_connect_data(GTK_OBJECT(widget), "activate", + GTK_SIGNAL_FUNC(on_states_save), (gpointer) 2, NULL, G_CONNECT_AFTER); + widget = glade_xml_get_widget(xml, "GtkMenuItem_SaveSlot4"); + g_signal_connect_data(GTK_OBJECT(widget), "activate", + GTK_SIGNAL_FUNC(on_states_save), (gpointer) 3, NULL, G_CONNECT_AFTER); + widget = glade_xml_get_widget(xml, "GtkMenuItem_SaveSlot5"); + g_signal_connect_data(GTK_OBJECT(widget), "activate", + GTK_SIGNAL_FUNC(on_states_save), (gpointer) 4, NULL, G_CONNECT_AFTER); + widget = glade_xml_get_widget(xml, "other2"); + g_signal_connect_data(GTK_OBJECT(widget), "activate", + GTK_SIGNAL_FUNC(on_states_save_other), NULL, NULL, G_CONNECT_AFTER); + + /* Emulation menu */ + widget = glade_xml_get_widget(xml, "run1"); + g_signal_connect_data(GTK_OBJECT(widget), "activate", + GTK_SIGNAL_FUNC(OnEmu_Run), NULL, NULL, G_CONNECT_AFTER); + widget = glade_xml_get_widget(xml, "reset1"); + g_signal_connect_data(GTK_OBJECT(widget), "activate", + GTK_SIGNAL_FUNC(OnEmu_Reset), NULL, NULL, G_CONNECT_AFTER); + + /* Configuration menu */ + widget = glade_xml_get_widget(xml, "plugins_bios"); + g_signal_connect_data(GTK_OBJECT(widget), "activate", + GTK_SIGNAL_FUNC(ConfigurePlugins), NULL, NULL, G_CONNECT_AFTER); + widget = glade_xml_get_widget(xml, "cpu1"); + g_signal_connect_data(GTK_OBJECT(widget), "activate", + GTK_SIGNAL_FUNC(OnConf_Cpu), NULL, NULL, G_CONNECT_AFTER); + widget = glade_xml_get_widget(xml, "memory_cards1"); + g_signal_connect_data(GTK_OBJECT(widget), "activate", + GTK_SIGNAL_FUNC(OnConf_Mcds), NULL, NULL, G_CONNECT_AFTER); + widget = glade_xml_get_widget(xml, "net1"); + g_signal_connect_data(GTK_OBJECT(widget), "activate", + GTK_SIGNAL_FUNC(OnConf_Net), NULL, NULL, G_CONNECT_AFTER); + + /* Cheat menu */ + widget = glade_xml_get_widget(xml, "browse1"); + g_signal_connect_data(GTK_OBJECT(widget), "activate", + GTK_SIGNAL_FUNC(RunCheatListDialog), NULL, NULL, G_CONNECT_AFTER); + widget = glade_xml_get_widget(xml, "search1"); + g_signal_connect_data(GTK_OBJECT(widget), "activate", + GTK_SIGNAL_FUNC(RunCheatSearchDialog), NULL, NULL, G_CONNECT_AFTER); + + /* Help menu */ + widget = glade_xml_get_widget(xml, "about_pcsx1"); + g_signal_connect_data(GTK_OBJECT(widget), "activate", + GTK_SIGNAL_FUNC(OnHelp_About), NULL, NULL, G_CONNECT_AFTER); + + gtk_main(); +} + +void OnDestroy() { + if (!destroy) OnFile_Exit(); +} + +void ConfigurePlugins() { + if (!UseGui) { + /* How do we get here if we're not running the GUI? */ + /* Ryan: we're going to imagine that someday, there will be a way + * to configure plugins from the commandline */ + printf("ERROR: Plugins cannot be configured without the GUI."); + return; + } + + GladeXML *xml; + GtkWidget *widget; + + gchar *path; + + UpdatePluginsBIOS(); + + xml = glade_xml_new(PACKAGE_DATA_DIR "pcsx.glade2", "ConfDlg", NULL); + + if (!xml) { + g_warning(_("Error: Glade interface could not be loaded!")); + return; + } + + UpdatePluginsBIOS_UpdateGUI(xml); + + ConfDlg = glade_xml_get_widget(xml, "ConfDlg"); + + gtk_window_set_title(GTK_WINDOW(ConfDlg), _("Configure PCSX")); + + /* Set the paths in the file choosers to be based on the saved configurations */ + widget = glade_xml_get_widget(xml, "GtkFileChooser_Bios"); + gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (widget), + Config.BiosDir); + + widget = glade_xml_get_widget(xml, "GtkFileChooser_Plugin"); + gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (widget), + Config.PluginsDir); + + if (strlen(Config.PluginsDir) == 0) { + if((path = gtk_file_chooser_get_current_folder (GTK_FILE_CHOOSER (widget))) != NULL) { + strcpy(Config.PluginsDir, path); + g_free(path); + } + } + + widget = glade_xml_get_widget(xml, "btn_ConfGpu"); + g_signal_connect_data(GTK_OBJECT(widget), "clicked", + GTK_SIGNAL_FUNC(on_configure_plugin), (gpointer) PSE_LT_GPU, NULL, G_CONNECT_AFTER); + + widget = glade_xml_get_widget(xml, "btn_ConfSpu"); + g_signal_connect_data(GTK_OBJECT(widget), "clicked", + GTK_SIGNAL_FUNC(on_configure_plugin), (gpointer) PSE_LT_SPU, NULL, G_CONNECT_AFTER); + + /* ADB TODO Does pad 1 and 2 need to be different? */ + widget = glade_xml_get_widget(xml, "btn_ConfPad1"); + g_signal_connect_data(GTK_OBJECT(widget), "clicked", + GTK_SIGNAL_FUNC(OnConfConf_Pad1Conf), xml, NULL, G_CONNECT_AFTER); + + widget = glade_xml_get_widget(xml, "btn_ConfPad2"); + g_signal_connect_data(GTK_OBJECT(widget), "clicked", + GTK_SIGNAL_FUNC(OnConfConf_Pad2Conf), xml, NULL, G_CONNECT_AFTER); + + widget = glade_xml_get_widget(xml, "btn_ConfCdr"); + g_signal_connect_data(GTK_OBJECT(widget), "clicked", + GTK_SIGNAL_FUNC(on_configure_plugin), (gpointer) PSE_LT_CDR, NULL, G_CONNECT_AFTER); + + widget = glade_xml_get_widget(xml, "btn_AboutGpu"); + g_signal_connect_data(GTK_OBJECT(widget), "clicked", + GTK_SIGNAL_FUNC(on_about_plugin), (gpointer) PSE_LT_GPU, NULL, G_CONNECT_AFTER); + + widget = glade_xml_get_widget(xml, "btn_AboutSpu"); + g_signal_connect_data(GTK_OBJECT(widget), "clicked", + GTK_SIGNAL_FUNC(on_about_plugin), (gpointer) PSE_LT_SPU, NULL, G_CONNECT_AFTER); + + widget = glade_xml_get_widget(xml, "btn_AboutPad1"); + g_signal_connect_data(GTK_OBJECT(widget), "clicked", + GTK_SIGNAL_FUNC(OnConfConf_Pad1About), xml, NULL, G_CONNECT_AFTER); + + widget = glade_xml_get_widget(xml, "btn_AboutPad2"); + g_signal_connect_data(GTK_OBJECT(widget), "clicked", + GTK_SIGNAL_FUNC(OnConfConf_Pad2About), xml, NULL, G_CONNECT_AFTER); + + widget = glade_xml_get_widget(xml, "btn_AboutCdr"); + g_signal_connect_data(GTK_OBJECT(widget), "clicked", + GTK_SIGNAL_FUNC(on_about_plugin), (gpointer) PSE_LT_CDR, NULL, G_CONNECT_AFTER); + + widget = glade_xml_get_widget(xml, "GtkFileChooser_Bios"); + g_signal_connect_data(GTK_OBJECT(widget), "current_folder_changed", + GTK_SIGNAL_FUNC(OnBiosPath_Changed), xml, NULL, G_CONNECT_AFTER); + + widget = glade_xml_get_widget(xml, "GtkFileChooser_Plugin"); + g_signal_connect_data(GTK_OBJECT(widget), "current_folder_changed", + GTK_SIGNAL_FUNC(OnPluginPath_Changed), xml, NULL, G_CONNECT_AFTER); + + g_signal_connect_data(GTK_OBJECT(ConfDlg), "response", + GTK_SIGNAL_FUNC(OnConf_Clicked), xml, (GClosureNotify)g_object_unref, G_CONNECT_AFTER); +} + +void destroy_main_window () { + destroy = 1; + gtk_widget_destroy(Window); + Window = NULL; + destroy = 0; + gtk_main_quit(); + while (gtk_events_pending()) gtk_main_iteration(); +} + +void OnFile_RunExe() { + GtkWidget *file_chooser; + + if (plugins_configured() == FALSE) { + ConfigurePlugins(); + } else { + file_chooser = gtk_file_chooser_dialog_new (_("Select PSX EXE File"), + NULL, GTK_FILE_CHOOSER_ACTION_OPEN, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, + NULL); + + /* Add file filter */ + GtkFileFilter *exefilter = gtk_file_filter_new (); + gtk_file_filter_add_pattern (exefilter, "*.exe"); + gtk_file_filter_add_pattern (exefilter, "*.psx"); + gtk_file_filter_set_name (exefilter, _("PlayStation Executable Files")); + gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (file_chooser), exefilter); + GtkFileFilter *allfilter = gtk_file_filter_new (); + gtk_file_filter_add_pattern (allfilter, "*"); + gtk_file_filter_set_name (allfilter, "All Files"); + gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (file_chooser), allfilter); + + /* Set this to the config object and retain it - maybe LastUsedDir */ + gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER (file_chooser), getenv("HOME")); + + if (gtk_dialog_run(GTK_DIALOG(file_chooser)) == GTK_RESPONSE_ACCEPT) { + gchar *file; + + /* TODO Need to validate the file */ + + file = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(file_chooser)); + + gtk_widget_destroy (file_chooser); + destroy_main_window(); + + cdrfilename[0] = '\0'; + LoadPlugins(); + NetOpened = 0; + + /* TODO If a CDR plugin image has not been selected, then OpenPlugins() will prompt us to + configure the plugin, even though we don't need it for EXE files. Need to pass a parameter + or something */ + if (OpenPlugins() == -1) { + /* TODO Error message */ + SysRunGui(); + g_free(file); + } else { + SysReset(); + if (Load(file) == 0) { + if (Config.Dbg) hdb_start(); + psxCpu->Execute(); + g_free(file); + } else { + g_free(file); + ClosePlugins(); + SysErrorMessage(_("Not a valid PSX file"), _("The file does not appear to be a valid Playstation executable")); + SysRunGui(); + } + } + } else + gtk_widget_destroy(file_chooser); + } +} + +void OnFile_RunCd() { + if (plugins_configured() == FALSE) { + ConfigurePlugins(); + return; + } + + destroy_main_window(); + + cdrfilename[0] = '\0'; + LoadPlugins(); + NetOpened = 0; + + if (OpenPlugins() == -1) { + SysRunGui(); + return; + } + + SysReset(); + + if (CheckCdrom() == -1) { + /* Only check the CD if we are starting the console with a CD */ + ClosePlugins(); + SysErrorMessage (_("CD ROM failed"), _("The CD does not appear to be a valid Playstation CD")); + SysRunGui(); + return; + } + + /* Read main executable directly from CDRom and start it */ + if (LoadCdrom() == -1) { + ClosePlugins(); + SysErrorMessage(_("Could not load CD-ROM!"), _("The CD ROM could not be loaded")); + SysRunGui(); + } + + if (Config.Dbg) hdb_start(); + psxCpu->Execute(); +} + +void OnFile_RunBios() { + if (plugins_configured() == FALSE) { + ConfigurePlugins(); + return; + } + + if (strcmp(Config.Bios, "HLE") == 0) { + SysErrorMessage (_("Could not run BIOS"), _("Running BIOS is not supported with Internal HLE BIOS.")); + return; + } + + destroy_main_window(); + + cdrfilename[0] = '\0'; + LoadPlugins(); + NetOpened = 0; + + if (OpenPlugins() == -1) { + SysRunGui(); + return; + } + + SysReset(); + + CdromId[0] = '\0'; + CdromLabel[0] = '\0'; + + if (Config.Dbg) hdb_start(); + psxCpu->Execute(); +} + +static gchar *Open_Iso_Proc() { + GtkWidget *chooser; + gchar *filename; + GtkFileFilter *psxfilter, *allfilter; + static char current_folder[MAXPATHLEN] = ""; + + chooser = gtk_file_chooser_dialog_new (_("Open PSX Disc Image File"), + NULL, GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_OK, GTK_RESPONSE_OK, + NULL); + + if (current_folder[0] == '\0') { + strcpy(current_folder, getenv("HOME")); + } + + gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER (chooser), current_folder); + + psxfilter = gtk_file_filter_new(); + gtk_file_filter_add_pattern(psxfilter, "*.bin"); + gtk_file_filter_add_pattern(psxfilter, "*.img"); + gtk_file_filter_add_pattern(psxfilter, "*.mdf"); + gtk_file_filter_add_pattern(psxfilter, "*.iso"); + gtk_file_filter_set_name(psxfilter, _("PSX Image Files (*.bin, *.img, *.mdf, *.iso)")); + gtk_file_chooser_add_filter(GTK_FILE_CHOOSER (chooser), psxfilter); + + allfilter = gtk_file_filter_new(); + gtk_file_filter_add_pattern(allfilter, "*"); + gtk_file_filter_set_name(allfilter, _("All Files")); + gtk_file_chooser_add_filter(GTK_FILE_CHOOSER (chooser), allfilter); + + if (gtk_dialog_run(GTK_DIALOG(chooser)) == GTK_RESPONSE_OK) { + gchar *path = gtk_file_chooser_get_current_folder(GTK_FILE_CHOOSER(chooser)); + strcpy(current_folder, path); + g_free(path); + filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER (chooser)); + gtk_widget_destroy(GTK_WIDGET(chooser)); + while (gtk_events_pending()) gtk_main_iteration(); + return filename; + } else { + gtk_widget_destroy (GTK_WIDGET(chooser)); + while (gtk_events_pending()) gtk_main_iteration(); + return NULL; + } +} + +void OnFile_RunImage() { + gchar *filename; + + if (plugins_configured() == FALSE) { + ConfigurePlugins(); + return; + } + + filename = Open_Iso_Proc(); + if (filename == NULL) { + return; + } + + destroy_main_window(); + + strcpy(cdrfilename, filename); + g_free(filename); + + LoadPlugins(); + NetOpened = 0; + + if (OpenPlugins() == -1) { + SysRunGui(); + return; + } + + SysReset(); + + if (CheckCdrom() == -1) { + /* Only check the CD if we are starting the console with a CD */ + ClosePlugins(); + SysErrorMessage (_("CD ROM failed"), _("The CD does not appear to be a valid Playstation CD")); + SysRunGui(); + return; + } + + /* Read main executable directly from CDRom and start it */ + if (LoadCdrom() == -1) { + ClosePlugins(); + SysErrorMessage(_("Could not load CD-ROM!"), "The CD ROM could not be loaded"); + SysRunGui(); + } + + if (Config.Dbg) hdb_start(); + psxCpu->Execute(); +} + +void OnEmu_Run() { + if (plugins_configured() == FALSE) { + ConfigurePlugins(); + return; + } + + destroy_main_window(); + + if (OpenPlugins() == -1) { + SysRunGui(); + return; + } + + if (Config.Dbg) hdb_start(); + psxCpu->Execute(); +} + +void OnEmu_Reset() { + if (plugins_configured() == FALSE) { + ConfigurePlugins(); + return; + } + + destroy_main_window(); + + if (OpenPlugins() == -1) { + SysRunGui(); + return; + } + + SysReset(); + + if (CheckCdrom() != -1) { + LoadCdrom(); + } + + if (Config.Dbg) hdb_start(); + psxCpu->Execute(); +} + +void OnEmu_SwitchImage() { + gchar *filename; + + if (plugins_configured() == FALSE) { + ConfigurePlugins(); + return; + } + + filename = Open_Iso_Proc(); + if (filename == NULL) { + return; + } + + destroy_main_window(); + + strcpy(cdrfilename, filename); + g_free(filename); + + if (OpenPlugins() == -1) { + SysRunGui(); + return; + } + + cdOpenCase = time(NULL) + 2; + + if (Config.Dbg) hdb_start(); + psxCpu->Execute(); +} + +void OnFile_Exit() { + DIR *dir; + struct dirent *ent; + void *Handle; + gchar *plugin = NULL; + gchar *dotdir; + + dotdir = g_build_filename(getenv("HOME"), PLUGINS_DIR, NULL); + + // with this the problem with plugins that are linked with the pthread + // library is solved + + dir = opendir(dotdir); + if (dir != NULL) { + while ((ent = readdir(dir)) != NULL) { + plugin = g_build_filename(dotdir, ent->d_name, NULL); + + if (strstr(plugin, ".so") == NULL && strstr(plugin, ".dylib") == NULL) + continue; + Handle = dlopen(plugin, RTLD_NOW); + if (Handle == NULL) + continue; + + g_free(plugin); + } + } + g_free(dotdir); + + bind_textdomain_codeset(PACKAGE_NAME, ""); + if (UseGui) + gtk_main_quit(); + SysClose(); + if (UseGui) + gtk_exit (0); + else + exit(0); +} + +void state_load (gchar *state_filename) { + int ret; + char Text[MAXPATHLEN+20]; + FILE *fp; + + /* check if the state file actually exists */ + fp = fopen(state_filename, "rb"); + if (fp == NULL) { + /* file does not exist */ + return; + } + + fclose(fp); + + /* If the window exists, then we are loading the state from within + within the PCSX GUI. We need to initialise the plugins first */ + if (Window) { + destroy_main_window(); + + if (OpenPlugins() == -1) { + /* TODO Error message */ + SysRunGui(); + return; + } + } + + SysReset(); + + ret = LoadState(state_filename); + if (ret == 0) { + /* Check the CD ROM is valid */ + if (CheckCdrom() == -1) { + /* TODO Error message */ + ClosePlugins (); + SysRunGui(); + return; + } + +#if 0 /* Whistler: this will cause crash when using the "Load Other" option */ + /* Check that the currently loaded CD ROM ID matches that of the CD + used when saving the state file. The latter is stored in the filename */ + gchar *cmp = g_strrstr (g_path_get_basename (state_filename), "-"); + cmp++; + if (g_ascii_strncasecmp (cmp, CdromId, 9) != 0) { + ClosePlugins (); + gchar *error_desc, *label; + gint pos; + label = g_strdup_printf("%.9s", state_filename); + error_desc = g_strdup_printf ("The Playstation CD that is currently in use is %s. It is not the same CD as that used when saving the state file. The state file is looking for %s.", + label, + CdromLabel); + SysErrorMessage ("The CD does not match the state file", + error_desc); + g_free (error_desc); + g_free (label); + SysRunGui(); + return; + } +#endif + sprintf(Text, _("Loaded state %s."), state_filename); + GPU_displayText(Text); + if (Config.Dbg) hdb_start(); + psxCpu->Execute(); + } else { + sprintf(Text, _("Error loading state %s!"), state_filename); + GPU_displayText(Text); + } +} + +void state_save (gchar *state_filename) { + char Text[MAXPATHLEN + 20]; + + GPU_updateLace(); + + if (SaveState(state_filename) == 0) + sprintf(Text, _("Saved state %s."), state_filename); + else + sprintf(Text, _("Error saving state %s!"), state_filename); + + GPU_displayText(Text); +} + +void on_states_load (GtkWidget *widget, gpointer user_data) { + gchar *state_filename; + gint state = (int) user_data; + + state_filename = get_state_filename (state); + + state_load (state_filename); + + g_free (state_filename); +} + +void on_states_save (GtkWidget *widget, gpointer user_data) { + gchar *state_filename; + gint state = (int) user_data; + + state_filename = get_state_filename (state); + + state_save (state_filename); + + g_free (state_filename); +} + + +void on_states_load_other() { + GtkWidget *file_chooser; + gchar *SStateFile; + + SStateFile = g_strconcat(getenv("HOME"), STATES_DIR, NULL); + + file_chooser = gtk_file_chooser_dialog_new(_("Select State File"), NULL, GTK_FILE_CHOOSER_ACTION_OPEN, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, + NULL); + gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER (file_chooser), SStateFile); + + if (gtk_dialog_run(GTK_DIALOG(file_chooser)) == GTK_RESPONSE_ACCEPT) { + gchar *filename; + + filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(file_chooser)); + gtk_widget_destroy(file_chooser); + + state_load(filename); + + g_free(filename); + } else + gtk_widget_destroy(file_chooser); + + g_free(SStateFile); +} + +void on_states_save_other() { + GtkWidget *file_chooser; + gchar *SStateFile; + + SStateFile = g_strconcat (getenv("HOME"), STATES_DIR, NULL); + + file_chooser = gtk_file_chooser_dialog_new(_("Select State File"), + NULL, GTK_FILE_CHOOSER_ACTION_SAVE, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_SAVE, GTK_RESPONSE_OK, + NULL); + gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (file_chooser), SStateFile); + + if (gtk_dialog_run (GTK_DIALOG (file_chooser)) == GTK_RESPONSE_OK) { + gchar *filename; + + filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (file_chooser)); + gtk_widget_destroy(file_chooser); + + state_save (filename); + + g_free (filename); + } + else + gtk_widget_destroy (file_chooser); + + g_free (SStateFile); +} + +int all_config_set () { + int retval; + + if ((strlen(Config.Gpu) != 0) && + (strlen(Config.Spu) != 0) && + (strlen(Config.Cdr) != 0) && + (strlen(Config.Pad1) != 0) && + (strlen(Config.Pad2) != 0)) + retval = TRUE; + else + retval = FALSE; + + return retval; +} + +GtkWidget *NetDlg; + +void OnNet_Clicked (GtkDialog *dialog, gint arg1, gpointer user_data) { + if (arg1 == GTK_RESPONSE_OK) { + GetComboText(NetConfS.Combo, NetConfS.plist, Config.Net); + SaveConfig(); + } + + gtk_widget_destroy(GTK_WIDGET (dialog)); + NetDlg = NULL; +} + +void OnConf_Net() { + GladeXML *xml; + GtkWidget *widget; + + if (NetDlg != NULL) { + gtk_window_present (GTK_WINDOW (NetDlg)); + return; + } + + xml = glade_xml_new(PACKAGE_DATA_DIR "pcsx.glade2", "NetDlg", NULL); + + if (!xml) { + g_warning(_("Error: Glade interface could not be loaded!")); + return; + } + + NetDlg = glade_xml_get_widget(xml, "NetDlg"); + + FindNetPlugin(xml); + + /* Setup a handler for when Close or Cancel is clicked */ + g_signal_connect_data(GTK_OBJECT(NetDlg), "response", + GTK_SIGNAL_FUNC(OnNet_Clicked), xml, (GClosureNotify)g_object_unref, G_CONNECT_AFTER); + + widget = glade_xml_get_widget(xml, "btn_ConfNet"); + g_signal_connect_data(GTK_OBJECT(widget), "clicked", + GTK_SIGNAL_FUNC(OnNet_Conf), xml, NULL, G_CONNECT_AFTER); + + widget = glade_xml_get_widget(xml, "btn_AboutNet"); + g_signal_connect_data(GTK_OBJECT(widget), "clicked", + GTK_SIGNAL_FUNC(OnNet_About), xml, NULL, G_CONNECT_AFTER); +} + +McdBlock Blocks[2][MAX_MEMCARD_BLOCKS]; /* Assuming 2 cards, 15 blocks? */ +int IconC[2][MAX_MEMCARD_BLOCKS]; +enum { + CL_ICON, + CL_TITLE, + CL_STAT, + CL_ID, + CL_NAME, + NUM_CL +}; + +GtkWidget *GtkCList_McdList1, *GtkCList_McdList2; +GtkTreeSelection *sel1, *sel2; +gint mcd1_row, mcd2_row; + +static void add_columns (GtkTreeView *treeview) { + GtkCellRenderer *renderer; + GtkTreeViewColumn *column; + + /* column for icon */ + renderer = gtk_cell_renderer_pixbuf_new (); + column = gtk_tree_view_column_new_with_attributes (_("Icon"), + renderer, "pixbuf", CL_ICON, NULL); + gtk_tree_view_append_column (treeview, column); + + /* column for title */ + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes (_("Title"), + renderer, "text", CL_TITLE, NULL); + gtk_tree_view_append_column (treeview, column); + + /* column for status */ + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes (_("Status"), + renderer, "text", CL_STAT, NULL); + gtk_tree_view_append_column (treeview, column); + + /* column for id */ + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes (_("ID"), + renderer, "text", CL_ID, NULL); + gtk_tree_view_append_column (treeview, column); + + /* column for Name */ + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes (_("Name"), + renderer, "text", CL_NAME, NULL); + gtk_tree_view_append_column (treeview, column); +} + +GdkPixbuf *SetIcon(GtkWidget *dialog, short *icon, int i) { + GdkPixmap *pixmap; + GdkImage *image; + GdkVisual *visual; + GdkGC *gc; + int x, y, c; + + visual = gdk_window_get_visual(dialog->window); + + if (visual->depth == 8) return NULL; + + image = gdk_image_new(GDK_IMAGE_NORMAL, visual, 16, 16); + + for (y=0; y<16; y++) { + for (x=0; x<16; x++) { + c = icon[y*16+x]; + c = ((c&0x001f) << 10) | ((c&0x7c00) >> 10) | (c&0x03e0); + if (visual->depth == 16) + c = (c&0x001f) | ((c&0x7c00) << 1) | ((c&0x03e0) << 1); + else if (visual->depth == 24 || visual->depth == 32) + c = ((c&0x001f) << 3) | ((c&0x03e0) << 6) | ((c&0x7c00) << 9); + + gdk_image_put_pixel(image, x, y, c); + } + } + + pixmap = gdk_pixmap_new(dialog->window, 16, 16, visual->depth); + + gc = gdk_gc_new(pixmap); + gdk_draw_image(pixmap, gc, image, 0, 0, 0, 0, 16, 16); + gdk_gc_destroy(gc); + gdk_image_destroy(image); + + return gdk_pixbuf_get_from_drawable (NULL, + GDK_PIXMAP (pixmap), + NULL, + 0,0,0,0,-1,-1); +} + +void LoadListItems(int mcd, GtkWidget *List, GtkWidget *dialog) { + int i; + + GtkListStore *store= gtk_list_store_new (NUM_CL,GDK_TYPE_PIXBUF, G_TYPE_STRING, + G_TYPE_STRING,G_TYPE_STRING,G_TYPE_STRING); + GtkTreeIter iter; + GdkPixbuf *pixbuf; + + for (i = 0; i < MAX_MEMCARD_BLOCKS; i++) { + McdBlock *Info; + gchar *state; + + Info = &Blocks[mcd-1][i]; + IconC[mcd-1][i] = 0; + + if ((Info->Flags & 0xF0) == 0xA0) { + if ((Info->Flags & 0xF) >= 1 && + (Info->Flags & 0xF) <= 3) { + state = _("Deleted"); + } else + state = _("Free"); + } else if ((Info->Flags & 0xF0) == 0x50) + state = _("Used"); + else + state = _("Free"); + +// if (Info->IconCount == 0) continue; + + pixbuf = SetIcon(dialog, Info->Icon, i+1); + + gtk_list_store_append (store, &iter); + gtk_list_store_set (store, &iter, + CL_ICON, pixbuf, + CL_TITLE, Info->Title, + CL_STAT, state, + CL_NAME, Info->Name, + CL_ID, Info->ID, + -1); + } + + gtk_tree_view_set_model (GTK_TREE_VIEW (List), GTK_TREE_MODEL (store)); + g_object_unref (G_OBJECT (store)); + gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (List), TRUE); + gtk_widget_show(List); +} + +void UpdateMcdDlg(GtkWidget *widget) { + int i; + GladeXML *xml; + GtkWidget *list1, *list2, *dialog; + + for (i = 0; i < MAX_MEMCARD_BLOCKS; i++) { + GetMcdBlockInfo(1, i+1, &Blocks[0][i]); + GetMcdBlockInfo(2, i+1, &Blocks[1][i]); + } + + xml = glade_get_widget_tree (widget); + list1 = glade_xml_get_widget (xml, "GtkCList_McdList1"); + list2 = glade_xml_get_widget (xml, "GtkCList_McdList2"); + dialog = glade_xml_get_widget (xml, "McdsDlg"); + LoadListItems(1, list1, dialog); + LoadListItems(2, list2, dialog); +} + +void OnMcd_Clicked (GtkDialog *dialog, gint arg1, gpointer user_data) { + GladeXML *xml = user_data; + + if (arg1 == GTK_RESPONSE_CLOSE) { + gchar *tmp; + GtkWidget *widget; + + widget = glade_xml_get_widget (xml, "GtkMcd1FSButton"); + if ((tmp = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (widget))) != NULL) { + strcpy(Config.Mcd1, tmp); + g_free (tmp); + } + + widget = glade_xml_get_widget (xml, "GtkMcd2FSButton"); + if ((tmp = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (widget))) != NULL) { + strcpy(Config.Mcd2, tmp); + g_free (tmp); + } + + SaveConfig(); + } + + LoadMcds(Config.Mcd1, Config.Mcd2); + gtk_widget_destroy(GTK_WIDGET (dialog)); +} + +void on_memcard_file_changed (GtkWidget *widget, gpointer user_data) { + gint memcard = (int) user_data; + gchar *filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (widget)); + + if (filename != NULL) { + LoadMcd(memcard, filename); + UpdateMcdDlg(widget); + gtk_widget_set_sensitive (widget, TRUE); + } + + g_free (filename); +} + +void OnMcd_Format(GtkWidget *widget, gpointer user_data) { + GladeXML *xml; + GtkWidget *memcard_fs; + GtkWidget *message_dialog; + gint result; + gchar *str; + + gint memcard = (int) user_data; + + /* TODO Check if the memory card actually has data on it before displaying this */ + + message_dialog = gtk_message_dialog_new (NULL, GTK_DIALOG_MODAL, + GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE, + _("Format this Memory Card?")); + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (message_dialog), + _("If you format the memory card, the card will be empty, and any existing data overwritten.")); + gtk_dialog_add_buttons (GTK_DIALOG (message_dialog), + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + _("Format card"), GTK_RESPONSE_YES, + NULL); + + result = gtk_dialog_run (GTK_DIALOG (message_dialog)); + gtk_widget_destroy (message_dialog); + + if (result == GTK_RESPONSE_YES) { + xml = glade_get_widget_tree (widget); + if (memcard == 1) + memcard_fs = glade_xml_get_widget (xml, "GtkMcd1FSButton"); + else + memcard_fs = glade_xml_get_widget (xml, "GtkMcd2FSButton"); + + str = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (memcard_fs)); + + CreateMcd(str); + LoadMcd(memcard, str); + UpdateMcdDlg(widget); + g_free (str); + } +} + +void OnMcd_Reload (GtkWidget *widget, gpointer user_data) { + GladeXML *xml; + GtkWidget *memcard_fs; + gchar *str; + + gint memcard = (int) user_data; + + xml = glade_get_widget_tree (widget); + + if (memcard == 1) { + memcard_fs = glade_xml_get_widget (xml, "GtkMcd1FSButton"); + str = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (memcard_fs)); + } else { + memcard_fs = glade_xml_get_widget (xml, "GtkMcd2FSButton"); + str = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (memcard_fs)); + } + + LoadMcd (memcard, str); + UpdateMcdDlg(widget); + + g_free (str); +} + +static int copy = 0, copymcd = 0; + +static int get_free_memcard_slot (int target_card) { + McdBlock *Info; + gboolean found = FALSE; + + int i = 0; + while (i < 15 && found == FALSE) { + Info = &Blocks[target_card][i]; + if (g_ascii_strcasecmp (Info->Title, "") == 0) { + found = TRUE; + } else { + i++; + } + } + + if (found == TRUE) + return i; + + // no free slots, try to find a deleted one + i = 0; + while (i < 15 && found == FALSE) { + Info = &Blocks[target_card][i]; + if ((Info->Flags & 0xF0) != 0x50) { + found = TRUE; + } else { + i++; + } + } + + if (found == TRUE) + return i; + + return -1; +} + +void OnMcd_CopyTo(GtkWidget *widget, gpointer user_data) { + gint mcd = (gint)user_data; + + GtkTreeIter iter; + GtkTreeModel *model; + GtkTreePath *path; + gint *i; + GladeXML *xml; + GtkTreeSelection *treesel; + gchar *str; + char *source, *destination; + + int first_free_slot; + + xml = glade_get_widget_tree(widget); + + if (mcd == 1) + treesel = gtk_tree_view_get_selection(GTK_TREE_VIEW(GtkCList_McdList1)); + else + treesel = gtk_tree_view_get_selection(GTK_TREE_VIEW(GtkCList_McdList2)); + + /* If the item selected is not reported as a 'Free' slot */ + if (gtk_tree_selection_get_selected(treesel, &model, &iter)) { + path = gtk_tree_model_get_path(model, &iter); + i = gtk_tree_path_get_indices(path); + copy = *i; + copymcd = mcd; + gtk_tree_path_free(path); + } + + /* Determine the first free slot in the target memory card */ + first_free_slot = get_free_memcard_slot(mcd - 1); + if (first_free_slot == -1) { + /* No free slots available on the destination card */ + SysMessage(_("No space available in the target memory card!")); + return; + } + xml = glade_get_widget_tree (GtkCList_McdList1); + + if (mcd == 1) { + str = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(glade_xml_get_widget (xml, "GtkMcd1FSButton"))); + source = Mcd2Data; + copy = mcd2_row; + destination = Mcd1Data; +// printf("Copying from card 2 to card 1 from slot %d into slot %d\n", mcd2_row, first_free_slot); + } else { + str = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(glade_xml_get_widget (xml, "GtkMcd2FSButton"))); + source = Mcd1Data; + copy = mcd1_row; + destination = Mcd2Data; +// printf("Copying from card 1 to card 2 from slot %d into slot %d\n", mcd1_row, first_free_slot); + } + + copy_memcard_data (source, destination, &first_free_slot, str); + UpdateMcdDlg(widget); +} + +void copy_memcard_data (char *from, char *to, gint *i, gchar *str) { + memcpy(to + (*i+1) * 128, from + (copy+1) * 128, 128); + SaveMcd((char*)str, to, (*i+1) * 128, 128); + memcpy(to + (*i+1) * 1024 * 8, from + (copy+1) * 1024 * 8, 1024 * 8); + SaveMcd((char*)str, to, (*i+1) * 1024 * 8, 1024 * 8); +} + +void tree_selection_changed_cb (GtkTreeSelection *selection, gpointer user_data) { + GladeXML *xml; + GtkTreeIter iter; + GtkTreeModel *model; + GtkTreePath *path; + + gboolean selected; + int i; + char *data; + + gint memcard = (int) user_data; + + if (memcard == 1) { + selected = gtk_tree_selection_get_selected (selection, &model, &iter); + data = Mcd1Data; + } else { + selected = gtk_tree_selection_get_selected (selection, &model, &iter); + data = Mcd2Data; + } + + if (selected) { + path = gtk_tree_model_get_path (model, &iter); + i = *gtk_tree_path_get_indices (path); + + /* If a row was selected, and the row is not blank, we can now enable + some of the disabled widgets */ + /* Check LoadListItems for determining state */ + xml = glade_get_widget_tree (GtkCList_McdList1); + GtkWidget *disable, *enable; + if (memcard == 1) { + mcd1_row = i; + enable = glade_xml_get_widget (xml, "GtkButton_CopyTo2"); + disable = glade_xml_get_widget (xml, "GtkButton_CopyTo1"); + } else { + mcd2_row = i; + enable = glade_xml_get_widget (xml, "GtkButton_CopyTo1"); + disable = glade_xml_get_widget (xml, "GtkButton_CopyTo2"); + } + gtk_widget_set_sensitive (enable, TRUE); + gtk_widget_set_sensitive (disable, FALSE); + } +} + +void on_memcard_delete (GtkWidget *widget, gpointer user_data) { + McdBlock *Info; + int i, xor = 0, j; + char *data, *ptr; + GtkTreeIter iter; + GtkTreeModel *model; + GtkTreePath *path; + gchar *filename; + GladeXML *xml; + gboolean selected; + GtkWidget *tree; + GtkTreeSelection *sel; + + gint memcard = (int) user_data; + + xml = glade_get_widget_tree (widget); + if (memcard == 1) { + tree = glade_xml_get_widget (xml, "GtkCList_McdList1"); + sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree)); + selected = gtk_tree_selection_get_selected (sel, &model, &iter); + data = Mcd1Data; + filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (glade_xml_get_widget (xml, "GtkMcd1FSButton"))); + } else { + tree = glade_xml_get_widget (xml, "GtkCList_McdList2"); + sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree)); + selected = gtk_tree_selection_get_selected (sel, &model, &iter); + data = Mcd2Data; + filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (glade_xml_get_widget (xml, "GtkMcd2FSButton"))); + } +// printf("Will delete from memory card %s\n", filename); + if (selected) { + path = gtk_tree_model_get_path (model, &iter); + i = *gtk_tree_path_get_indices (path); + + i++; + ptr = data + i * 128; + Info = &Blocks[memcard-1][i-1]; + + if ((Info->Flags & 0xF0) == 0xA0) { + if ((Info->Flags & 0xF) >= 1 && + (Info->Flags & 0xF) <= 3) { // deleted + *ptr = 0x50 | (Info->Flags & 0xF); + } else return; + } else if ((Info->Flags & 0xF0) == 0x50) { // used + *ptr = 0xA0 | (Info->Flags & 0xF); + } else { return; } + + for (j=0; j<127; j++) xor^=*ptr++; + *ptr = xor; + + SaveMcd((char*)filename, data, i * 128, 128); + UpdateMcdDlg(widget); + } +} + +void OnConf_Mcds() { + GladeXML *xml; + GtkWidget *dialog; + GtkWidget *widget; + GtkTreeSelection *treesel1, *treesel2; + gchar *str; + int i; + + xml = glade_xml_new(PACKAGE_DATA_DIR "pcsx.glade2", "McdsDlg", NULL); + + if (!xml) { + g_warning("We could not load the interface!"); + return; + } + + dialog = glade_xml_get_widget(xml, "McdsDlg"); + + gtk_window_set_title(GTK_WINDOW(dialog), _("Memory Card Manager")); + + /* Assign default memory cards */ + if (!strlen(Config.Mcd1)) { + str = g_strconcat (getenv("HOME"), DEFAULT_MEM_CARD_1, NULL); + strcpy(Config.Mcd1, str); + g_free (str); + } + + if (!strlen(Config.Mcd2)) { + str = g_strconcat (getenv("HOME"), DEFAULT_MEM_CARD_2, NULL); + strcpy(Config.Mcd2, str); + g_free (str); + } + + GtkCList_McdList1 = glade_xml_get_widget(xml, "GtkCList_McdList1"); + add_columns(GTK_TREE_VIEW (GtkCList_McdList1)); + GtkCList_McdList2 = glade_xml_get_widget(xml, "GtkCList_McdList2"); + add_columns(GTK_TREE_VIEW (GtkCList_McdList2)); + + treesel1 = gtk_tree_view_get_selection (GTK_TREE_VIEW (GtkCList_McdList1)); + gtk_tree_selection_set_mode (treesel1, GTK_SELECTION_SINGLE); + g_signal_connect_data (G_OBJECT (treesel1), "changed", + G_CALLBACK (tree_selection_changed_cb), + (gpointer) 1, NULL, + G_CONNECT_AFTER); + + treesel2 = gtk_tree_view_get_selection (GTK_TREE_VIEW (GtkCList_McdList2)); + gtk_tree_selection_set_mode (treesel2, GTK_SELECTION_SINGLE); + g_signal_connect_data (G_OBJECT (treesel2), "changed", + G_CALLBACK (tree_selection_changed_cb), + (gpointer) 2, NULL, + G_CONNECT_AFTER); + + for (i = 0; i < MAX_MEMCARD_BLOCKS; i++) { + GetMcdBlockInfo (1, i+1, &Blocks[0][i]); + GetMcdBlockInfo (2, i+1, &Blocks[1][i]); + } + + LoadListItems(1, GtkCList_McdList1, dialog); + LoadListItems(2, GtkCList_McdList2, dialog); + + /* Setup a handler for when Close or Cancel is clicked */ + g_signal_connect_data(GTK_OBJECT(dialog), "response", + GTK_SIGNAL_FUNC(OnMcd_Clicked), xml, (GClosureNotify)g_object_unref, G_CONNECT_AFTER); + + widget = glade_xml_get_widget(xml, "GtkButton_Format1"); + g_signal_connect_data(GTK_OBJECT(widget), "clicked", + GTK_SIGNAL_FUNC(OnMcd_Format), (gpointer) 1, NULL, G_CONNECT_AFTER); + + widget = glade_xml_get_widget(xml, "GtkButton_Format2"); + g_signal_connect_data(GTK_OBJECT(widget), "clicked", + GTK_SIGNAL_FUNC(OnMcd_Format), (gpointer) 2, NULL, G_CONNECT_AFTER); + + widget = glade_xml_get_widget(xml, "GtkMcd1FSButton"); + g_signal_connect_data(GTK_OBJECT(widget), "selection-changed", + GTK_SIGNAL_FUNC(on_memcard_file_changed), (gpointer) 1, NULL, G_CONNECT_AFTER); + gtk_file_chooser_select_filename (GTK_FILE_CHOOSER (widget), Config.Mcd1); + + widget = glade_xml_get_widget(xml, "GtkMcd2FSButton"); + g_signal_connect_data(GTK_OBJECT(widget), "selection-changed", + GTK_SIGNAL_FUNC(on_memcard_file_changed), (gpointer) 2, NULL, G_CONNECT_AFTER); + gtk_file_chooser_select_filename (GTK_FILE_CHOOSER (widget), Config.Mcd2); + + widget = glade_xml_get_widget(xml, "GtkButton_Reload1"); + g_signal_connect_data(GTK_OBJECT(widget), "clicked", + GTK_SIGNAL_FUNC(OnMcd_Reload), (gpointer) 1, NULL, G_CONNECT_AFTER); + + widget = glade_xml_get_widget(xml, "GtkButton_Reload2"); + g_signal_connect_data(GTK_OBJECT(widget), "clicked", + GTK_SIGNAL_FUNC(OnMcd_Reload), (gpointer) 2, NULL, G_CONNECT_AFTER); + + widget = glade_xml_get_widget(xml, "GtkButton_CopyTo1"); + g_signal_connect_data(GTK_OBJECT(widget), "clicked", + GTK_SIGNAL_FUNC(OnMcd_CopyTo), (gpointer) 1, NULL, G_CONNECT_AFTER); + gtk_widget_set_sensitive (GTK_WIDGET (widget), FALSE); + + widget = glade_xml_get_widget(xml, "GtkButton_CopyTo2"); + g_signal_connect_data(GTK_OBJECT(widget), "clicked", + GTK_SIGNAL_FUNC(OnMcd_CopyTo), (gpointer) 2, NULL, G_CONNECT_AFTER); + gtk_widget_set_sensitive (GTK_WIDGET (widget), FALSE); + + widget = glade_xml_get_widget(xml, "GtkButton_Delete1"); + g_signal_connect_data (GTK_OBJECT (widget), "clicked", + GTK_SIGNAL_FUNC (on_memcard_delete), (gpointer) 1, NULL, G_CONNECT_AFTER); + + widget = glade_xml_get_widget(xml, "GtkButton_Delete2"); + g_signal_connect_data (GTK_OBJECT (widget), "clicked", + GTK_SIGNAL_FUNC (on_memcard_delete), (gpointer) 2, NULL, G_CONNECT_AFTER); + + while (gtk_events_pending()) gtk_main_iteration(); +} + +GtkWidget *CpuDlg; +GtkWidget *PsxCombo; +GList *psxglist; +char *psxtypes[] = { + "NTSC", + "PAL" +}; + +/* When the auto-detect CPU type is selected, disable the NTSC/PAL selection */ +static void OnCpu_PsxAutoClicked (GtkWidget *widget, gpointer user_data) { + GtkWidget *combo; + GladeXML *xml = user_data; + combo = glade_xml_get_widget(xml, "GtkCombo_PsxType"); + + gtk_widget_set_sensitive (combo, + !(gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)))); +} + +/* When the interpreter core is deselected, disable the debugger checkbox */ +static void OnCpu_CpuClicked (GtkWidget *widget, gpointer user_data) { + GtkWidget *check; + GladeXML *xml = user_data; + check = glade_xml_get_widget(xml, "GtkCheckButton_Dbg"); + + // Debugger is only working with interpreter not recompiler, so let's set it + if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget))) + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check), FALSE); + + gtk_widget_set_sensitive (check, + gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget))); +} + +void OnCpu_Clicked (GtkDialog *dialog, gint arg1, gpointer user_data) { + if (arg1 == GTK_RESPONSE_CLOSE) { + GtkWidget *widget; + GladeXML *xml = user_data; + int tmp; + long t; + + widget = glade_xml_get_widget(xml, "GtkCombo_PsxType"); + + /* If nothing chosen, default to NTSC */ + tmp = gtk_combo_box_get_active (GTK_COMBO_BOX (widget)); + if (tmp == -1) + tmp = PSX_TYPE_NTSC; + + if (!strcmp("NTSC",psxtypes[tmp])) + Config.PsxType = PSX_TYPE_NTSC; + else + Config.PsxType = PSX_TYPE_PAL; + + Config.Xa = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(glade_xml_get_widget(xml, "GtkCheckButton_Xa"))); + + Config.Sio = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(glade_xml_get_widget(xml, "GtkCheckButton_Sio"))); + + Config.Mdec = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(glade_xml_get_widget(xml, "GtkCheckButton_Mdec"))); + + Config.Cdda = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(glade_xml_get_widget(xml, "GtkCheckButton_CDDA"))); + + Config.PsxAuto = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(glade_xml_get_widget(xml, "GtkCheckButton_PsxAuto"))); + + Config.Dbg = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(glade_xml_get_widget(xml, "GtkCheckButton_Dbg"))); + + t = Config.Cpu; + Config.Cpu = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(glade_xml_get_widget(xml, "GtkCheckButton_Cpu"))); + if (t != Config.Cpu) { + psxCpu->Shutdown(); +#ifdef PSXREC + if (Config.Cpu) { + if (Config.Dbg) psxCpu = &psxIntDbg; + else psxCpu = &psxInt; + } + else psxCpu = &psxRec; +#else + if (Config.Dbg) psxCpu = &psxIntDbg; + else psxCpu = &psxInt; +#endif + if (psxCpu->Init() == -1) { + SysClose(); + exit(1); + } + psxCpu->Reset(); + } + + Config.PsxOut = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(glade_xml_get_widget(xml, "GtkCheckButton_PsxOut"))); + + Config.SpuIrq = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(glade_xml_get_widget(xml, "GtkCheckButton_SpuIrq"))); + + Config.RCntFix = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(glade_xml_get_widget(xml, "GtkCheckButton_RCntFix"))); + + Config.VSyncWA = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(glade_xml_get_widget(xml, "GtkCheckButton_VSyncWA"))); + + SaveConfig(); + } + + gtk_widget_destroy(CpuDlg); + CpuDlg = NULL; +} + +void OnConf_Cpu() { + GladeXML *xml; + + xml = glade_xml_new(PACKAGE_DATA_DIR "pcsx.glade2", "CpuDlg", NULL); + + if (!xml) { + g_warning("We could not load the interface!"); + return; + } + + CpuDlg = glade_xml_get_widget(xml, "CpuDlg"); + + PsxCombo = glade_xml_get_widget(xml, "GtkCombo_PsxType"); + gtk_combo_box_set_active (GTK_COMBO_BOX (PsxCombo), Config.PsxType); + gtk_widget_set_sensitive (GTK_WIDGET (PsxCombo), !Config.PsxAuto); + + gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (glade_xml_get_widget(xml, "GtkCheckButton_Xa")), Config.Xa); + + gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (glade_xml_get_widget(xml, "GtkCheckButton_Sio")), Config.Sio); + + gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (glade_xml_get_widget(xml, "GtkCheckButton_Mdec")), Config.Mdec); + + gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (glade_xml_get_widget(xml, "GtkCheckButton_CDDA")), Config.Cdda); + + gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (glade_xml_get_widget(xml, "GtkCheckButton_PsxAuto")), Config.PsxAuto); + + g_signal_connect_data(GTK_OBJECT(glade_xml_get_widget(xml, "GtkCheckButton_PsxAuto")), "toggled", + GTK_SIGNAL_FUNC(OnCpu_PsxAutoClicked), xml, NULL, G_CONNECT_AFTER); + +#ifdef PSXREC + gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (glade_xml_get_widget(xml, "GtkCheckButton_Cpu")), Config.Cpu); + + g_signal_connect_data(GTK_OBJECT(glade_xml_get_widget(xml, "GtkCheckButton_Cpu")), "toggled", + GTK_SIGNAL_FUNC(OnCpu_CpuClicked), xml, NULL, G_CONNECT_AFTER); +#else + Config.Cpu = 1; + + gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (glade_xml_get_widget(xml, "GtkCheckButton_Cpu")), TRUE); + gtk_widget_set_sensitive (GTK_WIDGET (glade_xml_get_widget(xml, "GtkCheckButton_Cpu")), FALSE); +#endif + + gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (glade_xml_get_widget(xml, "GtkCheckButton_Dbg")), Config.Cpu && Config.Dbg); + gtk_widget_set_sensitive (GTK_WIDGET (glade_xml_get_widget(xml, "GtkCheckButton_Dbg")), Config.Cpu); + + gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (glade_xml_get_widget(xml, "GtkCheckButton_PsxOut")), Config.PsxOut); + + gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (glade_xml_get_widget(xml, "GtkCheckButton_SpuIrq")), Config.SpuIrq); + + gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (glade_xml_get_widget(xml, "GtkCheckButton_RCntFix")), Config.RCntFix); + + gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (glade_xml_get_widget(xml, "GtkCheckButton_VSyncWA")), Config.VSyncWA); + + /* Setup a handler for when Close or Cancel is clicked */ + g_signal_connect_data(GTK_OBJECT(CpuDlg), "response", + GTK_SIGNAL_FUNC(OnCpu_Clicked), xml, (GClosureNotify)g_object_unref, G_CONNECT_AFTER); +} + +/* TODO Check whether configuration is required when we choose the plugin, and set the state of the + button appropriately. New gtk tooltip API should allow us to put a tooltip explanation for + disabled widgets */ +/* TODO If combo screen hasn't been opened and the user chooses the menu config option, confs.Combo will be null and cause a segfault */ +#define ConfPlugin(src, confs, plugin, name, parent) { \ + void *drv; \ + src conf; \ + gchar *filename; \ + \ + GetComboText(confs.Combo, confs.plist, plugin); \ + filename = g_build_filename (getenv("HOME"), PLUGINS_DIR, plugin, NULL); \ + /*printf("Configuring plugin %s\n", filename);*/ \ + drv = SysLoadLibrary(filename); \ + if (drv == NULL) {printf("Error with file %s\n", filename);return; } \ +\ + while (gtk_events_pending()) gtk_main_iteration(); \ + conf = (src) SysLoadSym(drv, name); \ + if (conf) { \ + conf(); \ + } else \ + SysInfoMessage (_("No configuration required"), _("This plugin doesn't need to be configured.")); \ + SysCloseLibrary(drv); \ + g_free (filename); \ +} + +void on_configure_plugin (GtkWidget *widget, gpointer user_data) { + gint plugin_type = (int) user_data; +// printf("Configuring plugin - %d\n", plugin_type); + + while (gtk_events_pending()) + gtk_main_iteration(); + if (all_config_set() == TRUE) { + switch (plugin_type) { + case PSE_LT_GPU: + ConfPlugin(GPUconfigure, GpuConfS, Config.Gpu, "GPUconfigure", ConfDlg); + break; + case PSE_LT_SPU: + ConfPlugin(SPUconfigure, SpuConfS, Config.Spu, "SPUconfigure", ConfDlg); + break; + case PSE_LT_CDR: + ConfPlugin(CDRconfigure, CdrConfS, Config.Cdr, "CDRconfigure", ConfDlg); + break; + } + } else + ConfigurePlugins(); +} + +void on_about_plugin (GtkWidget *widget, gpointer user_data) { + gint plugin_type = (int) user_data; +// printf("About plugin - %d\n", plugin_type); + while (gtk_events_pending()) + gtk_main_iteration(); + if (all_config_set() == TRUE) { + switch (plugin_type) { + case PSE_LT_GPU: + ConfPlugin(GPUconfigure, GpuConfS, Config.Gpu, "GPUabout", ConfDlg); + break; + case PSE_LT_SPU: + ConfPlugin(SPUconfigure, SpuConfS, Config.Spu, "SPUabout", ConfDlg); + break; + case PSE_LT_CDR: + ConfPlugin(CDRconfigure, CdrConfS, Config.Cdr, "CDRabout", ConfDlg); + break; + } + } else + ConfigurePlugins(); +} + +void OnConfConf_Pad1About(GtkWidget *widget, gpointer user_data) { + ConfPlugin(PADabout, Pad1ConfS, Config.Pad1, "PADabout", ConfDlg); +} + +void OnConfConf_Pad2About(GtkWidget *widget, gpointer user_data) { + ConfPlugin(PADabout, Pad2ConfS, Config.Pad2, "PADabout", ConfDlg); +} + +void OnConfConf_Pad1Conf(GtkWidget *widget, gpointer user_data) { + ConfPlugin(PADabout, Pad1ConfS, Config.Pad1, "PADconfigure", ConfDlg); +} + +void OnConfConf_Pad2Conf(GtkWidget *widget, gpointer user_data) { + ConfPlugin(PADabout, Pad2ConfS, Config.Pad2, "PADconfigure", ConfDlg); +} + +void OnNet_Conf(GtkWidget *widget, gpointer user_data) { + ConfPlugin(NETconfigure, NetConfS, Config.Net, "NETconfigure", NetDlg); +} + +void OnNet_About(GtkWidget *widget, gpointer user_data) { + ConfPlugin(NETabout, NetConfS, Config.Net, "NETabout", NetDlg); +} + +void OnPluginPath_Changed(GtkWidget *wdg, gpointer data) { + gchar *path; + + path = gtk_file_chooser_get_current_folder (GTK_FILE_CHOOSER (wdg)); + strcpy(Config.PluginsDir, path); + UpdatePluginsBIOS(); + UpdatePluginsBIOS_UpdateGUI(data); + + g_free (path); +} + +void OnBiosPath_Changed(GtkWidget *wdg, gpointer data) { + gchar *foldername; + + foldername = gtk_file_chooser_get_current_folder (GTK_FILE_CHOOSER (wdg)); + strcpy(Config.BiosDir, foldername); + +// printf("BIOS directory is now %s\n", foldername); + + UpdatePluginsBIOS(); + UpdatePluginsBIOS_UpdateGUI(data); + + g_free (foldername); +} + +void OnConf_Clicked (GtkDialog *dialog, gint arg1, gpointer user_data) { + if (arg1 == GTK_RESPONSE_OK) { + GetComboText(GpuConfS.Combo, GpuConfS.plist, Config.Gpu); + GetComboText(SpuConfS.Combo, SpuConfS.plist, Config.Spu); + GetComboText(CdrConfS.Combo, CdrConfS.plist, Config.Cdr); + GetComboText(Pad1ConfS.Combo, Pad1ConfS.plist, Config.Pad1); + GetComboText(Pad2ConfS.Combo, Pad2ConfS.plist, Config.Pad2); + GetComboText(BiosConfS.Combo, BiosConfS.plist, Config.Bios); + /* TODO Validation */ + + SaveConfig(); + } + + gtk_widget_destroy (ConfDlg); + ConfDlg = NULL; +} + +void OnHelp_About(GtkWidget *widget, gpointer user_data) { + GladeXML *xml; + GtkWidget *about_dialog; + + xml = glade_xml_new(PACKAGE_DATA_DIR "pcsx.glade2", "AboutDlg", NULL); + + if (!xml) { + g_warning("We could not load the interface!"); + return; + } + + about_dialog = glade_xml_get_widget(xml, "AboutDlg"); + + gtk_dialog_run (GTK_DIALOG (about_dialog)); + gtk_widget_destroy (about_dialog); +} + +#define ComboAddPlugin(type) { \ + type##ConfS.plugins += 2; \ + strcpy(type##ConfS.plist[type##ConfS.plugins - 1], name); \ + strcpy(type##ConfS.plist[type##ConfS.plugins - 2], ent->d_name); \ + type##ConfS.glist = g_list_append(type##ConfS.glist, type##ConfS.plist[type##ConfS.plugins-1]); \ +} + +void populate_combo_box (GtkWidget *widget, GList *list) { + GtkListStore *store; + GtkCellRenderer *renderer; + store = gtk_list_store_new (1, G_TYPE_STRING); + + /* Clear existing data from combo box */ + gtk_cell_layout_clear (GTK_CELL_LAYOUT (widget)); + + renderer = gtk_cell_renderer_text_new(); + gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (widget), renderer, FALSE); + gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (widget), renderer, "text", 0); + + while (list != NULL) { + GtkTreeIter iter; + gtk_list_store_append (store, &iter); + gtk_list_store_set (store, &iter, 0, (char *)list->data, -1); + list = list->next; + } + + gtk_combo_box_set_model (GTK_COMBO_BOX (widget), GTK_TREE_MODEL (store)); +} + +#define ConfCreatePConf(name, type) \ + /* Populate the relevant combo widget with the list of plugins. \ + If no plugins available, disable the combo and its controls. \ + Note that the Bios plugin has no About/Conf control. */ \ + type##ConfS.Combo = glade_xml_get_widget(xml, "GtkCombo_" name); \ + if (type##ConfS.glist != NULL) { \ + populate_combo_box (type##ConfS.Combo, type##ConfS.glist); \ + FindComboText(type##ConfS.Combo, type##ConfS.plist, Config.type); \ + gtk_widget_set_sensitive (type##ConfS.Combo, TRUE); \ + if (g_ascii_strcasecmp (name, "Bios") != 0) { \ + controlwidget = glade_xml_get_widget(xml, "btn_Conf" name); \ + gtk_widget_set_sensitive (controlwidget, TRUE); \ + controlwidget = glade_xml_get_widget(xml, "btn_About" name); \ + gtk_widget_set_sensitive (controlwidget, TRUE); \ + } \ + } else { \ + if (g_ascii_strcasecmp (name, "Bios") != 0) { \ + gtk_cell_layout_clear (GTK_CELL_LAYOUT (type##ConfS.Combo)); \ + gtk_widget_set_sensitive (type##ConfS.Combo, FALSE); \ + controlwidget = glade_xml_get_widget(xml, "btn_Conf" name); \ + gtk_widget_set_sensitive (controlwidget, FALSE); \ + controlwidget = glade_xml_get_widget(xml, "btn_About" name); \ + gtk_widget_set_sensitive (controlwidget, FALSE); \ + } \ + } + +int plugin_is_available (gchar *plugin) { + int retval; + gchar *pluginfile; + struct stat stbuf; + +// printf("Checking plugin_is_available - %s\n", plugin); + pluginfile = g_strconcat (getenv("HOME"), PLUGINS_DIR, plugin, NULL); + + if (stat(pluginfile, &stbuf) == -1) + retval = FALSE; + else + retval = TRUE; + + g_free (pluginfile); + + return retval; +} + +/* TODO Combine this with all_config_set() */ +int plugins_configured() { + // make sure there are choices for all of the plugins!! +// if ((strlen(Config.Gpu) == 0) || (strlen(Config.Spu) == 0) || (strlen(Config.Cdr) == 0) || (strlen(Config.Pad1) == 0) || (strlen(Config.Pad2) == 0)) { + if (all_config_set() == FALSE) + return FALSE; +// } + // and make sure they can all be accessed + // if they can't be, wipe the variable and return FALSE + if (plugin_is_available (Config.Gpu) == FALSE) { Config.Gpu[0] = '\0'; return FALSE; } + if (plugin_is_available (Config.Spu) == FALSE) { Config.Spu[0] = '\0'; return FALSE; } + if (plugin_is_available (Config.Cdr) == FALSE) { Config.Cdr[0] = '\0'; return FALSE; } + if (plugin_is_available (Config.Pad1) == FALSE) { Config.Pad1[0] = '\0'; return FALSE; } + if (plugin_is_available (Config.Pad2) == FALSE) { Config.Pad2[0] = '\0'; return FALSE; } + + // if everything is happy, return TRUE + return TRUE; +} + +int is_valid_bios_file (gchar *filename) { + int valid; + struct stat buf; + +// printf(" Checking is_valid_bios_file - %s\n", filename); + if ((stat(filename, &buf) == -1) || (buf.st_size != (1024*512))) + valid = FALSE; + else { + valid = TRUE; +// printf(" %s is a valid BIOS file\n", filename); + } + + return valid; +} + +/* Add the name of the BIOS file to the drop-down list. This will + be the filename, not the full path to the file */ +void add_bios_to_list (gchar *bios_name, gchar *internal_name) { + BiosConfS.plugins+=2; + strcpy(BiosConfS.plist[BiosConfS.plugins-1], bios_name); + strcpy(BiosConfS.plist[BiosConfS.plugins-2], internal_name); + /* Error handling - BIOS + strcpy(BiosConfS.plist[BiosConfS.plugins-1], internal_name); + strcpy(BiosConfS.plist[BiosConfS.plugins-2], bios_name);*/ + BiosConfS.glist = g_list_append(BiosConfS.glist, BiosConfS.plist[BiosConfS.plugins-1]); +} + +void scan_bios_dir (gchar *dirname) { + DIR *dir; + struct dirent *ent; + gchar *filename; + +// printf("Scanning bios dir %s\n", dirname); + + dir = opendir(dirname); + if (dir == NULL) { + SysMessage(_("Could not open BIOS directory: '%s'\n"), dirname); + return; + } + + while ((ent = readdir(dir)) != NULL) { + filename = g_build_filename (dirname, ent->d_name, NULL); + if (is_valid_bios_file (filename)) + add_bios_to_list (g_path_get_basename (filename), g_path_get_basename (filename)); + g_free (filename); + } + closedir(dir); + +// printf("Finished scanning bios dir %s\n", dirname); +} + +void UpdatePluginsBIOS() { + DIR *dir; + struct dirent *ent; + void *Handle; + char name[256]; + gchar *linkname; + + GpuConfS.plugins = 0; SpuConfS.plugins = 0; CdrConfS.plugins = 0; + Pad1ConfS.plugins = 0; Pad2ConfS.plugins = 0; BiosConfS.plugins = 0; + GpuConfS.glist = NULL; SpuConfS.glist = NULL; CdrConfS.glist = NULL; + Pad1ConfS.glist = NULL; Pad2ConfS.glist = NULL; BiosConfS.glist = NULL; + GpuConfS.plist[0][0] = '\0'; SpuConfS.plist[0][0] = '\0'; CdrConfS.plist[0][0] = '\0'; + Pad1ConfS.plist[0][0] = '\0'; Pad2ConfS.plist[0][0] = '\0'; BiosConfS.plist[0][0] = '\0'; + + /* Load and get plugin info */ + dir = opendir(Config.PluginsDir); + if (dir == NULL) { + printf(_("Could not open directory: '%s'\n"), Config.PluginsDir); + return; + } + while ((ent = readdir(dir)) != NULL) { + long type, v; + linkname = g_build_filename (Config.PluginsDir, ent->d_name, NULL); + + // only libraries past this point, not config tools + if (strstr(linkname, ".so") == NULL && strstr(linkname, ".dylib") == NULL) + continue; + + Handle = dlopen(linkname, RTLD_NOW); + if (Handle == NULL) { + printf("%s\n", dlerror()); + g_free(linkname); + continue; + } + + PSE_getLibType = (PSEgetLibType) dlsym(Handle, "PSEgetLibType"); + if (dlerror() != NULL) { + if (strstr(linkname, "gpu") != NULL) type = PSE_LT_GPU; + else if (strstr(linkname, "cdr") != NULL) type = PSE_LT_CDR; + else if (strstr(linkname, "spu") != NULL) type = PSE_LT_SPU; + else if (strstr(linkname, "pad") != NULL) type = PSE_LT_PAD; + else { g_free(linkname); continue; } + } + else type = PSE_getLibType(); + + PSE_getLibName = (PSEgetLibName) dlsym(Handle, "PSEgetLibName"); + if (dlerror() == NULL) { + sprintf(name, "%s", PSE_getLibName()); + PSE_getLibVersion = (PSEgetLibVersion) dlsym(Handle, "PSEgetLibVersion"); + if (dlerror() == NULL) { + char ver[32]; + + v = PSE_getLibVersion(); + sprintf(ver, " %ld.%ld.%ld", v >> 16, (v >> 8) & 0xff, v & 0xff); + strcat(name, ver); + } + } + else strcpy(name, ent->d_name); + + if (type & PSE_LT_CDR) + ComboAddPlugin(Cdr); + if (type & PSE_LT_GPU) + ComboAddPlugin(Gpu); + if (type & PSE_LT_SPU) + ComboAddPlugin(Spu); + if (type & PSE_LT_PAD) { + PADquery query = (PADquery)dlsym(Handle, "PADquery"); + if (query() & 0x1) { + ComboAddPlugin(Pad1); + } + if (query() & 0x2) { + ComboAddPlugin(Pad2); + } + } + + g_free(linkname); + } + closedir(dir); + + /* The BIOS list always contains the PCSX internal BIOS */ + add_bios_to_list (_("Internal HLE Bios"), "HLE"); + + scan_bios_dir (Config.BiosDir); +} + +void UpdatePluginsBIOS_UpdateGUI(GladeXML *xml) { + /* Populate the plugin combo boxes */ + ConfCreatePConf("Gpu", Gpu); + ConfCreatePConf("Spu", Spu); + ConfCreatePConf("Pad1", Pad1); + ConfCreatePConf("Pad2", Pad2); + ConfCreatePConf("Cdr", Cdr); + ConfCreatePConf("Bios", Bios); +} + +void FindNetPlugin(GladeXML *xml) { + DIR *dir; + struct dirent *ent; + void *Handle; + char plugin[MAXPATHLEN],name[MAXPATHLEN]; + + NetConfS.plugins = 0; + NetConfS.glist = NULL; + + NetConfS.plugins += 2; + strcpy(NetConfS.plist[NetConfS.plugins - 1], "Disabled"); + strcpy(NetConfS.plist[NetConfS.plugins - 2], "Disabled"); + NetConfS.glist = g_list_append(NetConfS.glist, NetConfS.plist[NetConfS.plugins - 1]); + + dir = opendir(Config.PluginsDir); + if (dir == NULL) + SysMessage(_("Could not open directory: '%s'\n"), Config.PluginsDir); + else { + /* ADB TODO Replace the following with a function */ + while ((ent = readdir(dir)) != NULL) { + long type, v; + + sprintf(plugin, "%s/%s", Config.PluginsDir, ent->d_name); + + if (strstr(plugin, ".so") == NULL && strstr(plugin, ".dylib") == NULL) + continue; + Handle = dlopen(plugin, RTLD_NOW); + if (Handle == NULL) continue; + + PSE_getLibType = (PSEgetLibType) dlsym(Handle, "PSEgetLibType"); + if (dlerror() != NULL) { + if (strstr(plugin, "net") != NULL) type = PSE_LT_NET; + else continue; + } + else type = PSE_getLibType(); + + PSE_getLibName = (PSEgetLibName) dlsym(Handle, "PSEgetLibName"); + if (dlerror() == NULL) { + sprintf(name, "%s", PSE_getLibName()); + PSE_getLibVersion = (PSEgetLibVersion) dlsym(Handle, "PSEgetLibVersion"); + if (dlerror() == NULL) { + char ver[32]; + + v = PSE_getLibVersion(); + sprintf(ver, " %ld.%ld.%ld",v>>16,(v>>8)&0xff,v&0xff); + strcat(name, ver); + } + } + else strcpy(name, ent->d_name); + + if (type & PSE_LT_NET) { + ComboAddPlugin(Net); + } + } + closedir(dir); + + ConfCreatePConf("Net", Net); + } +} + +void SysMessage(char *fmt, ...) { + GtkWidget *Txt, *MsgDlg; + va_list list; + char msg[512]; + + va_start(list, fmt); + vsprintf(msg, fmt, list); + va_end(list); + + if (msg[strlen(msg) - 1] == '\n') + msg[strlen(msg) - 1] = 0; + + if (!UseGui) { + printf ("%s\n", msg); + return; + } + + MsgDlg = gtk_dialog_new_with_buttons (_("Notice"), NULL, + GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_OK, GTK_RESPONSE_NONE, NULL); + + gtk_window_set_position (GTK_WINDOW(MsgDlg), GTK_WIN_POS_CENTER); + + Txt = gtk_label_new (msg); + gtk_label_set_line_wrap(GTK_LABEL(Txt), TRUE); + gtk_container_add (GTK_CONTAINER (GTK_DIALOG(MsgDlg)->vbox), Txt); + + gtk_widget_show (Txt); + gtk_widget_show_all (MsgDlg); + gtk_dialog_run (GTK_DIALOG(MsgDlg)); + gtk_widget_destroy (MsgDlg); +} + +void SysErrorMessage(gchar *primary, gchar *secondary) { + GtkWidget *message_dialog; + if (!UseGui) + printf ("%s - %s\n", primary, secondary); + else { + message_dialog = gtk_message_dialog_new (NULL, + GTK_DIALOG_MODAL, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_CLOSE, + primary, + NULL); + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (message_dialog), + secondary); + + gtk_dialog_run (GTK_DIALOG (message_dialog)); + gtk_widget_destroy (message_dialog); + } +} + +void SysInfoMessage(gchar *primary, gchar *secondary) { + GtkWidget *message_dialog; + if (!UseGui) + printf ("%s - %s\n", primary, secondary); + else { + message_dialog = gtk_message_dialog_new (NULL, + GTK_DIALOG_MODAL, + GTK_MESSAGE_INFO, + GTK_BUTTONS_CLOSE, + primary, + NULL); + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (message_dialog), + secondary); + + gtk_dialog_run (GTK_DIALOG (message_dialog)); + gtk_widget_destroy (message_dialog); + } +} diff --git a/gui/Linux.h b/gui/Linux.h new file mode 100644 index 00000000..1f8e0e3f --- /dev/null +++ b/gui/Linux.h @@ -0,0 +1,83 @@ +/* Pcsx - Pc Psx Emulator + * Copyright (C) 1999-2002 Pcsx Team + * + * 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., 51 Franklin Steet, Fifth Floor, Boston, MA 02111-1307 USA + */ + +#ifndef __LINUX_H__ +#define __LINUX_H__ + +#include "config.h" + +/* FIXME */ +#include "../libpcsxcore/psxcommon.h" +#include <gtk/gtk.h> + +#include "Cheat.h" + +#define DEFAULT_MEM_CARD_1 "/.pcsx/memcards/card1.mcd" +#define DEFAULT_MEM_CARD_2 "/.pcsx/memcards/card2.mcd" +#define MEMCARD_DIR "/.pcsx/memcards/" +#define PLUGINS_DIR "/.pcsx/plugins/" +#define PLUGINS_CFG_DIR "/.pcsx/plugins/cfg/" +#define PCSX_DOT_DIR "/.pcsx/" +#define BIOS_DIR "/.pcsx/bios/" +#define STATES_DIR "/.pcsx/sstates/" +#define CHEATS_DIR "/.pcsx/cheats/" + +extern int UseGui; +char cfgfile[MAXPATHLEN]; /* ADB Comment this out - make a local var, or at least use gchar funcs */ +char cfgfile_basename[MAXPATHLEN]; /* ADB Comment this out - make a local var, or at least use gchar funcs */ + +int LoadConfig(); +void SaveConfig(); + +void StartGui(); + +void ConfigurePlugins(); + +void PADhandleKey(int key); + +void UpdateMenuSlots(); + +gchar* get_state_filename(int i); + +void state_save(gchar *state_filename); +void state_load(gchar *state_filename); + +int match(const char* string, char* pattern); +int plugins_configured(); + +void UpdatePluginsBIOS(); + +void SysErrorMessage(gchar *primary, gchar *secondary); +void SysInfoMessage(gchar *primary, gchar *secondary); + +typedef struct { + GtkWidget *Combo; + GList *glist; + char plist[255][255]; /* TODO Comment this out */ + int plugins; /* TODO Comment this out and replace with glist count */ +} PluginConf; + +PluginConf GpuConfS; +PluginConf SpuConfS; +PluginConf CdrConfS; +PluginConf Pad1ConfS; +PluginConf Pad2ConfS; +PluginConf NetConfS; +PluginConf BiosConfS; + +#endif /* __LINUX_H__ */ diff --git a/gui/LnxMain.c b/gui/LnxMain.c new file mode 100644 index 00000000..0b10aaee --- /dev/null +++ b/gui/LnxMain.c @@ -0,0 +1,487 @@ +/* Pcsx - Pc Psx Emulator + * Copyright (C) 1999-2002 Pcsx Team + * + * 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., 51 Franklin Steet, Fifth Floor, Boston, MA 02111-1307 USA + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <stdarg.h> +#include <dlfcn.h> +#include <sys/mman.h> +#include <errno.h> +#include <string.h> +#include <time.h> +#include <gtk/gtk.h> +#include <pthread.h> +#include <dirent.h> +#include <sys/stat.h> + +#include "Linux.h" +/* FIXME */ +#include "../libpcsxcore/sio.h" + +#include "config.h" + +#ifdef ENABLE_NLS +#include <locale.h> +#endif + +enum { + DONT_USE_GUI = 0, + USE_GUI +}; + +enum { + RUN = 0, + RUN_CD, +}; + +int UseGui = USE_GUI; + +static void CreateMemcard(char *filename, char *conf_mcd) { + gchar *mcd; + struct stat buf; + + mcd = g_build_filename (getenv("HOME"), MEMCARD_DIR, filename, NULL); + + /* Only create a memory card if an existing one does not exist */ + if (stat(mcd, &buf) == -1) { + strcpy(conf_mcd, mcd); + SysPrintf(_("Creating memory card: %s\n"), mcd); + CreateMcd(mcd); + } + + g_free (mcd); +} + +/* Create a directory under the $HOME directory, if that directory doesn't already exist */ +static void CreateHomeConfigDir(char *directory) { + struct stat buf; + + if (stat(directory, &buf) == -1) { + gchar *dir_name = g_build_filename (getenv("HOME"), directory, NULL); + mkdir(dir_name, S_IRWXU | S_IRWXG); + g_free (dir_name); + } +} + +static void CheckSubDir() { + // make sure that ~/.pcsx exists + CreateHomeConfigDir(PCSX_DOT_DIR); + + CreateHomeConfigDir(BIOS_DIR); + CreateHomeConfigDir(MEMCARD_DIR); + CreateHomeConfigDir(STATES_DIR); + CreateHomeConfigDir(PLUGINS_DIR); + CreateHomeConfigDir(PLUGINS_CFG_DIR); + CreateHomeConfigDir(CHEATS_DIR); +} + +void ScanPlugins(gchar* scandir) { + // scan for plugins and configuration tools + DIR *dir; + struct dirent *ent; + + gchar *linkname; + gchar *filename; + + /* Any plugins found will be symlinked to the following directory */ + + dir = opendir(scandir); + if (dir == NULL) { +// printf(_("Could not open plugins directory: '%s'\n"), scandir); + } else { +// printf("Scanning %s for plugins\n", scandir); + while ((ent = readdir(dir)) != NULL) { + filename = g_build_filename (scandir, ent->d_name, NULL); + + if (match(filename, ".*\\.so$") == 0 && + match(filename, ".*\\.dylib$") == 0 && + match(filename, "cfg.*") == 0) { + continue; /* Skip this file */ + } else { + /* Create a symlink from this file to the directory ~/.pcsx/plugin */ + linkname = g_build_filename (getenv("HOME"), PLUGINS_DIR, ent->d_name, NULL); +// printf(" Plugin file symlink: %s -> %s\n", filename, linkname); + symlink(filename, linkname); + + /* If it's a config tool, make one in the cfg dir as well. + This allows plugins with retarded cfg finding to work :- ) */ + if (match(filename, "cfg.*") == 1) { + linkname = g_build_filename (getenv("HOME"), PLUGINS_CFG_DIR, ent->d_name, NULL); +// printf(" Config file symlink: %s -> %s\n", filename, linkname); + symlink(filename, linkname); + } + g_free (linkname); + } + g_free (filename); + } + closedir(dir); + } +} + +static void CheckSymlinksInPath (char* dotdir) { + DIR *dir; + struct dirent *ent; + struct stat stbuf; + gchar *linkname; + + dir = opendir(dotdir); + if (dir == NULL) { + SysMessage(_("Could not open directory: '%s'\n"), dotdir); + return; + } + + /* Check for any bad links in the directory. If the remote + file no longer exists, remove the link */ + while ((ent = readdir(dir)) != NULL) { + linkname = g_strconcat (dotdir, ent->d_name, NULL); + + if (stat(linkname, &stbuf) == -1) { + /* File link is bad, remove it */ +// printf("unlink: %s\n", linkname); + unlink(linkname); + } + g_free (linkname); + } + closedir(dir); +} + +static void ScanAllPlugins (void) { + gchar *currentdir; + + // scan some default locations to find plugins + ScanPlugins("/usr/lib/games/psemu/"); + ScanPlugins("/usr/lib/games/psemu/lib/"); + ScanPlugins("/usr/lib/games/psemu/config/"); + ScanPlugins("/usr/local/lib/games/psemu/lib/"); + ScanPlugins("/usr/local/lib/games/psemu/config/"); + ScanPlugins("/usr/local/lib/games/psemu/"); + ScanPlugins("/usr/lib64/games/psemu/"); + ScanPlugins("/usr/lib64/games/psemu/lib/"); + ScanPlugins("/usr/lib64/games/psemu/config/"); + ScanPlugins("/usr/local/lib64/games/psemu/lib/"); + ScanPlugins("/usr/local/lib64/games/psemu/config/"); + ScanPlugins("/usr/local/lib64/games/psemu/"); + ScanPlugins(DEF_PLUGIN_DIR); + ScanPlugins(DEF_PLUGIN_DIR "/lib"); + ScanPlugins(DEF_PLUGIN_DIR "/lib64"); + ScanPlugins(DEF_PLUGIN_DIR "/config"); + + currentdir = g_strconcat (getenv("HOME"), "/.psemu-plugins/", NULL); + ScanPlugins(currentdir); + g_free (currentdir); + + currentdir = g_strconcat (getenv("HOME"), "/.psemu/", NULL); + ScanPlugins(currentdir); + g_free (currentdir); + + /* Check for bad links in ~/.pcsx/plugins/ */ + currentdir = g_build_filename (getenv("HOME"), PLUGINS_DIR, NULL); + CheckSymlinksInPath (currentdir); + g_free (currentdir); + + /* Check for bad links in ~/.pcsx/plugins/cfg */ + currentdir = g_build_filename (getenv("HOME"), PLUGINS_CFG_DIR, NULL); + CheckSymlinksInPath (currentdir); + g_free (currentdir); +} + +/* Set the default plugin name */ +void set_default_plugin (char *plugin_name, char *conf_plugin_name) { + if (strlen(plugin_name) != 0) { + strcpy(conf_plugin_name, plugin_name); + printf("Picking default plugin: %s\n", plugin_name); + } else + printf("No default plugin could be found for %s\n", conf_plugin_name); +} + +int main(int argc, char *argv[]) { + char file[MAXPATHLEN] = ""; + char path[MAXPATHLEN]; + int runcd = RUN; + int loadst = 0; + int i; + +#ifdef ENABLE_NLS + setlocale (LC_ALL, ""); + bindtextdomain (GETTEXT_PACKAGE, LOCALE_DIR); + bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); + textdomain (GETTEXT_PACKAGE); +#endif + + // what is the name of the config file? + // it may be redefined by -cfg on the command line + strcpy(cfgfile_basename, "pcsx.cfg"); + + // read command line options + for (i=1; i<argc; i++) { + if (!strcmp(argv[i], "-runcd")) runcd = RUN_CD; + else if (!strcmp(argv[i], "-nogui")) UseGui = DONT_USE_GUI; + else if (!strcmp(argv[i], "-psxout")) Config.PsxOut = 1; + else if (!strcmp(argv[i], "-load")) loadst = atol(argv[++i]); + else if (!strcmp(argv[i], "-cfg")) { + if (i+1 >= argc) break; + strncpy(cfgfile_basename, argv[++i], MAXPATHLEN-100); /* TODO buffer overruns */ + printf("Using config file %s.\n", cfgfile_basename); + } + else if (!strcmp(argv[i], "-cdfile")) { + if (i+1 >= argc) break; + strncpy(cdrfilename, argv[++i], MAXPATHLEN); + if (cdrfilename[0] != '/') { + getcwd(path, MAXPATHLEN); + if (strlen(path) + strlen(cdrfilename) + 1 < MAXPATHLEN) { + strcat(path, "/"); + strcat(path, cdrfilename); + strcpy(cdrfilename, path); + } else + cdrfilename[0] = 0; + } + runcd = RUN_CD; + } + else if (!strcmp(argv[i], "-h") || + !strcmp(argv[i], "-help") || + !strcmp(argv[i], "--help")) { + printf(PACKAGE_STRING "\n"); + printf("%s\n", _( + " pcsx [options] [file]\n" + "\toptions:\n" + "\t-runcd\t\tRuns CD-ROM\n" + "\t-cdfile FILE\tRuns a CD image file\n" + "\t-nogui\t\tDon't open the GTK GUI\n" + "\t-cfg FILE\tLoads desired configuration file (default: ~/.pcsx/pcsx.cfg)\n" + "\t-psxout\t\tEnable PSX output\n" + "\t-load STATENUM\tLoads savestate STATENUM (1-5)\n" + "\t-h -help\tDisplay this message\n" + "\tfile\t\tLoads file\n")); + return 0; + } else { + strncpy(file, argv[i], MAXPATHLEN); + if (file[0] != '/') { + getcwd(path, MAXPATHLEN); + if (strlen(path) + strlen(file) + 1 < MAXPATHLEN) { + strcat(path, "/"); + strcat(path, file); + strcpy(file, path); + } else + file[0] = 0; + } + } + } + + memset(&Config, 0, sizeof(PcsxConfig)); + strcpy(Config.Net, "Disabled"); + + if (UseGui == USE_GUI) + gtk_init(NULL, NULL); + + CheckSubDir(); + ScanAllPlugins(); + + // try to load config + // if the config file doesn't exist + if (LoadConfig() == -1) { + if (UseGui == DONT_USE_GUI) { + printf(_("PCSX cannot be configured without using the GUI -- you should restart without -nogui.\n")); + return 1; + } + + // Uh oh, no config file found, use some defaults + Config.PsxAuto = 1; /* ADB TODO */ + + gchar *str_bios_dir = g_strconcat (getenv("HOME"), "/.pcsx/bios/", NULL); + strcpy(Config.BiosDir, str_bios_dir); + g_free (str_bios_dir); + + gchar *str_plugin_dir = g_strconcat (getenv("HOME"), PLUGINS_DIR, NULL); + strcpy(Config.PluginsDir, str_plugin_dir); + g_free (str_plugin_dir); + + gtk_init(NULL, NULL); + + // switch to bios dir for scanning + str_plugin_dir = g_build_filename (getenv("HOME"), BIOS_DIR, NULL); + chdir (str_plugin_dir); + g_free (str_plugin_dir); + + // Update available plugins, but not GUI + UpdatePluginsBIOS(); + + // Pick some defaults, if they're available + set_default_plugin (GpuConfS.plist[0], Config.Gpu); + set_default_plugin (SpuConfS.plist[0], Config.Spu); + set_default_plugin (CdrConfS.plist[0], Config.Cdr); + set_default_plugin (Pad1ConfS.plist[0], Config.Pad1); + set_default_plugin (Pad2ConfS.plist[0], Config.Pad2); + set_default_plugin (BiosConfS.plist[0], Config.Bios); + + // create & load default memcards if they don't exist + CreateMemcard ("card1.mcd", Config.Mcd1); + CreateMemcard ("card2.mcd", Config.Mcd2); + + LoadMcds(Config.Mcd1, Config.Mcd2); + + SaveConfig(); + } + + // switch to plugin dotdir + // this lets plugins work without modification! + gchar *plugin_default_dir = g_build_filename (getenv("HOME"), PLUGINS_DIR, NULL); + chdir(plugin_default_dir); /* TODO Error checking - make sure this directory is available */ + g_free (plugin_default_dir); + + if (UseGui != DONT_USE_GUI) { + cdrfilename[0] = '\0'; + } + + if (SysInit() == -1) + return 1; + + if (UseGui == USE_GUI) { + StartGui(); + } else { + // the following only occurs if the gui isn't started + if (LoadPlugins() == -1) { + SysErrorMessage(_("Error"), _("Failed loading plugins!")); + return 1; + } + + if (OpenPlugins() == -1 || plugins_configured() == FALSE) { + return 1; + } + + SysReset(); + CheckCdrom(); + + if (file[0] != '\0') { + Load(file); + } else { + if (runcd == RUN_CD) { + if (LoadCdrom() == -1) { + ClosePlugins(); + printf(_("Could not load CD-ROM!\n")); + return -1; + } + } + } + + /* If a state has been specified, then load that */ + if (loadst) { + StatesC = loadst - 1; + gchar *state_filename = get_state_filename (StatesC); + LoadState(state_filename); /* TODO Error checking */ + g_free (state_filename); + } + + psxCpu->Execute(); + } + + return 0; +} + +DIR *dir; + +int SysInit() { +#ifdef GTE_DUMP + gteLog = fopen("gteLog.txt","wb"); + setvbuf(gteLog, NULL, _IONBF, 0); +#endif + +#ifdef EMU_LOG +#ifndef LOG_STDOUT + emuLog = fopen("emuLog.txt","wb"); +#else + emuLog = stdout; +#endif + setvbuf(emuLog, NULL, _IONBF, 0); +#endif + + if (psxInit() == -1) { + /* TODO Error handling */ + printf(_("PSX emulator couldn't be initialized.\n")); + return -1; + } + + LoadMcds(Config.Mcd1, Config.Mcd2); /* TODO Do we need to have this here, or in the calling main() function?? */ + + return 0; +} + +void SysReset() { + psxReset(); +} + +void SysClose() { + psxShutdown(); + ReleasePlugins(); + + if (emuLog != NULL) fclose(emuLog); +} + +void SysPrintf(char *fmt, ...) { + va_list list; + char msg[512]; + + va_start(list, fmt); + vsprintf(msg, fmt, list); + va_end(list); + + if (Config.PsxOut) { + static char linestart = 1; + int l = strlen(msg); + + printf(linestart ? " * %s" : "%s", msg); + + if (l > 0 && msg[l - 1] == '\n') { + linestart = 1; + } else { + linestart = 0; + } + } + +#ifdef EMU_LOG +#ifndef LOG_STDOUT + fprintf(emuLog, "%s", msg); +#endif +#endif +} + +void *SysLoadLibrary(char *lib) { + return dlopen(lib, RTLD_NOW); +} + +void *SysLoadSym(void *lib, char *sym) { + return dlsym(lib, sym); +} + +const char *SysLibError() { + return dlerror(); +} + +void SysCloseLibrary(void *lib) { + dlclose(lib); +} + +void SysUpdate() { + PADhandleKey(PAD1_keypressed()); + PADhandleKey(PAD2_keypressed()); +} + +/* ADB TODO Replace RunGui() with StartGui ()*/ +void SysRunGui() { + StartGui(); +} diff --git a/gui/Makefile.am b/gui/Makefile.am new file mode 100644 index 00000000..b9744092 --- /dev/null +++ b/gui/Makefile.am @@ -0,0 +1,19 @@ +INCLUDES = -DPACKAGE_DATA_DIR=\"${datadir}/pcsx/\" \ + -DPIXMAPDIR=\"${datadir}/pixmaps/\" \ + -DLOCALE_DIR=\"${datadir}/locale/\" \ + $(GTK2_CFLAGS) $(GLADE2_CFLAGS) \ + -I$(top_srcdir)/libpcsxcore -I$(top_srcdir)/include \ + -DDEF_PLUGIN_DIR=\"${libdir}/games/psemu\" + +bin_PROGRAMS = pcsx + +pcsx_SOURCES = \ + LnxMain.c \ + Plugin.c \ + Config.c \ + Gtk2Gui.c \ + Cheat.c \ + hdebug.c + +pcsx_LDADD = \ + $(GTK2_LIBS) $(GLADE2_LIBS) -lpthread -lz -lm ../libpcsxcore/libpcsxcore.a diff --git a/gui/Makefile.in b/gui/Makefile.in new file mode 100644 index 00000000..4ecf2cd9 --- /dev/null +++ b/gui/Makefile.in @@ -0,0 +1,520 @@ +# Makefile.in generated by automake 1.10.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +target_triplet = @target@ +bin_PROGRAMS = pcsx$(EXEEXT) +subdir = gui +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/include/config.h +CONFIG_CLEAN_FILES = +am__installdirs = "$(DESTDIR)$(bindir)" +binPROGRAMS_INSTALL = $(INSTALL_PROGRAM) +PROGRAMS = $(bin_PROGRAMS) +am_pcsx_OBJECTS = LnxMain.$(OBJEXT) Plugin.$(OBJEXT) Config.$(OBJEXT) \ + Gtk2Gui.$(OBJEXT) Cheat.$(OBJEXT) hdebug.$(OBJEXT) +pcsx_OBJECTS = $(am_pcsx_OBJECTS) +am__DEPENDENCIES_1 = +pcsx_DEPENDENCIES = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ + ../libpcsxcore/libpcsxcore.a +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)/include +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \ + $(LDFLAGS) -o $@ +SOURCES = $(pcsx_SOURCES) +DIST_SOURCES = $(pcsx_SOURCES) +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +ALSA_LIBS = @ALSA_LIBS@ +AMTAR = @AMTAR@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCAS = @CCAS@ +CCASDEPMODE = @CCASDEPMODE@ +CCASFLAGS = @CCASFLAGS@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DFOPENGL = @DFOPENGL@ +DSYMUTIL = @DSYMUTIL@ +ECHO = @ECHO@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +F77 = @F77@ +FFLAGS = @FFLAGS@ +GETTEXT_MACRO_VERSION = @GETTEXT_MACRO_VERSION@ +GETTEXT_PACKAGE = @GETTEXT_PACKAGE@ +GLADE2_CFLAGS = @GLADE2_CFLAGS@ +GLADE2_LIBS = @GLADE2_LIBS@ +GLIB2_CFLAGS = @GLIB2_CFLAGS@ +GLIB2_LIBS = @GLIB2_LIBS@ +GMSGFMT = @GMSGFMT@ +GMSGFMT_015 = @GMSGFMT_015@ +GREP = @GREP@ +GTK2_CFLAGS = @GTK2_CFLAGS@ +GTK2_LIBS = @GTK2_LIBS@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +INTLLIBS = @INTLLIBS@ +INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@ +LDFLAGS = @LDFLAGS@ +LIBICONV = @LIBICONV@ +LIBINTL = @LIBINTL@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LN_S = @LN_S@ +LTLIBICONV = @LTLIBICONV@ +LTLIBINTL = @LTLIBINTL@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +MSGFMT = @MSGFMT@ +MSGFMT_015 = @MSGFMT_015@ +MSGMERGE = @MSGMERGE@ +NASM = @NASM@ +NMEDIT = @NMEDIT@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +POSUB = @POSUB@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +USE_NLS = @USE_NLS@ +VERSION = @VERSION@ +XGETTEXT = @XGETTEXT@ +XGETTEXT_015 = @XGETTEXT_015@ +XGETTEXT_EXTRA_OPTIONS = @XGETTEXT_EXTRA_OPTIONS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_F77 = @ac_ct_F77@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target = @target@ +target_alias = @target_alias@ +target_cpu = @target_cpu@ +target_os = @target_os@ +target_vendor = @target_vendor@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +INCLUDES = -DPACKAGE_DATA_DIR=\"${datadir}/pcsx/\" \ + -DPIXMAPDIR=\"${datadir}/pixmaps/\" \ + -DLOCALE_DIR=\"${datadir}/locale/\" \ + $(GTK2_CFLAGS) $(GLADE2_CFLAGS) \ + -I$(top_srcdir)/libpcsxcore -I$(top_srcdir)/include \ + -DDEF_PLUGIN_DIR=\"${libdir}/games/psemu\" + +pcsx_SOURCES = \ + LnxMain.c \ + Plugin.c \ + Config.c \ + Gtk2Gui.c \ + Cheat.c \ + hdebug.c + +pcsx_LDADD = \ + $(GTK2_LIBS) $(GLADE2_LIBS) -lpthread -lz -lm ../libpcsxcore/libpcsxcore.a + +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu gui/Makefile'; \ + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu gui/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +install-binPROGRAMS: $(bin_PROGRAMS) + @$(NORMAL_INSTALL) + test -z "$(bindir)" || $(MKDIR_P) "$(DESTDIR)$(bindir)" + @list='$(bin_PROGRAMS)'; for p in $$list; do \ + p1=`echo $$p|sed 's/$(EXEEXT)$$//'`; \ + if test -f $$p \ + || test -f $$p1 \ + ; then \ + f=`echo "$$p1" | sed 's,^.*/,,;$(transform);s/$$/$(EXEEXT)/'`; \ + echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(binPROGRAMS_INSTALL) '$$p' '$(DESTDIR)$(bindir)/$$f'"; \ + $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(binPROGRAMS_INSTALL) "$$p" "$(DESTDIR)$(bindir)/$$f" || exit 1; \ + else :; fi; \ + done + +uninstall-binPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(bin_PROGRAMS)'; for p in $$list; do \ + f=`echo "$$p" | sed 's,^.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \ + echo " rm -f '$(DESTDIR)$(bindir)/$$f'"; \ + rm -f "$(DESTDIR)$(bindir)/$$f"; \ + done + +clean-binPROGRAMS: + @list='$(bin_PROGRAMS)'; for p in $$list; do \ + f=`echo $$p|sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f $$p $$f"; \ + rm -f $$p $$f ; \ + done +pcsx$(EXEEXT): $(pcsx_OBJECTS) $(pcsx_DEPENDENCIES) + @rm -f pcsx$(EXEEXT) + $(LINK) $(pcsx_OBJECTS) $(pcsx_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Cheat.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Config.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Gtk2Gui.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/LnxMain.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Plugin.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hdebug.Po@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c $< + +.c.obj: +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonemtpy = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$tags $$unique; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + test -z "$(CTAGS_ARGS)$$tags$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$tags $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(PROGRAMS) +installdirs: + for dir in "$(DESTDIR)$(bindir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-binPROGRAMS clean-generic clean-libtool mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-exec-am: install-binPROGRAMS + +install-html: install-html-am + +install-info: install-info-am + +install-man: + +install-pdf: install-pdf-am + +install-ps: install-ps-am + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-binPROGRAMS + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS all all-am check check-am clean clean-binPROGRAMS \ + clean-generic clean-libtool ctags distclean distclean-compile \ + distclean-generic distclean-libtool distclean-tags distdir dvi \ + dvi-am html html-am info info-am install install-am \ + install-binPROGRAMS install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ + pdf pdf-am ps ps-am tags uninstall uninstall-am \ + uninstall-binPROGRAMS + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/gui/Plugin.c b/gui/Plugin.c new file mode 100644 index 00000000..351ee778 --- /dev/null +++ b/gui/Plugin.c @@ -0,0 +1,387 @@ +/* Pcsx - Pc Psx Emulator + * Copyright (C) 1999-2002 Pcsx Team + * + * 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., 51 Franklin Steet, Fifth Floor, Boston, MA 02111-1307 USA + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <dlfcn.h> +#include <X11/keysym.h> +#include <signal.h> + +#include "Linux.h" + +#include "../libpcsxcore/plugins.h" +#include "../libpcsxcore/spu.h" +#include "../libpcsxcore/cdriso.h" + +#include "nopic.h" + +#define MAX_SLOTS 5 /* ADB TODO Same as Gtk2Gui.c */ + +void OnFile_Exit(); + +unsigned long gpuDisp; + +int StatesC = 0; +extern int UseGui; +extern int cdOpenCase; +int ShowPic = 0; + +void gpuShowPic() { + gchar *state_filename; + gzFile f; + + if (!ShowPic) { + unsigned char *pMem; + + pMem = (unsigned char *) malloc(128*96*3); + if (pMem == NULL) return; + + state_filename = get_state_filename (StatesC); + + GPU_freeze(2, (GPUFreeze_t *)&StatesC); + + f = gzopen(state_filename, "rb"); + if (f != NULL) { + gzseek(f, 32, SEEK_SET); // skip header + gzread(f, pMem, 128*96*3); + gzclose(f); + } else { + memcpy(pMem, NoPic_Image.pixel_data, 128*96*3); + DrawNumBorPic(pMem, StatesC+1); + } + GPU_showScreenPic(pMem); + + free(pMem); + ShowPic = 1; + g_free (state_filename); + } else { + GPU_showScreenPic(NULL); + ShowPic = 0; + } +} + + + +void KeyStateSave(int i) { + gchar *state_filename; + + state_filename = get_state_filename (i); + state_save (state_filename); + + g_free (state_filename); +} + +void KeyStateLoad(int i) { + gchar *state_filename; + + state_filename = get_state_filename (i); + state_load (state_filename); + + g_free (state_filename); +} + + +static short modctrl=0, modalt=0; + +/* Handle keyboard keystrokes */ +void PADhandleKey(int key) { + char Text[MAXPATHLEN]; + gchar *state_filename; + + short rel = 0; //released key flag + + if (key == 0) + return; + + if ((key >> 30) & 1) //specific to dfinput (padJoy) + rel = 1; + + if (rel) { + switch (key & ~0x40000000) { + case XK_Alt_L: + case XK_Alt_R: + modalt=0; + break; + case XK_Control_L: + case XK_Control_R: + modctrl=0; + break; + } + return; + } + + switch (key) { + case XK_Alt_L: + case XK_Alt_R: + modalt=1; + break; + case XK_Control_L: + case XK_Control_R: + modctrl=1; + break; + + case XK_0: + if (modalt && modctrl) + return; + if (modalt) KeyStateLoad(10); + break; + + case XK_1: + if (modalt && modctrl) + return; + if (modalt) KeyStateLoad(0); + if (modctrl) KeyStateSave(0); + break; + case XK_2: + if (modalt && modctrl) + return; + if (modalt) KeyStateLoad(1); + if (modctrl) KeyStateSave(1); + break; + case XK_3: + if (modalt && modctrl) + return; + if (modalt) KeyStateLoad(2); + if (modctrl) KeyStateSave(2); + break; + case XK_4: + if (modalt && modctrl) + return; + if (modalt) KeyStateLoad(3); + if (modctrl) KeyStateSave(3); + break; + case XK_5: + if (modalt && modctrl) + return; + if (modalt) KeyStateLoad(4); + if (modctrl) KeyStateSave(4); + break; + + case XK_F1: + GPU_freeze(2, (GPUFreeze_t *)&StatesC); + state_filename = get_state_filename (StatesC); + state_save (state_filename); + + g_free (state_filename); + + if (ShowPic) { ShowPic = 0; gpuShowPic(); } + + break; + case XK_F2: + if (StatesC < (MAX_SLOTS - 1)) StatesC++; + else StatesC = 0; + GPU_freeze(2, (GPUFreeze_t *)&StatesC); + if (ShowPic) { ShowPic = 0; gpuShowPic(); } + break; + case XK_F3: + state_filename = get_state_filename (StatesC); + state_load (state_filename); + + g_free (state_filename); + break; + case XK_F4: + gpuShowPic(); + break; + case XK_F5: + Config.Sio ^= 0x1; + if (Config.Sio) + sprintf(Text, _("SIO IRQ Always Enabled")); + else sprintf(Text, _("SIO IRQ Not Always Enabled")); + GPU_displayText(Text); + break; + case XK_F6: + Config.Mdec ^= 0x1; + if (Config.Mdec) + sprintf(Text, _("Black & White Mdecs Only Enabled")); + else sprintf(Text, _("Black & White Mdecs Only Disabled")); + GPU_displayText(Text); + break; + case XK_F7: + Config.Xa ^= 0x1; + if (Config.Xa == 0) + sprintf (Text, _("XA Enabled")); + else sprintf (Text, _("XA Disabled")); + GPU_displayText(Text); + break; + case XK_F8: + GPU_makeSnapshot(); + break; + case XK_F9: + cdOpenCase = -1; + break; + case XK_F10: + cdOpenCase = 0; + break; + case XK_Escape: + // TODO + // the architecture is too broken to actually restart the GUI + // because SysUpdate is called from deep within the actual + // execution of the emulation code + // Fixing this would probably require a complete reworking of + // all functions, so that they return 0 or 1 for success + // that way, execution wouldn't continue + if (CdromId[0] != '\0') + KeyStateSave(10); + ClosePlugins(); + UpdateMenuSlots(); + if (!UseGui) OnFile_Exit(); + StartGui(); + break; + case XK_Return: //0xff0d + if (modalt) //alt-return + //I just made this up: a special sym for fullscreen because the current interface can't handle key mods + //though it can be used in the future as a convention...eg bit 29 for alt, bit 28 for cntl, etc. + GPU_keypressed( (1<<29) | 0xFF0D ); + break; + default: + GPU_keypressed(key); + if (Config.UseNet) NET_keypressed(key); + } +} + +void OnFile_Exit(); + +void SignalExit(int sig) { + ClosePlugins(); + OnFile_Exit(); +} + +void SPUirq(void); + +int NetOpened = 0; + +#define PARSEPATH(dst, src) \ + ptr = src + strlen(src); \ + while (*ptr != '\\' && ptr != src) ptr--; \ + if (ptr != src) { \ + strcpy(dst, ptr+1); \ + } + +int _OpenPlugins() { + int ret; + + signal(SIGINT, SignalExit); + signal(SIGPIPE, SignalExit); + + GPU_clearDynarec(clearDynarec); + + ret = CDR_open(); + if (ret < 0) { SysMessage(_("Error opening CD-ROM plugin!")); return -1; } + ret = SPU_open(); + if (ret < 0) { SysMessage(_("Error opening SPU plugin!")); return -1; } + SPU_registerCallback(SPUirq); + ret = GPU_open(&gpuDisp, "PCSX", NULL); + if (ret < 0) { SysMessage(_("Error opening GPU plugin!")); return -1; } + ret = PAD1_open(&gpuDisp); + if (ret < 0) { SysMessage(_("Error opening Controller 1 plugin!")); return -1; } + ret = PAD2_open(&gpuDisp); + if (ret < 0) { SysMessage(_("Error opening Controller 2 plugin!")); return -1; } + + if (Config.UseNet && NetOpened == 0) { + netInfo info; + char path[MAXPATHLEN]; + char dotdir[MAXPATHLEN]; + + strncpy(dotdir, getenv("HOME"), MAXPATHLEN-100); + strcat(dotdir, "/.pcsx/plugins/"); + + strcpy(info.EmuName, "PCSX " PACKAGE_VERSION); + strncpy(info.CdromID, CdromId, 9); + strncpy(info.CdromLabel, CdromLabel, 9); + info.psxMem = psxM; + info.GPU_showScreenPic = GPU_showScreenPic; + info.GPU_displayText = GPU_displayText; + info.GPU_showScreenPic = GPU_showScreenPic; + info.PAD_setSensitive = PAD1_setSensitive; + sprintf(path, "%s%s", Config.BiosDir, Config.Bios); + strcpy(info.BIOSpath, path); + strcpy(info.MCD1path, Config.Mcd1); + strcpy(info.MCD2path, Config.Mcd2); + sprintf(path, "%s%s", dotdir, Config.Gpu); + strcpy(info.GPUpath, path); + sprintf(path, "%s%s", dotdir, Config.Spu); + strcpy(info.SPUpath, path); + sprintf(path, "%s%s", dotdir, Config.Cdr); + strcpy(info.CDRpath, path); + NET_setInfo(&info); + + ret = NET_open(&gpuDisp); + if (ret < 0) { + if (ret == -2) { + // -2 is returned when something in the info + // changed and needs to be synced + char *ptr; + + PARSEPATH(Config.Bios, info.BIOSpath); + PARSEPATH(Config.Gpu, info.GPUpath); + PARSEPATH(Config.Spu, info.SPUpath); + PARSEPATH(Config.Cdr, info.CDRpath); + + strcpy(Config.Mcd1, info.MCD1path); + strcpy(Config.Mcd2, info.MCD2path); + return -2; + } else { + Config.UseNet = 0; + } + } else { + if (NET_queryPlayer() == 1) { + if (SendPcsxInfo() == -1) Config.UseNet = 0; + } else { + if (RecvPcsxInfo() == -1) Config.UseNet = 0; + } + } + NetOpened = 1; + } else if (Config.UseNet) { + NET_resume(); + } + + return 0; +} + +int OpenPlugins() { + int ret; + + while ((ret = _OpenPlugins()) == -2) { + ReleasePlugins(); + LoadMcds(Config.Mcd1, Config.Mcd2); + if (LoadPlugins() == -1) return -1; + } + return ret; +} + +void ClosePlugins() { + int ret; + + signal(SIGINT, SIG_DFL); + signal(SIGPIPE, SIG_DFL); + ret = CDR_close(); + if (ret < 0) { SysMessage(_("Error closing CD-ROM plugin!")); return; } + ret = SPU_close(); + if (ret < 0) { SysMessage(_("Error closing SPU plugin!")); return; } + ret = PAD1_close(); + if (ret < 0) { SysMessage(_("Error closing Controller 1 Plugin!")); return; } + ret = PAD2_close(); + if (ret < 0) { SysMessage(_("Error closing Controller 2 plugin!")); return; } + ret = GPU_close(); + if (ret < 0) { SysMessage(_("Error closing GPU plugin!")); return; } + + if (Config.UseNet) { + NET_pause(); + } +} diff --git a/gui/hdebug.c b/gui/hdebug.c new file mode 100644 index 00000000..996ba2dd --- /dev/null +++ b/gui/hdebug.c @@ -0,0 +1,386 @@ +/*************************************************************************** + * Debugger-Interface for PCSX-DF * + * * + * Copyright (C) 2008 Stefan Sikora * + * hoshy['at']schrauberstube['dot']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., * + * 51 Franklin Steet, Fifth Floor, Boston, MA 02111-1307 USA. * + ***************************************************************************/ + +// TODO: +// - setting register values +// - step over instruction +// - Dumping/Loading of memory +// - Better gui-integration + +#include <gtk/gtk.h> +#include <string.h> +#include <pthread.h> +#include "r3000a.h" +#include "hdebug.h" + +// Global variables +char buffer[1024*2]; +GtkTextBuffer *hdb_listing = 0; +GtkTextBuffer *hdb_memdump = 0; +GtkTextBuffer *hdb_logging = 0; +GtkTextBuffer *hdb_registers = 0; +GtkWidget *hdb_command = 0; +GtkWidget *hdb_pausebutton = 0; + +u32 hdb_memptr = 0x80000000; + +int ready = 0; + +// little helper for syncing with emulator +void waitforpause() { + int actualPC; + + actualPC = psxRegs.pc; + while(actualPC != psxRegs.pc) actualPC = psxRegs.pc; + + gtk_button_set_label(GTK_BUTTON(hdb_pausebutton), "RESUME"); +} + + +// core-debugger-functions +void hdb_update_registers() { + snprintf(buffer, 1024, + "v0 0x%08x s0 0x%08x t0 0x%08x\n" \ + "v1 0x%08x s1 0x%08x t1 0x%08x\n" \ + " s2 0x%08x t2 0x%08x\n" \ + "a0 0x%08x s3 0x%08x t3 0x%08x\n" \ + "a1 0x%08x s4 0x%08x t4 0x%08x\n" \ + "a2 0x%08x s5 0x%08x t5 0x%08x\n" \ + "a3 0x%08x s6 0x%08x t6 0x%08x\n" \ + " s7 0x%08x t7 0x%08x\n" \ + "k0 0x%08x s8 0x%08x t8 0x%08x\n" \ + "k1 0x%08x t9 0x%08x\n\n" \ + "gp 0x%08x at 0x%08x ra 0x%08x\n" \ + "sp 0x%08x pc 0x%08x\n", + psxRegs.GPR.r[2], psxRegs.GPR.r[16], psxRegs.GPR.r[8], + psxRegs.GPR.r[3], psxRegs.GPR.r[17], psxRegs.GPR.r[9], + psxRegs.GPR.r[18], psxRegs.GPR.r[10], + psxRegs.GPR.r[4], psxRegs.GPR.r[19], psxRegs.GPR.r[11], + psxRegs.GPR.r[5], psxRegs.GPR.r[20], psxRegs.GPR.r[12], + psxRegs.GPR.r[6], psxRegs.GPR.r[21], psxRegs.GPR.r[13], + psxRegs.GPR.r[7], psxRegs.GPR.r[22], psxRegs.GPR.r[14], + psxRegs.GPR.r[23], psxRegs.GPR.r[15], + psxRegs.GPR.r[26], psxRegs.GPR.r[30], psxRegs.GPR.r[24], + psxRegs.GPR.r[27], psxRegs.GPR.r[25], + psxRegs.GPR.r[28], psxRegs.GPR.r[1], psxRegs.GPR.r[31], + psxRegs.GPR.r[29], psxRegs.pc); + + gtk_text_buffer_set_text(hdb_registers, buffer, strlen(buffer)); +} + + +void hdb_update_listing(u32 opc) { + int t; + u32 *cptr; + u32 ocod; + char tbuf[100]; + char *bptr; + + buffer[0] = '\0'; + bptr = (char *)&buffer; + opc -= 15*4; + + for(t=0; t<=30; t++) { + cptr = (u32 *)PSXM(opc); + if (t == 15) strcat(bptr, ">"); else strcat(bptr, " "); + ocod = cptr == NULL ? 0 : SWAP32(*cptr); + sprintf(tbuf, "%s\n", disR3000AF(ocod, opc)); + strcat(bptr, tbuf); + opc+=4; + } + strcat(bptr,"\0"); + gtk_text_buffer_set_text(hdb_listing, buffer, strlen(buffer)); +} + + +void hdb_update_memdump() { + int r, c; + char tbuf[100]; + char *bptr; + + buffer[0] = '\0'; + bptr = (char*)&buffer; + + for(r=0; r<8; r++) { + sprintf(tbuf, "0x%08x", hdb_memptr+r*16); + strcat(bptr, tbuf); + for(c=0; c<16; c++) { + if(c==8) sprintf(tbuf, "-%02x", PSXMu8(hdb_memptr+r*16+c)); + else sprintf(tbuf, " %02x", PSXMu8(hdb_memptr+r*16+c)); + strcat(bptr, tbuf); + } + strcat(bptr, "\n"); + } + strcat(bptr, "\0"); + gtk_text_buffer_set_text(hdb_memdump, buffer, strlen(buffer)); +} + + +// Callback-functions +void on_hdb_mainwindow_destroy(GtkWidget *widget, gpointer data) { + hdb_pause = 0; // Run CPU + gtk_widget_destroy((GtkWidget*)widget); + gtk_main_quit(); + while (gtk_events_pending()) gtk_main_iteration(); + pthread_exit(NULL); + ready = 0; +} + + +void on_hdb_pausebutton_clicked(GtkWidget *widget, gpointer data) { + if(hdb_pause == 0) { + hdb_pause = 1; + waitforpause(); + + hdb_update_registers(); + hdb_update_listing(psxRegs.pc); + hdb_update_memdump(); + } + else { + hdb_pause = 0; + gtk_button_set_label(GTK_BUTTON(widget), "PAUSE"); + } +} + +void hdb_auto_pause () { + if(hdb_pause == 0) { + hdb_pause = 1; + //waitforpause(); + + hdb_update_registers(); + hdb_update_listing(psxRegs.pc); + hdb_update_memdump(); + } + else { + hdb_pause = 0; + //gtk_button_set_label(GTK_BUTTON(widget), "PAUSE"); + } +} + +void on_hdb_tracebutton_clicked(GtkWidget *widget, gpointer data) { + // Let emulator do one step and refresh debugger when in pause-mode! + if(hdb_pause == 1) { + hdb_pause = 2; + + waitforpause(); + hdb_update_registers(); + hdb_update_listing(psxRegs.pc); + } +} + + +void on_hdb_dumpbutton_clicked(GtkWidget *widget, gpointer data) { + // Save psx-memory for external analysis/modification + // TODO +} + + +void on_hdb_loadbutton_clicked(GtkWidget *widget, gpointer data) { + // Load psx-memory + // TODO +} + + +void on_hdb_cmdbutton_clicked(GtkWidget *widget, gpointer data) { + const gchar *cmdentry; + char *actcmd; + char *cmdsub; + u32 tval; + u8 tval8; + int t; + + if ((cmdentry = gtk_entry_get_text(GTK_ENTRY(hdb_command))) != NULL) { + actcmd = strdup(cmdentry); + /* split and interpret debugger's commandline + t - trace one instruction + c - continue execution + d addr - disassemble at addr + m addr - show memory at addr (hex) + s addr op1 op2 op3 ... - set memory + b addr - break on addr + bc - clear breakpoint + r reg val - set register to a specific value (TODO) + */ + + // get the command + if ((cmdsub = strtok(actcmd, " ")) != NULL) { + if(!strcmp(cmdsub, "t")) { + hdb_pause = 2; + usleep(100); // wait 1/10th of a second + hdb_update_registers(); + hdb_update_listing(psxRegs.pc); + } + + if(!strcmp(cmdsub, "c")) hdb_pause = 0; + + if(!strcmp(cmdsub, "bc")) { + hdb_break = 0; + } + + if(!strcmp(cmdsub, "b")) { + cmdsub = strtok(NULL, " "); + sscanf(cmdsub, "%x", (u32*)&tval); + hdb_break = tval; + hdb_pause = 3; + + waitforpause(); + hdb_update_memdump(); + hdb_update_registers(); + hdb_update_listing(psxRegs.pc); + } + + if(!strcmp(cmdsub, "d")) { + cmdsub = strtok(NULL, " "); + sscanf(cmdsub, "%x", (u32*)&tval); + hdb_update_listing(tval); + } + + if(!strcmp(cmdsub, "m")) { + cmdsub = strtok(NULL, " "); + sscanf(cmdsub, "%x", (u32*)&tval); + hdb_memptr = tval; + hdb_update_memdump(); + } + + if(!strcmp(cmdsub, "s")) { + cmdsub = strtok(NULL, " "); + sscanf(cmdsub, "%x", (u32 *)&tval); + t = 0; + while((cmdsub = strtok(NULL, " "))) { + u8 v; + sscanf(cmdsub, "%x", (u8 *)&tval8); + PSXMu8(tval+t) = tval8; + t++; + } + hdb_update_memdump(); + hdb_update_listing(psxRegs.pc); + } + + free(actcmd); + } + else { + hdb_update_memdump(); + hdb_update_registers(); + hdb_update_listing(psxRegs.pc); + } + } +} + + +// init debugger and gui +void hdb_init() { + if (ready) return; + GtkWidget *hdb_mainwindow; + GtkWidget *hdb_tracebutton; + GtkWidget *hdb_dumpbutton; + GtkWidget *hdb_loadbutton; + GtkWidget *hdb_cmdbutton; + GtkWidget *hdb_listbox, *hdb_membox, *hdb_logbox, *hdb_regbox; + GtkWidget *hbox, *vbox, *hbox2, *hbox3; + PangoFontDescription *font_desc; + + hdb_pause = 0; // Run CPU + + gtk_init(NULL, NULL); + font_desc = pango_font_description_from_string ("Fixed 8"); + + // create and show the widgets + hdb_mainwindow = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_title((GtkWindow*)hdb_mainwindow, "PCSX-DF Debugger"); + gtk_window_set_position((GtkWindow*)hdb_mainwindow, GTK_WIN_POS_CENTER); + + hbox = gtk_hbox_new(FALSE, 2); + vbox = gtk_vbox_new(FALSE, 2); + hbox2 = gtk_hbox_new(FALSE, 2); + hbox3 = gtk_hbox_new(FALSE, 2); + + hdb_registers = gtk_text_buffer_new(NULL); + hdb_regbox = gtk_text_view_new_with_buffer(hdb_registers); + gtk_widget_modify_font(hdb_regbox, font_desc); + + hdb_listing = gtk_text_buffer_new(NULL); + hdb_listbox = gtk_text_view_new_with_buffer(hdb_listing); + gtk_widget_modify_font(hdb_listbox, font_desc); + + hdb_memdump = gtk_text_buffer_new(NULL); + hdb_membox = gtk_text_view_new_with_buffer(hdb_memdump); + gtk_widget_modify_font(hdb_membox, font_desc); + + hdb_logging = gtk_text_buffer_new(NULL); + hdb_logbox = gtk_text_view_new_with_buffer(hdb_logging); + gtk_widget_modify_font(hdb_logbox, font_desc); + + hdb_command = gtk_entry_new(); + hdb_cmdbutton = gtk_button_new_with_label(">"); + + hdb_pausebutton = gtk_button_new_with_label("PAUSE"); + hdb_tracebutton = gtk_button_new_with_label("TRACE"); + hdb_dumpbutton = gtk_button_new_with_label("DUMP"); + hdb_loadbutton = gtk_button_new_with_label("LOAD"); + + gtk_container_add(GTK_CONTAINER(hdb_mainwindow), hbox); + gtk_container_add(GTK_CONTAINER(hbox), vbox); + gtk_container_add(GTK_CONTAINER(hbox), hdb_listbox); + + gtk_container_add(GTK_CONTAINER(vbox), hdb_regbox); + gtk_container_add(GTK_CONTAINER(vbox), hdb_membox); + gtk_container_add(GTK_CONTAINER(vbox), hdb_logbox); + + gtk_container_add(GTK_CONTAINER(hbox3), hdb_command); + gtk_container_add(GTK_CONTAINER(hbox3), hdb_cmdbutton); + + gtk_container_add(GTK_CONTAINER(vbox), hbox3); + gtk_container_add(GTK_CONTAINER(vbox), hbox2); + gtk_container_add(GTK_CONTAINER(hbox2), hdb_pausebutton); + gtk_container_add(GTK_CONTAINER(hbox2), hdb_tracebutton); + gtk_container_add(GTK_CONTAINER(hbox2), hdb_dumpbutton); + gtk_container_add(GTK_CONTAINER(hbox2), hdb_loadbutton); + + pango_font_description_free (font_desc); + + gtk_widget_show_all(hdb_mainwindow); + + // connect signals to callback functions + g_signal_connect(G_OBJECT(hdb_mainwindow), "destroy", G_CALLBACK(on_hdb_mainwindow_destroy), NULL); + g_signal_connect(G_OBJECT(hdb_pausebutton), "clicked", G_CALLBACK(on_hdb_pausebutton_clicked), NULL); + g_signal_connect(G_OBJECT(hdb_tracebutton), "clicked", G_CALLBACK(on_hdb_tracebutton_clicked), NULL); + g_signal_connect(G_OBJECT(hdb_dumpbutton), "clicked", G_CALLBACK(on_hdb_dumpbutton_clicked), NULL); + g_signal_connect(G_OBJECT(hdb_loadbutton), "clicked", G_CALLBACK(on_hdb_loadbutton_clicked), NULL); + g_signal_connect(G_OBJECT(hdb_cmdbutton), "clicked", G_CALLBACK(on_hdb_cmdbutton_clicked), NULL); + + hdb_update_registers(); + hdb_update_listing(psxRegs.pc); + hdb_update_memdump(); + + ready = 1; + gtk_main(); + pthread_exit(NULL); +} + + +// Start debugger in own thread +void hdb_start() { + pthread_t tid; + + pthread_create(&tid, NULL, (void *)hdb_init, NULL); +// hdb_init(); +} diff --git a/gui/hdebug.h b/gui/hdebug.h new file mode 100644 index 00000000..4e53f69a --- /dev/null +++ b/gui/hdebug.h @@ -0,0 +1,33 @@ +/*************************************************************************** + * Debugger-Interface for PCSX-DF * + * * + * Copyright (C) 2008 Stefan Sikora * + * hoshy['at']schrauberstube['dot']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., * + * 51 Franklin Steet, Fifth Floor, Boston, MA 02111-1307 USA. * + ***************************************************************************/ + +#ifndef __HDBUG_H__ +#define __HDBUG_H__ + +// 0 = run cpu, 1 = pause cpu, 2 = trace cpu +int hdb_pause; +unsigned long hdb_break; + +void hdb_start(); +void hdb_auto_pause (); + +#endif /* __HDBUG_H__ */ diff --git a/gui/nopic.h b/gui/nopic.h new file mode 100644 index 00000000..553cede5 --- /dev/null +++ b/gui/nopic.h @@ -0,0 +1,1345 @@ +//////////////////////////////////////////////////////////////////////// +// following code taken from the gpuPeopsSoft +//////////////////////////////////////////////////////////////////////// + +// font 0-9, 24x20 pixels, 1 byte = 4 dots +// 00 = black +// 01 = white +// 10 = red +// 11 = transparent + +unsigned char cFont[10][120]= +{ +// 0 +{0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, + 0x80,0x00,0x00,0x00,0x00,0x00, + 0x80,0x00,0x00,0x00,0x00,0x00, + 0x80,0x00,0x00,0x00,0x00,0x00, + 0x80,0x00,0x00,0x00,0x00,0x00, + 0x80,0x00,0x05,0x54,0x00,0x00, + 0x80,0x00,0x14,0x05,0x00,0x00, + 0x80,0x00,0x14,0x05,0x00,0x00, + 0x80,0x00,0x14,0x05,0x00,0x00, + 0x80,0x00,0x14,0x05,0x00,0x00, + 0x80,0x00,0x14,0x05,0x00,0x00, + 0x80,0x00,0x14,0x05,0x00,0x00, + 0x80,0x00,0x14,0x05,0x00,0x00, + 0x80,0x00,0x14,0x05,0x00,0x00, + 0x80,0x00,0x05,0x54,0x00,0x00, + 0x80,0x00,0x00,0x00,0x00,0x00, + 0x80,0x00,0x00,0x00,0x00,0x00, + 0x80,0x00,0x00,0x00,0x00,0x00, + 0x80,0x00,0x00,0x00,0x00,0x00, + 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa +}, +// 1 +{0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, + 0x80,0x00,0x00,0x00,0x00,0x00, + 0x80,0x00,0x00,0x00,0x00,0x00, + 0x80,0x00,0x00,0x00,0x00,0x00, + 0x80,0x00,0x00,0x00,0x00,0x00, + 0x80,0x00,0x00,0x50,0x00,0x00, + 0x80,0x00,0x05,0x50,0x00,0x00, + 0x80,0x00,0x00,0x50,0x00,0x00, + 0x80,0x00,0x00,0x50,0x00,0x00, + 0x80,0x00,0x00,0x50,0x00,0x00, + 0x80,0x00,0x00,0x50,0x00,0x00, + 0x80,0x00,0x00,0x50,0x00,0x00, + 0x80,0x00,0x00,0x50,0x00,0x00, + 0x80,0x00,0x00,0x50,0x00,0x00, + 0x80,0x00,0x05,0x55,0x00,0x00, + 0x80,0x00,0x00,0x00,0x00,0x00, + 0x80,0x00,0x00,0x00,0x00,0x00, + 0x80,0x00,0x00,0x00,0x00,0x00, + 0x80,0x00,0x00,0x00,0x00,0x00, + 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa +}, +// 2 +{0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, + 0x80,0x00,0x00,0x00,0x00,0x00, + 0x80,0x00,0x00,0x00,0x00,0x00, + 0x80,0x00,0x00,0x00,0x00,0x00, + 0x80,0x00,0x00,0x00,0x00,0x00, + 0x80,0x00,0x05,0x54,0x00,0x00, + 0x80,0x00,0x14,0x05,0x00,0x00, + 0x80,0x00,0x00,0x05,0x00,0x00, + 0x80,0x00,0x00,0x05,0x00,0x00, + 0x80,0x00,0x00,0x14,0x00,0x00, + 0x80,0x00,0x00,0x50,0x00,0x00, + 0x80,0x00,0x01,0x40,0x00,0x00, + 0x80,0x00,0x05,0x00,0x00,0x00, + 0x80,0x00,0x14,0x00,0x00,0x00, + 0x80,0x00,0x15,0x55,0x00,0x00, + 0x80,0x00,0x00,0x00,0x00,0x00, + 0x80,0x00,0x00,0x00,0x00,0x00, + 0x80,0x00,0x00,0x00,0x00,0x00, + 0x80,0x00,0x00,0x00,0x00,0x00, + 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa +}, +// 3 +{0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, + 0x80,0x00,0x00,0x00,0x00,0x00, + 0x80,0x00,0x00,0x00,0x00,0x00, + 0x80,0x00,0x00,0x00,0x00,0x00, + 0x80,0x00,0x00,0x00,0x00,0x00, + 0x80,0x00,0x05,0x54,0x00,0x00, + 0x80,0x00,0x14,0x05,0x00,0x00, + 0x80,0x00,0x00,0x05,0x00,0x00, + 0x80,0x00,0x00,0x05,0x00,0x00, + 0x80,0x00,0x01,0x54,0x00,0x00, + 0x80,0x00,0x00,0x05,0x00,0x00, + 0x80,0x00,0x00,0x05,0x00,0x00, + 0x80,0x00,0x00,0x05,0x00,0x00, + 0x80,0x00,0x14,0x05,0x00,0x00, + 0x80,0x00,0x05,0x54,0x00,0x00, + 0x80,0x00,0x00,0x00,0x00,0x00, + 0x80,0x00,0x00,0x00,0x00,0x00, + 0x80,0x00,0x00,0x00,0x00,0x00, + 0x80,0x00,0x00,0x00,0x00,0x00, + 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa +}, +// 4 +{0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, + 0x80,0x00,0x00,0x00,0x00,0x00, + 0x80,0x00,0x00,0x00,0x00,0x00, + 0x80,0x00,0x00,0x00,0x00,0x00, + 0x80,0x00,0x00,0x00,0x00,0x00, + 0x80,0x00,0x00,0x14,0x00,0x00, + 0x80,0x00,0x00,0x54,0x00,0x00, + 0x80,0x00,0x01,0x54,0x00,0x00, + 0x80,0x00,0x01,0x54,0x00,0x00, + 0x80,0x00,0x05,0x14,0x00,0x00, + 0x80,0x00,0x14,0x14,0x00,0x00, + 0x80,0x00,0x15,0x55,0x00,0x00, + 0x80,0x00,0x00,0x14,0x00,0x00, + 0x80,0x00,0x00,0x14,0x00,0x00, + 0x80,0x00,0x00,0x55,0x00,0x00, + 0x80,0x00,0x00,0x00,0x00,0x00, + 0x80,0x00,0x00,0x00,0x00,0x00, + 0x80,0x00,0x00,0x00,0x00,0x00, + 0x80,0x00,0x00,0x00,0x00,0x00, + 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa +}, +// 5 +{0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, + 0x80,0x00,0x00,0x00,0x00,0x00, + 0x80,0x00,0x00,0x00,0x00,0x00, + 0x80,0x00,0x00,0x00,0x00,0x00, + 0x80,0x00,0x00,0x00,0x00,0x00, + 0x80,0x00,0x15,0x55,0x00,0x00, + 0x80,0x00,0x14,0x00,0x00,0x00, + 0x80,0x00,0x14,0x00,0x00,0x00, + 0x80,0x00,0x14,0x00,0x00,0x00, + 0x80,0x00,0x15,0x54,0x00,0x00, + 0x80,0x00,0x00,0x05,0x00,0x00, + 0x80,0x00,0x00,0x05,0x00,0x00, + 0x80,0x00,0x00,0x05,0x00,0x00, + 0x80,0x00,0x14,0x05,0x00,0x00, + 0x80,0x00,0x05,0x54,0x00,0x00, + 0x80,0x00,0x00,0x00,0x00,0x00, + 0x80,0x00,0x00,0x00,0x00,0x00, + 0x80,0x00,0x00,0x00,0x00,0x00, + 0x80,0x00,0x00,0x00,0x00,0x00, + 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa +}, +// 6 +{0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, + 0x80,0x00,0x00,0x00,0x00,0x00, + 0x80,0x00,0x00,0x00,0x00,0x00, + 0x80,0x00,0x00,0x00,0x00,0x00, + 0x80,0x00,0x00,0x00,0x00,0x00, + 0x80,0x00,0x01,0x54,0x00,0x00, + 0x80,0x00,0x05,0x00,0x00,0x00, + 0x80,0x00,0x14,0x00,0x00,0x00, + 0x80,0x00,0x14,0x00,0x00,0x00, + 0x80,0x00,0x15,0x54,0x00,0x00, + 0x80,0x00,0x15,0x05,0x00,0x00, + 0x80,0x00,0x14,0x05,0x00,0x00, + 0x80,0x00,0x14,0x05,0x00,0x00, + 0x80,0x00,0x14,0x05,0x00,0x00, + 0x80,0x00,0x05,0x54,0x00,0x00, + 0x80,0x00,0x00,0x00,0x00,0x00, + 0x80,0x00,0x00,0x00,0x00,0x00, + 0x80,0x00,0x00,0x00,0x00,0x00, + 0x80,0x00,0x00,0x00,0x00,0x00, + 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa +}, +// 7 +{0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, + 0x80,0x00,0x00,0x00,0x00,0x00, + 0x80,0x00,0x00,0x00,0x00,0x00, + 0x80,0x00,0x00,0x00,0x00,0x00, + 0x80,0x00,0x00,0x00,0x00,0x00, + 0x80,0x00,0x15,0x55,0x00,0x00, + 0x80,0x00,0x14,0x05,0x00,0x00, + 0x80,0x00,0x00,0x14,0x00,0x00, + 0x80,0x00,0x00,0x14,0x00,0x00, + 0x80,0x00,0x00,0x50,0x00,0x00, + 0x80,0x00,0x00,0x50,0x00,0x00, + 0x80,0x00,0x01,0x40,0x00,0x00, + 0x80,0x00,0x01,0x40,0x00,0x00, + 0x80,0x00,0x05,0x00,0x00,0x00, + 0x80,0x00,0x05,0x00,0x00,0x00, + 0x80,0x00,0x00,0x00,0x00,0x00, + 0x80,0x00,0x00,0x00,0x00,0x00, + 0x80,0x00,0x00,0x00,0x00,0x00, + 0x80,0x00,0x00,0x00,0x00,0x00, + 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa +}, +// 8 +{0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, + 0x80,0x00,0x00,0x00,0x00,0x00, + 0x80,0x00,0x00,0x00,0x00,0x00, + 0x80,0x00,0x00,0x00,0x00,0x00, + 0x80,0x00,0x00,0x00,0x00,0x00, + 0x80,0x00,0x05,0x54,0x00,0x00, + 0x80,0x00,0x14,0x05,0x00,0x00, + 0x80,0x00,0x14,0x05,0x00,0x00, + 0x80,0x00,0x14,0x05,0x00,0x00, + 0x80,0x00,0x05,0x54,0x00,0x00, + 0x80,0x00,0x14,0x05,0x00,0x00, + 0x80,0x00,0x14,0x05,0x00,0x00, + 0x80,0x00,0x14,0x05,0x00,0x00, + 0x80,0x00,0x14,0x05,0x00,0x00, + 0x80,0x00,0x05,0x54,0x00,0x00, + 0x80,0x00,0x00,0x00,0x00,0x00, + 0x80,0x00,0x00,0x00,0x00,0x00, + 0x80,0x00,0x00,0x00,0x00,0x00, + 0x80,0x00,0x00,0x00,0x00,0x00, + 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa +}, +// 9 +{0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, + 0x80,0x00,0x00,0x00,0x00,0x00, + 0x80,0x00,0x00,0x00,0x00,0x00, + 0x80,0x00,0x00,0x00,0x00,0x00, + 0x80,0x00,0x00,0x00,0x00,0x00, + 0x80,0x00,0x05,0x54,0x00,0x00, + 0x80,0x00,0x14,0x05,0x00,0x00, + 0x80,0x00,0x14,0x05,0x00,0x00, + 0x80,0x00,0x14,0x05,0x00,0x00, + 0x80,0x00,0x14,0x15,0x00,0x00, + 0x80,0x00,0x05,0x55,0x00,0x00, + 0x80,0x00,0x00,0x05,0x00,0x00, + 0x80,0x00,0x00,0x05,0x00,0x00, + 0x80,0x00,0x00,0x14,0x00,0x00, + 0x80,0x00,0x05,0x50,0x00,0x00, + 0x80,0x00,0x00,0x00,0x00,0x00, + 0x80,0x00,0x00,0x00,0x00,0x00, + 0x80,0x00,0x00,0x00,0x00,0x00, + 0x80,0x00,0x00,0x00,0x00,0x00, + 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa +} +}; + +//////////////////////////////////////////////////////////////////////// + +void PaintPicDot(unsigned char * p,unsigned char c) +{ + + if(c==0) {*p++=0x00;*p++=0x00;*p=0x00;return;} // black + if(c==1) {*p++=0xff;*p++=0xff;*p=0xff;return;} // white + if(c==2) {*p++=0x00;*p++=0x00;*p=0xff;return;} // red + // transparent +} + + ///////////////////////////////////////////////////////////////////// + // generic number/border painter + +void DrawNumBorPic(unsigned char *pMem, int lSelectedSlot) +{ + unsigned char *pf; + int x,y; + int c,v; + + pf=pMem+(103*3); // offset to number rect + + for(y=0;y<20;y++) // loop the number rect pixel + { + for(x=0;x<6;x++) + { + c=cFont[lSelectedSlot][x+y*6]; // get 4 char dot infos at once (number depends on selected slot) + v=(c&0xc0)>>6; + PaintPicDot(pf,(unsigned char)v);pf+=3; // paint the dots into the rect + v=(c&0x30)>>4; + PaintPicDot(pf,(unsigned char)v);pf+=3; + v=(c&0x0c)>>2; + PaintPicDot(pf,(unsigned char)v);pf+=3; + v=c&0x03; + PaintPicDot(pf,(unsigned char)v);pf+=3; + } + pf+=104*3; // next rect y line + } + + pf=pMem; // ptr to first pos in 128x96 pic + for(x=0;x<128;x++) // loop top/bottom line + { + *(pf+(95*128*3))=0x00;*pf++=0x00; + *(pf+(95*128*3))=0x00;*pf++=0x00; // paint it red + *(pf+(95*128*3))=0xff;*pf++=0xff; + } + pf=pMem; // ptr to first pos + for(y=0;y<96;y++) // loop left/right line + { + *(pf+(127*3))=0x00;*pf++=0x00; + *(pf+(127*3))=0x00;*pf++=0x00; // paint it red + *(pf+(127*3))=0xff;*pf++=0xff; + pf+=127*3; // offset to next line + } +} + +//////////////////////////////////////////////////////////////////////// + + +/* GIMP RGB C-Source image dump (NoPic.h) */ + +static const struct { + unsigned int width; + unsigned int height; + unsigned int bytes_per_pixel; /* 3:RGB, 4:RGBA */ + unsigned char pixel_data[128 * 96 * 3 + 1]; +} NoPic_Image = { + 128, 96, 3, + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0U\0\0U\0\0U\0\0U\0\0""8\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\34\0\0U\0\0U\0\0U\0\0" + "U\0\0""8\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0U\0\0\376\0\0\376\0\0\341\0\0\34\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\376\0" + "\0\376\0\0U\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0""8" + "\0\0\305\0\0\376\0\0\376\0\0\376\0\0\376\0\0\376\0\0\376\0\0\376\0\0\251" + "\0\0""8\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0U\0\0\376\0\0\376\0\0\376\0" + "\0\376\0\0\376\0\0\376\0\0\376\0\0\376\0\0\376\0\0\376\0\0\376\0\0\251\0" + "\0q\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\251\0\0\376\0\0\376" + "\0\0U\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0q\0\0\305" + "\0\0\376\0\0\376\0\0\376\0\0\376\0\0\376\0\0\376\0\0\341\0\0\214\0\0\34\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0U\0\0\376\0\0\376\0\0\376\0\0\251\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\376\0\0\376\0\0U\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\34\0\0\305\0\0\376\0\0\376\0\0\376\0\0\376\0" + "\0\376\0\0\376\0\0\376\0\0\376\0\0\376\0\0\376\0\0\376\0\0\305\0\0\34\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0U\0\0\376\0\0\376\0\0\376\0\0\376\0\0\376\0\0\376" + "\0\0\376\0\0\376\0\0\376\0\0\376\0\0\376\0\0\376\0\0\376\0\0\341\0\0""8\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\251\0\0\376\0\0\376\0\0U\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\34\0\0\341\0\0\376\0\0\376\0\0\376\0" + "\0\376\0\0\376\0\0\376\0\0\376\0\0\376\0\0\376\0\0\376\0\0\341\0\0U\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0U\0" + "\0\376\0\0\376\0\0\376\0\0\376\0\0""8\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\376\0\0\376\0\0U\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\34\0\0\341\0\0\376\0\0\376\0\0\376\0\0\305\0\0q\0\0U\0\0U\0" + "\0U\0\0\214\0\0\341\0\0\376\0\0\376\0\0\376\0\0\341\0\0\34\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0U\0\0\376\0\0\376\0\0\341\0\0\251\0\0\251\0\0\251\0\0\251\0\0\251" + "\0\0\251\0\0\251\0\0\341\0\0\376\0\0\376\0\0\376\0\0\341\0\0\34\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\251\0\0\376\0\0\376\0\0U\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\34\0\0\341\0\0\376\0\0\376\0\0\376\0\0\251\0\0U\0\0U\0\0" + "U\0\0q\0\0\251\0\0\376\0\0\376\0\0\376\0\0\376\0\0\34\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0U\0\0\376\0\0\376\0\0\376" + "\0\0\376\0\0\341\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\376\0\0\376\0\0U\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\34\0\0\341\0" + "\0\376\0\0\376\0\0\341\0\0""8\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0U\0\0\341\0\0\376\0\0\376\0\0\341\0\0\34\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0U\0\0\376\0\0\376" + "\0\0\251\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0U\0\0\341\0\0" + "\376\0\0\376\0\0\214\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\251\0\0\376\0\0\376" + "\0\0U\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\34\0\0\341\0\0\376\0\0\376\0\0\305" + "\0\0\34\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\34\0\0\341\0\0\376\0\0\376" + "\0\0\305\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0U\0\0\376\0\0\376\0\0\341\0\0\376\0\0\376\0\0\214\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\376\0\0\376\0\0U\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\305\0\0\376\0\0\376\0\0\341\0\0\34\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\34\0\0\341\0\0\376\0\0\376\0\0\251" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0U\0\0\376\0\0\376\0\0\251\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0U\0\0\376\0\0\376\0\0\376\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\251\0\0\376\0\0\376\0\0U\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\214" + "\0\0\376\0\0\376\0\0\341\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\34\0\0\341\0\0\376\0\0\376\0\0U\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0U\0\0\376\0\0\376\0\0U\0\0\376\0\0\376" + "\0\0\376\0\0""8\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\376\0" + "\0\376\0\0U\0\0\0\0\0\0\0\0\0\0\0\0\0\0""8\0\0\376\0\0\376\0\0\376\0\0\34" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\34" + "\0\0\376\0\0\376\0\0\376\0\0""8\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0U\0\0\376\0\0\376\0\0\251\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\341\0\0\376\0\0" + "\376\0\0U\0\0\0\0\0\0\0\0\0\0\0\0\0\0\251\0\0\376\0\0\376\0\0U\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\34\0\0\376\0\0\376\0\0\376\0\0\34\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0q\0\0\376\0\0\376\0\0\251\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0U\0\0\376\0\0" + "\376\0\0\0\0\0\251\0\0\376\0\0\376\0\0\341\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\376\0\0\376\0\0U\0\0\0\0\0\0\0\0\0\0\0\0\0\0\305\0" + "\0\376\0\0\376\0\0q\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\214\0\0\376\0\0\376\0\0\251\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0U\0\0\376" + "\0\0\376\0\0\251\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\251\0\0\376\0\0\376\0\0U\0\0\0\0\0\0\0\0\0\0\0\0\0\0\251\0\0\376" + "\0\0\376\0\0U\0\0\0\0\0\0\0\0\0\0\0\0\0\0q\0\0\376\0\0\376\0\0\251\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\34\0\0\376" + "\0\0\376\0\0\376\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0U\0\0\376\0\0\376\0\0\0\0\0\34\0\0\341\0\0\376\0\0\376\0\0\214\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\376\0\0\376\0\0U\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\376\0\0\376\0\0\376\0\0\34\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\34\0\0\376\0\0\376\0" + "\0\376\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0U\0\0\376\0\0\376\0\0\251\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\251\0\0\376\0\0\376\0\0U\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\251\0\0\376\0\0\376\0\0U\0\0\0\0\0\0\0\0\0\0\0\0\0\0\305\0\0" + "\376\0\0\376\0\0U\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0U\0\0U\0\0U\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0U\0\0\376\0\0\376\0\0\0\0\0\0\0\0U\0\0\376\0\0\376" + "\0\0\376\0\0\34\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\376\0\0\376\0\0U" + "\0\0\0\0\0\0\0\0\0\0\0U\0\0\376\0\0\376\0\0\305\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\305" + "\0\0\376\0\0\376\0\0U\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0U\0\0\376\0\0\376\0\0\251\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\34\0\0\376\0\0\376\0\0\376\0\0""8\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\251\0\0\376\0\0\376\0\0U\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\376\0\0\376\0\0\376\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0U\0\0\376\0\0\376\0\0\0\0\0\0\0\0" + "\0\0\0\305\0\0\376\0\0\376\0\0\305\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\376\0\0\376\0\0U\0\0\0\0\0\0\0\0\0\0\0\214\0\0\376\0\0\376\0\0\214\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\214\0\0\376\0\0\376\0\0q\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0U\0\0\376\0\0\376\0\0\251\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\251\0\0\376\0\0" + "\376\0\0\341\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\251\0\0\376\0\0\376\0\0U\0" + "\0\0\0\0\0\0\0\0\0\0""8\0\0\376\0\0\376\0\0\305\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0U\0\0\376\0" + "\0\376\0\0\0\0\0\0\0\0\0\0\0\34\0\0\376\0\0\376\0\0\376\0\0q\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\376\0\0\376\0\0U\0\0\0\0\0\0\0\0\0\0\0\251\0\0\376" + "\0\0\376\0\0U\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0U\0\0\376\0\0\376\0\0\251\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0U\0\0\376\0" + "\0\376\0\0\305\0\0U\0\0U\0\0U\0\0U\0\0U\0\0U\0\0U\0\0\214\0\0\341\0\0\376" + "\0\0\376\0\0\376\0\0U\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\251\0\0\376\0\0\376" + "\0\0U\0\0\0\0\0\0\0\0\0\0\0U\0\0\376\0\0\376\0\0\251\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0U\0\0" + "\376\0\0\376\0\0\0\0\0\0\0\0\0\0\0\0\0\0q\0\0\376\0\0\376\0\0\376\0\0\34" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\376\0\0\376\0\0U\0\0\0\0\0\0\0\0\0\0\0\251\0" + "\0\376\0\0\376\0\0U\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0U\0\0\376\0\0\376\0\0\251\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0U\0\0" + "\376\0\0\376\0\0\376\0\0\376\0\0\376\0\0\376\0\0\376\0\0\376\0\0\376\0\0" + "\376\0\0\376\0\0\376\0\0\376\0\0\376\0\0q\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\251\0\0\376\0\0\376\0\0U\0\0\0\0\0\0\0\0\0\0\0U\0\0\376\0\0\376" + "\0\0\251\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0U\0\0\376\0\0\376\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\305\0\0\376\0\0\376\0\0\305\0\0\0\0\0\0\0\0\0\0\0\0\0\0\376\0\0\376\0" + "\0U\0\0\0\0\0\0\0\0\0\0\0\251\0\0\376\0\0\376\0\0U\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "U\0\0\376\0\0\376\0\0\251\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0U\0\0\376\0\0\376\0\0\376\0\0\376\0\0\376\0\0" + "\376\0\0\376\0\0\376\0\0\376\0\0\376\0\0\376\0\0\376\0\0\341\0\0U\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\251\0\0\376\0\0\376\0\0U\0\0\0\0\0" + "\0\0\0\0\0\0U\0\0\376\0\0\376\0\0\251\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0U\0\0\376\0\0\376\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\34\0\0\376\0\0\376\0\0\376\0\0q\0\0\0\0\0" + "\0\0\0\0\0\0\376\0\0\376\0\0U\0\0\0\0\0\0\0\0\0\0\0\251\0\0\376\0\0\376\0" + "\0U\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0U\0\0\376\0\0\376\0\0\251\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0U\0\0\376\0\0\376\0\0" + "\305\0\0U\0\0U\0\0U\0\0U\0\0U\0\0U\0\0U\0\0U\0\0\34\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\251\0\0\376\0\0\376\0\0U\0\0\0\0\0\0" + "\0\0\0\0\0U\0\0\376\0\0\376\0\0\251\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0U\0\0\376\0\0\376\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0q\0\0\376\0\0\376\0\0\341\0\0\34\0\0\0" + "\0\0\0\0\0\376\0\0\376\0\0U\0\0\0\0\0\0\0\0\0\0\0q\0\0\376\0\0\376\0\0\251" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\251\0\0\376\0\0\376\0\0q\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0U\0\0\376\0\0\376\0\0\251" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\251\0\0\376\0\0\376\0\0U\0\0\0\0" + "\0\0\0\0\0\0\0""8\0\0\376\0\0\376\0\0\341\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0U\0\0\376\0\0\376" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\305\0\0\376\0\0\376\0\0\251" + "\0\0\0\0\0\0\0\0\376\0\0\376\0\0U\0\0\0\0\0\0\0\0\0\0\0U\0\0\376\0\0\376" + "\0\0\305\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\305\0\0\376\0\0\376\0\0""8\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0U\0\0\376\0\0" + "\376\0\0\251\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\251\0\0\376\0\0\376" + "\0\0U\0\0\0\0\0\0\0\0\0\0\0\0\0\0\376\0\0\376\0\0\376\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\251\0\0\376" + "\0\0\376\0\0\214\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0U\0\0\376\0\0\376\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0""8\0\0" + "\376\0\0\376\0\0\376\0\0U\0\0\0\0\0\376\0\0\376\0\0U\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\376\0\0\376\0\0\376\0\0""8\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0""8\0\0\376\0\0\376\0\0\376\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0U\0\0\376\0\0\376\0\0\251\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\251\0\0\376\0\0\376\0\0U\0\0\0\0\0\0\0\0\0\0\0\0\0\0\305\0\0\376\0\0\376" + "\0\0q\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\305\0\0\376\0\0\376\0\0U\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0U\0\0\376\0\0\376\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\214\0\0\376\0\0\376\0\0\341\0\0\34\0\0\376\0\0\376\0\0U\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\214\0\0\376\0\0\376\0\0\251\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\251\0\0" + "\376\0\0\376\0\0\214\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0U\0\0\376\0\0\376\0\0\251\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\251\0\0\376\0\0\376\0\0U\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0q\0\0\376\0\0\376\0\0\305\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\34\0\0\376\0\0\376\0\0\376\0\0\34\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0U\0\0\376\0\0\376\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\341\0\0\376\0\0\376\0\0\251" + "\0\0\376\0\0\376\0\0U\0\0\0\0\0\0\0\0\0\0\0\0\0\0\34\0\0\376\0\0\376\0\0" + "\376\0\0U\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0U\0\0\376\0\0\376\0\0\376\0\0\34\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0U\0\0\376\0\0\376\0\0\251" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\251\0\0\376\0\0\376\0\0U\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\34\0\0\376\0\0\376\0\0\376\0\0U\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\214\0\0\376\0\0\376\0\0\305" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0U\0\0\376" + "\0\0\376\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0""8\0\0" + "\376\0\0\376\0\0\376\0\0\376\0\0\376\0\0U\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\214\0\0\376\0\0\376\0\0\376\0\0U\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0U\0\0\376\0\0\376\0\0\376\0\0\214\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0U\0\0" + "\376\0\0\376\0\0\251\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\251\0\0\376" + "\0\0\376\0\0U\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\214\0\0\376\0\0\376\0\0\376" + "\0\0U\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0U\0\0\376\0\0\376" + "\0\0\376\0\0""8\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0U\0\0\376\0\0\376\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\214\0\0\376\0\0\376\0\0\376\0\0\376\0\0U\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\251\0\0\376\0\0\376\0\0\376\0\0\214\0\0\34\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\34\0\0\251\0\0\376\0\0\376\0\0\376\0\0\251\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0U\0\0\376\0\0\376\0\0\251\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\251\0\0\376\0\0\376\0\0U\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\305\0\0\376\0\0\376\0\0\376\0\0\214\0\0\34\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0q\0\0\376\0\0\376\0\0\376\0\0\305\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0U\0\0\376\0\0\376\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\341\0\0\376\0\0\376\0" + "\0\376\0\0U\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\34\0\0\251\0\0\376\0" + "\0\376\0\0\376\0\0\376\0\0\305\0\0\251\0\0\251\0\0\251\0\0\341\0\0\376\0" + "\0\376\0\0\376\0\0\376\0\0\251\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0U\0\0\376\0\0\376" + "\0\0\251\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\251\0\0\376\0\0\376\0\0" + "U\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\34\0\0\341\0\0\376\0\0\376\0\0" + "\376\0\0\376\0\0\251\0\0\251\0\0\251\0\0\251\0\0\376\0\0\376\0\0\376\0\0" + "\376\0\0\341\0\0\34\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0U\0\0\376\0\0\376\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0""8\0\0\376\0\0\376\0\0\376\0\0U\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0q\0\0\376\0\0\376\0\0\376\0\0\376" + "\0\0\376\0\0\376\0\0\376\0\0\376\0\0\376\0\0\376\0\0\341\0\0q\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0U\0\0\376\0\0\376\0\0\251\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\251\0\0\376\0\0\376\0\0U\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\34\0\0\251\0\0\376\0\0\376\0\0\376\0\0\376\0\0\376\0\0\376\0" + "\0\376\0\0\376\0\0\376\0\0\376\0\0\251\0\0\34\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\34\0\0U\0\0U\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0U\0\0U\0" + "\0U\0\0\34\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0q\0\0\251\0\0\376\0\0\376\0\0\376\0\0\376\0\0\376\0\0\251\0\0q\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\34\0\0U\0\0U\0\0""8\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0""8\0\0U\0\0U\0\0\34\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0""8\0\0\214\0\0\305\0\0\376\0\0\376\0\0\376" + "\0\0\376\0\0\341\0\0\214\0\0""8\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0", +}; + |
