summaryrefslogtreecommitdiffstats
path: root/bin/G-FSUAE/G-FS-UAE
diff options
context:
space:
mode:
Diffstat (limited to 'bin/G-FSUAE/G-FS-UAE')
-rw-r--r--bin/G-FSUAE/G-FS-UAE/.directory2
-rw-r--r--bin/G-FSUAE/G-FS-UAE/.gambas/FMAINbin0 -> 59964 bytes
-rw-r--r--bin/G-FSUAE/G-FS-UAE/.gambas/LEGGIbin0 -> 7424 bytes
-rw-r--r--bin/G-FSUAE/G-FS-UAE/.gambas/SCRIVIbin0 -> 8768 bytes
-rw-r--r--bin/G-FSUAE/G-FS-UAE/.gambas/SETTINGbin0 -> 3296 bytes
-rw-r--r--bin/G-FSUAE/G-FS-UAE/.hidden/CHANGELOG70
-rw-r--r--bin/G-FSUAE/G-FS-UAE/.icon.pngbin0 -> 4163 bytes
-rw-r--r--bin/G-FSUAE/G-FS-UAE/.project42
-rw-r--r--bin/G-FSUAE/G-FS-UAE/.settings18
-rw-r--r--bin/G-FSUAE/G-FS-UAE/.src/FMain.class663
-rw-r--r--bin/G-FSUAE/G-FS-UAE/.src/FMain.form1061
-rw-r--r--bin/G-FSUAE/G-FS-UAE/.src/leggi.module278
-rw-r--r--bin/G-FSUAE/G-FS-UAE/.src/scrivi.module220
-rw-r--r--bin/G-FSUAE/G-FS-UAE/.src/setting.module66
-rw-r--r--bin/G-FSUAE/G-FS-UAE/.startup13
-rwxr-xr-xbin/G-FSUAE/G-FS-UAE/G-FS-UAE.gambasbin0 -> 252716 bytes
-rw-r--r--bin/G-FSUAE/G-FS-UAE/README19
-rw-r--r--bin/G-FSUAE/G-FS-UAE/amiga.pngbin0 -> 24604 bytes
-rw-r--r--bin/G-FSUAE/G-FS-UAE/fs-uae-game-server-1.3.1.py725
-rw-r--r--bin/G-FSUAE/G-FS-UAE/fs-uae-gamepad-config.py364
-rw-r--r--bin/G-FSUAE/G-FS-UAE/fs-uae-netplay-server.py725
-rw-r--r--bin/G-FSUAE/G-FS-UAE/g-fs-uae.pngbin0 -> 2862 bytes
22 files changed, 4266 insertions, 0 deletions
diff --git a/bin/G-FSUAE/G-FS-UAE/.directory b/bin/G-FSUAE/G-FS-UAE/.directory
new file mode 100644
index 0000000..06dab1c
--- /dev/null
+++ b/bin/G-FSUAE/G-FS-UAE/.directory
@@ -0,0 +1,2 @@
+[Desktop Entry]
+Icon=./.icon.png
diff --git a/bin/G-FSUAE/G-FS-UAE/.gambas/FMAIN b/bin/G-FSUAE/G-FS-UAE/.gambas/FMAIN
new file mode 100644
index 0000000..d68c594
--- /dev/null
+++ b/bin/G-FSUAE/G-FS-UAE/.gambas/FMAIN
Binary files differ
diff --git a/bin/G-FSUAE/G-FS-UAE/.gambas/LEGGI b/bin/G-FSUAE/G-FS-UAE/.gambas/LEGGI
new file mode 100644
index 0000000..58ca61b
--- /dev/null
+++ b/bin/G-FSUAE/G-FS-UAE/.gambas/LEGGI
Binary files differ
diff --git a/bin/G-FSUAE/G-FS-UAE/.gambas/SCRIVI b/bin/G-FSUAE/G-FS-UAE/.gambas/SCRIVI
new file mode 100644
index 0000000..2540d3e
--- /dev/null
+++ b/bin/G-FSUAE/G-FS-UAE/.gambas/SCRIVI
Binary files differ
diff --git a/bin/G-FSUAE/G-FS-UAE/.gambas/SETTING b/bin/G-FSUAE/G-FS-UAE/.gambas/SETTING
new file mode 100644
index 0000000..dba6821
--- /dev/null
+++ b/bin/G-FSUAE/G-FS-UAE/.gambas/SETTING
Binary files differ
diff --git a/bin/G-FSUAE/G-FS-UAE/.hidden/CHANGELOG b/bin/G-FSUAE/G-FS-UAE/.hidden/CHANGELOG
new file mode 100644
index 0000000..f1a9605
--- /dev/null
+++ b/bin/G-FSUAE/G-FS-UAE/.hidden/CHANGELOG
@@ -0,0 +1,70 @@
+* Thu May 03 2012 Speedvicio <speedvicio@hotmail.com> 0.0.38
+- Fixed a bug with file recognize, introduced in previous version.
+
+* Thu May 03 2012 Speedvicio <speedvicio@hotmail.com> 0.0.37
+
+* Thu May 03 2012 Speedvicio <speedvicio@hotmail.com> 0.0.37
+- Added refresh button in Shaders path folder
+- Added support to Joystick configurator script (developed by FrodeSolheim)
+- Added a check to verify the correct insertion of ip adress or host name in the Net Play section
+
+* Thu May 03 2012 Speedvicio <speedvicio@hotmail.com> 0.0.36
+- Added zip extension support
+
+* Wed May 02 2012 Speedvicio <speedvicio@hotmail.com> 0.0.35
+
+* Wed May 02 2012 Speedvicio <speedvicio@hotmail.com> 0.0.34
+- Fixed a problem to recognize built-in Shaders
+
+* Wed May 02 2012 Speedvicio <speedvicio@hotmail.com> 0.0.34
+
+* Wed May 02 2012 Speedvicio <speedvicio@hotmail.com> 0.0.34
+- If Shaders path is empty, Shaders combobox will recognize built-in shaders from FS-UAE
+- Added Scale x/y and Align x/y options (default value will divided by 10)
+- Update server.py from last FS-UAE
+
+* Wed May 02 2012 Speedvicio <speedvicio@hotmail.com> 0.0.33
+- Fixed a problem to recognize shaders folder and file
+- Fixed a problem to store shaders setting
+- Shader combobox visualize shaders without ".shaders" extension
+- If you select a shader, scanlines option will be disabled
+
+* Tue May 01 2012 Speedvicio <speedvicio@hotmail.com> 0.0.32
+- Added Shaders option from FS-UAE v1.3
+
+* Sat Apr 28 2012 Speedvicio <speedvicio@hotmail.com> 0.0.31
+- Changed parameter: model => amiga_model sync => video_sync
+- (to prevent problem with existing configuration, please save it again)
+- Modified Net port to default 25100 and increased max text value to 5
+- Increased AA filter to max value 8
+- Added "--end-config" parameter to prevent loading of Host.fs-uae config file
+- Added missing ".fs-uae" extension from combobox and button loader
+
+* Wed Apr 25 2012 Speedvicio <speedvicio@hotmail.com> 0.0.30
+- When the emulation is integrate to GUI, will set the grab_input option to 0
+- Enable emulation of bsdsocket.library by default
+- Added all options from FS-UAE v1.1.9
+
+* Wed Apr 18 2012 Speedvicio <speedvicio@hotmail.com> 0.0.29
+- Bug Fix
+
+* Wed Apr 18 2012 Speedvicio <speedvicio@hotmail.com> 0.0.28
+- Add Embedder Tab (You can open FS-UAE inside the GUI)
+
+* Wed Apr 18 2012 Speedvicio <speedvicio@hotmail.com> 0.0.27
+- Added all options from fs-uae v.1.1.5
+- Added prefix .config at configuration save
+- Moved Path settings to indipendent tab
+
+* Sat Apr 14 2012 Speedvicio <speedvicio@hotmail.com> 0.0.24
+
+* Sat Apr 14 2012 Speedvicio <speedvicio@hotmail.com> 0.0.24
+- Added video and texture option from fs-uae v. 1.1.4
+
+* Mon Apr 09 2012 Speedvicio <speedvicio@hotmail.com> 0.0.23
+
+* Tue Apr 03 2012 Speedvicio <speedvicio@hotmail.com> 0.0.23
+- Added Picasso 96 options
+- Added missed Zorro III RAM memory
+- Fixed a bug with load saved configure options
+- Refresh config combo at save
diff --git a/bin/G-FSUAE/G-FS-UAE/.icon.png b/bin/G-FSUAE/G-FS-UAE/.icon.png
new file mode 100644
index 0000000..03ed2d0
--- /dev/null
+++ b/bin/G-FSUAE/G-FS-UAE/.icon.png
Binary files differ
diff --git a/bin/G-FSUAE/G-FS-UAE/.project b/bin/G-FSUAE/G-FS-UAE/.project
new file mode 100644
index 0000000..0f120b9
--- /dev/null
+++ b/bin/G-FSUAE/G-FS-UAE/.project
@@ -0,0 +1,42 @@
+# Gambas Project File 3.0
+# Compiled with Gambas 3.1.1
+Title=G-FS-UAE
+Startup=FMain
+Icon=amiga.png
+Version=0.0.39
+Component=gb.image
+Component=gb.qt4
+Component=gb.form
+Component=gb.desktop
+Component=gb.settings
+Component=gb.qt4.ext
+Description="A small interface written in Gambas3 for command line FS-UAE Amiga emulator."
+Authors="Speedvicio"
+TabSize=2
+ControlPublic=1
+ModulePublic=1
+SourcePath=/home/vincenzo/compilati
+Maintainer=Speedvicio
+Address=speedvicio@hotmail.com
+Url=http://sourceforge.net/projects/gfsuae/
+License=Public Domain
+UseRelease=1
+PackageName=g-fs-uae-0.0.39
+CreateEachDirectory=1
+Packager=1
+Systems=autotools,debian,fedora,mandriva,suse,ubuntu
+Menus=debian:"Applications/Emulators"
+Categories=debian:"Game"
+Groups=debian:"games"
+Menus=fedora:"Emulator"
+Categories=fedora:"Emulator"
+Groups=fedora:"Applications/Emulators"
+Menus=mandriva:"More Applications/Emulators"
+Categories=mandriva:"Emulator"
+Groups=mandriva:"Emulators"
+Menus=suse:"Emulator"
+Categories=suse:"Emulator"
+Groups=suse:"System/Emulators/Other"
+Menus=ubuntu:"Applications/Emulators"
+Categories=ubuntu:"Game"
+Groups=ubuntu:"games"
diff --git a/bin/G-FSUAE/G-FS-UAE/.settings b/bin/G-FSUAE/G-FS-UAE/.settings
new file mode 100644
index 0000000..84fd59a
--- /dev/null
+++ b/bin/G-FSUAE/G-FS-UAE/.settings
@@ -0,0 +1,18 @@
+[Breakpoints]
+Count=0
+
+[DebugWindow]
+Count=0
+
+[DebugWindow/a]
+Geometry=[0,0,210,210]
+
+[DebugWindow/opzione]
+Geometry=[0,0,210,210]
+
+[OpenFile]
+Count=0
+
+[Watches]
+Count=0
+
diff --git a/bin/G-FSUAE/G-FS-UAE/.src/FMain.class b/bin/G-FSUAE/G-FS-UAE/.src/FMain.class
new file mode 100644
index 0000000..dc5e5b0
--- /dev/null
+++ b/bin/G-FSUAE/G-FS-UAE/.src/FMain.class
@@ -0,0 +1,663 @@
+' Gambas class file
+
+Private processo As Process
+Private pFSUAE As Process
+Private server As Process
+onoff As Integer
+Public inShader As String
+
+Public Sub _new()
+
+End
+
+Public Sub Form_Open()
+Me.Center
+setting.rSetting
+Try popola_config
+Try popola_shader
+If Exist(User.Home &/ ".gfsuae/") Then
+ Else
+Try Shell ("mkdir " & User.Home & "/.gfsuae")
+
+Endif
+End
+
+Public Sub Button2_Click()
+Dim tconf As String
+
+scrivi.sConfig
+Dialog.Title = "Choose a Name for FS-UAE Configuration file"
+Dialog.Filter = ["*.config", ".config file"]
+Dialog.Path = textbox1.text
+
+If Dialog.SaveFile() Then Return
+ File.Save(Dialog.Path & ".config", scrivi.valori)
+ popola_config
+ Catch
+ Message.Info(Error.Text)
+
+End
+
+Public Sub Button4_Click()
+
+ Dialog.title = "Choose a default Configurations directory"
+ If Dialog.SelectDirectory() Then Return
+ textbox1.text = Dialog.Path
+ popola_config
+
+End
+
+Public Sub Button5_Click()
+
+ Dialog.title = "Choose a default Controllers directory"
+ If Dialog.SelectDirectory() Then Return
+ textbox2.text = Dialog.Path
+End
+
+Public Sub Button6_Click()
+
+ Dialog.title = "Choose a default Flash Memory directory"
+ If Dialog.SelectDirectory() Then Return
+ textbox3.text = Dialog.Path
+
+End
+
+Public Sub Button7_Click()
+
+ Dialog.title = "Choose a default Floppy Overlays directory"
+ If Dialog.SelectDirectory() Then Return
+ textbox4.text = Dialog.Path
+
+End
+
+Public Sub Button8_Click()
+
+ Dialog.title = "Choose a default Kickstarts directory"
+ If Dialog.SelectDirectory() Then Return
+ textbox5.text = Dialog.Path
+
+End
+
+Public Sub Button10_Click()
+
+Dialog.title = "Choose a default Save States directory"
+ If Dialog.SelectDirectory() Then Return
+ textbox7.text = Dialog.Path
+
+End
+
+Public Sub Button11_Click()
+
+ Dialog.title = "Choose a Amiga 500/600 kickstart"
+ Dialog.Filter = ["*.rom;*.zip", "kickstart files"]
+ Dialog.Path = textbox5.text
+ If Dialog.OpenFile() Then Return
+ textbox8.text = Dialog.Path
+
+End
+
+Public Sub Button12_Click()
+
+ Dialog.title = "Choose a Amiga 1200 or up kickstart"
+ Dialog.Filter = ["*.rom;*.zip", "kickstart files"]
+ Dialog.Path = textbox5.text
+ If Dialog.OpenFile() Then Return
+ textbox9.text = Dialog.Path
+
+End
+
+Public Sub Button13_Click()
+
+ Dialog.title = "Choose a CD32 rom"
+ Dialog.Filter = ["*.rom;*.zip", "CD32 rom"]
+ Dialog.Path = textbox5.text
+ If Dialog.OpenFile() Then Return
+ textbox10.text = Dialog.Path
+
+End
+
+Public Sub Button23_Click()
+
+ Dialog.title = "Choose a adf/adz/dms/ipf/zip Amiga disk image"
+ Dialog.Filter = ["*.adf;*.adz;*.ipf;*.zip", "Amiga disk image"]
+ Dialog.Path = textbox21.text
+ If Dialog.OpenFile() Then Return
+ textbox20.text = Dialog.Path
+
+End
+
+Public Sub Button19_Click()
+
+ Dialog.title = "Choose a adf/adz/dms/ipf/zip Amiga disk image"
+ Dialog.Filter = ["*.adf;*.adz;*.dms;*.ipf;*.zip", "Amiga disk image"]
+ Dialog.Path = textbox21.text
+ If Dialog.OpenFile() Then Return
+ textbox16.text = Dialog.Path
+
+End
+
+Public Sub Button20_Click()
+
+ Dialog.title = "Choose a adf/adz/dms/ipf/zip Amiga disk image"
+ Dialog.Filter = ["*.adf;*.adz;*.dms;*.ipf;*.zip", "Amiga disk image"]
+ Dialog.Path = textbox21.text
+ If Dialog.OpenFile() Then Return
+ textbox17.text = Dialog.Path
+
+End
+
+Public Sub Button21_Click()
+
+ Dialog.title = "Choose a adf/adz/dms/ipf/zip Amiga disk image"
+ Dialog.Filter = ["*.adf;*.adz;*.dms;*.ipf;*.zip", "Amiga disk image"]
+ Dialog.Path = textbox21.text
+ If Dialog.OpenFile() Then Return
+ textbox18.text = Dialog.Path
+
+End
+
+Public Sub Button18_Click()
+
+ Dialog.title = "Choose a adf/adz/dms/ipf/zip Amiga swap disk image"
+ Dialog.Filter = ["*.adf;*.adz;*.dms;*.ipf;*.zip", "Amiga disk image"]
+ Dialog.Path = textbox21.text
+ If Dialog.OpenFile() Then Return
+ textbox15.text = Dialog.Path
+
+End
+
+Public Sub Button14_Click()
+
+ Dialog.title = "Choose a adf/adz/dms/ipf/zip Amiga swap disk image"
+ Dialog.Filter = ["*.adf;*.adz;*.dms;*.ipf;*.zip", "Amiga disk image"]
+ Dialog.Path = textbox21.text
+ If Dialog.OpenFile() Then Return
+ textbox11.text = Dialog.Path
+
+End
+
+Public Sub Button15_Click()
+
+ Dialog.title = "Choose a adf/adz/dms/ipf/zip Amiga swap disk image"
+ Dialog.Filter = ["*.adf;*.adz;*.dms;*.ipf;*.zip", "Amiga disk image"]
+ Dialog.Path = textbox21.text
+ If Dialog.OpenFile() Then Return
+ textbox12.text = Dialog.Path
+
+End
+
+Public Sub Button16_Click()
+
+ Dialog.title = "Choose a adf/adz/dms/ipf/zip Amiga swap disk image"
+ Dialog.Filter = ["*.adf;*.adz;*.dms;*.ipf;*.zip", "Amiga disk image"]
+ Dialog.Path = textbox21.text
+ If Dialog.OpenFile() Then Return
+ textbox13.text = Dialog.Path
+
+End
+
+Public Sub Button17_Click()
+
+ Dialog.title = "Choose a adf/adz/dms/ipf/zip Amiga swap disk image"
+ Dialog.Filter = ["*.adf;*.adz;*.dms;*.ipf;*.zip", "Amiga disk image"]
+ Dialog.Path = textbox21.text
+ If Dialog.OpenFile() Then Return
+ textbox14.text = Dialog.Path
+
+End
+
+Public Sub Button24_Click()
+ popola_config
+ Dialog.title = "Choose FS-UAE configuration file to load"
+ Dialog.Filter = ["*.config", ".config file", "*.fs-uae", ".fs-uae file"]
+ Dialog.path = textbox1.text
+ If Dialog.OpenFile() Then Return
+ combobox5.text = Dialog.Path
+
+ leggi.leggi_conf
+
+End
+
+Public Sub Button1_Click()
+
+onoff = 0
+start_emu
+
+End
+
+Public Sub Button3_Click()
+
+ Try embEmbedder.Discard
+ Try processo.Kill
+ Try server.kill
+
+End
+
+Public Sub Button9_Click()
+
+ Dialog.title = "Choose a default Shaders directory"
+ If Dialog.SelectDirectory() Then Return
+ textbox33.text = Dialog.Path
+ combobox10.clear
+ popola_shader
+
+End
+
+Public Sub RadioButton4_Click()
+
+ If RadioButton4.value = True Then ComboBox1.text = "A1200"
+
+End
+
+Public Sub RadioButton3_Click()
+
+ If RadioButton3.value = True Then ComboBox1.text = "A500"
+
+End
+
+Public Sub Button22_Click()
+
+ textbox20.Clear
+
+End
+
+Public Sub Button25_Click()
+
+ textbox16.Clear
+
+End
+
+Public Sub Button26_Click()
+
+ textbox17.Clear
+
+End
+
+Public Sub Button27_Click()
+
+ textbox18.Clear
+
+End
+
+Public Sub Button28_Click()
+
+ textbox15.Clear
+
+End
+
+Public Sub Button29_Click()
+
+ textbox11.Clear
+
+End
+
+Public Sub Button30_Click()
+
+ textbox12.Clear
+
+End
+
+Public Sub Button31_Click()
+
+ textbox13.Clear
+
+End
+
+Public Sub Button32_Click()
+
+ textbox14.Clear
+
+End
+
+Public Sub Button33_Click()
+
+ Dialog.title = "Choose a hdf Amiga Hard Disk Image"
+ Dialog.Filter = ["*.hdf;*.zip", "Amiga Hard Disk Image"]
+ If Dialog.OpenFile() Then Return
+ textbox6.text = Dialog.Path
+
+End
+
+Public Sub Button35_Click()
+
+ Dialog.title = "Choose a hdf Amiga Hard Disk Image"
+ Dialog.Filter = ["*.hdf;*.zip", "Amiga Hard Disk Image"]
+ If Dialog.OpenFile() Then Return
+ textbox19.text = Dialog.Path
+
+End
+
+Public Sub Button34_Click()
+
+ textbox6.clear
+
+End
+
+Public Sub Button36_Click()
+
+ textbox19.clear
+
+End
+
+Public Sub Button37_Click()
+
+ Dialog.title = "Mount Folder as drives"
+ If Dialog.SelectDirectory() Then Return
+ textbox6.text = Dialog.Path
+
+End
+
+Public Sub Button38_Click()
+
+ Dialog.title = "Mount Folder as drives"
+ If Dialog.SelectDirectory() Then Return
+ textbox19.text = Dialog.Path
+
+End
+
+Public Sub Button40_Click()
+
+ Dialog.title = "Mount Folder as drives"
+ If Dialog.SelectDirectory() Then Return
+ textbox22.text = Dialog.Path
+
+End
+
+Public Sub Button39_Click()
+
+ Dialog.title = "Choose a hdf Amiga Hard Disk Image"
+ Dialog.Filter = ["*.hdf;*.zip", "Amiga Hard Disk Image"]
+ If Dialog.OpenFile() Then Return
+ textbox22.text = Dialog.Path
+
+End
+
+Public Sub Button41_Click()
+
+ textbox22.clear
+
+End
+
+Public Sub Form_Close()
+
+ Try embEmbedder.Discard
+ setting.wSetting
+
+End
+
+Public Sub Slider4_Change()
+
+ label6.text = "Floppy Drive Sound " & Slider4.value
+
+End
+
+Public Sub Slider3_Change()
+
+ label21.text = "Dark " & slider3.value
+
+End
+
+Public Sub Slider2_Change()
+
+ label22.text = "Light " & Slider2.value
+
+End
+
+Public Sub Slider1_Change()
+
+ frame3.text = "Emulation Accurancy " & Slider1.value
+
+End
+
+Public Sub Button42_Click()
+
+ Dialog.title = "Choose Amiga CD32/CDTV Image"
+ Dialog.Filter = ["*.cue;*.iso", "CUE/ISO Image"]
+ If Dialog.OpenFile() Then Return
+ textbox23.text = Dialog.Path
+
+End
+
+Public Sub Button45_Click()
+
+ Dialog.title = "Choose Amiga CD32/CDTV Image"
+ Dialog.Filter = ["*.cue;*.iso", "CUE/ISO Image"]
+ If Dialog.OpenFile() Then Return
+ textbox24.text = Dialog.Path
+
+End
+
+Public Sub Button43_Click()
+
+ 'If Exist(User.Home &/ ".gfsuae/fs-uae-netplay-server.py") Then
+ 'Else
+ Try Copy "fs-uae-netplay-server.py" To User.Home &/ ".gfsuae/fs-uae-netplay-server.py"
+ 'Endif
+
+ server = Exec ["python", User.Home &/ ".gfsuae/fs-uae-netplay-server.py", "--port=" & textbox25.text, "--player=" & combobox4.text]
+
+ If server.state = 1 Then
+ frame15.text = "Server ON"
+ Button48.enabled = True
+ End If
+End
+
+Public Sub start_emu()
+Dim fileConf, fuscreen, net, cuspar, videof, textf, fsmode, xwin, ywin, xfus, yfus, vsmode, other_emb, shaders, scX, scY, alX, alY, translations As String
+scrivi.sConfig
+
+
+ Try embEmbedder.Discard
+ Try processo.Kill
+ Try server.kill
+ Wait
+
+File.Save(User.Home &/ ".gfsuae/test.config", scrivi.valori)
+fileconf = User.Home &/ ".gfsuae/test.config"
+
+ If CheckBox1.value = True Then
+ fuscreen = " --fullscreen=1"
+ Else
+ Fuscreen = " --fullscreen=0"
+ Endif
+
+ videof = " --video-format=" & combobox6.text
+ textf = " --texture-format=" & combobox7.text
+ fsmode = " --fullscreen-mode=" & combobox8.text
+ xwin = " --window-width=" & textbox29.Text
+ ywin = " --window-height=" & textbox30.Text
+ xfus = " --fullscreen-width=" & textbox31.Text
+ yfus = " --fullscreen-height=" & textbox32.Text
+ vsmode = " --video-sync-method=" & combobox9.text
+ cuspar = " " & textbox28.text
+
+ If onoff = 1 Then
+ net = " --netplay-server=" & textbox27.text & " --netplay-tag=" & textbox26.text & " --netplay-port=" & textbox25.text
+ Else If onoff = 0 Then
+ net = ""
+ Endif
+
+ If CheckBox1.value = False And checkbox12.value = True Then
+ xwin = " --window-width=500"
+ ywin = " --window-height=450"
+ other_emb = " --grab-input=0"
+ Endif
+
+ scX = " --scale-x=" & (spinbox1.value / 10)
+ scY = " --scale-y=" & (spinbox2.value / 10)
+ alX = " --align-x=" & (spinbox3.value / 10)
+ alY = " --align-y=" & (spinbox4.value / 10)
+
+ If checkbox13.value = True Then
+ translations = " --localization=0"
+ Else
+ translations = " --localization=1"
+ Endif
+
+ If fileconf <> "" Then
+ processo = Shell "fs-uae " &/ fileconf & videof & textf & fsmode & xwin & ywin & xfus & yfus & vsmode & other_emb & scX & scY & alX & alY & translations & " --bsdsocket-library=1" & cuspar & net & " --end-config"
+ Else If fileconf = "" Then
+ Message.Warning("You must make and select a file .config to load FS-UAE", "Ok")
+ End If
+
+ If CheckBox1.value = False And checkbox12.value = True Then
+ Wait 1
+ inside
+ End If
+End
+
+Public Sub Button46_Click()
+If textbox27.text <> "" Then
+ onoff = 1
+ start_emu
+Else
+ Message.Warning("You must set a ip adress or host name in Net Play tab", "Ok")
+ TabStrip1.index = 5
+ textbox27.SetFocus
+End If
+End
+
+Public Sub Button48_Click()
+
+ Try server.kill
+ If server.state = 2 Then
+ frame15.text = "Server OFF"
+ Button48.enabled = False
+ End If
+
+End
+
+Public Sub Slider5_Change()
+
+ label23.text = "Floppy speed " & Slider5.value
+
+End
+
+Public Sub popola_config()
+Dim lFile As String
+Dim ifile As Integer
+
+combobox5.Clear
+
+For Each lFile In Dir(textbox1.text, "*.config")
+ combobox5.add(textbox1.text & "/" & lfile, ifile)
+Next
+
+For Each lFile In Dir(textbox1.text, "*.fs-uae")
+ combobox5.add(textbox1.text & "/" & lfile, ifile)
+Next
+End
+
+Public Sub popola_shader()
+
+Dim lFile As String
+Dim ifile As Integer
+
+combobox10.add("None")
+
+If textbox33.text <> "" Then
+ inShader = textbox33.text
+ Else
+ inShader = "/usr/share/fs-uae/shaders/"
+Endif
+
+For Each lFile In Dir(inShader, "*.shader")
+ combobox10.add(Replace(lfile, ".shader", ""), ifile + 1)
+Next
+
+End
+
+
+Public Sub Button49_Click()
+
+ Dialog.title = "Choose a default Adf/Ipf directory"
+ If Dialog.SelectDirectory() Then Return
+ textbox21.text = Dialog.Path
+
+End
+
+Public Sub ComboBox5_Click()
+leggi.leggi_conf
+leggi.leggi_conf
+End
+
+Public Sub Button44_Click()
+
+ textbox23.clear
+
+End
+
+Public Sub Button47_Click()
+
+ textbox24.clear
+
+End
+
+Public Sub Slider6_Change()
+
+ label30.text = "Picasso 96 RAM " & Slider6.value
+
+End
+
+Public Sub inside()
+
+ Dim aHandle As Integer[]
+ Dim iHandle As Integer
+ Dim dWindow As DesktopWindow
+ Dim a As Integer
+
+ For Each dWindow In Desktop.Windows
+ If dWindow.name Like "Amiga*" Then
+ a = dWindow.Id
+ Endif
+
+ Next
+
+ TabStrip1.index = 8
+ Try embEmbedder.Embed(a)
+
+ If Error Then Message.Warning(Error.Text)
+
+End
+
+Public Sub embEmbedder_Error()
+
+ Message.Error(("Embed error"))
+
+End
+
+
+Public Sub CheckBox1_Click()
+
+ If CheckBox1.value = True Then
+ checkbox12.Enabled = False
+ Else
+ checkbox12.Enabled = True
+ End If
+
+End
+
+Public Sub ComboBox10_Change()
+
+ If combobox10.text <> "None" Then CheckBox3.value = False
+
+End
+
+
+Public Sub Button50_Click()
+ combobox10.clear
+ popola_shader
+
+End
+
+Public Sub Button51_Click()
+ Dim controlla As Boolean
+ Try Copy "fs-uae-gamepad-config.py" To User.Home &/ ".gfsuae/fs-uae-gamepad-config.py"
+ Exec ["python", User.Home &/ ".gfsuae/fs-uae-gamepad-config.py"] To controlla
+ Wait
+
+ If controlla = True And Exist(User.Home &/ "Documents/FS-UAE/Controllers") Then
+ textbox2.text = User.Home &/ "Documents/FS-UAE/Controllers"
+ Endif
+
+End
diff --git a/bin/G-FSUAE/G-FS-UAE/.src/FMain.form b/bin/G-FSUAE/G-FS-UAE/.src/FMain.form
new file mode 100644
index 0000000..914abdb
--- /dev/null
+++ b/bin/G-FSUAE/G-FS-UAE/.src/FMain.form
@@ -0,0 +1,1061 @@
+# Gambas Form File 3.0
+
+{ Form Form
+ Move(0,0,560,553)
+ #Scaled = False
+ Text = ("G-FS-UAE")
+ Icon = Picture["amiga.png"]
+ Resizable = False
+ { Panel1 Panel
+ Move(10,4,539,35)
+ { Label18 Label
+ Move(7,3,119,28)
+ Text = ("Configuration File")
+ }
+ { ComboBox5 ComboBox
+ Move(133,3,224,28)
+ Text = ("Test")
+ Sorted = True
+ }
+ { Button24 Button
+ Move(364,3,28,28)
+ ToolTip = ("Open .config/.fs-uae file")
+ Text = ("...")
+ }
+ { Button1 Button
+ Move(399,3,28,28)
+ ToolTip = ("Start FS-UAE")
+ Picture = Picture["icon:/22/play"]
+ }
+ { Button46 Button
+ Move(434,3,28,28)
+ ToolTip = ("Start FS-UAE in Net Mode")
+ Picture = Picture["icon:/22/network"]
+ }
+ { Button2 Button
+ Move(469,3,28,28)
+ ToolTip = ("Save .config file")
+ Picture = Picture["icon:/22/save-as"]
+ }
+ { Button3 Button
+ Move(504,3,28,28)
+ ToolTip = ("Close emulation")
+ Picture = Picture["icon:/22/quit"]
+ }
+ }
+ { TabStrip1 TabStrip
+ Move(7,42,546,504)
+ Count = 9
+ Index = 0
+ Text = ("&Floppy Disks")
+ Picture = Picture["icon:/16/floppy"]
+ { Frame10 Frame
+ Move(13,15,511,175)
+ Text = ("Normal Disks")
+ { Label13 Label
+ Move(28,21,56,28)
+ Text = ("DF0:")
+ }
+ { TextBox20 TextBox
+ Move(98,21,322,28)
+ }
+ { Button23 Button
+ Move(427,21,28,28)
+ ToolTip = ("Select Adf/Ipf")
+ Picture = Picture["icon:/16/add"]
+ }
+ { Button22 Button
+ Move(462,21,28,28)
+ ToolTip = ("Remove floppy")
+ Picture = Picture["icon:/16/remove"]
+ }
+ { CheckBox8 CheckBox
+ Move(21,56,84,28)
+ Text = ("DF1:")
+ }
+ { TextBox16 TextBox
+ Move(98,56,322,28)
+ }
+ { Button19 Button
+ Move(427,56,28,28)
+ ToolTip = ("Select Adf/Ipf")
+ Picture = Picture["icon:/16/add"]
+ }
+ { Button25 Button
+ Move(462,56,28,28)
+ ToolTip = ("Remove floppy")
+ Picture = Picture["icon:/16/remove"]
+ }
+ { CheckBox7 CheckBox
+ Move(21,91,84,28)
+ Text = ("DF1:")
+ }
+ { TextBox17 TextBox
+ Move(98,91,322,28)
+ }
+ { Button20 Button
+ Move(427,91,28,28)
+ ToolTip = ("Select Adf/Ipf")
+ Picture = Picture["icon:/16/add"]
+ }
+ { Button26 Button
+ Move(462,91,28,28)
+ ToolTip = ("Remove floppy")
+ Picture = Picture["icon:/16/remove"]
+ }
+ { CheckBox5 CheckBox
+ Move(21,126,84,28)
+ Text = ("DF1:")
+ }
+ { TextBox18 TextBox
+ Move(98,126,322,28)
+ }
+ { Button21 Button
+ Move(427,126,28,28)
+ ToolTip = ("Select Adf/Ipf")
+ Picture = Picture["icon:/16/add"]
+ }
+ { Button27 Button
+ Move(462,126,28,28)
+ ToolTip = ("Remove floppy")
+ Picture = Picture["icon:/16/remove"]
+ }
+ }
+ { Frame9 Frame
+ Move(13,205,511,238)
+ Text = ("Swap Disks")
+ { Label12 Label
+ Move(28,21,56,28)
+ Text = ("DF0:")
+ }
+ { TextBox15 TextBox
+ Move(98,21,322,28)
+ }
+ { Button18 Button
+ Move(427,21,28,28)
+ ToolTip = ("Select Adf/Ipf")
+ Picture = Picture["icon:/16/add"]
+ }
+ { Button28 Button
+ Move(462,21,28,28)
+ ToolTip = ("Remove floppy")
+ Picture = Picture["icon:/16/remove"]
+ }
+ { Label14 Label
+ Move(28,56,56,28)
+ Text = ("DF1:")
+ }
+ { TextBox11 TextBox
+ Move(98,56,322,28)
+ }
+ { Button14 Button
+ Move(427,56,28,28)
+ ToolTip = ("Select Adf/Ipf")
+ Picture = Picture["icon:/16/add"]
+ }
+ { Button29 Button
+ Move(462,56,28,28)
+ ToolTip = ("Remove floppy")
+ Picture = Picture["icon:/16/remove"]
+ }
+ { Label15 Label
+ Move(28,91,56,28)
+ Text = ("DF2:")
+ }
+ { TextBox12 TextBox
+ Move(98,91,322,28)
+ }
+ { Button15 Button
+ Move(427,91,28,28)
+ ToolTip = ("Select Adf/Ipf")
+ Picture = Picture["icon:/16/add"]
+ }
+ { Button30 Button
+ Move(462,91,28,28)
+ ToolTip = ("Remove floppy")
+ Picture = Picture["icon:/16/remove"]
+ }
+ { Label16 Label
+ Move(28,126,56,28)
+ Text = ("DF3:")
+ }
+ { TextBox13 TextBox
+ Move(98,126,322,28)
+ }
+ { Button16 Button
+ Move(427,126,28,28)
+ ToolTip = ("Select Adf/Ipf")
+ Picture = Picture["icon:/16/add"]
+ }
+ { Button31 Button
+ Move(462,126,28,28)
+ ToolTip = ("Remove floppy")
+ Picture = Picture["icon:/16/remove"]
+ }
+ { Label17 Label
+ Move(28,161,56,28)
+ Text = ("DF4:")
+ }
+ { TextBox14 TextBox
+ Move(98,161,322,28)
+ }
+ { Button17 Button
+ Move(427,161,28,28)
+ ToolTip = ("Select Adf/Ipf")
+ Picture = Picture["icon:/16/add"]
+ }
+ { Button32 Button
+ Move(462,161,28,28)
+ ToolTip = ("Remove floppy")
+ Picture = Picture["icon:/16/remove"]
+ }
+ }
+ Index = 1
+ Text = ("&Memory")
+ Picture = Picture["icon:/16/hardware"]
+ { Frame4 Frame
+ Move(13,7,511,189)
+ ToolTip = ("Select Amiga kickstart file")
+ Text = ("Kickstart ROM File")
+ { RadioButton3 RadioButton
+ Move(17,42,168,28)
+ Text = ("Amiga <= 600")
+ Value = True
+ }
+ { TextBox8 TextBox
+ Move(202,42,245,28)
+ }
+ { Button11 Button
+ Move(465,42,28,28)
+ Text = ("...")
+ }
+ { RadioButton4 RadioButton
+ Move(17,77,168,28)
+ Text = ("Amiga >= 1200")
+ }
+ { TextBox9 TextBox
+ Move(202,77,245,28)
+ }
+ { Button12 Button
+ Move(465,77,28,28)
+ Text = ("...")
+ }
+ { Label11 Label
+ Move(17,112,147,28)
+ Text = ("Extended Kickstart")
+ }
+ { TextBox10 TextBox
+ Move(202,112,245,28)
+ }
+ { Button13 Button
+ Move(465,112,28,28)
+ Text = ("...")
+ }
+ }
+ { Frame5 Frame
+ Move(6,203,112,203)
+ ToolTip = ("Select amount of chip mem")
+ Text = ("Chip Mem")
+ { RadioButton5 RadioButton
+ Move(14,35,168,28)
+ Text = ("512 Kb")
+ }
+ { RadioButton6 RadioButton
+ Move(14,63,168,28)
+ Text = ("1 Mb")
+ Value = True
+ }
+ { RadioButton7 RadioButton
+ Move(14,91,168,28)
+ Text = ("2 Mb")
+ }
+ { RadioButton8 RadioButton
+ Move(14,119,168,28)
+ Text = ("4 Mb")
+ }
+ { RadioButton9 RadioButton
+ Move(14,147,168,28)
+ Text = ("8 Mb")
+ }
+ }
+ { Frame6 Frame
+ Move(125,203,112,203)
+ ToolTip = ("Select amount of slow mem")
+ Text = ("Slow Mem")
+ { RadioButton13 RadioButton
+ Move(14,35,168,28)
+ Text = ("None")
+ Value = True
+ }
+ { RadioButton10 RadioButton
+ Move(14,63,168,28)
+ Text = ("512 Kb")
+ }
+ { RadioButton11 RadioButton
+ Move(14,91,168,28)
+ Text = ("1 Mb")
+ }
+ { RadioButton12 RadioButton
+ Move(14,119,168,28)
+ Text = ("1,8 Mb")
+ }
+ }
+ { Frame7 Frame
+ Move(243,203,112,203)
+ ToolTip = ("Select amount of fast mem")
+ Text = ("Fast Mem")
+ { RadioButton14 RadioButton
+ Move(14,35,168,28)
+ Text = ("None")
+ Value = True
+ }
+ { RadioButton15 RadioButton
+ Move(14,63,168,28)
+ Text = ("1 Mb")
+ }
+ { RadioButton16 RadioButton
+ Move(14,91,168,28)
+ Text = ("2 Mb")
+ }
+ { RadioButton17 RadioButton
+ Move(14,119,168,28)
+ Text = ("4 Mb")
+ }
+ { RadioButton18 RadioButton
+ Move(14,147,168,28)
+ Text = ("8 Mb")
+ }
+ }
+ { Frame8 Frame
+ Move(363,203,168,203)
+ ToolTip = ("Select amount of Z3 mem")
+ Text = ("Z3 Mem")
+ { RadioButton19 RadioButton
+ Move(7,35,84,28)
+ Text = ("None")
+ Value = True
+ }
+ { RadioButton24 RadioButton
+ Move(77,35,84,28)
+ Text = ("16 Mb")
+ }
+ { RadioButton20 RadioButton
+ Move(7,63,84,28)
+ Text = ("1 Mb")
+ }
+ { RadioButton25 RadioButton
+ Move(77,63,84,28)
+ Text = ("32 Mb")
+ }
+ { RadioButton21 RadioButton
+ Move(7,91,84,28)
+ Text = ("2 Mb")
+ }
+ { RadioButton38 RadioButton
+ Move(77,91,84,28)
+ Text = ("64 Mb")
+ }
+ { RadioButton22 RadioButton
+ Move(7,119,84,28)
+ Text = ("4 Mb")
+ }
+ { RadioButton39 RadioButton
+ Move(77,119,84,28)
+ Text = ("128 Mb")
+ }
+ { RadioButton23 RadioButton
+ Move(7,147,84,28)
+ Text = ("8 Mb")
+ }
+ { RadioButton40 RadioButton
+ Move(77,147,84,28)
+ Text = ("256 Mb")
+ }
+ }
+ { Frame18 Frame
+ Move(34,413,469,49)
+ { Label30 Label
+ Move(21,10,140,28)
+ Text = ("Picasso 96 RAM 0")
+ }
+ { Slider6 Slider
+ Move(182,10,266,28)
+ MaxValue = 32
+ }
+ }
+ Index = 2
+ Text = ("&Chipset")
+ Picture = Picture["icon:/16/computer"]
+ { Frame2 Frame
+ Move(13,7,511,147)
+ ToolTip = ("Select a Amiga Model")
+ Text = ("Chipset Type")
+ { Label8 Label
+ Move(133,42,105,28)
+ Text = ("Chipset Model")
+ }
+ { ComboBox1 ComboBox
+ Move(259,42,119,28)
+ List = [("A500"), ("A500+"), ("A600"), ("A1200"), ("A1200/020"), ("A4000/040"), ("SUPER"), ("CDTV"), ("CD32")]
+ Text = ("A500")
+ }
+ { RadioButton1 RadioButton
+ Move(133,77,91,28)
+ Text = ("NTSC")
+ }
+ { RadioButton2 RadioButton
+ Move(294,77,84,28)
+ Text = ("PAL")
+ Value = True
+ }
+ }
+ { Frame3 Frame
+ Move(13,168,511,126)
+ ToolTip = ("Select emulation accurancy from -2 to 1\n-2 less accurancy but fast emulation\n1 better accurancy but slowly emulation\n")
+ Text = ("Emulation Accurancy 1")
+ { Label9 Label
+ Move(14,42,63,28)
+ Text = ("Fast")
+ }
+ { Slider1 Slider
+ Move(91,42,315,28)
+ MinValue = -2
+ MaxValue = 1
+ Value = 1
+ }
+ { Label10 Label
+ Move(420,42,77,28)
+ Text = ("Accurate")
+ }
+ }
+ Index = 3
+ Text = ("Game &Ports")
+ Picture = Picture["icon:/16/game"]
+ { Frame12 Frame
+ Move(1,7,133,168)
+ Text = ("Port 0")
+ { RadioButton29 RadioButton
+ Move(17,35,98,28)
+ Text = ("Auto")
+ Value = True
+ }
+ { RadioButton30 RadioButton
+ Move(17,63,98,28)
+ Text = ("Mouse")
+ }
+ { RadioButton31 RadioButton
+ Move(17,91,98,28)
+ Text = ("Keyboard")
+ }
+ }
+ { Frame11 Frame
+ Move(135,7,133,168)
+ Text = ("Port 1")
+ { RadioButton26 RadioButton
+ Move(17,35,98,28)
+ Text = ("Auto")
+ Value = True
+ }
+ { RadioButton27 RadioButton
+ Move(17,63,98,28)
+ Text = ("Mouse")
+ }
+ { RadioButton28 RadioButton
+ Move(17,91,98,28)
+ Text = ("Keyboard")
+ }
+ }
+ { Frame16 Frame
+ Move(269,7,133,168)
+ Text = ("Port 2")
+ { RadioButton32 RadioButton
+ Move(17,35,98,28)
+ Text = ("Auto")
+ Value = True
+ }
+ { RadioButton33 RadioButton
+ Move(17,63,98,28)
+ Text = ("Mouse")
+ }
+ { RadioButton34 RadioButton
+ Move(17,91,98,28)
+ Text = ("Keyboard")
+ }
+ }
+ { Frame17 Frame
+ Move(403,7,133,168)
+ Text = ("Port 3")
+ { RadioButton35 RadioButton
+ Move(17,35,98,28)
+ Text = ("Auto")
+ Value = True
+ }
+ { RadioButton36 RadioButton
+ Move(17,63,98,28)
+ Text = ("Mouse")
+ }
+ { RadioButton37 RadioButton
+ Move(17,91,98,28)
+ Text = ("Keyboard")
+ }
+ }
+ { Button51 Button
+ Move(206,189,126,28)
+ Text = ("Configure &Joystick")
+ }
+ { Label45 Label
+ Move(6,224,525,56)
+ Text = ("Joystick setup script developed by FrodeSolheim (author of FS-UAE).\nThe Joystick script requires Python (>= 2.6) to be installed")
+ Alignment = Align.Center
+ }
+ Index = 4
+ Text = ("&Hard Disks/CD-ROM")
+ Picture = Picture["icon:/16/harddisk"]
+ { Frame13 Frame
+ Move(13,7,511,161)
+ Text = ("Hard Disk")
+ { Panel6 Panel
+ Move(31,35,448,105)
+ { CheckBox4 CheckBox
+ Move(8,5,77,28)
+ ToolTip = ("Enable/Disable hdf")
+ Text = ("DH0:")
+ }
+ { TextBox6 TextBox
+ Move(98,5,229,28)
+ }
+ { Button33 Button
+ Move(339,5,28,28)
+ ToolTip = ("Select hdf file")
+ Picture = Picture["icon:/16/file"]
+ }
+ { Button37 Button
+ Move(375,5,28,28)
+ ToolTip = ("Select a folder")
+ Picture = Picture["icon:/16/directory"]
+ }
+ { Button34 Button
+ Move(411,5,28,28)
+ ToolTip = ("Remove hdf")
+ Picture = Picture["icon:/16/remove"]
+ }
+ { CheckBox6 CheckBox
+ Move(8,38,77,28)
+ ToolTip = ("Enable/Disable hdf")
+ Text = ("DH1:")
+ }
+ { TextBox19 TextBox
+ Move(98,38,229,28)
+ }
+ { Button35 Button
+ Move(339,38,28,28)
+ ToolTip = ("Select hdf file")
+ Picture = Picture["icon:/16/file"]
+ }
+ { Button38 Button
+ Move(375,38,28,28)
+ ToolTip = ("Select a folder")
+ Picture = Picture["icon:/16/directory"]
+ }
+ { Button36 Button
+ Move(411,38,28,28)
+ ToolTip = ("Remove hdf")
+ Picture = Picture["icon:/16/remove"]
+ }
+ { CheckBox9 CheckBox
+ Move(8,71,77,28)
+ ToolTip = ("Enable/Disable hdf")
+ Text = ("DH2:")
+ }
+ { TextBox22 TextBox
+ Move(98,71,229,28)
+ }
+ { Button39 Button
+ Move(339,71,28,28)
+ ToolTip = ("Select hdf file")
+ Picture = Picture["icon:/16/file"]
+ }
+ { Button40 Button
+ Move(375,71,28,28)
+ ToolTip = ("Select a folder")
+ Picture = Picture["icon:/16/directory"]
+ }
+ { Button41 Button
+ Move(411,71,28,28)
+ ToolTip = ("Remove hdf")
+ Picture = Picture["icon:/16/remove"]
+ }
+ }
+ }
+ { Frame14 Frame
+ Move(13,182,511,140)
+ Text = ("CD-ROM")
+ { Panel7 Panel
+ Move(31,35,448,77)
+ { CheckBox10 CheckBox
+ Move(8,5,91,28)
+ ToolTip = ("Enable/Disable CDROM")
+ Text = ("CDROM:")
+ }
+ { TextBox23 TextBox
+ Move(98,5,229,28)
+ }
+ { Button42 Button
+ Move(339,5,28,28)
+ ToolTip = ("Select a bin/cue - iso CDROM")
+ Picture = Picture["icon:/16/file"]
+ }
+ { Button44 Button
+ Move(375,5,28,28)
+ ToolTip = ("Remove CDROM image")
+ Picture = Picture["icon:/16/remove"]
+ }
+ { CheckBox11 CheckBox
+ Move(8,38,91,28)
+ ToolTip = ("Enable/Disable CDROM")
+ Text = ("CDROM_S:")
+ }
+ { TextBox24 TextBox
+ Move(98,38,229,28)
+ }
+ { Button45 Button
+ Move(339,38,28,28)
+ ToolTip = ("Select a bin/cue - iso CDROM")
+ Picture = Picture["icon:/16/file"]
+ }
+ { Button47 Button
+ Move(375,38,28,28)
+ ToolTip = ("Remove CDROM image")
+ Picture = Picture["icon:/16/remove"]
+ }
+ }
+ }
+ Index = 5
+ Text = ("&Net Play")
+ Picture = Picture["icon:/16/network"]
+ { Frame15 Frame
+ Move(146,14,245,210)
+ Text = ("Server")
+ { Panel8 Panel
+ Move(14,28,217,154)
+ { Label27 Label
+ Move(4,7,49,28)
+ Text = ("Server")
+ }
+ { TextBox27 TextBox
+ Move(58,7,154,28)
+ ToolTip = ("Select ip adress or host name")
+ }
+ { Label24 Label
+ Move(7,42,42,28)
+ Text = ("Port")
+ }
+ { TextBox25 TextBox
+ Move(58,42,154,28)
+ ToolTip = ("Set a custom port")
+ Text = ("25100")
+ MaxLength = 5
+ }
+ { Label25 Label
+ Move(7,77,42,28)
+ Text = ("Tag")
+ }
+ { TextBox26 TextBox
+ Move(58,77,154,28)
+ ToolTip = ("Sellect a nickname")
+ }
+ { Button43 Button
+ Move(14,112,28,28)
+ ToolTip = ("Start server")
+ Picture = Picture["icon:/22/server"]
+ }
+ { Button48 Button
+ Move(56,112,28,28)
+ Enabled = False
+ ToolTip = ("Stop Server")
+ Picture = Picture["icon:/22/close"]
+ }
+ { Label26 Label
+ Move(98,112,49,28)
+ Text = ("Player")
+ }
+ { ComboBox4 ComboBox
+ Move(161,112,42,28)
+ List = [("2"), ("3"), ("4"), ("5")]
+ Text = ("2")
+ }
+ }
+ }
+ { Label46 Label
+ Move(6,238,525,56)
+ Text = ("Netplay-server script developed by FrodeSolheim (author of FS-UAE).\nThe Netplay-server script requires Python (>= 2.6) to be installed")
+ Alignment = Align.Center
+ }
+ Index = 6
+ Text = ("&General")
+ Picture = Picture["icon:/22/tools"]
+ { Frame19 Frame
+ Move(6,7,525,420)
+ { Panel5 Panel
+ Move(7,10,511,35)
+ { Label6 Label
+ Move(16,3,119,28)
+ Text = ("Floppy Sound 20")
+ }
+ { Slider4 Slider
+ Move(145,3,98,28)
+ ToolTip = ("Set volume of Floppy")
+ Step = 5
+ Value = 20
+ }
+ { Label23 Label
+ Move(259,3,126,28)
+ Text = ("Floppy Speed 100")
+ }
+ { Slider5 Slider
+ Move(396,3,98,28)
+ ToolTip = ("Select read floppy speed from 0 to 800\n- 0 is Turbo mode ( in any case game don't load in turbo mode)")
+ MaxValue = 800
+ Step = 5
+ Value = 100
+ }
+ }
+ { Panel4 Panel
+ Move(7,55,511,35)
+ { CheckBox1 CheckBox
+ Move(7,3,105,28)
+ ToolTip = ("Start FS_UAE in full screen mode")
+ Text = ("Full Screen")
+ }
+ { Label36 Label
+ Move(161,3,154,28)
+ Text = ("Full Screen Resolution")
+ }
+ { TextBox31 TextBox
+ Move(323,3,70,28)
+ ToolTip = ("Specify the width of the FS-UAE fullscreen mode")
+ Text = ("800")
+ MaxLength = 4
+ }
+ { Label37 Label
+ Move(413,3,21,28)
+ Text = ("x")
+ }
+ { TextBox32 TextBox
+ Move(432,3,70,28)
+ ToolTip = ("Specify the height of the FS-UAE fullscreen mode")
+ Text = ("600")
+ MaxLength = 4
+ }
+ }
+ { Panel3 Panel
+ Move(7,100,511,35)
+ { CheckBox2 CheckBox
+ Move(7,3,105,28)
+ ToolTip = ("The amiga video output will be scaled with the aspect ratio intact, otherwise it will be stretched to fill the screen")
+ Text = ("Keep Aspect")
+ }
+ { Label20 Label
+ Move(119,3,84,28)
+ Text = ("Video - Sync")
+ }
+ { ComboBox3 ComboBox
+ Move(217,3,70,28)
+ ToolTip = ("Video sync behaviour can be configured with the “video_sync” option. The default setting is auto. The available values are:\noff – video and emulation will not be synced to the display\nvblank – video updates (but not emulation) will be synced to vblank to remove tearing\nfull – emulation and video will be synchronized to the display. This will give perfect scrolling if the display refresh rate matches the game’s refresh rate.\nauto – this sets video sync to “full” if the refresh rate matches the game’s refresh rate, and “vblank” if not.\n\nIt is generally recommended to leave this option at auto. If you change your desktop to a 50Hz screen mode, full synchronization and smooth scrolling will be enabled automatically.")
+ List = [("off"), ("vblank"), ("full"), ("auto")]
+ Text = ("auto")
+ }
+ { Label38 Label
+ Move(294,3,63,28)
+ Text = ("Method")
+ }
+ { ComboBox9 ComboBox
+ Move(364,3,140,28)
+ ToolTip = ("swap – Most basic method, but will probably not mantain emulation perfectly in sync on many GPUs due to pipelining and the swap buffers command returning before it is completed.\n\nswap-finish – This method should force the emulation to be in sync with the display, but glFinish may use a busy-loop and cause high CPU usage.\n\nfinish-swap-finish – Execute a glFinish also before swapping buffers. \nMay work better on some OpenGL drivers. Otherwise same as swap-finish (High CPU usage with many drivers).\n\nfinish-sleep-swap-finish – Same as above, but sleep before executing the swap command. \nThe sleep time is based on the (estimated) time left to the next vblank. \nThis should reduce the CPU usage a lot, but on some systems/drivers, the display will not be synchronized properly.\n\nsleep-swap-finish – Same as above, but do not issue a glFinish before starting the sleep. \nThis should use less CPU than the above option, but some drivers seem to need the finish before the sleep to achieve proper sync.\n\nswap-fence – Set a fence after swapping buffers and poll periodically (with sleeps) for completion of swap. \nThis seems to work very well on Linux with recent nVIDIA drivers.\n\nswap-sleep-fence – Same as above, but sleep a while (based on estimated time to next vblank) before starting to pool the fence, \n(in case the test-fence method blocks with high CPU usage, problem seen on nVIDIA/Windows).")
+ List = [("swap"), ("swap-finish"), ("finish-swap-finish"), ("finish-sleep-swap-finish"), ("sleep-swap-finish"), ("swap-fence"), ("swap-sleep-fence")]
+ Text = ("finish-swap-finish")
+ }
+ }
+ { Panel10 Panel
+ Move(7,145,511,35)
+ { Label35 Label
+ Move(9,3,63,28)
+ Text = ("Fs Mode")
+ }
+ { ComboBox8 ComboBox
+ Move(81,3,98,28)
+ ToolTip = ("You can set this option to fullscreen-window instead if you want to use the fake fullscreen support \n(a borderless window the size of the current display will be created instead of switching to real fullscreen)")
+ List = [("fullscreen"), ("fullscreen-window")]
+ Text = ("fullscreen")
+ }
+ { Label33 Label
+ Move(188,3,126,28)
+ Text = ("Window Resolution")
+ }
+ { TextBox29 TextBox
+ Move(323,3,70,28)
+ ToolTip = ("Specify the width of the FS-UAE window to create on start-up")
+ }
+ { Label34 Label
+ Move(413,3,21,28)
+ Text = ("x")
+ }
+ { TextBox30 TextBox
+ Move(432,3,70,28)
+ ToolTip = ("Specify the height of the FS-UAE window to create on start-up")
+ }
+ }
+ { Panel2 Panel
+ Move(7,190,511,35)
+ { CheckBox3 CheckBox
+ Move(7,3,98,28)
+ Text = ("Scanlines")
+ }
+ { Label21 Label
+ Move(128,3,63,28)
+ Text = ("Dark 5")
+ }
+ { Slider3 Slider
+ Move(206,3,98,28)
+ Step = 5
+ Value = 5
+ }
+ { Label22 Label
+ Move(319,3,63,28)
+ Text = ("Light 10")
+ }
+ { Slider2 Slider
+ Move(397,3,98,28)
+ Step = 5
+ Value = 10
+ }
+ }
+ { Panel12 Panel
+ Move(7,236,511,35)
+ { Label19 Label
+ Move(6,3,91,28)
+ Text = ("Anti-Aliasing")
+ }
+ { ComboBox2 ComboBox
+ Move(103,3,42,28)
+ ToolTip = ("Set to , 4 or 8 to enable full-scene anti-aliasing (reduces jagged edges in menu-mode). Specify 2 for 2x, 4 for 4x, etc. This may negatively impact rendering performance, depending on your GPU.")
+ List = [("0"), ("2"), ("4"), ("8")]
+ Text = ("0")
+ }
+ { Label31 Label
+ Move(151,3,91,28)
+ Text = ("Video Format")
+ }
+ { ComboBox6 ComboBox
+ Move(248,3,70,28)
+ ToolTip = ("Changing the video and texture buffer formats may improve texture upload speed on your computer.\nThis option allows you to configure whether video data is stored in RGBA or BGRA byte order in the internal video buffers. \nThis setting (together with texture_format) may affect texture upload performance. \nThe option should not be shared between computers, since appropriate values may differ for other operating system or OpenGL GPU vendors")
+ List = [("rgba"), ("bgra")]
+ Text = ("bgra")
+ }
+ { Label32 Label
+ Move(322,3,105,28)
+ Text = ("Texture Format")
+ }
+ { ComboBox7 ComboBox
+ Move(432,3,70,28)
+ ToolTip = ("This option allows you to configure the internal texture format used by OpenGL for displaying the Amiga video frames. \nThis setting (together with video_format) may affect texture upload performance. \nThe option should not be shared between computers, since appropriate values may differ for other operating system or OpenGL GPU vendors")
+ List = [("rgb"), ("rgb8"), ("rgba"), ("rgba8")]
+ Text = ("rgb")
+ }
+ }
+ { Panel13 Panel
+ Move(7,282,511,35)
+ { Label39 Label
+ Move(15,3,56,28)
+ Text = ("Shaders")
+ }
+ { ComboBox10 ComboBox
+ Move(86,3,140,28)
+ Text = ("None")
+ }
+ { CheckBox14 CheckBox
+ Move(241,3,126,28)
+ Text = ("Low Resolution")
+ Value = CheckBox.True
+ }
+ { CheckBox15 CheckBox
+ Move(383,3,112,28)
+ Text = ("Double Line")
+ }
+ }
+ { Panel14 Panel
+ Move(7,328,511,35)
+ ToolTip = ("All values divided by 10\nExample:\n-10 = -1")
+ { Label41 Label
+ Move(7,3,49,28)
+ Text = ("Scale x")
+ }
+ { SpinBox1 SpinBox
+ Move(63,3,63,28)
+ MinValue = -100
+ Value = -10
+ }
+ { Label42 Label
+ Move(133,3,49,28)
+ Text = ("Scale y")
+ }
+ { SpinBox2 SpinBox
+ Move(189,3,63,28)
+ MinValue = -100
+ Value = -10
+ }
+ { Label43 Label
+ Move(259,3,49,28)
+ Text = ("Align x")
+ }
+ { SpinBox3 SpinBox
+ Move(315,3,63,28)
+ MinValue = -100
+ Value = 5
+ }
+ { Label44 Label
+ Move(385,3,49,28)
+ Text = ("Align y")
+ }
+ { SpinBox4 SpinBox
+ Move(441,3,63,28)
+ MinValue = -10
+ MaxValue = 10
+ Value = 5
+ }
+ }
+ { Panel9 Panel
+ Move(7,374,511,35)
+ { Label29 Label
+ Move(14,3,133,28)
+ Text = ("Custom Parameters")
+ }
+ { TextBox28 TextBox
+ Move(161,3,336,28)
+ ToolTip = ("Set your custom parameters\n*Example:\n--hard-drive-0-label=My Volume --keep-aspect=0")
+ }
+ }
+ }
+ { Panel11 Panel
+ Move(14,434,511,35)
+ { CheckBox12 CheckBox
+ Move(7,3,168,28)
+ Text = ("Load Inside G-FS-UAE")
+ }
+ { CheckBox13 CheckBox
+ Move(350,3,154,28)
+ Text = ("Disable Translations")
+ }
+ }
+ Index = 7
+ Text = ("Pa&th")
+ Picture = Picture["icon:/22/directory"]
+ { Frame1 Frame
+ Move(13,7,511,287)
+ Text = ("Path")
+ { Label1 Label
+ Move(29,28,133,28)
+ Text = ("Configuration Path")
+ }
+ { TextBox1 TextBox
+ Move(192,28,231,28)
+ }
+ { Button4 Button
+ Move(434,28,28,28)
+ Text = ("...")
+ }
+ { Label2 Label
+ Move(29,56,133,28)
+ Text = ("Controllers")
+ }
+ { TextBox2 TextBox
+ Move(192,56,231,28)
+ }
+ { Button5 Button
+ Move(434,56,28,28)
+ Text = ("...")
+ }
+ { Label3 Label
+ Move(29,84,133,28)
+ Text = ("Flash Memory")
+ }
+ { TextBox3 TextBox
+ Move(192,84,231,28)
+ }
+ { Button6 Button
+ Move(434,84,28,28)
+ Text = ("...")
+ }
+ { Label4 Label
+ Move(29,112,133,28)
+ Text = ("Floppy Overlays")
+ }
+ { TextBox4 TextBox
+ Move(192,112,231,28)
+ }
+ { Button7 Button
+ Move(434,112,28,28)
+ Text = ("...")
+ }
+ { Label5 Label
+ Move(29,140,133,28)
+ Text = ("Kickstarts")
+ }
+ { TextBox5 TextBox
+ Move(192,140,231,28)
+ }
+ { Button8 Button
+ Move(434,140,28,28)
+ Text = ("...")
+ }
+ { Label7 Label
+ Move(29,168,133,28)
+ Text = ("Save States")
+ }
+ { TextBox7 TextBox
+ Move(192,168,231,28)
+ }
+ { Button10 Button
+ Move(434,168,28,28)
+ Text = ("...")
+ }
+ { Label28 Label
+ Move(29,196,133,28)
+ Text = ("ADF/IPF Folder")
+ }
+ { TextBox21 TextBox
+ Move(192,196,231,28)
+ }
+ { Button49 Button
+ Move(434,196,28,28)
+ Text = ("...")
+ }
+ { Label40 Label
+ Move(29,224,133,28)
+ Text = ("Shaders")
+ }
+ { TextBox33 TextBox
+ Move(192,224,231,28)
+ ToolTip = ("No selection will load built-in FS-UAE shaders")
+ }
+ { Button9 Button
+ Move(434,224,28,28)
+ Text = ("...")
+ }
+ { Button50 Button
+ Move(469,224,28,28)
+ ToolTip = ("Refresh Shaders")
+ Picture = Picture["icon:/16/refresh"]
+ }
+ }
+ Index = 8
+ Text = ("")
+ Picture = Picture["icon:/22/screen"]
+ { embEmbedder Embedder
+ Move(3,2,532,469)
+ Background = &H000000&
+ ToolTip = ("- Press middle mouse button to swap from FS-UAE to GUI\n- Press Alt+Enter to swap from window/GUI mode to Full Screen mode")
+ }
+ Index = 0
+ }
+}
diff --git a/bin/G-FSUAE/G-FS-UAE/.src/leggi.module b/bin/G-FSUAE/G-FS-UAE/.src/leggi.module
new file mode 100644
index 0000000..1f76967
--- /dev/null
+++ b/bin/G-FSUAE/G-FS-UAE/.src/leggi.module
@@ -0,0 +1,278 @@
+' Gambas module file
+
+Private opzione As New String[]
+Private tipo As String
+Private valore As String
+
+Public Sub leggi_conf()
+Dim hFile As File
+Dim sLine As String
+
+hFile = Open (fmain.ComboBox5.Text) For Input
+While Not Eof(hFile)
+ Line Input #hFile, sLine
+ Try opzione = Split(sline, "\n")
+ confronta
+ Wend
+Close #hFile
+
+End
+
+Public Sub confronta()
+ Dim a As Integer
+ Dim risultato As String
+ For Each risultato In opzione
+ Try a = InStr(risultato, "=")
+ If a <> 0 Then
+ tipo = Left(risultato, a - 2)
+ valore = Mid(risultato, a + 2, a + 100)
+ assegna
+ End If
+ Next
+End
+
+Public Sub assegna()
+
+ Select Case tipo
+ Case "amiga_model"
+ FMain.ComboBox1.text = valore
+ Case "accurancy"
+ FMain.slider1.value = Val(valore)
+ Case "ntsc_mode"
+ If valore = "1" Then
+ FMain.radiobutton1.value = True
+ Else If valore = "0" Then
+ FMain.RadioButton2.value = True
+ End If
+ Case "#mAmiga"
+ Select Case valore
+ Case "500"
+ fmain.RadioButton3.value = True
+ fmain.TextBox9.text = ""
+ Case "1200"
+ fmain.RadioButton4.value = True
+ fmain.TextBox8.text = ""
+ End Select
+
+ Case "kickstart_file"
+ If fmain.RadioButton3.value = True Then fmain.TextBox8.text = valore
+ If fmain.RadioButton4.value = True Then fmain.TextBox9.text = valore
+ Case "kickstart_ext_file"
+ fmain.TextBox10.text = valore
+
+ Case "chip_memory"
+ Select Case Left(valore, 1)
+ Case "5"
+ FMain.RadioButton5.value = True
+ Case "1"
+ FMain.RadioButton6.value = True
+ Case "2"
+ FMain.RadioButton7.value = True
+ Case "4"
+ FMain.RadioButton8.value = True
+ Case "8"
+ FMain.RadioButton9.value = True
+ End Select
+
+ Case "slow_memory"
+ Select Case Trim(Left(valore, 2))
+ Case "51"
+ FMain.RadioButton10.value = True
+ Case "10"
+ FMain.RadioButton11.value = True
+ Case "17"
+ FMain.RadioButton12.value = True
+ Case "0"
+ FMain.RadioButton13.value = True
+ End Select
+
+ Case "fast_memory"
+ Select Case Left(valore, 1)
+ Case "0"
+ FMain.RadioButton14.value = True
+ Case "1"
+ FMain.RadioButton15.value = True
+ Case "2"
+ FMain.RadioButton16.value = True
+ Case "4"
+ FMain.RadioButton17.value = True
+ Case "8"
+ FMain.RadioButton18.value = True
+ End Select
+
+ Case "zorro_iii_memory"
+ Select Case Left(valore, 2)
+ Case "0"
+ FMain.RadioButton19.value = True
+ Case "10"
+ FMain.RadioButton20.value = True
+ Case "20"
+ FMain.RadioButton21.value = True
+ Case "40"
+ FMain.RadioButton22.value = True
+ Case "81"
+ FMain.RadioButton23.value = True
+ Case "16"
+ FMain.RadioButton24.value = True
+ Case "32"
+ FMain.RadioButton25.value = True
+ Case "65"
+ FMain.RadioButton38.value = True
+ Case "13"
+ FMain.RadioButton39.value = True
+ Case "26"
+ FMain.RadioButton40.value = True
+ End Select
+
+ Case "uae_gfxcard_size"
+ fmain.Slider6.value = valore
+
+ Case "joystick_port_0"
+ Select Case valore
+ Case "auto"
+ FMain.RadioButton29.value = True
+ Case "mouse"
+ FMain.RadioButton30.value = True
+ Case "keyboard"
+ FMain.RadioButton31.value = True
+ End Select
+
+ Case "joystick_port_1"
+ Select Case valore
+ Case "auto"
+ FMain.RadioButton26.value = True
+ Case "mouse"
+ FMain.RadioButton27.value = True
+ Case "keyboard"
+ FMain.RadioButton28.value = True
+ End Select
+
+ Case "joystick_port_2"
+ Select Case valore
+ Case "auto"
+ FMain.RadioButton32.value = True
+ Case "mouse"
+ FMain.RadioButton33.value = True
+ Case "keyboard"
+ FMain.RadioButton34.value = True
+ End Select
+
+ Case "joystick_port_3"
+ Select Case valore
+ Case "auto"
+ FMain.RadioButton35.value = True
+ Case "mouse"
+ FMain.RadioButton36.value = True
+ Case "keyboard"
+ FMain.RadioButton37.value = True
+ End Select
+
+ Case "#df1"
+ If valore = "-1" Then
+ FMain.CheckBox8.Value = CBool(valore)
+ Else
+ FMain.CheckBox8.Value = False
+ End If
+ Case "#df2"
+ If valore = "-1" Then
+ FMain.CheckBox7.Value = CBool(valore)
+ Else
+ FMain.CheckBox7.Value = False
+ End If
+ Case "#df3"
+ If valore = "-1" Then
+ FMain.CheckBox5.Value = CBool(valore)
+ Else
+ FMain.CheckBox5.Value = False
+ End If
+ Case "floppy_drive_0"
+ fmain.TextBox20.text = valore
+ Case "floppy_drive_1"
+ fmain.TextBox16.text = valore
+ Case "floppy_drive_2"
+ fmain.TextBox17.text = valore
+ Case "floppy_drive_3"
+ fmain.TextBox18.text = valore
+ Case "floppy_image_0"
+ fmain.TextBox15.text = valore
+ Case "floppy_image_1"
+ fmain.TextBox11.text = valore
+ Case "floppy_image_2"
+ fmain.TextBox12.text = valore
+ Case "floppy_image_3"
+ fmain.TextBox13.text = valore
+ Case "floppy_image_4"
+ fmain.TextBox14.text = valore
+
+ Case "#hd0"
+ If valore = "-1" Then
+ FMain.CheckBox4.Value = CBool(valore)
+ Else
+ FMain.CheckBox4.Value = False
+ End If
+ Case "#hd1"
+ If valore = "-1" Then
+ FMain.CheckBox6.Value = CBool(valore)
+ Else
+ FMain.CheckBox6.Value = False
+ End If
+ Case "#hd2"
+ If valore = "-1" Then
+ FMain.CheckBox9.Value = CBool(valore)
+ Else
+ FMain.CheckBox9.Value = False
+ End If
+ Case "hard_drive_0"
+ FMain.TextBox6.text = valore
+ Case "hard_drive_1"
+ FMain.TextBox19.text = valore
+ Case "hard_drive_2"
+ FMain.TextBox22.text = valore
+ Case "#cd0"
+ If valore = "-1" Then
+ FMain.CheckBox10.Value = CBool(valore)
+ Else
+ FMain.CheckBox10.Value = False
+ End If
+ Case "#cd1"
+ If valore = "-1" Then
+ FMain.CheckBox11.Value = CBool(valore)
+ Else
+ FMain.CheckBox11.Value = False
+ End If
+ Case "cdrom_drive_0"
+ FMain.TextBox23.text = valore
+ Case "cdrom_image_0"
+ FMain.TextBox24.text = valore
+
+ Case "floppy_drive_volume"
+ FMain.slider4.value = Val(valore)
+ Case "floppy_drive_speed"
+ FMain.slider5.value = Val(valore)
+ Case "fullscreen"
+ fmain.CheckBox1.value = CBool(Val(valore))
+ Case "keep_aspect"
+ fmain.CheckBox2.value = CBool(Val(valore))
+ Case "fsaa"
+ fmain.combobox2.text = valore
+ Case "video_sync"
+ fmain.ComboBox3.text = valore
+ Case "scanlines"
+ fmain.CheckBox3.value = CBool(Val(valore))
+ Case "scanlines_dark"
+ FMain.slider3.value = Val(valore)
+ Case "scanlines_light"
+ FMain.slider2.value = Val(valore)
+ Case "#shad"
+ fmain.ComboBox10.text = valore
+ Case "line_doubling"
+ fmain.CheckBox14.value = CBool(Val(valore))
+ Case "low_resolution"
+ fmain.CheckBox15.value = CBool(Val(valore))
+ End Select
+
+
+End
+
+
+
diff --git a/bin/G-FSUAE/G-FS-UAE/.src/scrivi.module b/bin/G-FSUAE/G-FS-UAE/.src/scrivi.module
new file mode 100644
index 0000000..656aca9
--- /dev/null
+++ b/bin/G-FSUAE/G-FS-UAE/.src/scrivi.module
@@ -0,0 +1,220 @@
+' Gambas module file
+
+Public valori As String
+
+Public Sub sConfig()
+ Dim amiga, paths, jinput, media, audio, speed, video As String
+ Dim model, accurancy, ntsc, kickstart, exkickstart, chip, slow, cFast, zorro, picasso As String
+ Dim df1, df2, df3, df4, hdf0, hdf1, hdf2, cd0, cd1 As String
+ Dim fulls, fsaa, sync, aspect, escan, lscan, dscan, eShaders, shad, double, low As String
+ Dim port0, port1, port2, port3 As String
+ Dim ab1, ab2, ab3, ab4, ab5, ab6, ab7, ab8 As String
+
+ 'amiga
+ '
+ model = "amiga_model = " & FMain.ComboBox1.text & "\n"
+ accurancy = "accurancy = " & FMain.Slider1.value & "\n"
+ If FMain.RadioButton1.value = True Then
+ ntsc = "ntsc_mode = 1" & "\n"
+ Else
+ ntsc = "ntsc_mode = 0" & "\n"
+ Endif
+ If FMain.RadioButton3.value = True Then
+ kickstart = "#mAmiga = 500" & "\n" & "kickstart_file = " & FMain.textbox8.text & "\n"
+ Else
+ kickstart = "#mAmiga = 1200" & "\n" & "kickstart_file = " & FMain.textbox9.text & "\n"
+ End If
+ exkickstart = "kickstart_ext_file = " & FMain.textbox10.text & "\n"
+
+
+ If FMain.RadioButton5.value = True
+ chip = "chip_memory = 512" & "\n"
+ Else If FMain.RadioButton6.value = True
+ chip = "chip_memory = 1024" & "\n"
+ Else If FMain.RadioButton7.value = True
+ chip = "chip_memory = 2048" & "\n"
+ Else If FMain.RadioButton8.value = True
+ chip = "chip_memory = 4096" & "\n"
+ Else If FMain.RadioButton9.value = True
+ chip = "chip_memory = 8192" & "\n"
+ End If
+
+
+ If FMain.RadioButton13.value = True
+ slow = "slow_memory = 0" & "\n"
+ Else If FMain.RadioButton10.value = True
+ slow = "slow_memory = 512" & "\n"
+ Else If FMain.RadioButton11.value = True
+ slow = "slow_memory = 1024" & "\n"
+ Else If FMain.RadioButton12.value = True
+ slow = "slow_memory = 1792" & "\n"
+ End If
+
+
+ If FMain.RadioButton14.value = True
+ cFast = "fast_memory = 0" & "\n"
+ Else If FMain.RadioButton15.value = True
+ cFast = "fast_memory = 1024" & "\n"
+ Else If FMain.RadioButton16.value = True
+ cFast = "fast_memory = 2048" & "\n"
+ Else If FMain.RadioButton17.value = True
+ cFast = "fast_memory = 4096" & "\n"
+ Else If FMain.RadioButton18.value = True
+ cFast = "fast_memory = 8192" & "\n"
+ End If
+
+
+ If FMain.RadioButton19.value = True
+ zorro = "zorro_iii_memory = 0" & "\n"
+ Else If FMain.RadioButton20.value = True
+ zorro = "zorro_iii_memory = 1024" & "\n"
+ Else If FMain.RadioButton21.value = True
+ zorro = "zorro_iii_memory = 2048" & "\n"
+ Else If FMain.RadioButton22.value = True
+ zorro = "zorro_iii_memory = 4096" & "\n"
+ Else If FMain.RadioButton23.value = True
+ zorro = "zorro_iii_memory = 8192" & "\n"
+ Else If FMain.RadioButton24.value = True
+ zorro = "zorro_iii_memory = 16384" & "\n"
+ Else If FMain.RadioButton25.value = True
+ zorro = "zorro_iii_memory = 32768" & "\n"
+ Else If FMain.RadioButton38.value = True
+ zorro = "zorro_iii_memory = 65536" & "\n"
+ Else If FMain.RadioButton39.value = True
+ zorro = "zorro_iii_memory = 131072" & "\n"
+ Else If FMain.RadioButton40.value = True
+ zorro = "zorro_iii_memory = 262144" & "\n"
+ End If
+
+ picasso = "uae_gfxcard_size = " & FMain.slider6.value & "\n"
+
+ amiga = "[amiga]" & "\n" & "\n" & model & accurancy & ntsc & kickstart & exkickstart & chip & slow & cFast & zorro & picasso
+
+ '-------------------------------------------------------
+
+ 'paths
+
+ paths = "\n" & "\n" & "[paths]" & "\n" & "\n" & "kickstarts_dir = " & FMain.TextBox5.text & "\n" & "save_states_dir = " & FMain.TextBox7.text & "\n" &
+ "floppy_overlays_dir = " & FMain.TextBox4.text & "\n" & "flash_memory_dir = " & FMain.TextBox3.text & "\n" &
+ "controllers_dir = " & FMain.textbox2.text & "\n" & "base_dir = " & FMain.textbox1.text & "\n"
+
+
+ '-------------------------------------------------------
+ '
+ 'inputs
+
+ If FMain.RadioButton29.value = True Then
+ port0 = "joystick_port_0 = auto" & "\n"
+ Else If FMain.RadioButton30.value = True Then
+ port0 = "joystick_port_0 = mouse" & "\n"
+ Else If FMain.RadioButton31.value = True Then
+ port0 = "joystick_port_0 = keyboard" & "\n"
+ Endif
+
+ If FMain.RadioButton26.value = True Then
+ port1 = "joystick_port_1 = auto" & "\n"
+ Else If FMain.RadioButton27.value = True Then
+ port1 = "joystick_port_1 = mouse" & "\n"
+ Else If FMain.RadioButton28.value = True Then
+ port1 = "joystick_port_1 = keyboard" & "\n"
+ Endif
+
+ If FMain.RadioButton32.value = True Then
+ port2 = "joystick_port_2 = auto" & "\n"
+ Else If FMain.RadioButton33.value = True Then
+ port2 = "joystick_port_2 = mouse" & "\n"
+ Else If FMain.RadioButton34.value = True Then
+ port2 = "joystick_port_2 = keyboard" & "\n"
+ Endif
+
+ If FMain.RadioButton35.value = True Then
+ port3 = "joystick_port_3 = auto" & "\n"
+ Else If FMain.RadioButton36.value = True Then
+ port3 = "joystick_port_3 = mouse" & "\n"
+ Else If FMain.RadioButton37.value = True Then
+ port3 = "joystick_port_3 = keyboard" & "\n"
+ Endif
+
+ jinput = "\n" & "\n" & "[input]" & "\n" & "\n" & port0 & port1 & port2 & port3 & "\n"
+
+ '-------------------------------------------------------
+ '
+ 'media
+ '
+ If FMain.CheckBox8.Value = False Then ab1 = "#"
+ If FMain.CheckBox7.Value = False Then ab2 = "#"
+ If FMain.CheckBox5.Value = False Then ab3 = "#"
+ If FMain.CheckBox4.Value = False Then ab4 = "#"
+ If FMain.CheckBox6.Value = False Then ab5 = "#"
+ If FMain.CheckBox9.Value = False Then ab6 = "#"
+ If FMain.CheckBox10.Value = False Then ab7 = "#"
+ If FMain.CheckBox11.Value = False Then ab8 = "#"
+
+ df1 = "#df1 = " & FMain.CheckBox8.Value & "\n" & ab1 & "floppy_drive_1 = " & FMain.TextBox16.text & "\n"
+ df2 = "#df2 = " & FMain.CheckBox7.Value & "\n" & ab2 & "floppy_drive_2 = " & FMain.TextBox17.text & "\n"
+ df3 = "#df3 = " & FMain.CheckBox5.Value & "\n" & ab3 & "floppy_drive_3 = " & FMain.TextBox18.text & "\n"
+
+ hdf0 = "#hd0 = " & FMain.CheckBox4.Value & "\n" & ab4 & "hard_drive_0 = " & FMain.TextBox6.text & "\n"
+ hdf1 = "#hd1 = " & FMain.CheckBox6.value & "\n" & ab5 & "hard_drive_1 = " & FMain.TextBox19.text & "\n"
+ hdf2 = "#hd2 = " & FMain.CheckBox9.value & "\n" & ab6 & "hard_drive_2 = " & FMain.TextBox22.text & "\n"
+
+ cd0 = "#cd0 = " & FMain.CheckBox10.value & "\n" & ab7 & "cdrom_drive_0 = " & FMain.TextBox23.text & "\n"
+ cd1 = "#cd1 = " & FMain.CheckBox11.value & "\n" & ab8 & "cdrom_image_0 = " & FMain.TextBox24.text & "\n"
+ '
+ media = "\n" & "\n" & "[media]" & "\n" & "\n" & "floppy_drive_0 = " & FMain.TextBox20.text & "\n" & df1 & df2 & df3 & "floppy_image_0 = " & FMain.TextBox15.text & "\n" &
+ "floppy_image_1 = " & FMain.TextBox11.text & "\n" & "floppy_image_2 = " & FMain.TextBox12.text & "\n" & "floppy_image_3 = " & FMain.TextBox13.text & "\n" &
+ "floppy_image_4 = " & FMain.TextBox14.text & "\n" & hdf0 & hdf1 & hdf2 & cd0 & cd1
+
+
+ '-------------------------------------------------------
+ '
+ '
+ 'preferences floppy
+ '
+ audio = "\n" & "\n" & "[audio]" & "\n" & "\n" & "floppy_drive_volume = " & FMain.slider4.value & "\n"
+ speed = "floppy_drive_speed = " & FMain.slider5.value & "\n"
+
+ '-------------------------------------------------------
+ '
+ 'video
+ '
+ If FMain.CheckBox1.value = True Then
+ fulls = "fullscreen = 1" & "\n"
+ Else
+ fulls = "fullscreen = 0" & "\n"
+ End If
+ fsaa = "fsaa = " & FMain.combobox2.text & "\n"
+ sync = "video_sync = " & FMain.combobox3.text & "\n"
+ If FMain.CheckBox2.value = True Then
+ aspect = "keep_aspect = 1" & "\n"
+ Else
+ aspect = "keep_aspect = 0" & "\n"
+ End If
+ If FMain.CheckBox3.value = True Then
+ escan = "scanlines = 1" & "\n"
+ Else
+ escan = "scanlines = 0" & "\n"
+ End If
+ If FMain.CheckBox14.value = True Then
+ double = "line_doubling = 1" & "\n"
+ Else
+ double = "line_doubling = 0" & "\n"
+ Endif
+ If FMain.CheckBox15.value = True Then
+ low = "low_resolution = 1" & "\n"
+ Else
+ low = "low_resolution = 0" & "\n"
+ Endif
+ dscan = "scanlines_dark = " & FMain.slider3.value & "\n"
+ lScan = "scanlines_light = " & FMain.slider2.value & "\n"
+ eShaders = "shader = " & FMain.inshader & "/" & FMain.ComboBox10.text & ".shader" & "\n"
+ shad = "#shad = " & FMain.ComboBox10.text & "\n"
+ video = "\n" & "\n" & "[video]" & "\n" & "\n" & fulls & fsaa & sync & aspect & escan & dscan & lscan & eShaders & shad & double & low
+
+ '-------------------------------------------------------
+ '
+ ''scrittura
+
+ valori = amiga & paths & jinput & media & audio & speed & video
+End
+
diff --git a/bin/G-FSUAE/G-FS-UAE/.src/setting.module b/bin/G-FSUAE/G-FS-UAE/.src/setting.module
new file mode 100644
index 0000000..9c50b77
--- /dev/null
+++ b/bin/G-FSUAE/G-FS-UAE/.src/setting.module
@@ -0,0 +1,66 @@
+' Gambas module file
+
+Public Sub rSetting()
+FMain.TextBox8.text = Settings["kick500", FMain.TextBox8.text]
+FMain.TextBox9.text = Settings["kick1200", FMain.TextBox9.text]
+FMain.TextBox10.text = Settings["kickCD32", FMain.TextBox10.text]
+FMain.combobox5.text = Settings["fsuaeConf", FMain.combobox5.text]
+FMain.TextBox25.text = Settings["server", FMain.TextBox25.text]
+FMain.TextBox26.text = Settings["port", FMain.TextBox26.text]
+FMain.TextBox27.text = Settings["tag", FMain.TextBox27.text]
+FMain.TextBox1.text = Settings["pconf", FMain.TextBox1.text]
+FMain.TextBox2.text = Settings["pcont", FMain.TextBox2.text]
+FMain.TextBox3.text = Settings["pmem", FMain.TextBox3.text]
+FMain.TextBox4.text = Settings["pover", FMain.TextBox4.text]
+FMain.TextBox5.text = Settings["pkick", FMain.TextBox5.text]
+FMain.TextBox7.text = Settings["psave", FMain.TextBox7.text]
+FMain.TextBox21.text = Settings["padf", FMain.TextBox21.text]
+FMain.TextBox28.text = Settings["custom", FMain.TextBox28.text]
+FMain.combobox6.text = Settings["videof", FMain.combobox6.text]
+FMain.combobox7.text = Settings["textf", FMain.combobox7.text]
+FMain.combobox8.text = Settings["fsmode", FMain.combobox8.text]
+FMain.TextBox29.text = Settings["xwin", FMain.TextBox29.text]
+FMain.TextBox30.text = Settings["ywin", FMain.TextBox30.text]
+FMain.TextBox31.text = Settings["xfus", FMain.TextBox31.text]
+FMain.TextBox32.text = Settings["yfus", FMain.TextBox32.text]
+FMain.combobox9.text = Settings["vsmode", FMain.combobox9.text]
+FMain.TextBox33.text = Settings["dir_shaders", FMain.TextBox33.text]
+FMain.SpinBox1.value = Settings["scalex", FMain.SpinBox1.value]
+FMain.SpinBox2.value = Settings["scaley", FMain.SpinBox2.value]
+FMain.SpinBox3.value = Settings["alignx", FMain.SpinBox3.value]
+FMain.SpinBox4.value = Settings["aligny", FMain.SpinBox4.value]
+FMain.checkbox13.value = Settings["translation", FMain.checkbox13.value]
+End
+
+Public Sub wSetting()
+Settings["kick500"] = FMain.TextBox8.text
+Settings["kick1200"] = FMain.TextBox9.text
+Settings["kickCD32"] = FMain.TextBox10.text
+Settings["fsuaeConf"] = FMain.combobox5.text
+Settings["server"] = FMain.TextBox25.text
+Settings["port"] = FMain.TextBox26.text
+Settings["tag"] = FMain.TextBox27.text
+Settings["pconf"] = FMain.TextBox1.text
+Settings["pcont"] = FMain.TextBox2.text
+Settings["pmem"] = FMain.TextBox3.text
+Settings["pover"] = FMain.TextBox4.text
+Settings["pkick"] = FMain.TextBox5.text
+Settings["psave"] = FMain.TextBox7.text
+Settings["padf"] = FMain.TextBox21.text
+Settings["custom"] = FMain.TextBox28.text
+Settings["videof"] = FMain.combobox6.text
+Settings["textf"] = FMain.combobox7.text
+Settings["fsmode"] = FMain.combobox8.text
+Settings["xwin"] = FMain.TextBox29.text
+Settings["ywin"] = FMain.TextBox30.text
+Settings["xfus"] = FMain.TextBox31.text
+Settings["yfus"] = FMain.TextBox32.text
+Settings["vsmode"] = FMain.combobox9.text
+Settings["dir_shaders"] = FMain.TextBox33.text
+Settings["scalex"] = FMain.SpinBox1.value
+Settings["scaley"] = FMain.SpinBox2.value
+Settings["alignx"] = FMain.SpinBox3.value
+Settings["aligny"] = FMain.SpinBox4.value
+Settings["translation"] = FMain.checkbox13.value
+End
+
diff --git a/bin/G-FSUAE/G-FS-UAE/.startup b/bin/G-FSUAE/G-FS-UAE/.startup
new file mode 100644
index 0000000..028e999
--- /dev/null
+++ b/bin/G-FSUAE/G-FS-UAE/.startup
@@ -0,0 +1,13 @@
+FMain
+G-FS-UAE
+0
+0
+0.0.39
+
+gb.image
+gb.qt4
+gb.form
+gb.desktop
+gb.settings
+gb.qt4.ext
+
diff --git a/bin/G-FSUAE/G-FS-UAE/G-FS-UAE.gambas b/bin/G-FSUAE/G-FS-UAE/G-FS-UAE.gambas
new file mode 100755
index 0000000..77e84d7
--- /dev/null
+++ b/bin/G-FSUAE/G-FS-UAE/G-FS-UAE.gambas
Binary files differ
diff --git a/bin/G-FSUAE/G-FS-UAE/README b/bin/G-FSUAE/G-FS-UAE/README
new file mode 100644
index 0000000..0c07bfb
--- /dev/null
+++ b/bin/G-FSUAE/G-FS-UAE/README
@@ -0,0 +1,19 @@
+THIS IS A SIMPLE GUI FOR FS-UAE AMIGA EMULATOR
+
+At the moment this is a beta program but all options working well.
+
+You must have gambas3 dependencies installed in your os
+For debian/ubuntu (derivate) user you can add ppa from:
+ppa:nemh/gambas3
+
+A precompiled executable is included in source package, is not necessary to compile the source, simple double click on G-FS-UAE.gambas.
+
+Rapid Start: (a temporany 'file.config' will be created in the folder "home/.gfsuae"
+- Select all options from GUI
+- Start emu
+
+Normal usage: (Files saved with this method can be invoked via the combobox menu)
+- Select all options from GUI
+- Save 'yourfile' .config
+- Open/Select 'yourfile' .config
+- Start emu \ No newline at end of file
diff --git a/bin/G-FSUAE/G-FS-UAE/amiga.png b/bin/G-FSUAE/G-FS-UAE/amiga.png
new file mode 100644
index 0000000..bcc0055
--- /dev/null
+++ b/bin/G-FSUAE/G-FS-UAE/amiga.png
Binary files differ
diff --git a/bin/G-FSUAE/G-FS-UAE/fs-uae-game-server-1.3.1.py b/bin/G-FSUAE/G-FS-UAE/fs-uae-game-server-1.3.1.py
new file mode 100644
index 0000000..7c9086a
--- /dev/null
+++ b/bin/G-FSUAE/G-FS-UAE/fs-uae-game-server-1.3.1.py
@@ -0,0 +1,725 @@
+"""
+FS-UAE Netplay Game Server
+Copyright (C) 2012 Frode Solheim
+
+This library is free software; you can redistribute it and/or modify it
+under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or (at
+your option) any later version.
+
+This library 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 Lesser General Public
+License for more details.
+
+You should have received a copy of the GNU Lesser General Public License
+along with this library; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+"""
+from __future__ import print_function
+
+import sys
+import time
+from collections import deque
+import socket
+import traceback
+import threading
+import random
+from hashlib import sha1
+
+SERVER_PROTOCOL_VERSION = 1
+MAX_PLAYERS = 6
+
+if sys.version > '3':
+ PYTHON3 = True
+else:
+ PYTHON3 = False
+
+if PYTHON3:
+ def int_to_bytes(number):
+ return bytes([(number & 0xff000000) >> 24, (number & 0x00ff0000) >> 16,
+ (number & 0x0000ff00) >> 8, (number & 0x000000ff)])
+ def bytes_to_int(m):
+ return m[0] << 24 | m[1] << 16 | m[2] << 8 | m[3]
+ def byte_ord(v):
+ #print("byte_ord", v)
+ try:
+ return v[0]
+ except TypeError:
+ return v
+ def byte(v):
+ return bytes([v])
+ server_protocol_version = byte(SERVER_PROTOCOL_VERSION)
+
+else:
+ def int_to_bytes(number):
+ return chr((number & 0xff000000) >> 24) + \
+ chr((number & 0x00ff0000) >> 16) + \
+ chr((number & 0x0000ff00) >> 8) + \
+ chr((number & 0x000000ff))
+ def bytes_to_int(m):
+ return ord(m[0]) << 24 | ord(m[1]) << 16 | ord(m[2]) << 8 | ord(m[3])
+ def byte_ord(v):
+ return ord(v)
+ def byte(v):
+ return v
+ server_protocol_version = chr(SERVER_PROTOCOL_VERSION)
+
+max_drift = 25
+num_clients = 2
+port = 25100
+host = "0.0.0.0"
+game = None
+game_password = 0
+launch_timeout = 0
+
+def create_game_password(value):
+ # for python 2 + 3 compatibility
+ #if not isinstance(value, unicode):
+ value = value.encode("UTF-8")
+ #print(repr(value))
+ h = sha1()
+ h.update(b"FSNP")
+ val = b""
+ for v in value:
+ if byte_ord(v) < 128:
+ val += byte(v)
+ #print("update:", repr(val))
+ h.update(val)
+ return bytes_to_int(h.digest()[:4])
+
+for arg in sys.argv:
+ if arg.startswith("--"):
+ parts = arg[2:].split("=", 1)
+ if len(parts) == 2:
+ key, value = parts
+ key = key.lower()
+ if key == "port":
+ port = int(value)
+ elif key == "players":
+ num_clients = int(value)
+ elif key == "password":
+ #game_password = crc32(value) & 0xffffffff
+ game_password = create_game_password(value)
+ print("game password (numeric) is", game_password)
+ elif key == "launch-timeout":
+ launch_timeout = int(value)
+
+
+MESSAGE_READY = 0
+MESSAGE_MEM_CHECK = 5
+MESSAGE_RND_CHECK = 6
+MESSAGE_PING = 7
+MESSAGE_PLAYERS = 8
+MESSAGE_PLAYER_TAG_0 = 9
+MESSAGE_PLAYER_TAG_1 = 10
+MESSAGE_PLAYER_TAG_2 = 11
+MESSAGE_PLAYER_TAG_3 = 12
+MESSAGE_PLAYER_TAG_4 = 13
+MESSAGE_PLAYER_TAG_5 = 14
+
+MESSAGE_PLAYER_PING = 15
+MESSAGE_PLAYER_LAG = 16
+MESSAGE_SET_PLAYER_TAG = 17
+MESSAGE_PROTOCOL_VERSION = 18
+MESSAGE_EMULATION_VERSION = 19
+MESSAGE_ERROR = 20
+MESSAGE_TEXT = 21
+MESSAGE_SESSION_KEY = 22
+
+#MESSAGE_MEM_CHECK = 5
+#MESSAGE_RND_CHECK = 6
+#MESSAGE_PING = 7
+
+MESSAGE_MEMCHECK_MASK = (0x80000000 | (MESSAGE_MEM_CHECK << 24))
+MESSAGE_RNDCHECK_MASK = (0x80000000 | (MESSAGE_RND_CHECK << 24))
+
+ERROR_PROTOCOL_MISMATCH = 1
+ERROR_WRONG_PASSWORD = 2
+ERROR_CANNOT_RESUME = 3
+ERROR_GAME_ALREADY_STARTED = 4
+ERROR_PLAYER_NUMBER = 5
+ERROR_EMULATOR_MISMATCH = 6
+ERROR_CLIENT_ERROR = 7
+ERROR_MEMORY_DESYNC = 8
+ERROR_RANDOM_DESYNC = 9
+ERROR_SESSION_KEY = 10
+ERROR_GAME_STOPPED = 99
+
+def create_ext_message(ext, data):
+ return 0x80000000 | ext << 24 | (data & 0xffffff)
+
+class Client:
+
+ def __init__(self, socket, address):
+ #self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ self.socket = socket
+ self.messages = []
+ self.lock = threading.Lock()
+ self.address = address
+ self.ready = 0
+ self.tag = b"PLY"
+ self.player = 0
+ self.playing = False
+ self.frame = 0
+ self.frame_times = [0.0 for x in range(100)]
+ self.lag = 0.0
+ self.out_seq = 0
+ self.memcheck = [(0, 0) for x in range(100)]
+ self.rndcheck = [(0, 0) for x in range(100)]
+ self.ping_sent_at = 0
+ self.pings = deque([0 for x in range(10)])
+ self.pings_sum = 0
+ self.pings_avg = 0
+ #self.protocol_version = 0
+ self.emulator_version = b""
+ self.session_key = 0
+
+
+ self.temp_a = 0
+ self.temp_b = 0
+ self.temp_c = 0
+
+ threading.Thread(target=self.__thread_function).start()
+
+ def send_error_message(self, error_num):
+ print(self, "error", error_num)
+ message = 0x80000000 | (MESSAGE_ERROR) << 24 | error_num
+ self.send_message(message)
+
+ def send_message(self, message):
+ with self.lock:
+ #if message == 0x87000000:
+ # #print("queueing %08x" % (message,))
+ # #traceback.print_stack()
+ # self.temp_c += 1
+ self.__send_data(int_to_bytes(message))
+
+ def __send_data(self, data):
+ #if data[0] == '\x87':
+ # #print("queueing ping")
+ # #traceback.print_stack()
+ # self.temp_c += 1
+ self.socket.sendall(data)
+
+ def queue_message(self, message):
+ with self.lock:
+ #if message == 0x87000000:
+ # #print("queueing %08x" % (message,))
+ # #traceback.print_stack()
+ # self.temp_c += 1
+ #print("queueing %08x" % (message,))
+ #if message & 0x20000000:
+ # traceback.print_stack()
+ self.messages.append(int_to_bytes(message))
+ if len(self.messages) == 100:
+ self.__send_queued_messages()
+
+ def queue_bytes(self, message):
+ with self.lock:
+ self.messages.append(message)
+ if len(self.messages) == 100:
+ self.__send_queued_messages()
+
+ def send_queued_messages(self):
+ with self.lock:
+ self.__send_queued_messages()
+
+ def __send_queued_messages(self):
+ data = b"".join(self.messages)
+ self.messages[:] = []
+ #print("sending", repr(data))
+ self.__send_data(data)
+
+ def initialize_client(self):
+ print("initialize", self)
+ def read_bytes(num):
+ data = b""
+ for i in range(num):
+ data = data + self.socket.recv(1)
+ if not len(data) == num:
+ raise Exception("did not get {0} bytes".format(num))
+ return data
+ # check header message
+ data = read_bytes(4)
+ if data == b"PING":
+ # connection check only
+ self.__send_data(b"PONG")
+ return False
+ if data != b"FSNP":
+ print(data)
+ raise Exception("did not get expected FSNP header")
+ # check protocol version
+ data = self.socket.recv(1)
+ #print(repr(data), repr(server_protocol_version))
+ if data != server_protocol_version:
+ print("protocol mismatch")
+ self.send_error_message(ERROR_PROTOCOL_MISMATCH)
+ return False
+ # check net play password
+ password = bytes_to_int(read_bytes(4))
+ if password != game_password:
+ print("wrong password")
+ self.send_error_message(ERROR_WRONG_PASSWORD)
+ return False
+ # read emulator version
+ self.emulator_version = read_bytes(8)
+ # read player number and session key, session key is checked to
+ # make sure another client cannot hijack this player slot
+ self.session_key = bytes_to_int(b"\0" + read_bytes(3))
+ self.player = byte_ord(self.socket.recv(1))
+ self.tag = read_bytes(3)
+ # get package sequence number
+ self.resume_from_packet = bytes_to_int(read_bytes(4))
+
+ error = game.add_client(self)
+ if error:
+ self.send_error_message(error)
+ return False
+
+ message = create_ext_message(MESSAGE_SESSION_KEY, self.session_key)
+ self.queue_message(message)
+ data = (self.player << 8) | num_clients
+ message = create_ext_message(MESSAGE_PLAYERS, data)
+ self.queue_message(message)
+
+ game.send_player_tags(self)
+ self.send_queued_messages()
+ print("initialize done for", self)
+ return True
+
+ def __thread_function(self):
+ try:
+ try:
+ if not self.initialize_client():
+ print("initialize failed for", self)
+ return
+ self.receive_loop()
+ except Exception:
+ traceback.print_exc()
+ game.stop = True
+ finally:
+ try:
+ self.socket.close()
+ except Exception:
+ pass
+
+
+ def receive_loop(self):
+ data = b""
+ count = 0
+ while not game.stop:
+ data = data + self.socket.recv(4 - count)
+ count = len(data)
+ if count == 4:
+ self.on_message(data)
+ count = 0
+ data = b""
+
+ def send_ping(self):
+ with self.lock:
+ #print("ping?", self.ping_sent_at)
+ if self.ping_sent_at == 0:
+ self.temp_a += 1
+ self.ping_sent_at = time.time()
+ message = int_to_bytes(0x80000000 | (7 << 24))
+ self.__send_data(message)
+ assert self.ping_sent_at > 0
+
+ def on_ping(self):
+ # may not need lock here
+ with self.lock:
+ self.temp_b += 1
+ if self.temp_a != self.temp_b:
+ print(self.temp_a, self.temp_b, self.temp_c)
+ assert self.ping_sent_at > 0
+ t = time.time()
+ new = (t - self.ping_sent_at) / 1.0
+ #print(t, "-", self.ping_sent_at, "=", new)
+ old = self.pings.popleft()
+ self.pings.append(new)
+ #print(self.pings)
+ self.pings_sum = self.pings_sum - old + new
+ self.pings_avg = self.pings_sum / len(self.pings)
+ self.ping_sent_at = 0
+
+ def on_message(self, m):
+ message = bytes_to_int(m)
+ #with game.lock:
+ # if not self.playing:
+ # print(self, "is no longer playing/valid, ignoring message")
+
+ if message & 0x80000000:
+ # ext message
+ command = (message & 0x7f000000) >> 24
+ data = message & 0x00ffffff;
+ if command == MESSAGE_MEM_CHECK:
+ self.memcheck[self.frame % 100] = (data, self.frame)
+ elif command == MESSAGE_RND_CHECK:
+ self.rndcheck[self.frame % 100] = (data, self.frame)
+ elif command == MESSAGE_PING:
+ #print("{0:x}".format(message))
+ self.on_ping()
+ elif command == MESSAGE_TEXT:
+ print("received text command")
+ remaining = data
+ text = b""
+ while not game.stop:
+ part = self.socket.recv(remaining)
+ count = len(part)
+ text += part
+ remaining = remaining - count
+ if remaining == 0:
+ game.add_text_message(self, text)
+ break
+
+ elif message & (1 << 30):
+ frame = message & 0x3fffffff
+ #print("received frame", frame)
+ if frame != self.frame + 1:
+ print("error, expected frame", self.frame + 1, "got", frame)
+ #print("received frame", frame)
+ self.frame = frame
+ t = time.time()
+ self.frame_times[self.frame % 100] = t
+ game_t = game.frame_times[self.frame % 100]
+ self.lag = t - game_t
+
+ elif message & (1 << 29):
+ game.add_input_event(self, message & 0x00ffffff)
+
+ else:
+ print("warning: unknown command received %x" % (message,))
+
+ def __str__(self):
+ return "<Client {0}:{1} {2}>".format(self.player, self.tag,
+ self.address)
+
+def create_session_key():
+ return random.randint(0, 2**24 - 1)
+
+class Game:
+
+ def __init__(self, num_clients):
+ self.started = False
+ self.frame = 0
+ self.time = 0
+ self.clients = []
+ self.num_clients = 0
+ self.frame_times = [0.0 for x in range(100)]
+ self.lock = threading.Lock()
+ self.stop = False
+ self.session_keys = [0 for x in range(MAX_PLAYERS)]
+ self.emulator_version = b""
+ self.verified_frame = -1
+
+ def __start(self):
+ if len(self.clients) != num_clients:
+ printf("error - cannot start until all players have connected")
+ return
+ print("{0} clients connected, starting game".format(num_clients))
+ self.started = True
+ threading.Thread(target=self.__thread_function).start()
+
+ def add_client(self, client):
+ with self.lock:
+ if client.player == 0xff:
+ if client.resume_from_packet != 0:
+ return ERROR_CLIENT_ERROR
+ if self.started:
+ return ERROR_GAME_ALREADY_STARTED
+ client.player = len(self.clients)
+ if client.player == 0:
+ self.emulator_version = client.emulator_version
+ else:
+ if self.emulator_version != client.emulator_version:
+ return ERROR_EMULATOR_MISMATCH
+ client.session_key = create_session_key()
+ self.session_keys[client.player] = client.session_key
+ self.clients.append(client)
+ client.playing = True
+ if not self.started:
+ if len(self.clients) == num_clients:
+ self.__start()
+ else:
+ if client.player >= len(self.clients):
+ return ERROR_PLAYER_NUMBER
+ if self.session_keys[client.player] != client.session_key:
+ return ERROR_SESSION_KEY
+ old_client = self.clients[client.player]
+ # FIXME: must transfer settings for resuming to work
+ self.clients[client.player] = client
+ client.playing = True
+
+ if client.resume_from_packet > 0:
+ # cannot resume yet...
+ print("cannot resume at packet", resume_from_packet)
+ return ERROR_CANNOT_RESUME
+ return 0
+ # FIXME: start the game..?
+
+ def __thread_function(self):
+ try:
+ self.__game_loop()
+ except Exception:
+ traceback.print_exc()
+ self.stop = True
+
+ def __send_player_tags(self, send_to_client):
+ for i, client in enumerate(game.clients):
+ data = bytes_to_int(b"\0" + client.tag)
+ message = create_ext_message(MESSAGE_PLAYER_TAG_0 + i, data)
+ send_to_client.queue_message(message)
+
+ def send_player_tags(self, client):
+ with game.lock:
+ self.__send_player_tags(client)
+
+ def __game_loop(self):
+ with self.lock:
+ for client in self.clients:
+ self.__send_player_tags(client)
+ print("game loop running")
+ self.time = time.time()
+ while True:
+ if self.stop:
+ print("stopping game loop")
+ # try to send error message to all players
+ with self.lock:
+ for client in self.clients:
+ try:
+ client.send_error_message(ERROR_GAME_STOPPED)
+ except Exception:
+ traceback.print_exc()
+ return
+ self.__game_loop_iteration()
+
+ def __game_loop_iteration(self):
+ # FIXME: higher precision sleep?
+ target_time = self.time + 0.02
+ t2 = time.time()
+ diff = target_time - t2
+ sleep = diff - 0.001
+ if sleep > 0.0:
+ #print(sleep)
+ time.sleep(sleep)
+ self.time = target_time
+ while time.time() < target_time:
+ # busy-loop until target time
+ pass
+
+ with self.lock:
+ if self.frame % 100 == 0:
+ self.__send_status()
+ self.frame += 1
+ self.frame_times[self.frame % 100] = time.time()
+ message = (1 << 30) | self.frame
+ self.__send_to_clients(message, True)
+ if self.frame % 10 == 0:
+ for client in self.clients:
+ client.send_ping()
+ if self.frame % 200 == 0:
+ self.__print_status()
+
+ self.__check_game()
+
+ def __check_game(self):
+ oldest_frame, frames = self.__check_frame_status()
+ while oldest_frame > self.verified_frame:
+ self.check_synchronization(self.verified_frame + 1)
+ self.verified_frame += 1
+ diff = self.frame - oldest_frame
+ if diff <= max_drift:
+ # clients have not drifted too far
+ return
+ first = True
+ count = 0;
+ while diff > 0 and not self.stop:
+ if first:
+ first = False
+ print("---", self.frame, "acked", frames)
+ elif count % 100 == 0:
+ print(" ", self.frame, "acked", frames)
+ time.sleep(0.02)
+ oldest_frame, frames = self.__check_frame_status()
+ diff = self.frame - oldest_frame
+ count += 1
+ self.time = time.time() - 0.02
+
+ def __check_frame_status(self):
+ oldest_frame = self.frame
+ frames = []
+ with self.lock:
+ for client in self.clients:
+ af = client.frame
+ if af < oldest_frame:
+ oldest_frame = af
+ frames.append(af)
+ return oldest_frame, frames
+
+ def __print_status(self):
+ for i, client in enumerate(self.clients):
+ print("{0} f {1:6d} p avg: {2:3d} {3:3d}".format(i,
+ client.frame, int(client.pings_avg * 1000),
+ int(client.lag * 1000)))
+
+ def __send_status(self):
+ for i, client in enumerate(self.clients):
+ v = int(client.lag * 1000) & 0x0000ffff
+ message = 0x80000000 | MESSAGE_PLAYER_LAG << 24 | i << 16 | v
+ self.__send_to_clients(message)
+ v = int(client.pings_avg * 1000) & 0x0000ffff
+ message = 0x80000000 | MESSAGE_PLAYER_PING << 24 | i << 16 | v
+ self.__send_to_clients(message)
+
+ def add_input_event(self, client, input_event):
+ if not self.started:
+ # game was not started - ignoring input event
+ print("game not started, ignoring input event {0:08x}".format(
+ input_event))
+ return
+ with self.lock:
+ if not client.playing:
+ print("client", client, "is no longer valid, dropping msg")
+ return
+ # for now, just broadcast out again to all clients
+ message = (1 << 29) | input_event
+ self.__send_to_clients(message)
+
+ def add_text_message(self, from_client, text):
+ print("add text message")
+ with self.lock:
+ for client in self.clients:
+ #if from_client == client:
+ # continue
+ message = 0x80000000 | MESSAGE_TEXT << 24 \
+ | from_client.player << 16 | len(text)
+ message = int_to_bytes(message) + text
+ print("send", repr(message), "to", client)
+ client.queue_bytes(message)
+
+ def send_to_clients(self, message, force_send=False):
+ # using a lock here to ensure that send_to_clients can
+ # be called from multiple threads, but all clients will
+ # still get messages in expected order
+ with self.lock:
+ self.__send_to_clients(message, force_send)
+
+ def __send_to_clients(self, message, force_send=False):
+ for client in self.clients:
+ #print("send", message, "to", client)
+ client.queue_message(message)
+ if force_send:
+ client.send_queued_messages()
+
+ def check_synchronization(self, check_frame):
+ # FIXME: MOVE TO GAME CLASS
+ # FIXME: ONLY RUN ONCE PER FRAME, not once per frame * clients
+ #if self.frame == 0:
+ # return
+ with game.lock:
+ #print("received memcheck", data)
+ #check_frame = self.frame - 1
+ if check_frame < 0:
+ return
+ index = check_frame % 100
+ mem_check_data = []
+ rnd_check_data = []
+ for client in game.clients:
+ if client.frame <= check_frame:
+ # cannot check yet
+ return
+ #print(client, client.frame, client.memcheck)
+ mem_check_data.append((client, client.memcheck[index]))
+ rnd_check_data.append((client, client.rndcheck[index]))
+ check = mem_check_data[0][1]
+ for client, data in mem_check_data:
+ if check != data:
+ print("memcheck failed for frame", check_frame)
+ for c, d in mem_check_data:
+ print("* {0:08x} f {1:05d} {2}".format(d[0], d[1], c))
+ for c in game.clients:
+ c.send_error_message(ERROR_MEMORY_DESYNC)
+ c.send_queued_messages()
+ raise Exception("mem check failed")
+ check = rnd_check_data[0][1]
+ for client, data in rnd_check_data:
+ if check != data:
+ print("rndcheck failed for frame", check_frame)
+ for c, d in rnd_check_data:
+ print("* {0:08x} f {1:05d} {2}".format(d[0], d[1], c))
+ for c in game.clients:
+ c.send_error_message(ERROR_RANDOM_DESYNC)
+ c.send_queued_messages()
+ raise Exception("rnd check failed")
+
+address_map = {
+
+}
+
+def accept_client(server_socket):
+ server_socket.settimeout(1)
+ client_socket, client_address = server_socket.accept()
+ #client_socket.setsockopt(socket.SOL_SOCKET, socket.SO_SNDLOWAT, 4)
+ client_socket.setsockopt(socket.SOL_TCP, socket.TCP_NODELAY, 1)
+ client_socket.settimeout(None)
+ client = Client(client_socket, client_address)
+ #client.player = len(game.clients) + 1
+ print("Client connected", client)
+ #game.add_client(client)
+ #client.start()
+ #if game.can_start():
+ # game.start()
+
+#stop_accepting = False
+
+def accept_thread(server_socket):
+ while not game.stop:
+ try:
+ accept_client(server_socket)
+ except socket.timeout:
+ pass
+ except Exception:
+ traceback.print_exc()
+ time.sleep(1.0)
+
+def _run_server():
+ global game
+ game = Game(num_clients)
+ address = (host, port)
+ server_socket = socket.socket()
+ server_socket.bind(address)
+ server_socket.listen(4)
+ if address[0] != "0.0.0.0":
+ print("server listening on", address[0], "port", address[1])
+ else:
+ print("server listening on port", address[1])
+ print("want", num_clients, "client(s)")
+ if num_clients > MAX_PLAYERS:
+ print("ERROR: max clients are", MAX_PLAYERS)
+ threading.Thread(target=accept_thread, args=(server_socket,)).start()
+ t1 = time.time()
+ while not game.started:
+ time.sleep(0.1)
+ if launch_timeout:
+ t2 = time.time()
+ if t2 - t1 > launch_timeout:
+ print("game not started yet, aborting")
+ game.stop = True
+ return
+ #sys.exit()
+ print("game started")
+ while not game.stop:
+ time.sleep(0.1)
+
+def run_server():
+ try:
+ _run_server()
+ except Exception:
+ traceback.print_exc()
+ except KeyboardInterrupt:
+ traceback.print_exc()
+ game.stop = True
+
+if __name__ == "__main__":
+ run_server()
diff --git a/bin/G-FSUAE/G-FS-UAE/fs-uae-gamepad-config.py b/bin/G-FSUAE/G-FS-UAE/fs-uae-gamepad-config.py
new file mode 100644
index 0000000..5c22feb
--- /dev/null
+++ b/bin/G-FSUAE/G-FS-UAE/fs-uae-gamepad-config.py
@@ -0,0 +1,364 @@
+import os
+import sys
+from cStringIO import StringIO
+import wx
+import pygame
+
+windows = sys.platform == "win32"
+linux = sys.platform.startswith("linux")
+macosx = sys.platform == "darwin"
+if windows:
+ platform = "windows"
+if linux:
+ platform = "linux"
+if macosx:
+ platform = "macosx"
+
+if windows:
+ from win32com.shell import shell, shellcon
+ import win32api
+
+joysticks = []
+joystick_names = []
+current_joystick = None
+current_joystick_index = None
+
+def unicode_path(path):
+ if isinstance(path, unicode):
+ return path
+ return path.decode(sys.getfilesystemencoding())
+
+def get_home_dir():
+ if windows:
+ path = shell.SHGetFolderPath(0, shellcon.CSIDL_PROFILE, 0, 0)
+ return path
+ return unicode_path(os.path.expanduser("~"))
+
+def get_documents_dir(allow_create=True):
+ if windows:
+ path = shell.SHGetFolderPath(0, shellcon.CSIDL_PERSONAL, 0, 0)
+ else:
+ path = os.path.join(get_home_dir(), "Documents")
+ path = unicode_path(path)
+ if allow_create and not os.path.isdir(path):
+ os.makedirs(path)
+ return path
+
+def set_current_joystick(index):
+ global current_joystick
+ global current_joystick_index
+ current_joystick = joysticks[index]
+ current_joystick_index = index
+
+def save_config():
+ if not current_joystick:
+ pass
+
+ config = []
+ config.append("# {0}".format(current_joystick.get_name().strip()))
+ #config.append("# buttons: {0}".format(current_joystick.get_numbuttons()))
+ #config.append("# axes: {0}".format(current_joystick.get_numaxes()))
+ #config.append("# hats: {0}".format(current_joystick.get_numhats()))
+ #config.append("# balls: {0}".format(current_joystick.get_numballs()))
+ #config.append("# platform: {0}".format(platform))
+ config.append("")
+ config.append("[default]")
+ config.append("include = universal_gamepad")
+ for i, button in enumerate(buttons):
+ panel = button_panels[i]
+ if panel.event:
+ config.append("{0} = {1}".format(panel.event, button[3]))
+
+ file_name = get_joystick_id() + ".conf"
+ dest = os.path.join(get_documents_dir(), "FS-UAE", "Controllers")
+ if not os.path.exists(dest):
+ os.makedirs(dest)
+ dest = os.path.join(dest, file_name)
+ f = open(dest, "w")
+ for line in config:
+ f.write(line)
+ f.write("\n")
+ f.close()
+
+def get_joystick_id():
+ if not current_joystick:
+ return ""
+ name = current_joystick.get_name()
+ buttons = current_joystick.get_numbuttons()
+ axes = current_joystick.get_numaxes()
+ hats = current_joystick.get_numhats()
+ balls = current_joystick.get_numballs()
+ name_lower = name.lower()
+ name = ""
+ for c in name_lower:
+ if c in "abcdefghijklmnopqrstuvwxyz0123456789":
+ name = name + c
+ else:
+ if not name.endswith("_"):
+ name = name + "_"
+ name = name.strip("_")
+ return "{0}_{1}_{2}_{3}_{4}_{5}".format(name, buttons, axes, hats,
+ balls, platform)
+
+def init_joysticks():
+ #num_joys = wx.Joystick(-1).GetNumberJoysticks()
+ #for i in range(1):
+ # joy = wx.Joystick(wx.JOYSTICK1)
+ # print(joy)
+ # joysticks.append(joy)
+ # joystick_names.append(joy.ProductName)
+ pygame.init()
+ pygame.joystick.init()
+ num_joys = pygame.joystick.get_count()
+ for i in range(num_joys):
+ joy = pygame.joystick.Joystick(i)
+ joy.init()
+ joysticks.append(joy)
+ joystick_names.append(joy.get_name())
+
+buttons = [
+ (160, 240, -1, "dpad_left"),
+ (160, 160, -1, "dpad_right"),
+ (160, 200, -1, "dpad_up"),
+ (160, 280, -1, "dpad_down"),
+
+ (160, 400, -1, "lstick_left"),
+ (320, 400, -1, "lstick_right"),
+ (160, 360, -1, "lstick_up"),
+ (160, 440, -1, "lstick_down"),
+ (320, 440, -1, "lstick_button"),
+
+ (480, 400, 1, "rstick_left"),
+ (640, 400, 1, "rstick_right"),
+ (640, 360, 1, "rstick_up"),
+ (640, 440, 1, "rstick_down"),
+ (480, 440, 1, "rstick_button"),
+
+ (640, 160, 1, "west_button"),
+ (640, 200, 1, "north_button"),
+ (640, 240, 1, "east_button"),
+ (640, 280, 1, "south_button"),
+
+ (320, 80, -1, "select_button"),
+ (480, 80, 1, "start_button"),
+ (480, 40, 1, "menu_button"),
+
+ (160, 40, -1, "left_shoulder"),
+ (160, 80, -1, "left_trigger"),
+ (640, 40, 1, "right_shoulder"),
+ (640, 80, 1, "right_trigger"),
+]
+
+button_panels = [
+
+]
+
+class MapDialog(wx.Dialog):
+
+ def __init__(self, name):
+ wx.Dialog.__init__(self, frame, -1, "Press Joystick Button")
+ self.name = name
+ self.event = None
+ self.timer = wx.Timer(self)
+ self.Bind(wx.EVT_TIMER, self.on_timer)
+ self.timer.Start(100, wx.TIMER_CONTINUOUS)
+ self.CenterOnParent()
+
+ def set_result(self, event):
+ self.timer.Stop()
+ self.event = event
+ # remove event from other panels
+ for panel in button_panels:
+ if self == panel:
+ continue
+ if panel.event == event:
+ panel.event = None
+ panel.Refresh()
+ self.EndModal(wx.ID_OK)
+
+ def on_timer(self, event):
+ #print("on_timer")
+ pygame.event.pump()
+ if not current_joystick:
+ return
+ joy = current_joystick
+ for i in range(joy.get_numbuttons()):
+ if joy.get_button(i):
+ return self.set_result("button_" + str(i))
+ for i in range(joy.get_numaxes()):
+ value = joy.get_axis(i)
+ if value < -0.8:
+ pos = "neg"
+ elif value > 0.8:
+ pos = "pos"
+ else:
+ pos = None
+ if pos:
+ return self.set_result("axis_" + str(i) + "_" + pos)
+ for i in range(joy.get_numhats()):
+ value = joy.get_hat(i)
+ if value[1] == 1:
+ pos = "up"
+ elif value[1] == -1:
+ pos = "down"
+ elif value[0] == -1:
+ pos = "left"
+ elif value[0] == 1:
+ pos = "right"
+ else:
+ pos = None
+ if pos:
+ return self.set_result("hat_" + str(i) + "_" + pos)
+
+
+class Button(wx.Panel):
+
+ def __init__(self, parent, position, direction, name):
+ size = (120, 22)
+ if direction < 0:
+ position = (position[0] - size[0], position[1])
+ wx.Panel.__init__(self, parent, -1, position, size)
+ self.name = name
+ self.event = None
+ self.direction = direction
+ self.Bind(wx.EVT_PAINT, self.on_paint)
+ self.Bind(wx.EVT_LEFT_DOWN, self.on_click)
+ self.SetCursor(wx.StockCursor(wx.CURSOR_HAND))
+ self.SetBackgroundColour(wx.WHITE)
+
+ def on_paint(self, event):
+ #print("on_paint")
+ dc = wx.PaintDC(self)
+ dc.SetFont(self.GetFont())
+ if self.event:
+ text = self.event
+ dc.SetTextForeground(wx.Colour(0x00, 0x80, 0x00))
+ else:
+ text = "click to configure"
+ dc.SetTextForeground(wx.RED)
+ tw, th = dc.GetTextExtent(text)
+ y = (self.GetSize()[1] - th) / 2
+ if self.direction > 0:
+ x = 4
+ else:
+ x = self.GetSize()[0] - 4 - tw
+ dc.DrawText(text, x, y)
+
+ def on_click(self, event):
+ dialog = MapDialog(self.name)
+ result = dialog.ShowModal()
+ #print(result, wx.ID_OK)
+ if result == wx.ID_OK:
+ self.event = dialog.event
+ #print(self.event)
+ self.Refresh()
+ dialog.Destroy()
+
+class Frame(wx.Frame):
+
+ def __init__(self):
+ wx.Frame.__init__(self, None, -1,
+ "Joystick/Gamepad Configuration Tool")
+ self.frame_sizer = wx.BoxSizer(wx.VERTICAL)
+ self.SetSizer(self.frame_sizer)
+ self.panel = wx.Panel(self)
+ self.frame_sizer.Add(self.panel, 1, wx.EXPAND)
+ self.panel_sizer = wx.BoxSizer(wx.VERTICAL)
+ self.panel.SetSizer(self.panel_sizer)
+
+ self.top_panel = wx.Panel(self.panel)
+ self.panel_sizer.Add(self.top_panel, 0, wx.EXPAND)
+ self.top_sizer = wx.BoxSizer(wx.HORIZONTAL)
+ self.top_panel.SetSizer(self.top_sizer)
+
+ self.line_panel = wx.Panel(self.panel, size=(1, 1))
+ self.line_panel.SetBackgroundColour(wx.Colour(0xaa, 0xaa, 0xaa))
+ self.panel_sizer.Add(self.line_panel, 0, wx.EXPAND)
+
+ self.joystick_choice = wx.Choice(self.top_panel, -1,
+ wx.DefaultPosition, wx.DefaultSize, joystick_names)
+ self.joystick_choice.Bind(wx.EVT_CHOICE, self.on_choice)
+ self.top_sizer.Add(self.joystick_choice, 0,
+ wx.ALIGN_CENTER_VERTICAL | wx.ALL, 10)
+
+ self.id_label = wx.StaticText(self.top_panel, -1, "")
+ self.top_sizer.Add(self.id_label, 0,
+ wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 10)
+
+ self.top_sizer.AddStretchSpacer(1)
+
+ self.save_button = wx.Button(self.top_panel, -1, "Save")
+ self.save_button.Bind(wx.EVT_BUTTON, self.on_save)
+ self.top_sizer.Add(self.save_button, 0,
+ wx.ALIGN_CENTER_VERTICAL | wx.ALL, 10)
+
+ self.hor_sizer = wx.BoxSizer(wx.HORIZONTAL)
+ self.panel_sizer.Add(self.hor_sizer, 1, wx.EXPAND)
+ #with open("joyconfig.png", "rb") as f:
+ # data = f.read()
+ #with open("joyconfig.txt", "wb") as f:
+ # f.write("bitmap = ")
+ # f.write(repr(data))
+ # f.write("\n")
+ stream = StringIO(bitmap)
+ image = wx.ImageFromStream(stream)
+ #self.bitmap = wx.Bitmap("joyconfig.png")
+ self.bitmap = wx.BitmapFromImage(image)
+ self.joystick_panel = wx.Panel(self.panel, -1, wx.DefaultPosition,
+ self.bitmap.GetSize())
+ self.joystick_panel.Bind(wx.EVT_PAINT, self.on_paint)
+ self.hor_sizer.Add(self.joystick_panel, 0, 0)
+ self.help_ctrl = wx.TextCtrl(self.panel, -1, help_text,
+ style=wx.TE_MULTILINE | wx.TE_READONLY | wx.NO_BORDER)
+ self.hor_sizer.Add(self.help_ctrl, 1, wx.EXPAND)
+ self.hor_sizer.SetItemMinSize(self.help_ctrl, (180, -1))
+
+ if len(joystick_names) > 0:
+ self.joystick_choice.SetSelection(0)
+ self.on_choice(None)
+
+ self.Fit()
+
+ for x, y, direction, name in buttons:
+ b = Button(self.joystick_panel, (x, y + 4), direction, name)
+ button_panels.append(b)
+
+ def on_save(self, event):
+ save_config()
+
+ def on_paint(self, event):
+ dc = wx.PaintDC(self.joystick_panel)
+ dc.DrawBitmap(self.bitmap, 0, 0)
+
+ def on_choice(self, event):
+ index = self.joystick_choice.GetSelection()
+ set_current_joystick(index)
+ for panel in button_panels:
+ panel.event = None
+ panel.Refresh()
+ self.id_label.SetLabel(get_joystick_id())
+
+help_text = """
+INSTRUCTIONS
+
+The joysticks listed are those connected when you started the program. If you connect more, you must restart the program!
+
+Your gamepad may not look exactly like this, so just try to map the buttons as closely as possibly.
+
+Some gamepads do not have a "menu" button or similar, in which case you can skip configuring this.
+
+Some gamepads have the d-pad and left stick physically swapped. This is not a problem, just map the d-pad buttons against the d-pad etc.
+
+Left and right trigger buttons are located *below* left and right shoulder buttons.
+
+"""
+
+bitmap = '\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x03 \x00\x00\x01\xf4\x08\x03\x00\x00\x00\xf2\x9e\xb9J\x00\x00\x00\x01sRGB\x00\xae\xce\x1c\xe9\x00\x00\x00`PLTE\x0b\t\x0e\x15\x16\x1c\x1d\x1e$$%,,-50/2/3522:68?:9=;?A@>A@A?ABJFDGKJNHMORKTTRVWY\\^`ckilqru~\x7f\x81\x8b\x8b\x8d\xa0\xa0\xa4\xb5\xb6\xb9\xc7\xc8\xcb\xff\xcc\x06\xd5\xd7\xd8\xe7\xea\xeb\xff\xff\xff\xce\xdaE\xea\x00\x00\x00\tpHYs\x00\x00.#\x00\x00.#\x01x\xa5?v\x00\x00\x00\x07tIME\x07\xdc\x04\x0b\x123\x1cta\x19T\x00\x00 \x00IDATx\xda\xec\x9d\x87B\xe3:\xb3\x80cK.\xb2\xaf\xcb\xef\x92J\x92\xf7\x7f\xcb\xab)j\xb6\xc3\x02{X H\xbb@\x08\x01\xc7\xb6>M\xd5\xcc\xee\x1eG\x1cq<\x1c\xbbx\t\xe2\x88#\x02\x12G\x1c\x11\x908\xe2\x88\x80\xc4\x11G\x04$\x8e8" q\xc4\x11\x01\x89#\x8e\x08H\x1cqD@\xe2\x88#\x02\x12G\x1cqD@\xe2\x88#\x02\x12G\x1c\x11\x908\xe2\x88\x80\xc4\x11G\x04$\x8e8" q\xc4\x11\x01\x89#\x8e\x08H\x1cqD@\xe2\x88#\x02\x12G\x1cqD@\xe2\x88#\x02\x12G\x1c\x11\x908\xe2\x88\x80\xc4\x11G\x04$\x8e8" q\xc4\x11\x01\x89#\x8e\x08H\x1cqD@\xe2\x88#\x8e\x08H\x1cq|\x15 R\xfa\xdf\x9d\xbbb\xbeL\xf8\xf0vh\x8az\xbe.^\x10\xfe\xd6\xe6\xcf\xe2\xf8\xf8\xbd\x90\xb7\xfbE\xberU\xf5\xcf\xa4\x9a\xae\xe6[\xb8U|\xbb\xfe\xd1\xfb\x93\xc5\xe4O\t~\xf4M\xa6\xcc?\x00\xa4\xd0w\x88\x9f\x99\xe5\xe5~\x96]\x04\xe4\x9f\x02r\xbe\x9f^\x07D\xcfF9\xfa\xdf\xfe\xc3{@G\x1f\x1e\xce\x83\xaf\x9e2\x9f\n\xc8\xb1,\x8f\xb8DH\xbeA\x85<\xf1\x0b.c{\xd1/\xa8\xf5\x0b\xcc-\xd1\xffoS{\x82\xef\xf0\x17\xf5\x13\xf3\x18g\xf8_\xdf\x8b\xeep\xdfw\xfeU=\x8f\xf5\xd9\xe3\x00?\xcb\x92\x9fq\xb7\xcb\xdc\x1c|\xf9\xa7\x02r\x97\xca\xbb\xfb49\xbe\xcb\x94\xf9L@\x8e\xf2|\x96G\x7fI\x1a\xa5\x1c.\xf8\x82\xc3E\xaf\n\'y\xd2\xff\xbd\xb3\xdd\xcb\xf3Q\x7f\xb1\xbfx\xbe\xc5\x19\xfe\xd7\xf7\xe2\xd0\xdd\x9b\x83\x7fU\x8f\x17\xd9,\x00\xf1\xee\x93\xf9\x81\xbd9\xf8\xf2O\x05\xe4,\'\xf8z\x90\xe7\x03\xbe\x01\x9c\x1c\xdfd\xca|& -\xe8V\x9d\x0f\xc8m\xd0\xeb\xc2L\x1a\x97,\xee\x9d\xbc\xdd\xdc\xbd\xd2\xffk\xbd"\xe8/\xf6\x17\xf9d_\xfe<\x9efB\xbf\xfc\x87\x83\xee\xc5Y^\xf5GpU\xf5\xb5\xf7\x01\xd1c\xbc-\x01\xb17\x07_\xfe)\'b\x8fNS\xa2\x81#J39\xfen\xca\xfc\x08@\n4\xc0B\xa5\xf6\xd4HZ.\xe0\xa3p\x97\x82\xce\xb60\xcf\xdb_\x8c\x80\xfc5 W9\xeb\x0f\xb9\xb8\x1dK\t\xd2-\x7f\x10\xde\x9cO\x04\xe4~.\xc6\xbbw\xf7\xbd\xf7\xf1\xf1)\xf3#\x00i\xe4\x96\xd5wr\xe7\x87\xcbA\x07\xe7\xc8+\x07\xaf!\xcd{m\xaf\xa7\x02\xe4?\xbf\x17\r-\xb9\xe1\xed\xe0\x8bn\xad\x80\xc2\xbb\rN\x82t\x7f\x03\xc8\x9b\xe7\xca\x89\x14<_\x82\xc8\xcf\x9e2\xdf\xc3\x069]\x98\xfd\x92\x9e\x19\xe4\xf9~\x91\xb39\xdb#)\x94\x9d\xbc\x1c\xe9l\x0fF\xa1\xb4\xbf\x18\x01\xf9\xfb{\xb1\x97\xfb{xUq\xa6\xe1E\xb7~\xa4\xc9<S\xf2\xed:\x06\xda\xfe\xa7\x02r\x1f\x8a\xabo\x83\xe0\xb3\x9f<e\xbe\x18\x10\xf2A\x1c\xea\x82\xb5\xc7s\x81^\xed\xdb\xbe.\x9a\x83Uw\xd9%q\xae\xbb\xabqI\x94\xa7\xf0\x17# \x7f\x0f\x88\x99\xe7\xee\xaa\x82^C\x17\xfd\xee"\x11\xfc\x0c\xdc*\xbc]\x81\xbf\xe8s\x019i-\x90\x1dR\xca\xbe\xc1\xcf\x9d2_\n\xc8\x8f\x9fU\xf1T\xbe\xe2D\xca\xd3\xed*\xa7ov\x1a\x11\x90x*\xdf\xe5D\xceC\xe1E\xf4# qVE@\xbe\xff\x88\x80\xc4S\x89\'\x12\x01\x89\xa7\x12O$\x02\x12O%\x9eH\x04$\x02\x12O$\x02\x12\x01\x89\'\x12\x01\x897#\x02\x12\x01\x897#\x02\x12\x01\x897#\x9eJ\x04$\xde\x8cx*\x11\x90x3\xe2\xa9\xc4\x13\x89\x80\xc4S\x89\'\x12\x01\x89\xa7\x12O$\x02\x12\x01\x89\'\x12\x01\x89\x80\xc4\x13\x89\x80\xc4\x9b\x11\x01\x89\x80\xc4\x9b\x11\x01\x89\x80\xc4\x9b\x11O%\x02\x12oF<\x95\x08H\xbc\x19\xf1T" \xf1f\xc4S\x89\'\x12\x01\x89\xa7\x12O$\x02\x12O%\x9eH\x04$\x02\x12O$\x02\x12\x01\x89\'\x12\x01\x897#\x02\x12\x01\x897#\x02\x12\x01\x897#\x9eJ\x04\xe4yn\x86i.\xcccU5\xdf\xf4A\xbe\xb5\xf7U\x9f\xe4G\xbf\x13\xe7U\x04\xe4\xfd\xe3a\xcbt\xd7\xfa\xf6?\x98j\xef\xbe\x19\xa6\xb9\xf0\xa3\xc9n\x9e8\xef\xef\xab>\xc9?\x0b\x10\xb3\x14,[\x9f{\xb7\xe1s\x9a\xa4\xbf\xb9?\xc8\xeb}\xd2\xff\xd3y\xf2\xed\x01\xf1Z\xa6\xff\xa7\xe7\xfb\xeeYe\x9a\x0b\xdb\xee\xc1\xe6\xd1\xfdX\x17\xd3\xcd\xde\x93\xe3\xe9\xee\xfa$_\xa7\xa2\x18.\xfc\x12l\xde\xd24\xb7\xef\x0e\xc8\xa3>\xe3\xde\xa4\xfb\x9c&\xe9\xefh\xa0\xf3Z\x9f\xf4/\xe2\xe2\x9f\x03\xb2j\x99nn\xc6<z\xdd\xae?\xda\xf4\xfa\xdd\xb3\xca4\x17v\x9d\xaa\xf9\x91\xfer\x95\xa3}\xdb\xe3\xf5\xee\xfa$\x8f\xe7\xfbU\xb6\xee%\xb7\xb6\xf9\xef\xbbY\xfc\xd7\x80\xf0R\x80\x97\x9d\t\x87\xebjn\xc3\xe75I\x7fO\x87\xa9W\xfa\xa4\xff\xa7\xf3\xe4\xfb\x02\xb2n\x99nN\xfc|\xf3Zd\xf3\x97w7\xbd~\xbf\r\xc2\xcd\x85]\xa7j~\xd4R\x1fa\xf3&K+\xe5G\xfd\xfc\xf58\xf0+\xf1\xd9V\x9e\xef\xdf\x1e\x10\xd7g\xfcn\x087\x17\xfd~\xff\xcc&\xe9\xef\x00\xe4\xb5>\xe9\xff\xe9<\xf9\xbe\x80\xac[\xa6\x9b\x13\xbf\xdd\x83^\xa5\xb77\xf7I\xff\xcbYE\xcd\x85]\xf7`~T\xc8\xb5\x1el\xfa$\x9f\xe4|\xe1\xc6\xc3\xf8lQO\xaf\xbc\xbf\x0f6\xab~\xe3\x8b\xde|8\xbf\xcf8\x13n.\xba\x0f\xff\x7f\xdc$}u"\x1f\xee\x93\xfe\x9ey\xf2\x83\x01\xd9j\x99n\xbf\xf1\xbb]\xbf\xbdO\xfa_/\xbb\xd0\\\xd8u\x0fn\x1c\xc9\xeem\x9f\x8eN\x0b(\xee%YP\xf6%\x97\xa3\xbc|{@\xfc>\xe3L\xb8\xb9\xe8>\xfc\xffq\x93\xf4\xf7\x00\xf2\xa7>\xe9o\x9f\'?\x18\x90\xad\x96\xe9\xfe\x89\x87+\xc3\x1b\x9a^\xff% \xa6\xb9\xb0\xeb\x1e\xcc\x8f\xd8\xc0\xe0\x1e\xc4\xfb\xb3\xb3#\'\xfd\xb6.\'\x12\xecl\xa6\xdc\x8a\xe9\xe3S\xe6\xd3_\x14.\x05\xd4\x8c\xfbv\xdf\x00\xe4\x13\x9a\xa4\xbf\xfd\x0e\xfd\xb9O\xfa_\xcd\x93\x9fc\x83,[\xa6\x87\'\xbe\xd4-\xff\xd8\xf4\xfa/\x011\xcd\x85\xbd\xee\xc1\xf4\xe8~(\x95\xfe\xc2=\x88\xdb\x9b\xef\x89<\xd7\xf5\x01_\x89/\x81G3\x8b\x90o\x0c\x88Y\n\xe0\xb23\xe1x]K\xdf\x8b\xf5\x9f7I\x7f\x1f \xaf\xf5I\xff\xcby\xf2\x03\x00y\xd02=<qn\x91\xcd_\xde\xd0\xf4\xfa?P\xb1\xfe\xf9\xf8\x12@\xccR\x80\xed\xcf\x89p\xbc\xae|\x1b>\xa9I\xfa;\x01y\xa5O\xfa_\xce\x93o\x0f\xc8\x1b\x07\xb7\xc8~{\xa7\xec\x08\xc8\xcf:\xdb\xb7\xbc\xe37\xdc\xfdw\xcf\x93g\x01\x84[d\xbf\xbdSv\x04\xe4\xf9\x00y\xc3\xdd\x7f\xf7<y\x16@\xfe\xc1\xe5\x8f\x80\xc4;\x14\x01\x89\x80\xc4;\x14\x01\x89\x80D@" \x11\x90\x08H\x04$\x02\x12\x01\x89\x80\xc4\xcb\x1f\x01\x89\x80\xc4\xcb\x1f\x01\x89\x80\xc4\xcb\x1f\x01\x89\x80D@" \x11\x90\x08H\x04$\xde\xa1\x08H\x04$\xde\xa1\x08H\x04$\xde\xa1\x08H\x04$\x02\x12\x01\x89\x80D@" \x11\x90\x08H\x04$^\xfe\x08H\x04$^\xfe\x08H\x04$^\xfe\x08H\x04$\x02\x12\x01\x89w(\x02\x12\x01\x89w(\x02\x12\x01\x89w(\x02\x12\x01\x89w(\x02\x12\x01\x89\x80D@" \x11\x90\x08H\xbc\xfc\x11\x90\x08H\xbc\xfc\x11\x90\x08H\xbc\xfc\x11\x90\x08H\x04$\x02\x12\x01\x89\x80D@\xe2\x1d\x8a\x80D@\xe2\x1d\x8a\x80D@\xe2\x1d\x8a\x80\xbcz\xf9?e\xfc\x16@\xfe\xc1\xe5\x8b\x80D@" \x11\x90\x1f\xa6\xae|\xb3C|c@\x9e\xe1~E@" \x11\x90\x08H\x04$\x02\x12\x01\x89\x80D@" \x11\x90\x08H\x04$\x02\x12\x01\x89\x80D@" \x11\x90\x08H\x04$\x02\x12\x01\x89\x80D@" \x7f7n\xb7\xeb\xf5r6\xe3r\xb9\xde\x9e\x11\x10}\x96\xfa$O\xa7\xd3\x91\xc6I\x9f\xea\xf5v\x8b\x80D@^\x9b5\xd7\xf3\xf10On\xcc8\xf6z\xfa\xfc=&\xdf\x07\x90\xdb\xe5t<\xec\xf7\x87\xfdA\x8f\xbd\x19\xf3\x1e\xcf\xf4r\x8b\x80D@\xb6g\xcd~\x1e\xc7\xd1\xb01\xe2\xf0(9\xfc\xe5\xdc\xf9&\x80\\O\x86\t}R\xd34\xd0\x18\xf14\xf1\xb9y\xff\xdfC\x12\x01\xf9\xe1\x80\xdc.\xc7\x19g\xc98\xf4}[U\x19\x8c"+\nU\xb5m\x07?\x99y\xea\xfcl@n\xa7\xfd\x84\x18\xe8\xf3l\xe9,s\x1c\xf8\xb0j\xfba\x84\xf3\x9c\xe6\xd3-\x02\x12\x01q\xf3f\xd63c\x1c\xba\xba\x80\x19\xa3`\x9e\x18\xc11\x0f\xbd\xaaT\xa6\x9fCH\xda\xe3O\x06\xe4\xd2\x0f\x9a\x8e\xa1U\xb9>\xcb\xba\x1f5\t3)W(J\xdaZiVT;L\xf30\xdf" \x11\x10\xc3\xc7\xa8\xf4\xa4\xc9D\x86s\xc3\x90\x81sG+\xe9`\xc2\xee\xa7A\xe1\xcc\x19\xb2\xe3\xcf\x05\xe4\xdaV\xe3P\xc9\xa2\x1d\xb4\xc2\xe8\xf4\xac`LC_\xe5\xfaBds\x04$\x02\xc2\xe3$\xa4\x94\xa8_\xe8\xa1?M\xf0\xcf\x1a\xe9\x80\t:z\xf6\x9d\x96$\xaa\xbd\xfeX@\x0e"\xcb\x14\xc31\xaf\x86>[:c\xadg\xea\xe5";G@" \xa4\x97W\xb9\xea\x06c\xad"%> \xe8\xddaJ\xf6\x83\xfa\xf0\xc4\xf9r@\xaem\xd6O\x9a\x8e\xf9\xc1\x98\xfc1\xb4\xed\xfe\x12\x01\x89\x80\xdcOm\xa6e\x87\x1e\xbdu\xe8X:&\xabj\xed-#cw\xfe\x99\x80\x9c\xaavF\xcfU\x88\x05\xbaw\xc92\x0f\x1cx\xd3\xd8V\xc7[\x04\xe4\x97\x03r\xdbgU?\xf4\xbd\x03d\x1c\x06\xa7bMv\x16\x11&\xc0\xc8\xac\x8e?\x11\x90C6\x90cwis\xa0\xb3w\x0b\x90i\xc8\xe7\x08\xc8\xef\x06\xe46\x8a\xb6\xeb\xfa\xbe\xeb}\x112N}\xd5\x06\x80\xf8v\xfbq\xce\x0e?\x0f\x90\xbd\x18f\x8f\x0f{bC\xd5\xb6\xad\xb6\xbf\xf0\xa9)\x0c\x02\x8dC\xbe\x8f\x80\xfcj@\xa6\xb4\x06>\x0c\x1a\x84\x87\x06d(\xab)TFX\xc9B!2\x8a\xe3O\x03\xe4 Z2\xc3\x97\xc4\x8f\x95\x02J\x14/\x06\x01 z\xcd\x10\xc7\x08\xc8/\x06\xe4\x98\x16M\xd7\r@H\xef$\x88\xb6B\xfa\xa2\xa5\xd9\xb2\x94\x1f\xf0q\xd8W\x1f\xb1\xd4\xbf\x12\x90\xb3\xc8\x07sB\xa1\x0c\x19\xb5e2\x8f\xb5\xf2\rt\x82\x03>\x86\xfa\xbfqfE@~$ \x97Lj>@\x82\xd0pB\xa4\x07\t2\xe1\x8a;Y+doc#\x83ho?\t\x90[/*cb\xd0\t\xd9Oc\x05\x1cT\x83\xa3\x03\x01!<\x86\xa1\x13S\x04\xe4\xd7\x02r\xd0\x02\xa4\xd9\x00d\x18\xfb\xac\x9d\xc8n]{C5!\x93L\x8f?\t\x90\xa3\xc8<\x9bj\xb2|\xe8/cY\xb6}\xa5j\'=\x98\x0f&$\x17\x97\x08\xc8/\x05\xe4\xd6\x8a\xd2\x012\xf8\x80\xb4\xaa\x9f\xad\xf3s^\x99"s\x96\x8e\xb7\x9f\x03\xc8uJE\xef\\\xd7S\x10\xf7(\x8bq\x1e\n5\xfa\x02d0x\x0c}\x95\x1e" ?\x11\x90\xff`\\RQo\x032\xb4\n1\x99\x02\x87\x0f\xabY\x00H\x99V\x97\x1f\x03\xc8\xed\xdc\xa7r\x98\xa7\xa5\x8dN\nW\x9e\x8f\xf8\t\x85\x87g\x82\xf0\xa8\xd2\xe1;\x96\xa9\x8c\x80\xfc\x03@\x8e\xa9\xd4|4\xcdJ\xc3\x82y\xa1G\xddO\xf3\xb6\x8e\xb5oRq\xbe\xfd\x14@.\xa7L\x03\xc2\xf2\xc3\x9e\x8f\xa1e\x180Sq\xf6mt\'A\x86Fd\x11\x90_:\xa64\xab}\x1b\xc4w\xf5:}d\x9a\xd7*\xd6~H\xd3\xe3{\xb7\xdf}\x15 \xd7\xcbQ\xa4\xd9\xe86\x81\x19\x87\xeel\x98Y\r\'C\xfaN\xa6\xe7_<G~3 \xb7\nl\xf4vC\xc1\x1a\xcc\xbe\xa9y\nl\x10\xabb\xed\'\x91\xec\x7f\n \x97\xcb!M+k\xa33!\x0f\xd0@#\x84\xc2 \x032\xd2\x17\xe91\x02\xf2+\xc7%\x03\x13\x84%H\x17\n\x10\xb7\xb1pC\xc3\x82\xffY2\xde\xdeI\xc8\x17\x01r\xbb\\\xe6D\xd4\x93\xcb$1z\xa3}\xc6Ge\x06D<+\xa4\xaf\xd31\x02\xf2+\xc7\x89mt\x1eC\xa8a\x8d\x8f\xf8\x80\xec\xbe\xfd\xac\x92\xe6\xfac\x00\x19\x12\xd1\x8f\x86\x03\xd2\xb0&\xfe\xef\xeb]A \xc4\x02\xd2\x08u\x8b\x80\xfc\xc6\xb1Od\xd36\x1c)\xd4\x9f\\$}z(>L\xacpnRy\xf9\x19\x80\\/\xe7*A\x1b\xdd\xf0\xc0\x9e9\xc7\t\xe9\x91\xd3\xbca\x83\x0cC\x97\x89K\x04\xe47\x9a \xad6A\xda\r/oP\xaaa\x9e\xb7u,m\xa5\x9f~\x0c \x92\x9dX\x81\x00\xf1\xdc\xbd\xab-!\xbe\x9f\xb7\xcf\xd3S\x04\xe4\x17\x8ek&\xca\xa6m{\xcb\x07\x89\x90\x11\x05\x88\xdd\x06B\xd9W$5|FF\x91\x1c\xdfi\x84|\x11 \x97\xcb1M\x8bq2\x1b\x06\x03\xf4]\x06\xa6=\xc1e \xa4/\xd39\x02\xf2\x0b\xc7Y\x9b m\x0bV:\x1b\xe9\xbd\xd3\xaeF\x8b\xc8\xe1\xe0jG\xf9\x94LY2\xfd\x08@\xb4\t\xb2O\xd2z\xe9t\x803\xe3\xe2&=\xa4\xbb\xb7\xb0\xe1x0\x88\x8c\x81\x95.\xea[\x04\xe4\xf7\x8dc*\t\x10\xb4B\xc8F\x87\xb9\x92g"5C\x88\xacj\x87q\xf601\x80\xa8\xa4\xfd)\x80L\x89hXm4\x9b\xec\xa1XK\x95\xe93L\xfc\x91\xa6\x99\xd2\xe7K\xae,+C\x1a\xf9\x8b\x8d\x90O\x06DJY\x98\x1d\xaa\xf0\xb8\x0c\x8b\xc9H\xc9?\x90\xc5t\xe5\xef\xdc\xf3\xf7\xcb\xf4\x99\xefm\x848z\xd3\xb4\xa4ea\x99(\x7f\xbe\xe8\xc7\xee;\x91\xb7\xd3|8ZF\xf4\xff&\xc9\xaf?\x02\x90\xeb\xa5ODkC\x9f\xfa\xedO}%\xf8\x1c\x85\x90\x99R\xaaPY\x96\t\xc1\'\xacEk70!\xfa\xd24\xf2\x13\x8d\x90V\xeakX\xcaR\xbfQ\xd9.t\xc3\xc9\xcc\x1b\xa9\xa6\xab\xff\xec\xe7N\x8c\x7f\x0b\xc8\xfd6\xcb\x8b\x9d\xf5\'\xb9\xdf\x02D\x9f\xb2\x1cV\xcf{\xc4|\x86\x8d\xae L\x88\x88@\xa98ihHE\x06\x9bp\xa90\xd6\x04\xd9z\x19O\x1cQ\x8d\x87\xe3\xd1\xe8\xeb\x1d$\x9b\xfc\x00@\xae\xd7\x8b\x02//\x95\xbe\xd3t\xb4\x82N3\xd7g9\xcd\x9eu5Mz\x95\xc8\xf9d\xf5\xea10 }\x91\xee?\xed6\x1c\xe4Y\xdf|\xa9\xe7\xc8Y\x1eV\x93\xc7M\x8f\xd1\x7f\xf6S\'\xc6\xbf\x06D\x9f\xdcl\x1f\xdfdq\xbfNE1\\\xee\xb7\xa9=9@\xeeR!M\xf4\xa4\xbe^c\x0b\x97\xed\x13/\x846A\x18\x90\xa6\xc8\x0c\x1bE\x83%\xe3\xa6\xa0.\xd6\x01p\xc8SfdOO\xcc\x83HN\xf7w\x11\xf2U\x80\x9cE\x9aA.\xaf>\x9d\x91\xe8\x10\xaa\x1dm\xed\xd1i\xb0\xd5\x8e\xd0\xfc\x1a@\x94\x12#\xb0P \xdd\xa7\x19!\x80\xc5Q\xdf\xe8\x93F\xe5t?\x96\xe5Q\xbf\xe5\xb1(\xe6\x9b\xb9\xfd4=J&\xc3\x0c\xad"\xd7\xf0Z)\xcfc}\xfe\xd1\x80\xdce\xe3\x01R\xde\xc7\xf3\xfd\xaa\xa5\xe9^\x9e\x8f\xee\n\x9c\xe5\x04_\xf5zr@@\x0e\x17\xd9}\xeeBAa\xc2\xa6)\xa5Y2\xeb\x8e\x9cX\x8b\x013KK\x8e\xc3<\xe0\xbcI+\xb0H\xf6\x00\xc8\xfe\xf6\xfd\x01\xb9]\xaf\xa74\xcd\xd1\xae\xe8%\xae\x02\xd5@\xc5G}W\x95?F\xa8\x1dW\xa0\xba\x99\xc2\x8a\xa1\x15\xb2\xec\xfaY\xb7\xe1\xaa\xef\xf3(\x0f\xfa\xf6w\xf2z\x94\xe7\xb3<\xde\x1by\x03\x91\xe1I\x10x\x96E\x87\x91 \'y\xd2\xff\xf5\xc3\xe3\x85\'\xd8\xcf\x05\xa4\xb0\x8f\x8f F\xaf\xc7A?\xae\xb5\xbcp6\x88\xd4\xc6\x89\xfeN_\x9a\x9b\xb9\x08\x85\x05\xe4\xe5;\r=m\xb2\xe9\xa0q\x91\xc9\xf03\x009$/\xdfs\xe0\x1bl\xe4\xb5(n\xfa\xbf\xac\xd1 \xd1\xc0\x94r8\xfb*\x96\x1e\xe3m\tH\x07S\xa5\xd1\x0fof\x82\xfd\\@J{\xa2\xe5\xfe\xa6\xd1\x9f\xb5\xf6t/\xa4g\x83\x9c\x8b\x11\xbf\x16\xc1E\xf8\x96\x80\xd4\xa0\xa4\x88a?\xc9\xf7Z\xe9_\x05\xc8\xf4\xad\x01\xd9\xcb\xbd\xd6\xc1g-Df}\xfb\xc1_s?\x97z\xa6\x9cC\t\xd2-\x01)\xdc"\xfa\x99\x9a\xc6?\x00\xe4\xaa\xcf\\z\xd3\xbd\x94\xbe\xb0\xb0\xd6\xfbq)A\xe4\xa7\x9e\xf8U\xa6d\x8e\n\xaci\xee\xe5\xbb\xf7+\x95c\xa4m\xa8\xf0\xe5\xe5\x056\xa4\x83\xf2Q\xcb4\xbd|\x7f@\xb4\x8d^i@R\xf4M\x8d\xb6\xdb\xc1\xe6\xe0\xf3wW\xa3\xd7\xda\x15jZ\xe9\xf0y\xaa\xae,\xb4\xa6D\x9f\x1bs\xc3\xb5\xa2]\x06\xae\x1a\x8dBA*G A\xbag\x00d\xefy\xb1H\xa6^Ndnx6\xc8}(\xae\xbe\r\x82\xcf\x96\x9fx\xe2\xa7\xd4\xe0\xd1{\x83fI\xbfA\x08X\xb1\x9a\x92\x97\x17m\x90\xcc\x84H\xf2N+\xfd\x8b\x009\x0b\r\x88\xc6\xa3\xd1\xef\x7f\x18^\x07\x04\xff\x05\xa3C\x8b=\xfd\xbc|E}\xdb\xf5\x8a\t\x9f\xafZ\x03?]\xb452\xca\xcb\x155-\xcf\xd1\x036\xca\xe5\xc8\x93\xa2Dm\x9dm\x90\x1f\x0e\x88,\x86s\xe0\xb5\xbb\x9f\xeb\xfa\x80\x0e\xab\xd2\xf7bi\xc5\xcbx\xb1\x949\xebs\xf1y\xfe\xee\x03\x00\x02\xa1B\x1f\x8f\r8l\xc5^\xe2\xe4\xe5\x05\xddA3x\x83\xd2\xf4\xf0\x03\x00\xb9\x9dD\xfa\xf2\xa2-\xf3\xc9?\xb9~!8H^\xd8\xa4\x1b\xef\xc1\xd0+\x91\xa6\xe2\xf3\x1cE\xa5f\x01\x04\x02\x18\x12\x87\xba\xd0\xc6\xe8m. .\xc6\xb7\xdf\x84\xc9\xceuw\xe5I\x81?1^\xac\x1f\r\xc8\xbb/\xd6\xe9v\x95\xff"\ntmS\xc0\xa3\xe9\x82\xe5rx\x05\x92\xd1\x01\x02!\x85Q\xe9Y\xf3>+\xfd\x8bl\x909\xd5\x80\xf4\xaf\x08\x8f\xfeO\xa3\xcd\xb5z\xf6K\xb7\x15~3@\xceC\xe1\x05M?\xf18U\x9a\x82\xed\xf1hlK\x11\xcd\xc8\xcb\xcb\x88\xb5\r\xc6\x19\xbc\xbe\xe9t\x81&\x9f<\xec\x83\x07\xe3\xe5\xe5\xe1\x8f.[/\xba<|\x99{\xd1\xe5\xd1\x91/4N\xa7I\x83\xfc\xf2\xb2\xa4\x9e%\xc6\x9f\xe1\xa0QeB\x1cn\x11\x90_2n\x07\x91\x8a\xa2\xda\x04\xe31\x1f\xa8gi@\xccf\xc3y\xaa\xd2$\x1b^M\x8c\x0f\x86\xb6_\xde\xf3\xa2\xfd_\xfd%S}A&i\xdek@\xfa\x85\xb1\xb1\xd4\xa7XVpv\xb3\xdd\xa7o~\xdef"\xed.\x11\x90_1\xaeC*D\xdd<\xc4\x83-\xd5MD\x10\x10\xb3\xe1P\x0b\x91$m\x86\xb7\x0eX\xc7\xff\xe1\x8bp\xe5\xd7\xe2C\xf5\x8b\xd7\xeb\xe7U&\x85\xd0R\x81G*\xdc\xc82\xfd\xb3\xa2\t,36\xd6\xb3S\x04\xe4\x17\x8c\xb3\xd6\xa8\xb3f\xb9\x80v]\x87i\'UU\xb5\xad\xfe\xd4,\x06\xb5\x10\xd1\x80\x0cn\xbb\xfa4\rR\xabj}]\xd5U[\xd7\x90\xf7\xf8\xdaxyy\xf8\xa3f\xebE\xcd\xc3\x97\xb9\x175\x0f^\t\xa7Pi\xdb!\x15\xed\n\xa8\xaeO\x05\xbc\xdf\xf5h[\xday\\\xa5\xc2[3x\xddhs!\xf6\xb7\x08\xc8\xb3\x8fc\xa6\x17\xd5\xb5\xf5\xd1\xd5\xder\xca\xc9\xeeI\xf0%\xeb\x8d\x04\x99\\\x15Bm\xab\'\xa2\xca\xbd\x91\xa9<\xcb\xf2L?\x80\xce\xb1Je*\xa3G\xd9\xcb\x0bv\xce\xcd\x8b\xa2\xcce\xfe`\xbc\xbc\xe0\x1f\xf1\x9e)\x8a\x02\xfe"\xfc"\xfc\xa5B?\xd0/\x82\x03\xe0A\xf08EVH\x99\xcbBfR?\x03~\x1f!\xa5\x92I\x92\xb5k\x89\xd3gb\x7f|}\xecE5X\xaf\x05\xbb\xbf\xfbV\t1\\# \xcfm~\xec\xf5\xa2ZnX\xe7UZ\xfdAe\xc9D\x0fSm\xe2\x92\'d\xacO\xa36DD!|\x1d\x05\xfe\x81\xfa"\xe1;\x9c\xaa\x12\x1f\xbf\xbc\xc0w\xa8\xc4d\xe2\xd1\xd0/\x12\xbe\xf6\xf3\xca\x8b^\x1f\x9a\xf3$\xcdP \xf4\x06\x10\x8e\x04\xa6S@\x03\xf6d\xe36\x8cf\x0c"\xf4\xf8\xe2/7E\x96\xfe\xdf%\x02\xf2\xcc|\x8cz\xdeT\xed\x86y*\x14\xb4\xb6\x844D\xfeX\x8d\x19\x10"\x1b\x84D\x08\xe9Z\x90 +d\xd9\xb5T\x1bE/\xb4h\xebjk\x17\xfe\xb1\xe9\x8b_^^`\xa6a\x01\x95\x8e4\x17\xac\xa5\xd2\xe3f-\xb3X\xc3\\\xee:3%\xd9\xd7\xa4\xbf\xc2+m]\t\x9c\xf0\x9ds\x1d\x8c\xaeT\x11U`\x18\xa7Ah\xc9\x971\x11\x9e\x04\x81w\x90\xceG\x8b\xc3\xe1\x7f"\xd9\xc1\x80\xf4\x19\x07\xc8\x98\x86f\x19?\xa8D\xaa\xce\x11\x90\xa76\xcfM\xe8|\x01H:\xee\x83\xa1\x97\xd5\xc3\xe2\x19\x95\xe1\xd4\x1cGS\xc4\x16\xbb\x94\x8d@H\xaa\x0e\xb8\x85\x957\xb0\xafz\x8a`\xe6\xfc\xcb\x8b\xeduH\x9d\x01\xcdwXGhO/\x84P\xbd\xd9\xa3\xb1\xb7\x95\xea\xec\x1f\xa1\xd7\xf3_r\x9b\xe6\xe7\xc5{?\x1c\xeaT\xebW\x1d\xaf\xfe\x0b\x1bDh@\x0e\x07\x80\xe4Pk2\xb2Z\x13\xd6H\xcd\x89BD\xe0\'\x93X\x04\x88\xd8\xba\xaf2\x91\x9f# \xcf\xcaG\xff0\xfa\xd1\xa6\xe1\x1c\xdb\xcf*\xeb\x16\x80\xd4\x82\xa7\x1a\xe4lL\xa3- \x85\x9b,r\xdaKaSS8?\xc5\xf8\xbc\x80(\x0c2\x06\xe5=7\xea\x1aB\xa0%x\x11\x1b<\xc1\x8b\x17\x7fiY\x17Q\x8b\x11\x05i\x02\x9d)F\xb1\t\x88FdLvi3\x9a}\xe8\x8d\xd8%\xc3\x11\x7fd\x01\x19\x82\xd0\x10\x12\x92\xfe*B~\x13 \xb7>\x15\x92\xf9XF\xc8ZAK\xb8Y\xa3\x87l\xdc\xd7\xed\x02\x90\xcc\x00\xe2iY\xb8\xa7\xa2M}B\xc6\xadA\xd3z\x9c\xac\x93\x98\xb2\xbb&[\xa1\x8e\xe7\xf7\x92\xa2\xe0\x8by\xfc\n T\xb7\x04\xf9h\x8d\xa7\x9a\x95;;\xc4D|t\xbb]\xed\xbf\xc7\xa1Jv\xf5!\x00d\xe5\x04\x87\xddT\xf9%\x02\xf2\x9c\xf6G\x9a\xd5\xedv\xf8\\K\x90\xd9\xab\xf0\xd3f\xa0`u*\x10+\x16\x90\x81\xe66\x89\x06\xb4\x02*Ph\x98\x10k\x13\xb8\xf8;\r\x8e\xa1\x8c\xa6\xf8\xed@Y\xc2\x81p\x08\x01ao\xc0\x86\x98y$;\xa8\x96\xb0\xe6#\x91-\xcfjV\xb1,"}\xc7\x80\xb4\xbb\xa4\xe57;p\x19\x93N\xec\n\xfc\x99\x06d\x15egoo-Su\x89\x80<\xdf\x98\xc1\xfep|\x84\x8f@\x82X\xb3`\xcej\x92\x19 G\xf6V\xf57\x80\x8c<\x9f\xfc\xf2Q\nd\x08\xf7fZF\xdf\xc7\x10\x10_\xc4\xb8TzC\x847\xf7G\x124\xd3\xb8\x82d\x0b\x10\x17\xe0\x9f*\xdc\x1c\xecE\xf98\x92nr\xcd\x08\x90a\x974\xf4\x06\xf1I:\x17\xadf\xa1\x0c\x99\xd2\x87i\'M)\xd2\xf6\x1a\x01y\xba\xf8\x07\xfa\xaf|\xf9\xd1z\x0f4 .\xbf#3\xba\xd5a\xca\x06g\x0c;\t\xc2k\xae\x13\x13\x03\xeefU\xd4ZcX\x13b\xd2T\xecc\xfb\xc5\xd81\xa37\xf7\xc7\r\x1b%x\x8e\x01\t\x9es\xafn!\x17\x1f\\\xd9\x9cR\x82^,\xcf[;\xa4\x00\xc8\xac\xd5)?Q\x91N\xa8o\xd2\xdd\xf4* m\x07\x84\x8c\xb7\x08\xc8s\x8d\x93H\xa1\x90\xa2#\x82<\xb1\xf8\x08\x1c\xb2\xa9\xcb\xa8\x9aZ\xeb\x88\xda\x0f\xf5\x9e\x93\xa2\xf6{%\x9d\xc3ttS\x9f-_ \xa4\xf5\t\x19\xd7y\\[)\xf4\xae)\xf9h\xd3\x85\xad\x04\x19\x97\x96<=\x17J\x90q\xf2-\x9diHi/\xb9\xb7\xb7C\xdb $>\xc8i,F\x8d\x80\xda\x89\x80\x0fB\xbe\xeb\xca]\x8a\x80<\xcc\xe4l\x9a&O?\xb1\xceI\x04\xe4+\xc6\x05\xe3\xe7\x1a\x84\xaewa\n\x12"\xf4\x8d\x98\xe6U\x85Z.D\xc8\xb8\x00 \xc1$\x0f\x00\x19\xb0^H\xbf\xa1d\xbd\x9eA\xe5)[6\x9f\x9e\';?79)c\x9e\x03e\xcd\xea`\xe6u\xe4Y\x9b`3W\x9a\xb5\x81\xe5\x80F\xba\xfb>\x1b\xb4\x00\xd9%\xf5\xd2\x08G+D+Y\x03\x01\xd2\xfazh\x90\xd1XkBN\x11\x90g2\xd0+\xa8\x03\x05w\xbbk\x89\x08\x96\x1c\x04\xcb\x12\x10S\x98w\xf6J)\xce\xfb\\\xf6\xfd\xa34A\xa8\xfd\x91\xc2^\xf5\xc7|l\x02\xb2\xf2vYK\xdeI\x851\x941vW\xca\x18\x08\x0f\xf3\xda\xdc\x19 \x16\t\x00\xc4\x13\x15B\x13P\xef\x04U\\\xed\xfa\xce\x93!$B\xf6$A\xda\xb6\xdf\xf6\x89\xb7\x95H\xb3K\x04\xe4\x99\x0c\xf4\x94\r\xf40\xa5\xcfJ\x93*\xfdc\xd2\xba\x05d\xb9u\xdd\xb8wr,x2\xbe3\x07w\xf4\x9a\x92\xfb\x80x\xcf\xf96=\xff%\x94\x1e\x88\x88\xff\xbaa\x98j0@\xd4\xc2\x97\xed\x03\xa2\x9f\xd5\x80\xec\xd3]\x81\x19\x9a\x1d}\xb2\x1b\xf2\xfb\x0e\xad\x10\r\x88\xa3\x83UQ|\x04\xd5\xf0\xdb\x06\xcc\x90\xfe\x16\x01y\x1a\x03\x04\xf8\xa8B6\xea\x8a\x8c\x0f\xe2\xc4\x002\xb5r\x95\xd2\xa4(\x946g\x9b\x12\xc4MD\xaddi\xbd\xe5\x9d\x80\xd8\xb9o\xf7\xf4\x8e\xa3s\xbb\xaeE\x0c\xe7\x14O\xae\x90\x04?G\x16?\x18 \x89\\jFK@\xba\xbd\xd6\xb0\xca\x1e\xc90|8\x13C\xec:\x04\xc4s\xf3\xf1U\xd2\x0fa\x0f&T4\xce~\x89\x19\xf2+\x00\xb9*T\xb0Z\'?*\x1c~v8\x01\xd2S^\xd2rt\x1e \xeb-\xb9vn\x81\xaf7}\xbc\xb9\xf5\xa1\x8a\xe5?F\tb\x9c\\K\x01bb\x16(A\xa6\xf09\xc2*C\x05\xebU@z\x00d\xd4(w]c\x84H\x00\x88\xdc)\rHm\xb7O\xe9\xc5\xa4\xad\x9c\xc0e%K~\xe6>\xf5\x08\xc8?\x1dS\x9a\xca\x80\x86\xaa&Bj\x8b\x8cJ\xc1\x1e\x1f5\x1fX\xd1\xd9\x1f\xc0\xcc@\x80\xb44\xd5\x82\x86\x9f\xben\xae\xa7\xe7.{\x1f \xfa_\xc7y\x87\xa3\xf3\x05\xdb\xef\xf9\xdf\xe29x\xd1\xb8z\x9d~\xaa"\x05\xab]\x10\xb2\x06d\xd8\xa5\xdc\x00{!@4 \xc5N~V\x11\xac\x08\xc8w\x1cg\xad\'\xc1f \xbb\xb7\xa8\xb2\xc3\x97 \x9a\x90\n\x92ZU\xd7P\x92-\xcc\x9efh\x80\x90,\x00\xc4\xdb\x95\xdb\xfb\x84\xb4u\x9aPtz\xa3\xdc\x14\xc7\xb3\x97EEF\xcc\xef\x1dz\x13\x80g\x15\xcb\x02c\xe4H\xf0\x1c\'\xbc\x84\xaf\x83\x7f\xe9N+X+\x01\xb2\x02\xa4\x05@j\xde"\x06J\x93e\xa4\xd3\x80d;\x11\x01\xf9E\x80\x80\x07\xab\xa8\x16\x80\xd4\x0b@T\n\xd5\xdc\xb3\xddNpUs\x92\x12z\xea6"\xdd\xedR\x00DX\t\xe2\x89\x90\xc0\xbb\xd3d\t\xa4\x8d\xbf\xcfH\x1f8\x05\xdehO^V!~\xb2\xc1w\xff/\xad\x9f\x03\x99\xa2v,@\xde\x00\x88\xb6A\x80\x0e\xf8d\xf9\x00a\x02\x80\xc8\xfd\x98\xd6\xd6\x81Q\xb7vy\x81\xa7\xf0\xb7\xda\xa6z\x97\'+\x02\xf2}\xc7\x11\x14,F\x01\xdb\x81\x10\x1d-\x03\xa2o\xbf\x01d\x12zz\xcb\xba\xaeKo\x1bj)\xf5\xb2\x8c\xf2%\xcbB\x15\xab\xefW%s\xc0\xd5\xbb\xab\xc6a\xb3D\xe3#\x17qG$\xea\xaf#hO]\xbfh\xd9\xeel\x8c\xf0/-\x8bO\x8f=\x08\x90\xadt3\x06\x84\x9f\xefD3k@2\x84\x83\x85\x08\xda!\x14#\x02\x1bd?\xa5\x95\xaf\x92\x9a\xcb\x1489\n\xf1\xf6\xdel\x11\x90\xefk\xa1gZ\x80\xa8\xd6\x03\xa4v*VMw?\x00\x84\xe8h\x1a\xfc\xa2j=\r\xf4\xb3\x103\x84\x9d\xecn-\x1e6\xea\xe6\xb4h\x85\xa4^\xa1\xc2\xa1w\xa8\xd0\xb4\xee\xfd\xa7z\xa6\x83vQu\xfc\xa2n@\xcb\x84\xa9YG\xe59\xa7x\xf5\x03\xc5\x16\xc8*n\x11J\x90F\x032\';\xc9\xc2\x00\tA\xe3\x03>\xda\xa6\x14\xbbVK\x10\x07H]WZ\x84\xe8\xeb\xe4!\x02\x96\xbb\x16!o\xb6\xd3# \xdfv\x1c\xb4\x00)\x9b\xe6! -~V\x82\x00IE\xa6\xa9PH\x08!\xf2: \xcb\xa9\x88\xad\x9b*\xaf\xf8\x94\'LX\x82\xf8fHO\x12\x84\xe1\x80\xafK1\xd3uC\xf7\xd6\xaa&\xe8\xe2\xad8\x0c\xea\xa5dB\x91\x87 \xb5_\x03"w)\x16\xa6h\xad\x14\xe9\t\x95\xbaHv\xc3\xac\x01A\r\xab\xc2\x0fp\x89kH\xaa\x85\x08\xc9E:E@\x9eA\x80\x94\xb6\xe2H\xd3\x06F:k[\x08\xc88N\xe9.\x95\x05\xabV\r\x7f-\xa0|\xb3\x01\xc4_\x8b-!\x14\x91\'\x95\xbd\xce\xad\x08\t\x11\x1a`\xdbR\xbf\xb1Mod\tb\xe7\xbe~826+M\xcb\xd9 \xab\xe7\x86!\xa3$E\xf6\xc7\x1a>\xe0\x1b}\xe4\xd6\x94\xbd\xd2\x0f\xa4\x06D\xebXEP\xb5\x85\xfeUZ\xa5\xdc%\xf34\xb0\x04\xa9\x11\x0f\xbeT^\x01\x152\xe5\xb4\x08\xb9D@\xbe\xe5\xf8\x9cz\xfc\x04\x08\xae\xa3\xaav*V\xb2\x03|\xc6\x05 \xa1\xe8\xe8\xad;\xcc\x88\x10/\x9b\x96\xberF\xd4\xe0=\xd5\xfb\x16\xba\x11\x0e\xdd\xc7\xeab\xf5P\x8c[6\x16\x10\xfe\xd4\xe2\x91[\x9ba\x03Fz\xadqOw\xa2\\\xd66j\xb4P)\xd3]=O#\xda\xfa\xb4\x80\x04a\xa3\x00\x90<}\xab\x08\x89\x80<\r \x99\'>@\xcb*\xb0\x1f\x08\x02"\x11\x90v\x99\x98Dt\xb0\xd3\xa7\xb6V\x88\xed\xbe\xeerj\xd9-\xec\xd7R\x0f\xe4\x87\xb6\x95\xb5\x04\t\xd43j\xbf\xdb\xbf!\xa2\x821\x90\xb2a_\x93\x17\xd8k{\xae\xa3\xc5\x8c\xb4BM\xe3\xd4i3= \xa4\x05\x95\xab\xd2\x02d\x07\x89^Bq\xbaA\x85\xd6\x87\xfe\\;%\x15\xf4\xb2\x1a$\xcb\x9bEH\x04\xe4\x9f\x03\xf2\xb6\xd7\x9d\xb4\x05RT4\xd5\xf9\x06\xeb\x87\xfa\x7f\x15\x8e\x1c\x9d\xb3\t\x00R\xb2\xe8`N\n\x88\x83\xa4\xa0\xcc\x10 a\x14.0[\xd9a\x8c"d\xddH\x80\x93\xceM\x99\x92\xa1\xf7Lvg\x99\xe0\xdc7;\x01\xfb~#r\xf2\x08\x10\x049\xac^\xd7o\x96\xac\xeb\xa5\x824.m\x85l\x88\x10m\x81\xb4\x90\xb3\x9ff\xa1\xb5\x06\xb2\xc4\x00R\xb7\xd4\x1e\xb8\x82\xbe\xc0\x87\x08\xc8\x0f\x06\xe4\xd6j@T\x85\xc2\xa0\x82\x02\x88\x15\x01R\xe3\xadW\xfa\xbf\xa2\xdb\x9f9@\xac\x83\x97\x14\xad\x0c\x00\xc1\x9f\xa6\xb2\xae\xddTc\x05\x7f]\xd2P@8}\xd8\xa8\x0b\xed\'\x9d\xbb\x82u\xbd\x97\xfc\xe8\x12oQ\xa68\xfd\xec\x15@\xcc\x0fZ\xd4\xb0\xaa\xb0\xc8b\xbd\x02\x04\x9eA@\xc6\xffi\xcd\xb1.\xf1\xc2\xd8U\xa3VZ\xf3\x82\xee\x85\x83\xc8,\x1e\xec\x17\'/\x169\xca\xf1\xe5\xfa7\x95H\xf3[\x04\xe4\xbf\x1ao\xec\x9a~\x7fC\xc7\x877^\xf0s\nYXU\x85@x\x92Ci0T\xa9\x1f\x94e\xa9\x1f\xab\xda\x02"\n\xcb\x07\n\x90\xa6\xf4\x00\xa9\xaa\x97\x97j\x99\x0b\\\xb7\x95\x1f"\xd0k0\x84\xd37\\\\\x8b\x94A\xf7\xd5\xe5<\x0eKK~\xb0\xc6\xfc\x03@\xec\xcfr\x08\x12\x96\xf4V*\x87\x03\nN\x02\xc4\xbe\xcbL\xd1\xdey}\xba\xa5Y\x0b`5P\x90\xa8\x98R*\x98(H\xcd4\x02\xa4\xe1W\xd9\x81n\xbeR\xbcqc\xc8\xc3\xfb%\xe5yu\xbb\xa1\r\xba{\xea\xa9\xfb\xa4//\xc6\x1b\xbb\xa6\xffg\x80\xecQ\x800 \xa8g\x19>TYh44\x1fe\x06\xdf\x08+A</\x16L\nUX@\x84P\x00\x08\xfb\xbe`\xbd\xb5JH\xb0jk\xa3~W\r\xebr\x07k\x03\xdf\xebw\xf6P\xcc\x0c}\xbfJ\xb0\xdf\x02$\xc54\xc5\xda\xf3\xc2V`&\xe0\x1b|y1\x12\x13\xbf\xa45\xc7PR(\x8a\xc5\xfe\x08\xd5\x0cC\x03\xe6\x13\xfdL\x8a\x1a/\x8f\xe2\x01\x17KYDP\xbc\xc2\x17\x99\xa6\xe3_\x022\xacnw\xd8\x14\xe7\xa9\xfb\xa4\xaf\x00yk\xd7th\xc3v.\xa7\xdb\xdf\x01rSi\n6EC\x13#\xcf2\xee\xb2m\xab\x84\xda\xacve%\x88\x9e\r\x8d\t\x146> j]\xf43+2(\x87\x0bk\\e\x122j\xa9E\x88\xd8h1\xb0\xed\x01\x1b6\xb6nl\x17\x14qr\x86\xad\x196e\xf0\'\xacaU\x1c\xa0\xf0\xca\xb6\xa7\xd0@\x87\xca\x0b\xeb\xef\xe1s\xd6\x99\xec\x94L\x9bW\x10\x19m4\x1dS\'v\xbb\xcc\xb8\x8e[*P\x9cx\x83+\x16\xd3_eg_\xf9\xc6|\x93W\x00\x91\xd4\xae\xd34\x8c\x9aGI\x1d-/c{q\x13\xe2I\xfb\xa4\xaf\x01yS\xd7t\x02\xe4\x00M\xe6\xff\n\x90\x13hX%+\nPA:\x0f\x08\xd1|\xf0\x0c\x105\xba\x92\xd0\xcd\xabH|(\xfc\xb5VI\x0b\xc8\xd0\xca\x04\xdb@\x07\xb3\xc5\x0cY\xb1\x12S\x81\x1f+\xd9(\xbe\xb5\xf6\x80\x85\xf3\xff\x15\x8a\xcc\x96\xf2a\xf0\x8a\x98\xda\x86\x9bV\xc3\xcaP\x824z\x06\x9b\xf3\xcc\xb0N\xf0\xcb\x8b\xa6\x18\xce\x1eL\xae\xba\xf6,{\x80B\x9f>p\xb4\xd3\xa7\xd9\xb9\xc8J\xaf\xa8\x167\xb6C\xc8Lg\x84\x0c\xff\x90\x1e\xa94\x80\xa4\xc7\xbf\x03\xa4\xc1N\x9c\xb6\xed\xf9\xd94|>\\\xb07\xdbs\xf7I\xdf\x02\xe4\x8f]\xd3\r \xd8\x07\xfb\xa1O\xf7-\x07\x9c\xb5\x86\xa5\x01A\xcb#\x934{\x8cS3\x18\x9c\xec!\x12}\xdf\x01\r\x94\x1f\xf8k\x05J\x90\xc2\x84\xc6!\x8e\xb75\xfaZd\xe6\x8f\x95\xa8c\xd1\xc4\xf6{ \xc2\xb4n]x{\x9b\x96G\x81\x96\xd5\x16\xda\xc1\x81\x85\xfa\x1f\xe6)\xe2)fI\xfb\xf6~"c\x87^\x05Hc\xae\xbb\xf1\x8f=G\xec\x83\x12\xcc\x17}\xad\xa4TL-\x00\x00 \x00IDAT\xe4\xdb:\xe0>\xbc\x85R\xeai\xaeo\xb7\xd7\xf6\xdc2@S\xe5\xa9\xfb\xa4o\x01\xf2\x87\xae\xe9\x05S\xe4\xae\xd0\x87\x01\x01\x1fV\xaeX\x14\xe8\t\xbc5\xb1\x83\xef\xeaD\xa4u\xe7g*\x16M\t\xb3\xa7\xdb\x9aja\x84/\x13l\x1c\xd7\n\x13\xe4\xfb\xf6\xf5\xb9\xdfn?v/z\xdc \xce\xa1f\x99i\x13\x10\x04\n\xd3f\xd2<LVikm\x83t\x8f\xc3\x8f\xe30l\xfe\xb4\xe7\x93\xd4\x7fA\xa9\\\x855[;!a\x15Q\xb9\xbe\xc4\xd7\xbf\x02\xe4\xde4f\x02\x14\xce\x02u\xa6\xc6S\xf7I_\x03\xf2\xc7\xae\xe9\r\xae!\rI\x90\xfa\xaf\x8ew\x86\xae\x1e%Z\xd5M\x0b\xa1a\xe6\xa0\xb5{M\x17\x80tY\xba\x13\x8dG\x88^%A\x1c\xe4\x83y\xc1Z\x82\x0c\x8cI\x8d\xc9\x19\x08#\xc6\n\xd7S\xdc\xe6\xd4\xb6~\x00\xbe\xdf\xd0\xc3\xda5!\xc1\x0eq\xfcKA\x7f\xdej\x87&\x08\xe4\xdcVim\xca\xb8\xc3.G.\xde\xae\xcd\xf1\xb6\x7f\xd4\xc3w\x95\\\xdc\xc3\xefbc\xa9\xbe\xaf\xa4\xd9f\x99\x16\xae\xe9i\xa7u#\xd4D\xc5\xdf\x158A\xed\xcaH\x90\xee\x11 O\xdb\'}\r\xc8\x1f\xbb\xa6\x1f\xb0\xad\xfc\x01\x94\xcbWl\x907\x8dc\nyX\x08H]\xc3F\xd4\x0e\xfe\xe1\xff\x16vW\x03 -l\x1b\xb2\xb2\xa5\x87\xbd\x1f\x9a\x10\xf4~6\xaah\x14\xa8\xe6\xe9\xf0\n \x86\x93*\x05\x8f\x11\xa6\xbdB>\xd6\xae\xf5jp\xf9\x80\xf89\x1f\xfec\x0b\x88_v%\xa8\x9b\xc0\xbb]\xfb\x96\x01\xf1~\xdcgd\x82\xa0\x08\xd3\x80\xd0.A\r<\xecO)\xf5\x996\x05\x9e\x19tP\xf8\x13\x1e\x84\x86\xf9\x0b\r\xfc^\x86I\x8ch\x8c\x99wN\x80\x90\x8e\xb5\xff\xcb9\xa1\xff\x96\xdf\xf6\x1c\xdb\xa0\x87\x80<m\x9f\xf4u\x1c\xe4\x8f]\xd3\xef\x87\xa6h\x0e VNj\xfa\xbb\xc2\x19#\x99 \xe8\xce\xaf\xd3E\xd1\x866\x13\xbe\x8b\x86\xed\xf4\x16\xc2\xe6M\xa3J\xc8\xe9-!1I\xaf\xbd\x88\x8e\x02U\x1d\xff\xd3\xcb\xab~\x05\x08,\xe0\r\xc6\x97\xf5\xeb\x14\xe7y\xb4\xfe\xdc\xb7\xf5\xb8l\x16HP{\nC\x16\xee\xc9 \xe7\xd0\xa6\x1e\xb6D\x91\x8f^\x8f;A\xd2\x1c\x82z\x1a\x10\xf01\x83l\xac\xf5\xbb\xad\x06S\xd5\xb4\x97\xbb\x9d\xe4\xfd\xb5\xc3\xb6V\xe5m\xbf\xd5\x0f\xfe\xd7\x01x;\x89\xd5\xecA\xac\xb4\x85\xfe\x83E\xd7\xe0\xea"\x05\xfa\xf9 \x98\xde\xde\xfe\x0e\x90\xb3\xef\x94\x829QL\x01 \xb1O\xfaC\x83\xe5\xaf\xc6\xad\x82(\x88\x01D\x04\x80t\xadX\x95f\x10\xa8z%V\x86(\xc8\xfc&>\x86z]\xcd!\x0b\x8d\x99\n\x08$\x8f\xb2J\xd9\x08\xb1\x89\xb5&\xe9\xdc\xe5C\xb5\xc1c[\xc1\xce\xe6\xb2\x98:"\xad/?\xcc\xef\x98\xdc\xaa\xdeV\xbfCf\xd1E\xe5\x00Qzr\xff\xcf6\xd7\xd1\x9ch\x8cLE\xd1\xc1)\x88\x98l\xef\xce\x83\xc7\xffP\xff\xd4\xdc5\x9a\x0e\x83L\xdfh\xc8\xf2\xe6\x7f\x90\xfc\xcb\x80\xa8\\\xa4\xd9\x13W\xea}f@\xa0\x9abV\x96\xec\xb1]\x00"A+\xcf\xc8\x8d\x89=\xd1\xa0^C\xcd\x84\x88f\xea\xb4\xfe\xc0|\xe8\xe9\xa4P\x05w.\xdd\x0c\xf0RK@pg\x11\x00\x02\xc2FP9\xa9\xb75\xf1|\xf4\xa2>H\xa9Z\xbd\xc8bT\x1b@Z\x0bHWj\xf1\xb1T\xa8$\xf9\x9f}c{\x01\x86\xf7\x03\x90K\xe90\xea\xd3w\x1b\x0f\xc1g\x91\x81\x0f\x90\x00\xd1*V\xa6\x8d\x90s\x04\xe4\'\x8e\x13\x9a \xa5\xc2\xd4\xc3\x05 \xd0YI\xfa\xe5I\x1a\x98\xf3I\xdb\x13!r\x1c\xf4\xdd\x07\n\xd2\x8e\x14\x8d$\x955g,\xe9/\x03\xa8\xe6\x891\xf8- \x14`\xaf\xd00N\xde\xd3\xe5\xf6/)\xea\x15\xda\xe8\xe4\xc4\xaaS\x85\x0efP\xaf\xfe7\xac\x08I\xdf\x08\x08X0\xfa\xec\x87.l\xf6\x0b\x84\xe4mk%H\xa5\xb2\xb7EB" \xdfn\xec\xc1\x87\xa5\x08\x10\xa5B\x1b\xa4\xd2b"Cc\xd44\rl$\xcc\xf9J\xcfx\xd4\xb28t\x96\xc2\xdc\xc9!J\x90)7M:\xaau\x82\xd6>)l\xdaF!@P\x82\x80\xdf\'A\x8d\xcb\xcf\x1b\x84i]{9Q\xfec\xfb\xd0\xbch\xa3\xaf3\x7f\xd5\xff\\n\x15\xd7\xab\xca\x0c \x90\x99\x8e\x804\xc9\xae\xe8\xd7\x96F\'\xb4\xee\xf7\x96\x01y\xc9\xd0\x1f\xa1_\xf5\xc3\xd6r\x15\xd6\x08\x00\x04sq\xb4\xc55G@~\xe2\xe8a\xb3-\x00\xa2?\x95\x0b\tR\xc1\xees\x9bVD\x0f\x90\x10\xf4t\xa5\xc6\xce\x90\xb0\xc2\x92\xfc(m\x00\x11~\xa1\x04\x0b\xbf\xed\xbc\xddI*U\x15g}A\xff\xe5\xdd\xae2\xdd\xca7\xd7\xfd\xda\xfc\xa06@\x18@\xd0W\xbb\xac\x90\x80\xefYU\x1ej\xa1\xc3\xc1I\x90\xaaJ\xc1\xfc\x91;\xd1\xb9HMgC\x1b\x1a\x9cf\x0b\x88\xa5$\xd1\x7f\x14\x10k\xd6\xa3\x16;`\x83\x00\x01\x1d+M\xbb[\x04\xe4\xe7\x8d[\x866\xbaf\x03?\t\xc5\xf30\x03E]\xaf\x8e\x9cN\xe4\xdd\xf9\xcc\xca\x10\xf4\xee\xeeD\r\xfe`\x94\x1fR\xb9\x04_\xf8Z@\x8e\n\xa0\xa1\x14\x1b\x02Z\x82T\xbe\x04\x01E\x04s\x05[\xca\x13\x84DGL\xfb\xc3y\x8e.\xe1\x8a+\xa0\xd6\x86\x99\x1a\x00\xb1\x9b\x93j\x07G\x85\x01r\xb03\xa0B$\xbc\x88\xaa)p\xc6n\x8bN,\x03\x88\xc8{X\x00J/Pc\x01\xe9\xb4\xf1%\xdf,@\xbafk\xa8dW\x02 \xbc\xe7R_\xe6\'\xb6\xd2\x9f\x18\x90\x8b\x00\t\x82\xf9\xa8\x00\x08\xe7a\xb4\x12\xb3\xa6\xf4\xdd\x97\xc5\xe2\xc6wD\x08R\x01\xd5\xa0\xd0\xc2\x00>\xb46Vk\x05K\xf1V\x12\xe0\x04%H\rE\x1a`:.\x01\xc9\xd0\x8d\xd5\x9a\xe4\xd9\x84\\`//\x1c\xb2\xa3\x8d\x7f\x15\xe5\xa2\x13\x06d\xe0\xebOZ8`\xd8\xa6j9\xf1\xd0\xb0\x02r\x0c\x9d\x06\xa9P\x0cHE\xbf\x08\xa1\x0f\x0c\x83(:~\x9a\xc3\xab\xd3\xb6k\x1a\xdf\x0f\xcd\x8e\xdcZ\x8b\x90\x96+a\xb5a#\x08o\x8fd\xdb\xe8\xbf)\xb7\x01\x01\x11bm\x10\x8c\xa5?q\x11\xd2\'\x06\x84\xe2\xe8f(\xce\xc3\xc8D\x05)} A\xb2\xf5\xbd\x07-+m\x17\xc6\xcaN\xaf\xcd\x8d\xdb%R\x02*\x04\x88\x06-oYw#\x15\x8b\xd2\xca\x0b\x00D\xd4\xa4p\x81E\xe3\xa55B\x8a\xac\x96S\x15\x8b\x11\x163\xec\xff\xaa)3\xbd\xa5\x1f\xb7\xb8\x1d\xbc&A\x91\xeeR}X\x992$y\xc5\x80\xe0K\x13#A`\x08\t\xaf\xce\xd1`\xe8\x16\x88\xf4\xd0\xffC\xb5k\xe7X\x13|\x81G\xbb]\xbd\tHU\x97z\x15\xd1\x97\ns:\xab\xb2\x10\xcf\xdc,\xe4\x89\x019\x80\x13K\x05\x80T\xad\x12\x10\x11\xa9Ai\x97\x1b\x80\xa0r\x94\xfb\x13\x07\xa6\xb7\xb4{p\xf1\xab\xaa\x1b\xda\xa9\x8e\x80\xf4\x95\xa8\t\x90*\x04$\xe5\xbd\x8a;\x98\xd9v-\x07A\x95\xc21\xccv=[3\x84\n$\xd0\x86\x13\n\xfe\xd3\x86\x8e\x96\x1e\xa5;A\xfb1,$\x19jT\xf8\xfb\x08HA\x80\xd4\x1a\x10\xada)\xac\xe8\xc3\x87t\x03jS\x8b78\xca\xda\x02\xb4Mwe|k\xa4Lw%\x02R# ZB\x1e" ?oLz\xad.\x80\x8dB\x0f\x96 \x9a\x0f\xad\x15\xe9\x07\x00\x88l\xfe,B\x14\xb8\x83K\xcb\x06\xef\xa2\x82\xea\xff\xb0\xf3\x1c%H[A1\xc36\xb7\x80\xa8\x00\x10\x01\xb2\xc4\xdf\x85\x07*\x8a\xd5\xbf\xaae\t"\x0b\x88e\x86G\xb2\x13\xe8\'\xc0\xbfUP\x86\x95\xd6\xb6(\xf0\xb2\xb3*\x16\x01\xa2\xb8\xea\x95W:\xd1\xf0\xd1\x14\x1b\x1e\xe8\x8d\xc1J\x9a\r\x80x\xb0(\xb1\xcb|@\xd2d\x8a\x80\xfc<\x1b\xbdN\x13A\x80\xc0\x7f\x92 \xb2\x04#\xb6\xaa\x9am\t\xd2\xb1\xaf\xd7Y\xc7\x19\t\x10N\x7fgN4(\x85\x01\x04\x92\xdcU^\x03 \xacb\xa9j\r\x88\xd9\xafJ\x7f\x05\x00\xa9\x1e\x0c\x0f\x90\x90\x9d\x94\x01\xb1U\xed\x10\x92$\xab\xac\x04\xc9h\x83}\xab\x01\xc9\x1f\x01\xa2\xa7y\xb9\x01\x08\x1aCP\xa9\xc4\xeaX\r\xf8\xaa\xfc+\xe3J\xcc\x95e\xa6\x7f\xc6\x80\xd4Z\xc5J\xd2\xfa\x16\x01\xf9\x19\xe3\xcd\x15~6\x8ct\xe3\xea\xddQ)Z\xb4\x94\x05Z\xe8A)9\x8c\xac`"\x97\x01\xa4\xcds\x1f\x10E\xbb\x10\x13\xabb\x89\xbaq\x95\x1a!o2y/ \x15\x02\xa2j\x8a\xe9\xd4\xb8\x8d\x9e\x18\x01E\xabR> `\x83d\xafI\x90$\xf1\xc0\xe0Q\x99j\xab`\xb8\xd4\x90S\xe6\x03\xd2C\xb5jKH\xa9\xb2\xdd\xd3Uq\x8f\x80\xac\x00\xd92\xd2K\x02\xa4\xaex3w+\x01\x90\x12=\xfeF\x04\xd4M\t{\xb1\t\x90\x0c\x8a\x7f\xb4J\x91\x8a\xa5p\xdb\xb7"/VB\xb5!X\xc5B\xdf\x17#\xa2\x9f\x92\x1f\x00$-\x95\xb7U\x05i\x81"$\x08H\x12\x1a\xe9\x16\x90\xd6|\xe6D\xff\x16\x13hL\x99\x89\x9am\x1do\xa0\x19\x0e_\xc5.\xad\xbd\x00\xbaW\xa5\xb4\xacd\x04\xe4\xe7\x02b\x1e\x1d\xb5j,2E\x16HY\xe4\x1c\xc7\x03\xb7+\xae\xebZ\xffZ\xf3QH\xb7\xf4# 9\x98\xd8e\xc9U\x80\xac\x0c\xa8\xc9\x06\xa9\x01\x10v\xc8\x92\rRbA\x88\x82\x00\xc1\xa8GF*\x96Y\x9eY\xc5\x12\xe6\x08D\x94\xa9\xb5\xe2\x01b\x9e\xf4\x01\xf1\xb7r\x81m\x95\xe9\xb7\x0b\x7f\xa9 /V\x8eA\x96\x8aT,\x85\xdb&\xdd\xfeImE\xb4h\x05\xe9SG\x19a\xc8\x08K\x84Y@B!\xd4\xfae|\xb5\r\x92\xd7N\xc5\x82\xd2\xac\xc7\xd5\xf5\x8f\x80|s@\xf6\x1a\x10\xadu\x14f\x08\x9aq\xb5b@\xd2\xb5\x8aU\xd6\xb0k"4\xa0!\xc1\xbb.\xca\xd2\x99 (NH\x82Tu\x9eA:\x12\xa8d\x19\x12\x08\x80T9I\x10*\x07bl\x10\xafR\x8aV\xb1$\x95VQ\xa6\xd2H\x85\x0f\x15F\x13\xad$r\x85HB@\x80\r\xd8V\x8f\x1b\xea\xa1\xaaQI\x80@}\x16\x94 U\xb5K\xf2\xda[\xfc\x03-R\x8b\x86\x95\xe00\x98\xd0K\x10u8\xf1\xa0*\xa9S\xb1\xd2\x9d\xd2K\x04+\x9c9\xf4\x9d\xdeG@~\x18 \xb7\x0e\x01)\x1c %\x96\xad\xd1\x10hT`\x96\x90\x8ae"uP\x80\xa0\xc6\xdd\xe4;\x7f\xde`|\xae\xc0\xfd!\xb6\x1e)|\xa0\x04\xd1sL\x03Ra\x895\rH\xa9\xb8\xa4P\x919\tR\x02 \x05\xbb\x9aK\xccz\x81\xa7d\r4pQ\x9d\x8a\xab\xeb\xc0/\xbc\xbc@~%\xd1\xa2\x14\x97\xf0\xd2\x9fk\x06D\xff\xbe\x16\x1c\x00\x07\xc6\x1c\xd3\x1c^\x99\xe36\x15\x90 \x04H\xdd$p\x04\xa8(\xd96\x01\'\x9a\xaet\x97\x99:/[\xee\x00c\x94T\xfaM6\xabJX$g\x93]U\xea\x05\x81,"\xf0x\xdb}\xe9\x11\x90\x9f\x02\xc8\xb5\x02@\xa4\x07\x08\xd4\xc1\x82\x08\x06\xa0\xa2\x01!\x15\xcb\xd3"4?\xe8<\xcd\x02\xc5\x03\xf2\xb2@\x86\x14\xa6\xd4\tY\xca\xd2\x02R\xe5\xf8\xd2\x1a%\x08M\xf8\x02#\x15\xa9\xb2\x80`5\x10(\x07\x82\xfa\x1el\xaa\x10%\x17\x9b\xe2\xaaS\xf6\x91\x06$Sv\xb0\x98\x01\x08\x92]Z\x90\xe4HMh^/\x00\x14wQ! \x02\xb48m\xd2W\x8bZo\xb8\x08d\xc9\xae^\x8a\x8fM@\xc0+\xb0\x15(\x04\x13$E@\x00y\xd4\'\x9d\x1b+\x02\xf2S\x009KX\xd8d A\xc0\xc8\x05\xd7\x8fB@\xa0\xc8hUyS\x87\xf8@\xf3\x19\x12\x8cRAn\xa9\x1d\xca\x90\xb2\xf4\xb4\x7f\x06DOA(fP\x19@\x94\x05$a@2T\xb1v\x8b\nA\x90O_\xea\xdft\x80\xa8\xc2\x01b\x1ee\x96\x10\x94"`m\x08\x07\xc7.\xc9\x98\x07\x0b\x88\xc8\x89&\x00\xa4\xd0:V\x89\xfb\xa7\x96Qpm|7\xbe\xb1o\x81\x08\x00\xd1\x7f&\x87\x85\xa1^\xd4~G/o\xaa/\x12K\x10\x8d\x08\x02\xc2\x1b\xa9# ?\x06\x90S\n\x8e\xd8@\x82\x14\xa4\xe5\xa8\x82$Ha|SlV4N~\x98\xdd\x86\xb0\x8b\xb6AB\xca\x86\xc2)\xfa\x8f\xc0\'l\x1a\x92\x94\xb5\x135\x08\x08M\xf9<\xc3d\xc5\x14\xf7c\xa9l\xab\xb3tR \x1b\x05N\xe9,SNh 8\xf7-$4\xa0\x8d\x8f\xf9S\x89\x909\xff\xd8\x07\x84\x91\x11Pr\x17\xc2&e\xc32\x84=h8\xbb!\xd3\xd0\x14S\xb4\x82\xab,\x8dW\x80\x9f/\xf1\x88\x9a\xb4*\xa4\xa3\x01\xe7\xc0.\x81XH\n\xaf\xa9\x81\x14\r\x88\xc9\xc6\x8a\x80\xfc\x14@\x0eX\x15PSQ\xb0\x1d\x92\x16\x85M\xcc*\xb5\n\x92\x14\xf3HM\xce0\xe1u\x1cpV\x0b=w\xb0\xd0\x0f\xd8\xbf`h\x936\xae_=\x0cN\x0bo2h;\x95\x80\xd1a\xfb\x19\x1a@\x80\n\x9c\xc9\x02\x00\xd1\xb4\xe4\xe9\x8a\x8f4\xd7v\x8a\x9e\xde%\xd0\x04\xf3\xdc\x82\x90\x91\x04\xc9\x19\x0f\xa8\xdc\xc6\x98d\x06\x8e\x94$E\xeeka)\x03\x92\x012B\x8bHU\xeevE\xb92!\xaa\x12\xc2\x1b\xfa\xfc\x15\x86\x1c\xdd\x05Q\xf4ap\xd1\x8f\xf4:\xb0\xdb\x95V\x07\xad\x90\r\r\x12\x08\x90\xa2f@J|\x00e\xf4\x8e\x11\x90\x9f\x05\xc8h\x01q\x12\x84\x08\x81\xcfh\x83\xf8\xd9\x13\r\xe9W\x02t\x06\x00\x03\x9a\xcb\xa0K\xab\x00\tA\x84t6\xe6\x86\xa9\x1eh\x83Tf5\xae4 z\x05\xcfaNK\xfa[X\x980\xc7\xef\xc1\x02\x91\x19\xd5&\xa4\xd5\xbf\x80y_\x00#8\xab\x01\x94\xccH\x90\x8c\xa0\x81/\xb9\x93!\x19\x8a\x8e\x9c\x98\tl\x14\x06$\x05 \x11\x10}\x92\xdaP\xd0t\x976\xc9E\x9f3XY\xfa\xd9\x9a\xbd\x05\xca\x07dc\x14\xd0k\xaa\xe9\xb4\xec,Ir\x00\x1c%\x06H\x9a\x1c4\xab\x92$\xa6\xc4:\x93s\x04\xe4G\x01rkQ\tb#D[\xc7YZpD\x04\x94$U\x80_\xd4+\xb2K\xaa=l\x1f)\x12\xaa28\x0c\x039}\x99\x10\xfdd*\x84\xa9q\x0b\xe5MDM2\x83\xb6\xf4fZ,d\xf0_\x03\x81.&\t\x1b\xde\xf3\xad\xc1\xc2C\x03\x92\xe7\x85V\xb3P\xc7\xc2\x97f\xf2\xe5E\x120\xca\xfczF\xc4\x800\xa2\xef\x91\xa8\xc2\xd6\x94\x86\x17\xa6\x94\x8c\xe5\x01RhY\x91\xd0j\xa0\xa78\xbd\xd3\x06\xf6\x0e[\x99\xa1\xf8\x8b\x93\x1a\x1e\x1c\x05\x12"\xa0L=\xaa\x91%\xfd\x1d\xd8\x07\xb2\xdb\xd5$\x92y+\x01\x01\xc2{\xa6" ?\x04\x90k\x9e2 \x19\xc4\xd3\xb2B\x12 h@\xe8Q\xcb\r\xd3\x00\x92\xe3\x91\x0f\t\x89\xde\x9d!\x04\x94\xf3\x8dW\'\x8ag\x19h+e\x05\x04\xd2,g@2*\x06\xec \xb1\x0f@8\x14@\x05\x02R\x98\x1f\xa3\xaa\xf2\xf2\x92b\xd6H\xe6\xe1d\x14-\x0f\xb1\xac@\x19\xa3e\x10\x8c\x1c+\xec\xa6\x12\xe9Q\x1a\x10}\xce\xe0MN\xe0@\x88\x01\x14\x98\xec:P,K_\xd5,\x8bm@\xf8*\xc1\x9f(\xba\xae6\xcf6\x83\x96\x1fI]\xe4\xfa\xafd)\x01\x86y\xcdiZ\\# ?\t\x903\xf8{\xc0\xcf\xcbEK\x8c\x04\xe1\x01V\xc8r\xce\'9\xd8\xdf\tm\xff\xc01tX\x17K\xea\xe7+\xb1\xb6\xb3U\xa0\xc0\x13\x81\xb4\xc6\xa3\xf9\x02p`\xd1\x94@z\x14\xb0\xcag\xf9B\xba\x00\xc0T\x10\x1b\x0b\xb0\xa3G*\x0f\xc0\xca\xcc\x17~\ny1\xdfe\xb8\x9b*A\t\xc2\x80\xe8\xb7\x02\xefY\x9b\xf3\x80H\r\x9a!\xc4\xdbq\xea{Z\x14\x9c2IU\\<\x02<`H\xac\xa7HWc\x1c\x07p6\x93\xfc\x80\x0b\x8a\'\x8e\x80X+=\x02\xf2C\x009\x19@\xac\x8a%\x02@\xf4j_\xb0\xbe\xc4>XP[\x98\x8f\xda\xd8\x1a\xc3\x80\xa1C\xfd\xbc\xc2\x8e\x95<`&K\xf4\x86y\xca\x8a\x06\x84\'l\x86\xd5\xa0\x13[Q(\x0bd\x01\xbeh\xa9ze\xb4\x0cg\xcd0\x8d-\x12\x92H\xe5K\x9d\xc5/\xacT7\xe3\xc6\x02\xed\x8c\x01)\xf2\n\xc5a\ne\x8a2L2\x13\xb5;\xff\xa2\xb4\xe2\xd4-\x1bE\x88\x07\xfc\x91\x14+V\x00aP\xe5\x05CB\x16\x10 \x04\xf3\x9a\xf5AN\x11\x90\x9f\x04\xc8\x81{[\x08\xd0\xb0\xa8^\xbf\xbd\xe7\x99\x99\x0e\x8a\x16\x7f\x08w\x81I\xa0\xd5\x1d\xe4\x83\rq\\5\x07l0\x85\x8cQ\xc4\xceW\xd3\xed\x94\xc2\rY\xa9Y\xe5%\x1a4\xe9\x1a\x90|\xd3$\x81\x9f\x83\xf8Hd?\xef\x0f\x87\xe3\xf10\xc8\x04\x85\xc8\xa3_\xd8x\xca\x18!\xf0.\xcdZ\xa0\xc5\x15%_\x1a\xdfpYf\x99\xbfFl\x0e\x8f\x10\xbd\xach\xe8Ia$\xef\x9b\x16\xa6\xa0\x1c\xc2\x0f$\x02\xa2\x0c \xec\xc6\x8a\x80\xfc\x10@&\x04\x04\xd2\x15\xcd$M\x8b\xacxe\x80G\xb6@\x9d\x84\x9d[\x1d\xfe\x1f\x06\xdcB\x05\xe6\x05\x00\xb4\x9cL\xee3t\x92\xe1\xc9\xea\x00\x91\xf6\xf0+s\xdd\xa3&#\xf9\xa1&\x8d\x87\xe6\xe3t:\xee\x1bt\xc1\xc1\x8bV\xda\x98\xfd.P\xd1\xd8\x08\xc9H\x82\x80\xc8\xe4\x99\\\x16\x12%\xa5\x00\xb3!\xdb\xba\x04\xf0:l%\xb2\xf1\x03P\x19\xf5\xf3\x12\x9b\xe6H\xfa\x13\x99,@q\x95)-/\x92\xdb\xa5\xec# ?\x08\x90[\x8f\x1a\x16\xce\x19\xbc\x9b\x85J33A\xb6\xa7\t\xac\xff;N@\xc1]\xaa\xf41@\xae\x88\x16!\xf9\x06\x1f\x01+zZ\xb2\x8e\x85\x99X\x10\x06\xd1\x93NHc\x05-\xa7\xb9\x99\xea\xf0\x13\x98`\xf54k:\x8e\x08\xc8an\x80o\xb2b\x02\xd3\x83\x8c\x1a\xfac\x995\xf9\xf3\x1c}\xc0\x89\xc4Pa\x9e\xe2\xeb\n\x98\xdb\x8e\x89\xccZc\xfeY\x03\xc4,!4\x91\xd2\xfba\xe6\r\x16\xbb\xe8Z0]t\x84D\x7fq)\x18\x90\xe1\x16\x01\xf99\x80\xb0\x13+1V\xba\xcc*%r)3\xb9=2t%I6@:\xbb\xc7H\x7fBk^\xc0R\n\x89P\xee72\x9e\xe3\xa6tiZ\xe2,\x82\xb9\x89\x81\xc6\x0c[\xbe\t\xe9\xcd\xb3\xdcp\x92\x85C[\xbei\xd1\x8d\xf3\xe1\x80"D+Y\xf3\x08\x19\xf3)\x8b\x1e\xc3W\xf0KV\xfcP\x00\x1c\xad\r\x8a(jk\tNT\xe0\x7f\xfd!\xec[\xce\xf8\x02\x18ES\x7fKg\xa7\xea\x02\x951\xfd\x8bx\x12H\x17X.\xc26\xe4\xca\xf0\xa1\xb0\x17\xa0l\xd0\xb9U\xf0u\xe6l\xac\x08\xc8\xcf\x00\xe4,L{=!\xb9mX\xd5\xe6\x0f\xe9(\xa5\xb4\x80\x90\xfd\xd15\xbc\x0b\xbb\xc3:\xa2\xe4\xa1\x82\xf2\xe9\x18\x9b\xf32@l2UYP\x8b\xb3\xba\x1d\xfa*\x97`\x19\xe7\x98G\xb2\x1cK<\xb4z\x04\xa5|\x9aa\xde\xc3\x00H\xe6\xa9o`a\x17\xab\xdf\n\x0c\x1b\xfdA\x9b\x16\x81\x92\xbe\x1f\xe6\x81\xca\x9c\xd4}\x0f[T\xfa\xcaT\xc1\xe6\x82\xee\xa6\xff\xa0\xd1\xc1$\x84\xfc\x93b\xc0\xe2\xef\xd3\x08n\xae\x1d\\\xaf\x02=\xc7\xd9\x00\xbf\xdc\xd8\x04\xf8\x1as\x80kJH\xae\x1bt\xfe\x16\xaca\x81\xfds\x8d\x80\xfc\x1c@N\x0c\x08:z\x81\x01\x89\x9e\xd1Gfi\xd3\xa0\x01!H\x82P\x19\x106\xd4\xbb\x92\x00\xd1kjb\x9b\xacU\xab\r\xddm\r\xa1\x06\x9c\x96TL}\x00L\x84\xc0\xd0\xe1\x1a\x92\xcc\x17,\x9a\xe2D\x16\xed0\xcdfLC\x0b\xfdcY\x84l\x0et\x8aQ\xe7E\xd5N\xf0\xab0\xcb\xb1IA\x0b\x84\xf4\xbdW\x00\xdb\x15\x8f\xc7\t\xafH\xaejy\x00\r\x11F\xd7x\xad\xc3%\x82\x9d\x1aU[a|\xa4\xe0p"z2\x94\xe7\xd9\xc6\x9f\x98\x1e\x12\xec\xe7\x8d\x80|h\xbc\xbdA\xba\x1d\xe7\xae\x98?\xd0\x0e\x9bo\xd0\xd1hX\x0c\x08\xe8\x17\xees >\xe0S^\xa6z\xb1\x06\x1b]@\x8c\xd0\xd4\xca\xa1\xec<\x03\x88L\xbc\xd8\xc4rP\x1e\xad\xd7\xea\x83{\xc5*m\xddB\xc2\x88\xc9w\xdfb\x05=XY\xddz\xa5\xd8\xbbF\xab<)\x18\x15\xabW\xf3\xdb\xce!\x8d\x18\x92Wz\xbd\xf6\x8f~sA`\x81z\xfa\x9a\xe8\t\'s\xd9\xf4\xad"\xa1\x0b\x01\xb1\xf6\x01;\xa6\x0fF\xbeT\xd0\t\x1b/KR\x92\xfd\xbeXL\x14~\xb2\xe1\xf6"\xb5MM\x8f\x1f\x02\xc4\xf4\xac\r\x06\xdf\xf8\xaf\xed\x90\xfeo\x01yk\x83t;\nj\xcd\xf6A@\xf6\xae\x81\xb1 \x08^\x05D\xaf\xe2\xac\xeb \r\xd3\xd1q\x85\x1d\xa89\x05\x13Y.<\xac\x99\x0bi(*\xc2\x1b6\xae\xc5.\xcd-4\xd0@D6\x01\x015Gc,dYc%7\xb3\xe9"\x03@X\xc7Z\xa3\x05\xd2C\xeb~\xa2\xddj-H\xe2\xa3R&7%\xe7\x06\xb7N\x1dL\xa4\xe5\xe3\x7fA\xaf\xc2\xbek\x88\x10}\xb6Ko\x06\'\xe9\x90\xfc\xe0\xa4\x1d\xabai@\x0e\x1f\x04\xe4\xf1\x93_\xdb!\xfd\x1f\x03\xf2\xc6\x06\xe9\xb0\xfa\x97\xe5\xf1\xee\xdaa\x7f\x04\x90\xdb`\x00\x01\x11B\xf6\xe9\xab\x80\xc8\x04\xa7*V\xb4&\t\xc2z\x16\xaaX\x04\x88H2?p\xe7lmVvL\x11\xcf\xa0w3\xb4on\xa1[\xbb\xcaC9\x90\x99\x83g\xa9\xd6\xb0R\xf0\xa1\xd2\x96E\x8e\xb2\x80s\tt,)\xd7h\x01\x8f\x19\xe21n\xf110!\x95\xca\xbd\xc0$\x04\xde\x0bJ-\xd1\x80\xc0\xd8\xed\xb2aY\xfc]\xdb\\\x1aK\xf8iR\x18\x0f\x16\xf9\x8c9\xec\x83\xc1se\xd3\xb5\x84\x03d\xfe\x1b@x6\\\xc7\xa2\x98o\xe6\xc6\x7fm\x87\xf4\x7f\r\xc8\xdb\x1a\xa4C\xef\xb9\xb3\xd7\x15\xfb\x83\x80T\x0e\x90D\xfcY\xc5\x02@\xa4\xf4$\x88\xc1\xa3\xe9\x06\x02\x04Wz\xeb\xa85F\x00;\x93\x942E\xdc\x83\x86j\xa8h\xd1WD\xc4\x93\x1a\x12\xb3z\xc1\x0b,$\x03\xa2go\xed\xc5\xb1\x01\x10\x01\x80\xa0\xad@\xf9\xc0\x16\x10\xa5\xa5GZ\x85\x8d\x99\xbd\xe6\xb4\\b\xb7RY(\xf2\xd0\xd3\x00\xca\x16^\x13\rB\xb3\xfc\xcd\x0ez\xabAM=\tg+\x0b\xe3\xe4u\x03\xab`\xe4$@\x8a\xc2\n\x90\x84\xbbA\x7f\x14\x10\x9e\r\x8d\xbc\x81\xc8\xf0$\xc8\xd7uH\xff\xf7\x80\xbc\xa5A\xfa\xfd\xde\x82n\x15v/}y\xcf\x08\xbc\xbcF\x84d\x7f\x02$\x15\x06\x10\x8e\xa3\x9bT\xf8\x8eU,\x00D\x1au\x85C\x11\x0c\x08\xf2a\xfa\x15\xac\x9b\x9a\xd3l\xcd\x92\x94E\x0e\x1c\x08C\x88\xf8\x1f\x12\x89\x05\xa5\x83\x14\x81\x1f\x99\x9e\xa7\x88\x03""\xbc\x88J\x92\xe4\x16@KI\xcf\xffm\xefPD\xc4\x0b\xa3\xe0\x04G\t"H\xc1Zu\x0f\x81\xc2\'M\nJ\x96H\xa4\x11\\! \xec"&\x90\x85\x07\x08v*|\xf7\xad2R\x81fC)\xb1\x87\xa5\x05\xe4+;\xa4\xff{@\xfe\xd0 \x9d\xaf\nN\x92\xe2\xef\x00q^^+B2\xab\xd3<\x96 )J\x10\xae\xb4I\xa6\x88Q\xb1`\x19\xa7\x1dI\xa1\x9f\x16\xd5+E\xf2\xa3v\x90\x84\xc6\x08\xaa^U\x8a\x81?M\x04\xcevd#\xa3L{\x04D\x04o\x0b\x9f\xcf@\xb2\xe0\x8b\x89\x12\xd4\xb5\xf4D\xd73\xb8\xc6C\x0c\xfc\x7f`T\xecS\x0c\x88!\xc4\x88:\x1f\x10lx\xdd\x05\xcd\x9f\x11\x90\x0ej\x93\x12 \x19+\xa7+@L\xad\x18\x99z\x80\xa0\x9f\xf7\x03\x80\x90\xdb\x91f\xc3\xb9\xd4\x93\xe3\x1cJ\x90\xaf\xea\x90\xfe\xaf\x01\xf9c\x83t\xfeA#\xff\xde";y\x80X+\xe4\xcf*VJiyF\xc7\x1a\x96\x80d.\xc8\xe7\xec\x0f\xde\xa9J\xa5\xa7\xbd\xaeh\xeb\x96\x1b\x02\x92G0v-\xddF\x14\xcd\x08\x03"\xa4\xf7\xb4\x01\x84b}\xd4\x18\x91\xa6+\xc4\xdd\xb3\xf6\r\xfd=\xb84\x9c\n\x9c\t\x19\xda"\x00H\xb2\x93\x0e\x10\xdbG\x04\xaa\x9f\xd4`\x850 .\x8e\x9ey\x89l\x19\xd9\xecA\x9f\xe0T\\>\xbcv\xda\xd9\xa0\x818\xb0\xd1q\xff\xea\x0e\xe9\xff\x1a\x90?6H\xb76\xc8\t\xbabKn\x87\xfd\xa1q\x10\xa9[\xd90X\xf8G@`\xfe\xa5\xa6\xed\x14!\x82z\x16\xabX\x92$\xc82\xce\x07\xe6\xb2\xda\xa8\r\xe27\x80\x1e\xec\xa4\xcd\x93\x14\xf9\xc8Ht\x18B\x00\x90T\xac\x86~qBd0H\x92\xbb\x8d\xc2\x16\x8d\xce\xf6\xbfuH\xd0\x91\xab\xb0\xb1\x01\x99\xea\x0c\xb5\xb1E\xf4i\x82\x86\xc5\x19\xcb~\x93[,\x0f\xa4m\x1f\x00\xc4\x84\xdf3?\xc53w\x8f\xc3>\xda\x1fl\x12b\x97E\x9c\r\xa3\xbc\\\xf5\xdc/}/\xd6WuH\xff\xd7q\x90?6H7\xda\xe8\xa1.fZN\xb0\x1d\xf6G\xc6\xec\xd9\xe8):Q=\xf5\xca\x8b&\xb8a\x00\x91Y\xe9\xb7\x92\xaa\xeb\x12\xa6AJ\x80\xac\xbd\xad\xb9W\xff\x10\xab5P\x03\x10X\xee\x0b\n\xd7\x05}iT\x92P\xfa\x06iW\x84\t\xa5\xee.\x0747\xd12\xc4\x974\x92\xb6F\xa5j\xd1\x7f\x1d\xea\xcbgf\xb3c\x8a\xc1I\'\xcd\xea\x96dH\x86\xa9#\x94R\x03\x92)\xc5\xca\xa2\xddr P\xd9\x1b\x8dt\x92!\xc5\x834OO\xc1\xf2\xf2y?\x16\x071\xb3a.\xb0%:\xdd\xf8\xaf\xed\x90\xfe\x9c\x91t\x1a\x93\x89\x13\xa6\xfc!\x82,\xaaW\x00)\xba\xa6\xb6\xb5n(\xf2,C\t\xb2\xc8\x1a1\xc5\xe2\xa0\x1dr\x12\x8eT\xaa6\xec\xdc\xd4*\x88\xfd\t\xb9\x94\x15\xda\xda]\xf31\xd4\x89A\x03?S|;I\x95\xef\x0c\xd0\x07\xa8\xf2\xe5\x81!\xcb\x1f\xed"j`\xc5\x84\x90\xb7\x18wg\xe9\x0b\xb2\r\x08\x15\x98\xd3F\x88\xc0\xe5\x00\x12\xb2$\xf8y\xe5Vz\xa7X\x00\xb2\x7f\xc6\xa9\xf4\x94\x80\xdcZ\x0b\x88\x15!\xd9\xdb\x00\xc9\xe6i\xe15\x9d\n\x8a6n\x00B[\xc0\xa1\x1f\x88\x9d,\xa9\xdb\xe3N\x19J\x95\xebo\xd6\x12!"[\x01"\xa7P\xc7\xc2d\xf7!M\x98\x8f\xd4tg\x07>\x8c\xbf\x8ce\x07\x98H\xbbdc\x88\x9c\xb5=\x8c\xaa\xdbX>\xa5\x1b\x1b@\x9a%\x1f8\n\x94 \x12a\xda\x14!\x94\xc9\x15r\x99\xa6S\x04\xe4\x87\x8c\xabr\x80\x00\x1d\xa1\x08y\x0c\x88\x80R>\x8d\xdb\x11\x85\x99Hh\x83,\x00\x91\xc6\x81E\x85Ex\x0bajs\xdb)P\x8d\x95\x1d\x12Q\xb5\xbe\xdd\x0c2D\xae\xcc\x8d\xb6\xc29\xcdj\x12\xbe\xf7ZZ\xbd+eD\xc0\x8b\xa0*\xbf\x01\xae\xb2xx=\xdeh\xc2BYRU\x99\x1e\xa2LHFI\x99pM\xb8\xad\x8fe\xa3s5^X\x82H\x974\xb6\x0c\x88\xe0\x1e\x91\x85\xbc\xe4\x84\xf7\x08\xc8\xf7\x1f\x97\x8cS\x15\x19\x90\xd4\x03\xe4A\xc2;"\x80\xc9\xadv\x86\xda\xb1#?X A\xa4\x05D\xe5\x84\x87\xb0\xb5E\xd1\xc5C\xee*\xda\x9c^\xdb\xd6\x98L\xc8J\xa1J\xb8r\x8f\x1b\x95\xda\xa5Y\xf8*\xf8s\x85\x9f\'Y\t\xae\xe0\x95\n\xef\xd8\x19O]\xdc\xdcbkc{q\x7f\xb0ADJ\xf5\xdf9\xab&lF\xe8\x01\xc2"\xc4\xc3#\xdbT\xb0l $\x02\xf2\x03\x86\t\x83\xa4\x1b\x84<\xf0fQ\xb0\xa4\x10\xbb\xad\x91\xca\x00\x10!M\x80\x10\xd3\xd9S\x83\x07\x80Q\x9b\xa2l%\x16\x99FD\xd0l0\x12\xa4\xaerKHj\x95\xa7d\xd7N\xbd#R\xf4C\x05\x9e\xa4\x15\x1f\xb2\xa9\x9d\x00\xc9R\xb3\xcf\x89\x8b\x97\xd8m\xe6\xb8M\x16\x11\x91\x1e!\xb9\x05\x04U\xac\xa4l\x1e\x8c,\x04D\xf2&+GI\xb6\xb4\xd0\xf1b\xabk\x04\xe4g\x0c\x1b\x06a\x85\x85<Kav\xe2& b}\xdf)\x06\x12J\x10\xc9\xf2\x03$\x08U\x19E>\xb0|\xaf+{\x0b\xfa\x19\x186(D\x1c!U%\x93\xb5S7\xd9\xe5}\xc7\x07\x17\xfd~\xce\xb7\xf9\x80\xa6W55N\xaf3S\x85\x14\xd0\x04.Kl=U\x978\x97\xb1\x97\x1c\xfc\xd8\x11b%\x88\x04}l\xd1\xdd l\xf3\x9cz*\x964\x8acA\x92\x13M\x10\xa3`\xa5\xfeu\xbaD@~\xc68\xf8|\xa0\xf8\x10@\xc9\x1b\x00\xc1\xd4_\x9c\x14//6\x8d\xd6\x04\xb7\x13?\xb56\xc7\xb2T\xb9\xb0\xe2\x036Mx\xfd\x9f\xa8\x15\tVc\xc7\x95\xdc\x8fL\x80\xf5\x9d\xfa\x12\x84,\x87T\xc2\xe6oYB\xd2yb]]\x9em\x01\xfd\x0flwOAuHq\x7f`phn\x91\x90q]\xa3\x94]\xd1\xca%\xc7\x10 ;\xf9\x08\x90t\x07\xc8\nK\x87\t\xe2\x1b!\x82\x1b(iGs\xfa\xd7\x81\x90\x08\xc8\xbf\x1f\xb3\x07\x08\xcc\xc2\x04g\x97\xc8\x1em\xb8E\xe9\xb0\x08e\xbf\xbc\xf8\xab7&C\x11 \x08\x11\xd9\x1f\x99\xe5\x03\xa5G\xa9\xeaU\xd9\xf4\xb2`KDT^\xe8n#\xeea\x10Q\x059\x15\xb6~L\x95\xe4\xc9u+\x9c\xf8(\xcb\x05 Xu\xb7\xc8\xb8\xa2\t\xd4\xafR,Bh\xaf0\x02\x02V\xfa6!\xa5~\xb7\xa9\t\x14R6\xb1>C\n\xa3\x18\x19\x9a&\x0cH\x9a\xb8\x9c\x85S\x04\xe4g\x8c1\x90 \xa0`A\xcel*\xe4\xe3\xb1\x9a\xb1\x0c\x88\xf4U\x1c\xca\xc3%\x0b\x043e\x05ORJq\xaaq\xd3\x1dP\xe1\xf7U/3.\xd5K\xee\'\x14\x01\xc2\xb9\x83S_\x89\xa2j\x90i\x10(!\xc6Q\x06*\xd7\x0c\x8a\xcc\x0fi\n\'\xc2\xd1P\xc1\xe3z\xa45[@\x96\x10\x88\xf7\x93\xa1.\x19\x10\x08\xa5o\x01\x02\xed;\xe1\x80\x89\x08\xfc~\x04\x17\xe6S2\x1f\\\xe9\xeeo#\x85\x11\x90\x7f\x1f\x06\xe9}\tB\xca\x89|\x85\x10\xb1\x04\x04~\xef\xe5%\xec\xe9\x01\x7f\x05%\x8ctuE,\x1ff\x1bjQ\x96\xc1BN\xea\x8eO\x08\xb5>S\xa8\xf6a\x11EO\x87\x12~\xaa\xa2w\\\xa3`\x15.\x95\x05\xaa\xfc\x80\xef\xc0T\xf7R%a\x81\x9d\xb0j\xde#\x8b[\xc0\x88\x10\xaes\x9d\x13\xdf\x04H\x1a\xf4y\xf6\x05\x08V!&\xbf\x9e\xf1i\x04EN\xb0\xf5JjM\xbc\xa7\x8e\x14>% \xd5\x02\x10\x98\x87\xb0\xb3U\x88\x87eM\x1eI\x10\x1f\x11\xb4\x0b\xc8\xb7\x93C\x19^\xac^\xc2|\xd0F;^\xc6\xb9S\x1bj]\xb0\x98gl\x87\xc0\xe2O)S\x92\x94\xac4\x15\xe9\xd2\x14_\xe5,\x9aj\xa4\xd2e\xb5d\xec[\xcb\x98L\xd2\xaa\x8c\x9e\xc5m\x0c\xa8W\x1b\x12"\xb8\xa7[NN)I\\&\xbbr\xd8\x00$\xd9\x99\xb3\xf5\xfc\xe2\x88Ha\xdd\x15\t\xd7\x97\xf4\x11y\xceH\xe13\x02\xe2j\xfeX\x19\x80jK\x18P\x7f\x0c\x08\xce\xd0\xc0\x06\xb1K9\xda\xe8\x90\xf4W@\xb8\x8d\xf9(\xb8\xff`\xcd3U9\x01\x82\x8e\xa5\x1a\xa2jT\t\x08\x8b\x83@\x87Lt>\xa7L\xc8V\xaa\xa2\x08u0\xd8\x92R\x9a\xc4\xaf<a>\n\xc3\xa5R\x16\x14\xae\xe4NU\x15X\xcb\xd2\xda\x99\x11!\x19\x01\x82\x88\xec\x92fj\xab\xb6\xa9\x9a\x8a\x1a\xe5\xd4M\xdf\xa7;\x92jI(o\xd9\x8d\x05\xbf-\xc8s\xbe\x06\xa4\xbbE@~H\x18\xc4\xdc\xb2\xd4\xcc\xf8T\xdb\xbfb\xadd\x89M\t\xa2_\xba\x01\x08\x88!\x8a\x10RVl\x8aV\x80\xe4\xc6U\xa8\xdc\x94\x1c\x05\xa1\x86\xe6\xd4\x17\x01\xf6_\xb0\xc1\x9c*\xd3\x8fJ\xa1\x08I7D\xc8\x16\x96$@J\x06\xa4.\xf9\xc8Y\xc1u\xb3\xc9.\x87w\x80\x9f\x15\xf2\x82\x8c\x14\x92,\xf5\x84\xb7\xa4\xb3\xca\x94r#\x87]ROC\xdb\xb4\x15\xb5\x9f\xaa\xfa\xb1Kv\xfc\x96\x12\xb1\xca{\xe6R(\xec\x16\\\xf0aKcE@~@\x18$\xf1%\x08\xed\xcf\x13Y\xb9&\xc4\xe8\xfd"T\xb1d!7\x00I\x82\x92m\xec\xc0"+\xd9\t\x10\xd2v\xea\x9a\xe5\x08\xf6\xb5\x05\x11\x82\xf2F\x9aV\x80P\r$\x15\xbc\x90\xbf"@R\xa6\x08\xa2\x0c\x05u\xd1\xad\xa0\t\x8e\xfeS\xe6\xc8F\x82\x18\x17\x9a\xaa\xc9j\xa7\xba<|`0\xd4Kn\xad\xaeO\x8f\xd56(\x0b!\xbb\xe1\x7f\x1d6\x90j\x87i\xaa!\xa6Coh\x03\x10\xb3\xd3\xd1\xbc\xe9% \xc55\x02\xf2\x13\xc61\r\x01\xe1\x10\x87\xc0\x9d\xdcB<v\xf3\xfa\x84<\x00\x84J(\xe4\\>1\xb5\x06z\x89&2\xda \xb0\x84\xb3\xb5\xcc\xb4Xk )\x8c\x04\x81r Aq\xf9\x07\xfa\x15\x8f\x1d\xf6\xca"@\n\xac\xfc\x9b\xf1\x91\xd1}\x16\x02R\xa3;\xad\xa6-\xeel\x86\x80\x00BB\xf2LH\xf7w!\xb7&\xab`O\xc88Mc\x03\xeeha\xa2G\xdb.\r\xc3\x87{\x7f\x0e\x10y\x89\x80\xfc\x84\xb1OC\x15\x8bTh\xfd\x15\xfa\xd8&\xb6\x82\xa6\x95\x1e\x1b\x80l\xaaX\x89W\x80\x87\x04\x881@\xd8\xd3Z\xd6\xac\xf0\x94\xce\n\xa9\xb0\x0b:\xaf\xe4\x89\xa0\xb6\xcd\xd0w\xc6\x00"\x1e!\x92\xda\x1d\x1e\xa9\xe9\xfd\xa1\t\x81\xc6&\x89S\xedj>0\xbb\x96\t\x13\xc5\xe5\x0fq{-\x87C\xa8)5&\xb0\x8b\x00N\x16\x84z\xe6c\xbee\xea\x82G+\xe1\xb1\xe4C,\xdcX\xcf\x18)|B@n\xe3\x06 P`\xc0\x10"|\xe3#\xa3\x17\xa4o\x00\x04\\a.\x9d\t-t\xab`)\xea\xfc\xa7\x14\xcfT\x173\xa4\x9f\x18;\x1d]\xb5\xa61\xd3\xab\x12\xc4\x93/P\xa6\xd7\xe4~)\x14 "\xb3\n\x16\xe4\x98\x94\xb5s\x11\xe0\x07=\xcb\x05\x84d\xe2\xac\x10\xac\x04/\xd3$\xf8\xebI\xf2\xf2\xb2\xa3\x9et\xde\xb3\xdb\x92\x16=\xe6\xa9\xff&\x9d5(\xb8\xf9\x00\x00 \x00IDAT\x04\x11\xa7\x08\xc8O\x00\xa4\x15\xe9J\xc3\x82,>!2lp\x1e\x18"\xe6q\xfa\'@R?\x99\x97\x05HV\x18\x17\x16e)\x96\xd6\xf2\xe0\xf5\\\x99\xb86\xcdSH~7\x80,t\xac\xf45B\xb8Y\x08\x96\x02\xc5#g\\\xdfP\x8b\xa7\x9aXd\xb3\x83\x1f)\xd3\x82\xb0\xb0\x11\xf5\x14\xfa\xeab&V\xb6\x00D\x1f\x1d\xcf6|.\xd9\xe6\x03=\x1e\xd6\x15\xbd\x08\x84\x1c" ?\xc1\xcb\xab\x16\x81t\x06D\xeb\xfd"o\xbb\xc2\x12\x12\x98#&S\xc9|\xff\xf2\xe2\x05\x91\xf9y\x8f\x10S\n\xdd\x1a\x02\xc6B\xc7x\x83\x85D)\xb3\x92\x93\x15\x92\xa4\xca\x00\x92y\x8e\xd2te\xa9[\xd5\x8b\x04\x0866\xc0f\x89P\xf5}\'\xe8\xc0\xe8\xb9%\x89aL\x8f\x9a\xa3/\xa6\xafsid\x97\xfe\x13\xd8B\x91\xad\xf40\xcfPK\x10\xf3\xbdm\xa2%\xb6\xcc\x8f\x04u1WHb\x01\xc8\x1c\x01\xf9\x01\xc3\xec\x061\x80ps\x18\x98\xb0R\xe6m_m\x9a\xeaf\x7f\xb9\x89d\xbc\xbc\xf0\x847\xc9\xb0\x85\x14\xb6!\x0e\x99\xe8\xd2$\x99\xf3\xda\x8db\x84\xfe\x84\x1fJGO/\x12\x02jL\xc6\x80\x94\xa1\x8e\xb5R\xb3\x16\x1a\x96\xcc\x19\x10\xec\x7fHd*\xfd\xe6J\x15\x1c\xcc\xa4\x9cP\x0b\xf3\xda8\xaeP\xc7\x12X\xb4=+\xa8\x94w8\xf5I^\x9a&:\x99,\xeaFm\x99\xe7\x90N\x99\n/\xd8\x1f\xeaXO\xb8e\xea\t\x019\x8b0N\xc8|@\r\xf7:\xd32dh\xe5f@D\xb8N\x1a\x12U,\xb9\x8cq\x0b+T\x8c\x86e[\xb0\xa1\xe7\n\\W\x95\x9d\xac\xa5\x91!6y\x10\xb7\x1dn\x03BI\'\xe9\x03@\xa0"\x17\x01L[\x08\xd9\xf6\xd1r\xa22\xbe+\x8f\x14z7\x14\x08QN\x84\xa4\xa6y(\x98\xe9e\x90\xa8\xa8\x97\x03U\x07\xcft\xe5"L\x08BG\x14%\xf2\x81\xd1\x90\r7\x96\xa8# ?\xc1\xcb\xbb\x0e\x83d\xdc\xe4\xa0\xa9\xf2L\x132\xe4k\x15B\x84\x1a\xd7:\x0e"\xb9\xb4\x88\xb4\x1a\x1668+\x8c\x86\xa5j\xe5\x0b\rZ\xcc=+\x84bv\t\x18\xcbP\xdd\xb6\x08\x8c\x90\xb5++4\xa2\x05\'\x10C"\xb0\xd5\xb0j\x8c\xb7\xf8[PJ\xe3Z\xe6\xdc\xc92\xd0\xb1\xa88\xbdU#Mb\x0b\x9c;,\x07<\xe9\x03\x0f\x9f\xf5b\xa1\xf8\xd0"X\x80^\xe9\xf2\xc5\xc2P\xbax\xc2\x1d!O\x08\xc8~\xd3\x89\xa5\xd7R\rH\xdfjC\xb5\x1a\xc66$DX\x19"\xcd\xbe\x90\x8dT\x13\xc1\xa9&\xd8\x13\n\xfa2q+\xd0\x9aCs\xcaNP0E\x82\x04t4:8\xdfD\x1b\xd6\xd8X@&>!6`\x98n\x08\x10\xaa0\x82^(\xaba\xd5F>\xe9\xf3\xb2)\x92\x8ds\x9e\xa9\xb2\x0e\xfdX;iv\x15J\xb1\xc8#0\x16\xd7\xc3\x01\xe2\xa3lJ(W\x84*\x98\xdc\x12!\xe93\xfay\x9f\x10\x90)u\x1bn\x85\x95 \n\x15\x88\xbe\xef\xab,S\xfd48[]\n\xff\x13\x7f\xd5*\x96\x14\xab\xfa<\xb8\xbcZ@\x8c\x8f\x17\xe5E\xe3\x19\x02\xb5\xd1\xb4L\xf2\xa0rV:\xbb[!\xaf)]\xe8X6\xba 8Tm\xf8\xc0\xbc\x12"$\xc3\xef\n\xb0u(/xq\xbc\xaa\xf6\x82\x95\x8a\x11!?V", r\xa9a\xc2\xd9\x1a\xb77\x96\x03\xf6\x8a\xd0\x93\xf8\xc8\xa1B\x04\xd4\xa0/\x19\x10\xb9\x05\xc8)\x02\xf2\xfd\xbd\xbc\xbd\xb7!\xdd\x02\x02\xeb<\x01\xd2\xb7\xb96\xd5\xc7\xa9\x13\x8f\x12\xe0Q\x90\x84\x1b\xa6\xdc\xd6\x10\x04\x04\xd3\xd71\x8a\x8e\xb60\xe92\xb4\x82\xd7\xe5r\xcb\x14y`\x951\x96\x85\x01D\xa6\x0b+\xdd\xec\xfc\x08|X\xdc\x8aTr\x97\x9e\x8c\x9c\xbceY/6g5\x8b\x1dSx`\xf6c\x19G/iX2sK\x02\xbb\xf3P^:s#l\xd4\x80\xdaUK\xfd\xb2P*\x11!b\xa5c\xe9w}\x8c\x80\xfc\x18/\xaf\xef\xe4\xc5\x19\xc5\x12\x04\xda(i!2\xccs\xbb\xa8\x07$\x1f\xd9 \xd2\xdf9%\xd9\x04I2\xa3_\xd1z]\x87SV\xb9y[\xda\x1a\x0e\tu+/m\xe5\xce% "]\x9a \x06\x10\xde\xc4(, \xe89\xc3\xd3Z B._\xfd\x83\x92\x83!&H\x99X@V\xeb\x81S\xb1\xc4\x86oW+\xa5}%\xb5\xc4\xc4\xb3\t\x00\x11\x01 O\xb8#\xe4\xf9\x00\tj\xfe\xb0\x00\x81\xde\x1bX&\x8d[\xa3\xf5z\x9e\xd4\x93FD&\x9e\xcb\xd7\xdf\xb0\x84JG @l.<\xf4\\\xa7\xb8\x02\xe7\tz\x8b\xb8"3\xa0i\x16b\x04K)P\xb6I\xca\xad5\xa8:\xcf\xd2\xd1\x9b.\xf3LR\x06\x84\xfd\xbc)u\x14e\t\x82\x19\xea\x8e\x8d\xe0\xb0\xe4\xc2\xa2\tm\xactSaQ,\x935\x1f\xd8 \x98\xb4\x9b\xb5\xb0\xa4\x08\x91CF<\xda[\xd4X}\xcb\x08\x19# \xdf~\x9c\xcc\xc6\xd5\xc0\x04\xf1\x01\x19\xa7qhE\x96\xf7\xd3<\xa1\x14Y\x85E0\x92.\xb7\x13\x08\xb1)\x94\x07\x08\xcd\xfff!9|\x15\x8b\xa4\x8b\xf5\xf3\x06\xe5\xabBG\xafo\xaf\x07\xca\x0b\x02\x92\x87\x80\xe0\x9e\xac\xd2 \xd2\x18L\x1a\x03\nI\x19\xf4\xf3fF\x82d\xdc\xdcyy\xc2[\x80\xe06x\x91u\xd3\x08\xd6GVa{8S\x11bK\xc7\xd2o\xfa\xf9v\x84<\x1f \xc7u\xa2\t\xe9\xecP"\xad\xa7n\x95\xd34\r\x15#\xd2a\x1fJ\xafA\x87\xe0\xd0\x99\x0c\xcd\x0f\xb7\xb9DR#t\x89\xd8\x91B\xc3\x89P5N\xd7\xa6\xb1\x9c(/*\x12\x02\x92mH\x10\x17Q\x0f5,\xca\x1b\x04@\n\x02\x84-\x1e\x03\xa3\x11$\x06I\x96$\x141\\\x02Bf\xc5\xc2\x87\xb7\x02\x04W\x99T6\xc34\xf5J\xbf\xa2\x02\xeb\x8d\x01\xe1F?\x1b:\x96x\xbe\xd2X\xcf\x07\x08\xb7>H\xfd\t\xcd\x80\x18\t\x02\x80\xcc\xd3\x90\x8bL\xb5\xe3<\x0f\x8d\\0\xc2~\x1d\x7f\x0fl\x9ar\xa1\x85\x8c%\xc8NRGK\'(\x1a\x9c\x9b\xc1lm\x82J<TC+\xe3x\xdd\x16 \xd6\x14\t\x9c\xbc\x06\x10mR$\x9e\x04\x01YQ\xbac\x80\xb7\xd7?p\xc3\xe1\x188\xb2Q\xb18i\x86kCl\xbby\xb9zi\n\xce\xbey\xea\xb5~&\x15\xf5\x92\x83\xfa\x8b5\xf6\xb9\x95rS\x84<a \xe4\xf9\x00\x99\xd2% \x99\xb5\xd1;\xcb\x07v\x16\x1f\xc0qY\r\x00KM\x95\xd0\xec\x0eD\n\x9d\xf9\x8b$\xc5\xb9e\x00\x88\xd9AX\xd3TeM\xa7)Y\xdf\xf1\xcdgTL\xc8\x97D\x05\xa4\xe5& bE\x8bQ\xb10[\x0c%H\x02\xddf\x1bR\xae\x1a</<0~\xad|%\x8bSO\x8a\x10\x10\x8c\x8a\x0b\x12N\xe6"\x19\x97\x849\x1e\\\x16h\xd7^\xe1.\x81n\xa4\xee\xa0\x08\x88zU\xc7:E@\xde>\xde\xd8\x1d\xdd\xbd\xe8?\xf1\xf2\xb6V\x82X\x9b\xc1\x98 \xad\xd3\xb0f\x1aZ\xd1\x12R[\xa1\xfa\xa9\xa1\xc5\xce\xcb\xc6z\x81)\xe3Vt=5\x15\x16[\'O(\x02"p\x9e\x92FC\xb3\x94\x94\x1d\xce\xd9\xe0Y\x8a\xb40 \xa8\xe8\xa4\xca\x84\xb3\x1fJ\x90\x00\x15\x06\x84\xca~\x96T\xa7\x91\xf2f\xacv\x85<\xd2\x11\x1b\x92-\r\x06I\x1a\xda\x16\xa2\x01I- \xec\x99*(]\xc4(G\x90\xach}\xb5E\xdd\xd3\xa2\x91g)\xa4\xe6L\xd8L\x17\xcb\x0b\x93q\xb3\x0e\x15\x9aE\xe9\xfd~\xde\xdb\xa1)\xeay\xa9\x99A;t\xd7\xfb\xe3+\xfb\xa5\x7f. o\xeb\x8en_\xf4\x9fxy\xbd\x92&~\x1c\xbd\xb6N^\x92 L\xc8<\xb6Y\x9a\xca\xbc\xc2^\x98\x1d\xf4\'\xa7\x9b\xfe\xf2b54\x0c\xcb\x95\\\x88\x04\xff^jw\xb16F\xb5a\xc3\xb8\xe1)\x1b\x02\xc3\x80\xa4k@6\xact\x11\x00\x928@@\x10" \x12\xd3f\x88\x8a\x86%\x86a\xd2\x10\xe3T;\x07\x08\x97\x8e\xc3\xd4}\xdcpU\x98\xac*\x8e\x83h\xe0\x1bvb@O\x9e4\xab\x07\xb8V\xa3\xed\xbf\x8e\x7f\xb9,|\t"\x02\t\xf2\xee\x84w\xb8\xf7\xd0\x87p5u\x02@\xbe\xae_\xfa\'\x03\xf2\xb6\xee\xe8\xf8"j\x1b\xa4?N\xf2|?\x96\x1fm\x83\xed\xe7\xf2.\x9dX\x08\x88/@phEBbA\x06e\xe6\x19\x98\xa1//YQ\x98\xfd\x82\x05\xa9\xed! IQz3\x11^\xd6\xac\xcaA\xbbnU\xf0G(\xa9\x16\xf7d\xf0\xee\x8aG"D\x88P\xc5\xe2j\\\xd0\x8b\x81\x94\xbb\xa61\xf8\x19&\xc2\xc36\xb5\r\\\x9a#\xefR\xe5$\x085\x9e\xc6\x13\x84s|yq\xad\x10\x1a-9\xc0\xcf\x9c)-Gf\xb0\xd6\xa6q\xb4\x804\xac-.\x001\xb2\xf7\xdd\t\xef\x85d\xad\xcco\x1b%\xa9\x99\xe5el/v)\xfd\xa2~\xe9\x9f\r\xc8\xdb\xba\xa3\xeb\x17q\xf3\xeba\xb8OZ\xd0\xe8/\x1fl\x83}r\x95\xdd\xc5\x1a\x90a\x03\x10`\xa4UR\xa4^\xc27\x00B\xf5\xcc\x9d\xd5j\xea\x85b\xbfh\x18\x99\xcb\xa1]O\xd2\xda\x9f\xac\x0e\x10\xda\xfa\x94[S\xf9\x15\x1d+p\x0fQ\xfc%/\xa8\x8ci\xe9\xa4\x86w\x98\x86}h\r\x99\xef\rC\xa2\xe7\xbf\xa4\xeaX\x99\x01\xc4\xae \xceH7\xbeg\x12\xa0"\x07\xd3\x0c/\xcd\x8c"d\xc4V\xbam\xf3(ThjB\x0e\xef\xbd_\xa3\x94\x03Pp\n\x1a\x0f\xd2\x97\xc3\x85D\xcbW\xf6K\xff|@\xde\xd2\x1d]\xbf\x88\x9b_\xef\x8b\x9b\xd6\x87n\xc5\xdeo\x83\xfd\xf2%\xc3\xbb\xfb\\\x9a\xca\x95?\xa4\tgmp\x83\x83\x0f\x8a\x9d\xc0\xa5\x99U\xe4k\x95l\xa2c\n\xc7\xab%\x1b<\x0b\x98\xc2\x9d\x1a\x10\x89M\r\xc2\xba\xec\xdb\x05D\x11\x92\x12c%%\xef\x98\xca\r\xf0|*.\xc1\xe5k\xae0\xda z:Hm\x9c\xda\xe6\xe7\x01\x034\x01\xbe\xb2_\xfa\xe7\x03\xf2\x96\xee\xe8\xfaE\xdc\xfc\xfa(\x8f\xf2\xa4?\x8e\xbe\xa6\xf95\xb7\xcfy\xc1\xa8X\x8e\xf1\xf3b\xa5R\xd2\xb1\xd8\xc6XO\xd3\x8d9[\x16\xd4\xed&s\x80@\xad\xae\x8d*+\xa6\xa4:[\xc2\xd0\x92A\xf0\x9e\xae<\xa3\xae\x1f\xf5\xe3\xe38;D\x1fS! E\xe2\x03B\xac\xbb\xba\xa2d\x83|\x15 Z\x104RN\x8b\xe6\xe7\x16\x10\xe9$\xc8\xd7\xf4K\xffl@\xde\xd4\x1d\x1d^\xc4\xcd\xafO\xdax\xbc\x17\xea\xe3]~\xc7\xb5\t\x92\xb1\x8d\xdem\x9a \xa0F\xa0%:r\xc3p\x0e\x17\xe3~\xee\xd2\xee\x97\xe5z\x1e\\}\x94:\x0b\xc8\xd0\n\xa8\x9bWF\xcd&HZX@\xb4\xed]J\xcf\xd2!\x9f\xb1\xe2\xc2V\xec\x80\x82\xae>\xd6\x7f\x06\xe5\xea0\xe11<\x94\xdf\x1f\xaa\r\xe4\x18\n\x92\xd2\xf0L\x9b\xa5\x18uj>\x886\x88\xe2T\xce\xa6#/\xdf\xa8\xf5*wqP\xc7\x82\xabc"!\xea\xb1\x95.\xb2\x8f\x18\x03\'\xa3At\xcb\x96\xe8\xa1\x96\xf1\x15\xfd\xd2?\x1b\x907uG\x87\x17q\xf3k-^\xe6\xfb\xac\xed\xb3\x0f\x9e\xb7_\xb1\xc1\x00R\xda(H\x8f\xf5\x9f\x18\x90=\x7f\x10!l\x87r\xa7>\xb2\xbb\x95kWh\x01\xe1f\x08T\x14\xfaU$\x16K:9\xa0\x12Q\x98-K\x82\xe3\x1b\xab\xcep\xa6\xb8\x90\xcd\xf8HM\xc2X\x96s\xbf\xb7b\xfb \x04\x88\xf7\x05\xcf\xa4\xe0\xf2\xa3\x05\xa5a\x19@\x88\x0fl\x8be\xf8\xe8\x86 N4/\xafNO\x7fR\xb1\x95.\xd7\x80\xbc;\x102\xc83:i\xf8\xfe{-\xd1C@\xbe\xaa_\xfag\xc7A\xde\xd2\x1d\x9d^D.\x89\xab\xd4\xa7\xac\x7ft\xfd\xe0y_\x95Xm\x06)M\x1c\xbd\xdb\xb8\xfbn\x8d4\xf2\xc3\xcc+[\x19\x847s\xa4.\x8ehu\xac\xc6\xef}\xf9:&$@\x12Y\x98\xa9/\x12\xe7Gx\xd8\xb7D\xb8 >\xa7\x9b$(BB\xb9\xd1\xbd\x8a&\xebvn\xa7,\xe7\xf9#\x1c\xdc]\xa4q\x80\xd0\x1ab\xae\xd1\x84\x84\x8c\x93\x8d\x84\x90\x1b\xcb\xcf6\t\xfc\xbc\xef\r\x84\xdc\xf6u\xd1\x1cn\xe6\xfe{-\xd1}@\xbe\xae_\xfa\xb3E\xd2\xbd\\\xde\xa5\x13\x0b3\xb1\xc2 \xc8r\x8d\xb4\x80\x98\x10\xc2\xa6\x08\x81\xe4%\xee\xde\xa4\x95\x92\xee\xb5)\xea\x06\xa6\x19\x82\x86\x95\xb9]z.R\xf3g@\xe8{\xa3c%e\xd3\x85hv\x0fIU$@\x12\xa7\x16Q\x96?)w&\xe3\x92\xff\x86%d\xf6/\x12\xeb\x9f\xc6\x8fU>\xdaU\xa8O\xe8\xd9\x12\xde\x9f\r\x10\xd7\xbfSx\x89&\xb5\x03dy\xef\x19\x10c\x818>\x0c J\x99\x9a\xb6\xa9k\xfc\xc4:V"\x9bnkfv\x16\x98\x0eeL\xe7\x0b\x10\x97\x0e\xf8g@\xa4\x99\x80\xce\x08\x11,B\xba\x87Ht\xe1#\x16 \xa2\xf02\xad\x84\xf0\x8c\x1f\x03HG6\x88\xaf\x86\x86&Zo\x1d\xbd\xc5\xf6\x9e\x90\xf4\xf9\x12\xde\x9f\r\x90\xe3\x1a\x90bi\xa3o\xc8\x8f-@j3{\xac\x8e\xb5\xf2ci%\x0b\xe6T\xb7Rt\xfcgp"\x17,@\xfc"\x9e\x7f\xd6\xb0\x1c!\xd6\xe1\x90g\xf4\x97\nX\xee\r\x88\x04\x8bk\xe7\xdcy|\xd6\xdc\x0b\xd7\xe9D\x14W15\xbd\xa8\xae\x84\xfd3\x9b\xa1\xa2\xd0\n!@\xe4\x86\x08\xd1_\xfa[\x04\xe4;\x8f\xbd\x8d\xe9zNZ\xd4\xb08\xcf\xe4\x91\x86\x85\x99&\x81\x86e\xaa\xdbR\x1f\x01\xc8\xbcp"\xc4\x98\xe9\x89\xc0U\xd7\x9f\x90\xcbG\x1d\x02$\x82e\x9c\xeb\xcd\xa7\xe2\xcd\x84\xd8\x12\xc3\x19\x8b\x90\xb4\xf6\xd0d"\x96\xa0"7VtY\x8d\xcdjX\x1e \x96\x90~)B\xa6ie\xa6\xbf"A\x9e.\xe1\xfd\xd9\x00\x99V\xc9\xee\x0cH\xdb?\x12 \x86\x8f\xa1\xf7MtS\x12D\xa1\xcb\x15\xad\x10?\xa8gE\x88l\x06#{\xb6t\xad\x8e\xf8 \xbf\xb0\x16 \xb9\xc7\x87x\x0b\x1f\xd2v\xc3\x95\xb6\xe6\x10:\xb2\x08\xcd\xde\x89\x8e\xce\x13$\x9d\x15+\x1c\x03I\x9d\x00qy&\xae}\x82#\xc4\xe5sZB\xc8\n\x19|@\xc2|E\x9f\x90gKx\x7f2@n\xbdX\xd8\xe8\x98\xe3WC&\xafQ\x1e\xfe\x0c\x88g\x82`<Bq\xf5i?\xeaM}\x07!\xf0G\x84\x0c\x1d\xe9;\x9dQt:\xe3\x15\x025\x87\x05\x88W\xd1P\xa4\x99-\xa2\xe2\xd78}d\xa8\xbb\xd3\xc9H\x84 !z]g\xb1g\x98@N\x8cG\xaakJV\xb0d\xbe\xe0\xc3\x13 XI\xd2\x00\xbd\x91\xcf\xe9D\x88\xdd\x13\x12\x966\x11~\xd2\xd89\x02\xf2\x8d\x87W\xb1a\xb1\x19\xa4\xb5\x1a\x96#co\x9c\xbc#\x03\xe2\xf3\xa1J\xd3B\x00?\x96\x80\x08n\xb2\xbcK\x0b\x9a\xa8\x84\x88\xf9\xcf\x1e!\x9a\xbf\xca(X\xae?\xbbH\x9bi\xd5\xe2l\x1b\x10\x98\x85M\xc9\x95\xeb2+B\xd0C0\xd8\xe8\xa6\x01\xa4\xe7\x94\xc3\x81\xf9HL\x0c\xc4Nf#@H\xbb*\xd9\x9bmD\x08\x03\x12\\\xa8\xc9\xd3\xb1\xdc\xb6\xdbm#\xe4\xd9v\x84<\x19 \xe7l\x0b\x10\xe5\x03\xb2\xe0c^D\xd1\x03\tBuu\x15\xef[\x95a\xe2\x14+Y\xbbT\xd5\x18`1\x7f\xa3\x1b\xcc\x04\xa5\'\x06c\x06\xa4\xc1\x96\xd6\xe3\xb9w\n\x97\x1b\xb9WB\xde>n\x8f\x07S9\x82\x1a\xc6\x052\xc4\x1c\xd6\xda\x1d|\xf0\x8e=\xbc @\x02\x05+\x15\xb6v)\xd2\xc1\xe5\xe7\xf4Ej[\x06;\x88\x16\xb9L\x03\xb3)d\xb1\xedVx\x80\x1c" \xdfx\x9cB\'\x96tN\xac\xd6\x84\xd1\x03:\x0c \xbe\x89n\xcbwr\xbdD\xaa\xba[\x12 "]\xbaza\x13m\xe3\x08Y\x0c\x8f\x8f$\x90\t\xdd\xf5v\xaa\xa8\xa7ffv\xa9{\x83\x9f*\xb0\xe1\xb4:^\xce\x8d4\x1b~)\x08O\x82\xa1\xa6\xd9\xccG\x1a\xc8\xe41\x87nJ\xa3\x06riU\x0e\x12\xa6\xa9m\xbfS{F\x08\\\xa4\xb6\xef\xbcp\xfa\xbcE\x88\x05\xc4U\xe8\r\x00\x99# \xdf\xd9\xcb\x9bn\x01b\x9dX\xe3+ABg\xa27\xa6\xbc-\xad\xad\x9c\x16U\xa2\x8e%\xd2-B\x8af01\xb6\x90\x0e\xfdd\xc3Q\xf7\xa0\xa5\x99\x10\xe7\xdb\xedv\xc0f\x87\x19w\xd7\xa4\xd6\x87\x85\x19\x1c\xa1T*\xd7|\\\xae\xc7LzF\xbd%\xa4\xe0\xfc\xa9aI\':x\xbd#\xfbS\x99\x05\x88\xaa\xdd\x89\x96l\xa6\xb7A\xb00\xbcH> \x0b+]x\t\xef\xb7\x08\xc8\xf7\x1d\x87\r\tB\xa9X\x06\x907\x9a\xe8\x98h\xa2L\x03@.P(B\x11\x02m6\x88\x90$\xabzTs\xfa\xd1\x83\x03g\x1aG\xd0C>\xa4\x984\x1f\xb7\xeb\xfc\xea\xd8\xb3\x94;^/\xe7\xcbe\xc0\xc9\x98\xb1\x92\x95\xb2\x96\x95J\xd58}\xceC\xb3oh[\x17\xf1\x11\xe6\xcf\x13 \xcavL(\x1d \r\xbf\xefE\xc6\x81\xddW\xc8\xb1\xc2ra\x84x\x80\xb4\x11\x90\xef\xec\xe5\xb5a\x90eE\x13\x00\xe4A\x940\xccS\xf4lt\xe2\xa3V\xb5I\xacMWM\nv\xec\xedM\xf3\xaac\xf3\x98\xf7\xe5\xc1\xe6\xd5N\x19%\'\x11A\xcd\xc2\x1c\x04\xc8\xed\xadS\xe9v9\x9f/\xa7\xbc\xf0+\xd9\x19B\x10\x91\x80\x112\x83j\xff\xc8K>dQX\x01\xc2\xf5I]\xf7\x03\x0e\x16\xae\xa3\xe9\xa3\x9fm\xb2\xe1\xe8\xe5@\xc8%\x02\xf2\xad\xbd\xbc\xa9_\x9d\xc4\xe5\xba\xbf\x12F\xe7<\xa3\x00\x10k\xbcr\xafs\x93\xf2\xae\xf5w\x17\xc1H\xed<\xc5\xb5\xdcnc\xe2\xb0\x027\xf3\xb0|x\xa5\xb7\x0eZ|\xbc\x99\x8f\xfb\xedz\xd6\x84\xcc\xd0\x88\xd4\xc6)9i\x99Z\x0bfemw\xcc\xa2\xb9\xcex,\xf9\x08\x04\x08sQ\xda\x9cL_\x82\xf8"d\xb2VH\x98m\xb2\x91\x8e\x95~0\xe1=\x02\xf2\x8f\xbc\xbcU\x10\x06\xf13\x15_3A\x06\xeb\xe1_jX~\xe9\x03\x06$e9b81\x84\x90\x9b\xaa\xd0&C\xa5-\nE\xe9\xbf<I\x13Wb\x0bG\x7f\xbd]\xdf\x01\x08\x8a\x90\xf3\xb9\xad]\x81`\xd0\xb5\x12\x0b\'8n\xb5\xd9b\xb6f9\xd5o\xc9\x873A\x94\xd7\xad\x81c\xa1u A\x16i\xcf\x9e\x95N\x9e\xdeM\t\x02\xb0\x9e" \xdfv\\2\x1f\x10)]\xc9\x9f\x00\x90\xfd>\xc8S\xf4b\xc46\x91\x97U,\xd3V\xa0\xe4\xe4\n\xcc\xc0\x95\t\x17b\'\x0c\xed<\xa5\xa9J\x86\xbc\x87Mb3\xb8l\x8b\xaa\xf3\xfb\xf8\x00\x11r:\x9d\x8fe\xe3u*1;\xe5\xddQ\xb8&\xa3;\xf4.I\xd7\x1bx\x13_\xc3\xb2\xa5\xe0\x95\x03\xc4\x16h\rV\x13O\x84\xd0\x96\x90\xb5\x08\xe1\x0b\x7f\x8c\x80|_/o\x16\xd8 \x92\xdd\x99\xcb(\xc8~\xbf_jX\xc1N\x10\xf2~\xfa]\xcd\x94\xa9S\x00\x7f\xba\x90I\xd02-\xf5\x11\xd9AG\xe5\x9d\x03\xc6\xd6z\xb0\x0e\xa8<?\xde.\x97w\x01\xa2E\xc8\xe9x:O\xaaYv\xde\r\x0e\x9dx\xc7\xa6#\xcb\xb0\xf1\xba\xc9\x9cg\x1b\x1d\xc5\x06u\x91V\xb5)\x0b\xe9\x94\xac\xe5\xa6\x90 \xa3Wm\xf6\t\x81+\xb2\x8f\x80\xfc\x10/\xafiN\xb8\x8a\x82\x04\x80<\x0c\x12zu\xdb\xedzIa\x84R&\xb6\x90\x15jZ\xfeD]N\xd24\xc0C\x8a"\x9f\xafZcz\x17\x1f B\x8e\xc7\xe3ih\x1a\xb1\x85H\xb2\x80\xd3\xd4BY\xd4\xdf\x96\xa6\xabi\xc6\xc5K\t\x10\xe5\xfaW\xfb9\xbd\xa1\x9d\xee;z\x03#d\x01H\x9aN\x11\x90\xef\xee\xe5]\x9b }\xb0\xdbv\xbf\xb7\x88\xf0\xb2\xb8\x04D\x95\xbe\x0e\xe2+\x148\xc7\x9a,\xf5\x0c\x91\x10\x91\xe5$\r\x0b\xc3\x8b\xb2\x1c.\xe7\xd3\xf9\xf2>@\xb4\x089\x1e\x0e\xc7c\xdb\xf6\xe96"xt}l\xd7\x94y]\x97\xde\xf8\xb0L\x93\\\xa7b)\x15d,n\x03b\xad\xb5W6\xa6?]\x85\xf7\xe7\x02d\xfe# \x0b\x13ds\xaf\xads\xf2\xfa\x1a\x16\x01\x92\xd1$k\x8a$\xb5Z\x0b\xa7\x12\xda*\x9e\xbbGsT\xcf&\xa5\xfa3\xd8\x13\xd7\xf7\xce\xa2\xeb\xf9\xb0\xdf\x1f\x0fm?\xack\x03\x05\xed\xce\x1f\xd0\x91\x8a\x82\xe0\xceB@J\xf6\xd2-\x00\xe9\xb7C!\x9e\x8e\xb5\xbd\xadP\x1f\xa0\xbeF@\xbe\xab\x97w\xd8\x04d\xe9\xe4\xdd\x87*V\x18E\xe70z\x196\x194\xcbe\xae$\xc9\x8c\xae6\x06\x88\x10.E\x0b\x8e\xce\x93t\xa3\xda\x95\x9eKu\xddkc\xfbxz\xaf\x00A\x11\xa2M\x81\xc3\xbe\x1e&\xb91\xfdS\x07\xc8f\xa1\xadT\x96\xf8V\x05\xe6\x7fe\x99\x0f\x08.\x08*P\xb2\x169\xbd\x93/m\xb7\x01\x11\x16\x90\xe2\x12\x01\xf9\xc6^\xde4pb\x05Q\x10\xba\xdb\x9e~\xb5\x91fB\xcdnT ?\xb8/_\x06|\xd49\xbb\x90\x86\xcev\xf2\x08+\xb2\x9b\x8as\xd4i0\xe8\xe3&\xfb\xb6?\x9d\x8e\x87\xe3\xfb\x05\x88&\xe4|\xd0o\xf7\xb0o\x86\xb9L\x1fT\x99\xdbn\xcd\x0b\xa3\xac\xf9]\x81\x03\x9a6\x83\xf8n,<[0H*\x0f\x10\xa3c\xf1\x1e\xe5 \x1d\xabYZ\xe9\x0e\x90\xe7Jx\x7f*@.\xf9B\x808@\x86\xd0F\xdf\xef\x97\x80t\xfeV)k\xb4\xa2\x9bW\x19\'o\xa6\xb0\xd5+\xf4\x9b\xcdE\xc7+\xf9\xa2\x9d\x07T\xbd^\xb6\xc3a>\xf2\xb1\x1aN\'\xd0\x94.\x1fQ\xd3/\xa7y\x1c\xa6\xfd\xdc\xb4\xfb\xfeA1\xc6\x87\x804\n2\xe5\xe1\x91\xaa\x80uL\xf62F:\xdb\xe98*\xcf\xd1k\x08\x996t\xac\xfa\x81\x8e\x95~\xa0\xf2O\x04\xe4_\x8ds&\xd2t\xa9a\xa9\xda\xf4\x051&\x88\xd5\xb1&\xa3a-\xf7\x12\xd6\x8a\xbb7a\xb3e\xc5\x06iVT}_\x81\x1c\xa9\xeavj\x9a\xb91m\x05\x1f\x00\x12\xea:\xddTMZ|h>\xce\x1f2co\xe7\xe3\x0cg1\xb7\xd5<\xcb\xf4\xed\x80\xa4\xe5P\x0es\xa50{^\x9fQY\x10 \x14\x1d$\xc9\xc1\x1b(\x9b\x00\x90\xd1\x02\xc2\xfbn\xa9H/\',\xaa\xf2\x01 \x87\x08\xc8w\xf5\xf2n\x99 \xca\x0b\x13\xce\x0b\x1b\x84\xd6\xc4\xde\x16\x08\xa9)\xf7\x02\\\xbc\xe8\xd8\xa9\x10\x10R\xb0\xf4\xb2[\xb5\xd8f\x1d\xda\xf5M\x10\x95\x98\xad9\xf0\x00\x10\xd7\x94ST\xd3P\xed\x8fG}\xe0\xc3\xe9\xfa1?\xcf\xedt\x80\xe99L\xbd\x1a\xf6\x8dH\x971\x0e\x02\xc4\xe8u\xb6\xeb\xba\x1c\x86\xeax\xbbLM\t-\xd8\xf4{\xd7\xa8P\xba\xb0\xb2\x95)LGu[\x9e\xbe[\xb5RY[\xe9\x9b}B\x9e-\xe1\xfd\xa9\x00\xd9\x8b4\x90 \xec\xee\xa7\xdb\x1dhX\xee\x96\x0f\x81\x93\x17;+\x97\x08\x87o\x820 \xb0\xaf\xbd\xa9\xb4\x00i\xa7\xf3\xf5r\xda\xeby:\x08\xe3\xcd\x12! \xe1"^\x8cs\xdb\x1f\xb4\xf8\xd0\x87\xff(\x1f\xda\xc6:\xcd\xb8\xefc\x1c*-D\xaa\x05 (AR\xc7\x07\xbf\xb1n\xea{\x10Y\x97\xa9\xd5o\\\x13\x0f\x84,\x00)\x1d \xb5\x0f\x88\x1fN\xa7jr\x83\xcb6\xd92B\xf0\xcc\x9f*\xe1\xfd\xa9\x00\xb1\xb9\xbc\xcb\xce9\xddR\x80X\x13dXZ \x94\x06^\xba\x0e\x99N\xc1\xaa\x01\x90\xf1x\xea\xab\xf1|\x83\xec\x8fC_L\x06\x11\xbb\x8e\x13 \x81q\x9e\x8ds_\xcd\xa0]\xe9\xa3\x7f\xcc\x00a3D+YT\xde\xabW\xed<\x95\xbe\xf8H\r \x1e2Zz\xec\xc7vO>\xb3\xcb\xfe\xa0\xdfq\xa5\xc5`\xad\x8a\xc2\x98\xe9\xa6(p\x19\x8a\x90P\xc9\xf2\xfa 8@\xca-#\x04N\xbd\x8f\x80|W/\xef\x02\x10WS1\xc83\x99\xd7&z\xe3\x03\xa2\xb8\x86\xb4saQ{\'\xcdG\xdb\xeb\xd9v\x850\x06\x00r\xdcOu5\xcd\x83\xf4\x11\xf1\xed\x00\x9c:\n\xf0\x18\x0f\x07\xe6\xe3\xfc7a\x82\xcba\x1a\xa8~\xd1\xd0\xaav\x18\xcaP\x86\x90\x8ae\x9f(\xa7yl\xc7\x93Ik\xb9i3F\x1b0 B\xca"\xd8RX\xba2\xc4$C\xba\xed\xf2&\xc6dk\x9bG)\xef\xe9\xb3U\xfey*@*a\x13\x82, \x95g\xa3\xbb<\x93\xfd& \x14\x17P\xbe\x82U\x19\x05+\xcf\xb0\x13\xf2\x84\n\xd2\x15\xc6E\x8b\x90y\x1a\x94\x1a&=Q\xed\xd2\x1d\x1a\xca\xb2\x19\xa7\xbe\x1a\xf6\x80\xc7\xdf\xf3q\xbf\x9f\x81\x10:\x9f\xa1\xcd\xca\xae\xab\xe5\x03#=k\xe7\xfdP\r\x873\xf0q\x03<\xee\xd8\x9d\x05DH\xbb\xd2\xb1\xca\x10\x10\xbb\xc3dZ\xa5\x9bp,\xdd\xaa\x9ea\xb6\t\x00\x92\x9f# \xdf\xd6\xcb\x8b\x8e\x94\x00\x90ja\x82\x80\x17\x0b\x01q\xb9\x13\xeb\xcd\xe8\x06\x8f\xaa"EB\x03\xa2\x17l=3\x0e\x17\x9c\xe0\xc0\xc7\x05D\x88&\xa4W\xd0\xe1\xb0Qr9IeVwz\xa1\xaf\xa6\xc3\x81\xe4\xc7\xfe\xf0\xb7|\x90\x0c\x198\xf5r\xeckt\xd6\xda5\x9c\x8f\r\x07\x9e\xa6\xbem\xc7\xc3\xe9l\x00aO\xd8\xbe\x05@\xd0Gk\x82\x85\xa8c\xad\x01\xe9\x86~m\xa6[@\xd6\xc5\x7f, O\x95\xf0\xfeL\x80\x9c\x85\xe7\xe55Q\x10\x00$\xcc\xde^\x05Az\xc3\x07\xbawK\x0f\x10\x1bD\xcf2UA\x0f\xb2\xc9\x84\xf84\x1f\x00\xc8a\x8f)\xe0\xad\xca\x0bl\xb8\xecZ\x8eC\xac\xba\xd2\xa2H\x0b\x8f\xe3\x11\xf9\xd8\xebG\xa7\xbf\xd7>.\x87y\xb4%t\xe7\xb1\xafh3{F\x9d\xd42h_\xd8h6\xaav\x98\xf5\x91\r \x86\x90\xebq\xac5!M\xcd\xfb\xdeM\xafh\xabby\t\'a\x9d^\x97\xd1k\x00Q\x0f\x00y\xaa\x84\xf7g\x02\xe4H\xe9\xdc\x16\x10L\xa8\xd0*\xf7B\xc3\xf2\x9b\x82\xf8[\xa5(\x00B[P\x03>\n\xe3\xe2\xed\x87\xa3\xf5@\xdd\x18\x10Ss\x10\xeaqb=\x12\x06D\xca\xa2\xeaG\xa0\xc3\xf0q\xf8O\xf8\x809\xce\xc7\x84F7\xd8\xa8\xb7o\xab\xdc\xb6\x1a\x84h_\xdbOt\xe4\xd3I\xf3q\xf57\xf7\x9e\xf7=\x02\xc2"\xa4\xf4\xb6\x84\x19@\xca\xc6o\x16\x12\x96!\x9dlu,\xaf\x9b\xe7\x92\x90}\x04\xe4[\x8e\xc3B\x82\xb8LER\xa6\xc3\xb69\xab\x8a\xd5\xdeN\x90j%@\xca\x06\x04\xc8\xec\xe5\x88\xdc\x18\x10\x9e\xad\x831__^\xb4\x15\xdc\x0f\xe3\x0cD0\x1e8\x8e\x1f\x0c\x10n\xc4C\xf68Q9\xb9L\xa3\x87BQ?\xf7\xf2\x02\xcc\xec\x0f|d\'@\xfc_\x9e \xfb\xbf\xa9r\x8e\x16*\xbb\xab\xb0\xb4\xda\xa5\xd7\x0b\xc1\x07d\xb9\xefv;\x12"\xc4x\x8b\x80|K/\xaf\x1f\x06!@\x94\x01\xe4\xb1\x0f+\xe4\xa36\xc58U\xc0GQ5\xa1\x00ABN`\x84\xcc\xbcQ\xc2T#|yAm\xeap\x08\xf9\x80\x0c\xc5\xff\xce\xda:PW\x1b\x93\\f\x8e\xf6\xf2r4\x83\x05\xc8\xe9\xbc\xdc\x9au9tP \xaebWo\xa9jo\x0fe\xe9\xd5X\xec\xb8\xa1\xcaH\xf9X\x1c\x08!\x11\xd2\x86\x80\xc8\x10\x906\x02\xf2-\x9dX\xed\n\x90r\x0b\x10KH\x90\xe8\x1eh\x1a\x0b\x05Kj\x0b\x1d\x04\xc8~ab_I\xc7\x02@F\xbb\x87\xdb\x03\xe4\x80\x13\x95A9\xff\x97\xb3F\x1b\xdb\xc48\xc7u\xf8h\x1e G\xd6\xb0\x00\x90\x85\xfc\x99\xa1\xfcU[\x15>!f\x93qHH\xbf\xe87\x15\x00\xf2\xd8J\xaf\xae\x11\x90o8\xae\xd5\x16 \x9c\xcak\x15,k\x83\xd8\xbd\xb6\x8d\xb3@\x08\x90 \x04\x82\x1e\xac\x1c-\xf4q\x15\x03\xbf\x9d\x8f\x07\x16!V\x13Y\x00\xc2\xe3\xfc_\xcf\x99\xeb\xc9\x9c\x88w4\x0b\x08\x1f\x1b\xf8X%\xd6_\x0e\xda\x84\xe8(\x16b\xeb\xc7\xd5^\xbf9W\xdf\xc4\xe5\xf4N^\x11kpw\xbb>![~\xdeK\x04\xe4\xab\xc7\xcb7\x1ek@\xce\x9f\xb1\xa4^O\xe8\x1a\xdb\x9b\xc3\xa1\x8a\xf5}G\x04$\x02\xc2c\x0e\x019\x9d?K%\x07\x01f\x8f\x15\x01\x89\x80\x04\x80l{y\xd3\x85\x13\xcb&b=\xa8\xd6\xb0\xdaj\xcb\xe5~\\\x12V\x9e\x91\x052p\x8cpc\x9e^ `8\xf9\x81\x16\xb3\xa4\x1fO\xe7\xebgZ\xacP\xcc\xc1y\xc9B\xfb\xe3\xf1\x91\xc1\n\x01%+/\xac\xaf\x97K\xc8\x85\x19Y\xde\xbe\xa9U\xbeb\xf3\xb0O\x88\xbe\xfc\xf3[\xeeW\x04\xe4\x1f\x032\xbb\x1bd\xda?\xab\x15 \x86\x91\xb0\xa4{\x8d\x85lLJ\xab\xe7\xc12\x16\x88\x06d>\xbd\x92e\x08ey\xf6\xb3\x97\x0cI~\xab\xcf\xa5\xc30\x02\xfe\xe6\x95q\xfe\xda\x91!\xe1D_\x16vd\x15\xd4\x04\xa5V\x1e \xf5*c\xd1\x01\xe2\x82\xab\xaerC\x08H\xb6\xf6\xf3F@\xbe\x1e\x90\xc1\x03\xc4\x95\x8ckC\x1f\x96\x0b\xa3\xa3o\x96}X\xb81JA^\t\xd9\xac\xca\xf3`\xc9\x82\x04\xc8\x1f\xb3pq\xa6\x9eN\xe0[\xdd\xf2\x1e}6%X|\x11\xc6\x1bJn\x9d\xe7\xaen\xbb\xc6\x03d\x91\x92e\x8b,v^_.\xf6\xf3\xba\x94\xf7\xcd\xf2q\xe8\xe7\xbdF@\xbe\x1d \xe4\xc4J\x83\xcd \x0b@\x9c\x00YhXvg\xb6+\x86\xc3\x80\xe4\x10\x03\xc1\x18\xe1\xe9\x89\\3\xb7\xe3\xd8Boj\x95q\xabB.`o]Y\x81\xa7\xd7\xfa\xb0\xa7\xe9\xe1\xa6)\x19\x00\xb2.`\x1d\x01\xf9r@.\xb9\x07\x88|\x00\x88\xc9\xe4eM\xc1\x06A\x1a\xdb2\xc7z=\xe1\xdeC\x8a\x13Z \xc3p<?\xd3.\x87\xf3L\xae^\x1b-4\xe5yk\x1b\x08\xdaj\xea\xe9\xebX[\xe5\xe3\xac\x8eu\x8e\x80|;@\xce^(W\xfa\xad\xd7\x86 \n\x12\x98 \xcb \xa1%\x84\xaaUS\x0b4\x10 \xc3S\t\x10\x14!\xd0OJ\x03\x92g\x05\xb7cS\xde\xbe\x10\x0f\x10\xd7\xb3\x10\xf7\xa7/\x01)7\x019E@\xbe\x1d \'\xae]\xbe\x91\x885nl\xb6\xf57\x82\xb862%\x96P3}\x9f\x91\x0f\x14 \xe3s\t\x10\xccY\xd4kG[A\x1fD\xe9\x11\xc2:\xd6\xaa\xeb\xedHp\xcc\x8b\xfa\x8a\x9b\xa5M\xb6\xf2y# _\x0e\xc8!\xb4\xd1M\xbd\x86>p\xf2Z@x\xa7\xd4\xa2\xda(.\xa1\xf4\xd8\xf4\x0c\x84 \xfa0\xecOOU.\x90\xad\x90\xbe\xc5R\'\xb9\xe4\x86oV\xcdD)\xba\xd1\xf5\xd6O\xc7\xea\xdd\xae\xc2l\x05\xc8>\x02\xf2\xed\x00\x99\x97\x80\xe0:\xd8yy&\xb3W0\x8e,\xcd\xd0\x02a\x1d\x83\x1e\x159\xf6\xd7\xcc`c\xeb0\x1d\x9f\xac\xfb\xb76\xd9\xc0\niZ\xac\x05\xc4"\xa4\xb4r\xd4k8\xc5\xc5\x1bF\xab`=\x00$\xccW\x9cn\x11\x90o\x06\x08\x94\x1d\rU,\xbf\x9e\x89I\xc3r\xfb\x1a\x96\x8d\xd1+\xd3\xd6\x96\xf4p4@\xf4_yR\x01\x02*\xe9\x08W\xa7V\xa8d\x15\xb4\xa9\xd0IR\x07\x88W\x85\xd4U\xc7\xb2\x80l\xa6c\x89ua\x93\x08\xc8\x97\x03\xd2\x87\x12\x844,\xcf\x87\x85Y\x8a\xbcq"\xd8JH\x02\xa4\xaa\x8c\x85NM\x9f\xad\x05\xf2\x9c\x02\xe4~\xbf\xee\x87\n\xf7\x85\x80\x92\x95\x17Fr,c!\xbdW\x85\x94\xe5\x87\xb1\xd2!i\xde6\x84FB\xac\x97\xa4\x8b\x80|;@Z\x07\xc8kN^/\x08\xc26z\xedw}V\x94m\x82u~\xd0\x02\xe9P\x80\xdc\x9e\x0f\x10-BZ\xadc\x81\x15B\x05\xc4\x16\x80\x98}Sa\x91\xc5\xc9\xef\xe6\xd9.\xdb <\xde\x11\x12\x01\xf9j@\xae\xd56 ~\xd7\x83\x85\x0f\xab\x0fk5\x94.T\x06\x1dm\xe5s\x0b\x10\xbd\xa4\xec\xb5\x96\x04\xbb\xd3q-0\xd1\x90\xe5\xde\xf4.hY\xc8\x91Bo\xdf\xad\xb2\xfbn}\x15k\xd5\x02!\x02\xf2m\x00\t\xf6\x82p\xdb\x83\xb5\x8d\x1e\n\x10\xb7\'\xdb\xb5|\x86Y\x93\xd709\x0eO)@\xee\xf7\xf3\xd4\xe9\x0b\x04\xe1t$$\xac\x02\xe4\xf7\x9b\x1aBBFo\'\x8d\xd7\x1a\xc2k\xa3\x1b\x01\xf9\xee\x80d> \x9e\x00\xd9\xef\xfd4\x93\xce\xfax\x1d \x96\x0fk\xa1\xcf\xc7\xcbS\xf2q\xbf\x1d\x06\x8e\x85\xd8\x9e:n\xd3\x94\nEH\x98n\xe2\x01\xb2HX\x8c\x80|g@\x84\x03\xc4\x84\t\x87\x8d0\xa1\x01\xc4S\xb0j6A()\x89\x83\xc3O.@@\x84\xa0\xa1\x8d\xb1\x10\x16!e\xb9Q\xff\xa7\xf3\x83\xe9\xce\x081\x80\x94\xcbt\xach\x83|S#]\xacw\xdb:\x1f\xd6~\xa1aq\x14\xbd\xaa\xfd\xf0\x98b\x0f\x16ukf\x01r}R>H\x84hBP\xa1\x94E\x98\xd0\xab^\x03$\x08\xa6\x07\xf9X\xd1\x8b\xf5]\x01\xe9\xe5\x1a\x90n#\xcfd\x9a\xb6\x00\xa9X\xbf\xaa=\r+C\x13\x7f\x7f\xba?\xed\xb8L\x1dT_\x80\xf9]d\xcb\x1a\xa4*\x00$ \xc4v\x9ajW\r\xa1Q\xc7\xcab\x1c\xe4\xdb\x01r\x1f\xb3P\xc3\xe2(\x88\xedL8\xcfaMwc\x81\xd8\xe2\xff\xca$}\x1b@\xea\'\x17 z\x1c\xd1\x15U\x81\xa77\x0b$\x88\xf2\xf7M\xf9"d\x0e\x1d\xbd-w8\x95\xbe\xa7W\xc8\xe9\xfeK\x00y\x89\xe3\'\xed\xbe\x8e\xd7\xfe?\xbf]\x11\x90\x08H\xbc]\x7f\x03\xc8\x8f\x91\x85\'\x91\t\x0e\x13\x86\xa9\xee\xb3\xdb\x0b\xe2\x17T\xb4\rA\xa8\x1e\xafur\x9a4^\xb4@\xa6\xc3\xf9\xef\x95\xbf\xafT<\xffh\xa7C\xce\xa2k\xcaf\x1d\x15\xa6z\xdez\xdb\xd4\xc2\nY;z\xa1\xba\xfc\xcf\x89\xac\xfe\x1e@.\xd0\xc6u\xe1\xe4\xed\xc3\x9a\xbc\x81\x0f\xab\xe34\xac\xb26\xe1c\xb4\xd41x\x0b~\x92\x00\x00\r\x9eIDAT\xf7\x8e\xf6I\xe9\xdf\x9eO\xf7\xe7\x06\xe4~\x1a0\xe1$+,!\x01 ^\xd2\xbb\x1f\n\x19\xad\x11R\xaf\x13\x16E\xa6.\x11\x90\xef\xe7\xb3\xec\xf9\x06\xf9\x80\xf4\xce\x89\x15d\xba{\x05G\x19\x8f\x82\x00\xe1~\xcf\xdad\xadQ\x80\\\x9e\x1d\x90\x1b\xd4YlL\x81\x13S\xe2\xc4\xed=\x0e\xb6MM\xa3\xcf\x07\x00R\xa9\xd26q\xb4\x12$\xfbAM\xd8~\x0f \xf7\x19\x1a\x81;\x15k\xd9\xdbv\xa3\x1c\x16)X\xa5\x01\xc4\x94\x1a\xa5\xde\x05-f)\xde\x9f\x1d\x90\xfby\xc4K\xe1\x00\xe12\xd6X\xe9\xa4^\xd4\xff\x191\xd1\xc4\xf1\x01\xe9\xbc\x8a\xfcXn\xd7\x94\xc8~P\x9f\xdb_\x04\xc8IOm\x03\x88\xdd\x0b\xb2\x06d\nZ\x1ePK\x10R.\xbc\xb6\x94F\x80\\\x9f\x1f\x10r\xf5R\xbf\x10NXT\xd0\xfb\x87\xfb\xa4\x18@<+\xc4\x07\xa4\xc5\xbd\x02! Y~\x8a\x80|G#D\xe5\x99Q\xb1Va\xc2\xb0\x03LP\xd2\xdd\xa9X\xe4\xd1\xb7YX\xe3\xfe|\xff\x05\x80\\\xa7\x9e\xea7\xa0\x1d\xa2\xfcH\xa1r\xa1\x90\xc1\xec,\x1c=\r\xcbk\x83\x90\x1b\x15K\x9b\xe8\xd5%\x02\xf2\x1d\x8d\x90!\xcb- \xf5& \xd3F\xb1\x06\xf2\\\xb1\x9eE\n\x16Wj\x98\x8e\xb7\xdf\x00\xc8\xfdd\x00\xc9\x18\x10\xde|L\xfdt\xfc\xe2\rd\xa8\xc3\'\xee\x17D\xb5M\xcc\x0ee\xc9\x02d\xbaE@\xbe\xe38f,B2.\xca\xeb\n\xfe8\x0bdZl\xb5\xa5\xec\xdd\x82\xf9`\x05\xeb/\x04\xc8\x0f\x04\xe46\xf7\x98\x91E"\xc4+\x1f\xb7\r\xc8\x18\xc8\x10\x93\x8e\x95\x17\x0e\x90\x9f\x94\x9c\xf3\x9b\x00\xb9V9\x88\x10\xdc\xdb\x10f*.[\xecy\x12\xa4\\\x03\xa2\x05\x08X \xf3\x87\x04\xc8\x0f\x04\xe4~\x1e\xda\x86\xfb\x85\x14\xcb\x84\x93\xd0Lg;\xc4\x13!\xa6\x88\xb5\xd9\x12\x92ey{\x8d\x80|S?V\x0e\x8e,\x02\xa4\xf5\xb7\xa3\xcf~\x16V\xefo%\x84\r\xb6>\x1f\x18\x03\xc9+h2\xb8\xbf\xdc\x7f\t \xe0\xeam\xdb\xba\xf0\t1\xcd\xa6\xca\xa5\x99\xce\x12\xc4DB\x18\x10"\x04\x04H\x9e\x1f\xee\x11\x90\xef9\xceJ\xe5\x92U,\x03H\x18\x04q\xfd\xc3\x9c\x85\xae\xa7DQ\x061t\x12 \xa7\xfbo\x01\xe4~\x81\x8b\xc2\x8e\xac@\x86\xd4&c\xb1\xc3\xb6R\xd6\x0c\x99B@\xc0\x8f\x05|\xc0\xf2\x94\xb7\x97\x08\xc8w\x1dS\xa6P\x86\xe4\x8a\x001\xa9\xbc~\xb1\x86\xde\x07Dy\x80,\x04\xc8\xe1\xfa{\x00\xb9\x1f\xc1\xdc\xae\xd7\x80\xb8*\x8b\x06\x10\x1bO\x1f\xdd\xae\xa9\x86\x01\xd1\xd7^_\xfa\x9f\xd5\x04\xfaw\x01rVUNw\xa9j\xdb\xa0\xaa\xbbS\xb1\x02@<\x0b\xa40A\xf4\x1c7J\xcd\xe7\xfb/\x02\xe4:\xf5\xa6wz`\xa7+k\x85\xb4m\x10\x0b\xb1\xa5\x1b|@pi\xfaY\x02\xe4\xeb\x00\x912\x98\xb9]1_&Vx\x9b\xa2\x9e\xafw\xf8\xd6\xbdh\xf1(\xfc\xedwX!Ji>D^\xa8j\x13\x90\xd1\xee\x14\xb5m:\xc9\xcb\xabl}\'-@\x9a\xee\x83.\xde\x1f\x0b\xc8\xfd\xd4c\xde\xc8Z\xc7Z$d\xc15\xa5X\x88\xad\xd1\x8b\xc5\x7fP\xf8j\x1dK\xa9\xc3{\xe7\x89\x94\xc5t]O\x85\x7f1]\xbe\x0f \x85\xbc\x99gfy\xb9\x9fe\xb7x\xc1\x7ft\xc6\x97\xaa\xd2\x84\x80\x11\xe2\x03\xe2\xdaJy\x80`/t\xeb\xe4U\xcc\x07\xe8Xu7\x0c\xfb\xeb\xfdW\x01r\xdb\xf7T\xa9wI\x88\x97\x91\xe5\x8b\x90\xd1\xed\x9a\xe28<\xb9\xff\xaa\xfe\xfa\xfeyr\x91\xc3z*\xfc\x8b\xe9\xf2\x1d\x009\x96\xe5\x11\x97\t\x18\xc4\xca\xc9\xac\x1c\xf8\xa2c]LH\xcf\xadin\xde\x19Ky.\xdf\x1fq:\x80\x92%\xa0\xebM\x1bt^s|\xb8Z\x03\xca\xd40\xe1a\xaaU\xeb\xdf\x9c\xce\xf7\xdf\x05\xc8\xfd2\xea\xa9\xae\xe5\xef\xb6\x08a3d\xf0\x8b,\x8e\xdcI\xa7C@X\xf8\xd6\xa7\x8f\xcc\x13\xa9p\x02L\xed\to\xfcel/\xfff\xba|= Gy>\xcb#~\xc7\xcf\x8cR\x0e\x17~\x81\xfe\xd0/\xb8\xcaQ?\xba\xb5\xcd\xf5\x1e\x9c\xf1\xe1,\xdf\xed1\xbc\rZ\x84\xc0\xfe\xd1\xaa\xd6\xf7s\xda\x02\x84\xf8pE\xd2pF\x14\xdcN\n:\x12j\x01r\xbc\xff6@@\xc9\x82"r+%\x8bZ\x9azUH\x07\xaa\x8de\x1dY\xa8a\x15\xcc\xc7\xfe#\xf3\xe4,\'\xf8z\x90\xe7\x03\xdd\xf8\x8b\x13\x19\x9f;]\xbe\x1e\x90\x16t\xab\xce\x07\xe46\xe8k9\xdf\xccS\xf0\x02|y+\xcf\xf7\xf0\x8co7\xd9\xbc\xdfN\xaf*\xf4E\xa1\x112\x86y&\xa3\x8b\xfdz-]q>\xa0\x85\x8e\x80@\x96\xc9\xfe\xf6\xfb\x00\xb9\x1d\xb4,\xf0\x93zW\x15\xb2\x16n,\xb8\x9e#\x02\xd2\x94\x05{\xff\x86\xeb\xfb\xe7\x89\x9d\x0e\r\xdcq\x92\x13\xb2\xf8G\xd3\xe5\xcb\x01\xc1\xdc\xbf\xc2\x07D\xafU\x8d\xa4%\x03>\n\xa3q\x16\xf5\xc4\xbfq\xb7\x17H\x7fy\xbf\xc7Ra\r\x02pd=\x04\xa4\xb1\xa5~\nO\xc1\xcasn(5]\xee\xbf\x0f\x10\xf0d\xb5\xcdF,\x04E\xc8\x06 #\xebX-jX\xb4\xb4\x9c?2O\xce\xc5h\xa7\x82\x01D\xfe\xab\xe9\xf2\xd5\x804\xce\xe2\xf2,\xa9\x93c\xc6.\t\x97\xa3\xbc\xd0o\xe8\'\xf4Z\x80KB\xfd\x81#\xef\x95\xa2\x99\x1e\x8a\x90\x89\x01i\xd9Fw\xf2\x83Fn\xfb\xe5L\xe7\xfbo\x04\x04\xcc\x10\x1b-\x0c\x12N\x18\x90\xd6\xdb\x15\xe2\x03Ra\x0b\x05\xcdG{\xfc\xd8<9\x91\x12\xeeK\x10\xf9\xaf\xa6\xcb\xd7\xdb \xa7\x0b\xf3_\xd23\x83\x96\x8d\x179\xe3\xb7\xbeRy\xbf\x15\xb8&\x1c\xb4Uv\xd2\xda\xa4\x94\xc7\x0f)\x95\xf7\xdbH\xb5\x02\xc9\xda^l\x95Z\x00\xc2\xb2\x03\xec\x8f\xdc\xb6;8\xde\x7f\' \xf7\xf3\xd0\xd7\x9eL-\x165\xe4\xda\xdek75\x1a@Z\xad\xd3\xe6\xfa\xf2i\xf9q\xb8}p\x9e\x0c\xc5\xd5\xb7A\xf0\xd9\x7f4]\xbe\x10\x10\xf2C\x1c\xea\x825\xc83\x9d\xd1m_\x17\x8d\xbe\x94\xf0-\xbe\xa0T\xfc\x82\x99\xd6\x84C\x83?\x97\xf2\xa4>\x967}\x1d\x94\x92F\x86L\x0b\r\xcb:y}\xf9\x91\xd16B\x14 \xe3\xe1\xf6[\x01\xd1\x86\xba\x16\x07Z\x1eXB\xcc6u[\xa5\xb7_\xc4\xd21\x00\x0f\x92\xb7l\xfb\x0fYn\x08\xc8I\xce\xc6\x8b\xa5\x0c \xffh\xba\xfc\xdcH\xfa_\xf8\xb6/\x9d\xa2z\xb3\x9e\x96\xe5w]3\x80X\xe7.7;@3\xf3/\xf9\xf8\xd1\x80\x10! M\x8b@\x90(\xbb7\xdd\xe9X,?\xea\xaa\xcc\xa8W\xf6\xfeow\x81\x94\xa7\xdbu]r\xee\x93\xa7\xcb\xaf\x04\xe4~\xe9\x03B\x16\x9d\xf58\xc9\xa4\xb0\xbd\x91$\x03\xa2o\xf3_\xf3\xf1\xb3\x01\xd1\x84\xd4`R\xa0\xc2\xb9A\x88\xbf+\x04\xf5\xd5\x9a6Z\xfd\x17|h\r\xafP\xd35\x02\xf2/\xc6u\x84\xc4^\xf2eiD\x86qt\xf5\x96\xdd.)K\x87\xb4\x1d\x9f\x8f\xb7\xfb\xaf\x06D\xcf\xd2\n\t\xa1+b\xe3"\xce\x91e\xf6\x85`Z4\x89\x1b\xb0\xf6\x0e?\xb4\x04\xfe/\x05\xe4~\x9d\xf5\xadCo/ \xd2\xdb-\xd4T\xe8\xac,\x03\xe9\x81|\xc8\xa2l\x87\xd3\xb7\x98\xa4_z\xec\xcb\x04)Y\x99\xb7nd\x8e\x90\xb6\xa7\xac\xde\x1e/&\xa1\x94\x83\xe7\xef\xf8C\xe7\xc9\xaf\x05\xe4~;*\xba{y\xa6\xaa\xba\xe5QW\xb4\x03$\x90\x1e\xc4\x87\x96\x1f\xd3\xf9\x1e\x01\xb9_\x0f\xb0\xfd6\xcf=\xed\x93\xf4,\xd0L\xe92\xe2\x16]-=2\xb8\xbeZ\xee\x8e?\xb7G\xdd\xaf\x05D\xab\xd3-\xd0\x81\x8ch1R\xbb\xfdQ\x96\x0e*\xf1\xc0\x0be\xd5\xee\xaf\xf7\x08\x08\x19"(\x1a\x02\tK\x8cTv(^\x7f@@\xcf?\xb8\x05\xd7/\x06D\xabY9G7<\x8b\x93=\xba\xa6\xc6\x197l\xd3\xf7y\xf8\x8f\x1aI=\x01 \xf7\xeb\xbeV\x0bD\xa8\xd1g\x96\x9b\xcc5\x92\xce\x98\xafp\xfc\xc9\x1d\xb8~3 \xb0\x14bs\xb1,\x0b\xbdU\xb87\x14\x0bp\x88,\x03\x19\xa2\xb5\xe8\xc3\xe5\x9bM\xd2/=\xf6\xed\xcc\x96H\xa0\x89\xc2\xd5\x93\xa4W\xd1(\xeb\xa6\xdf\xff\xec\x0e\x8e\xbf\x1b\x10m\x89\xb4D\x88\xc4\x7f@\x83\x16\x19b)>\xda\xfd\xf9\xfbM\xd2\xaf=\xf6\xed4\x91\x8b\xca\x0ck\xb2S\xc4\x88\x14\xd7v>\xff\xf0\x06\x8e\xbf\x1c\x10@\xa472$\x83z\x0eYf\xd8\x00I\x02\xd2Cu\xfb\xf3\xf7\x9c\xa4_{\xec\xdbyn\x95\x89\x89\xb0\x086t`\r\xc6\xaa\xdf\x9f\x7f|\x7f\xd3_\x0f\x08\xde\xe8\xca,\x82P\xd1A\xd0\x87\xa0\r\xe8\xd5\xf4\x1f\xf7X{\x1a@\xf4\xb8\x1c\xc7\x1a2\x11\xf3,\x0b\xa5\x07\xc4\x8c\xa6\xd334\xa7\x8b\x80\xa0\xd1y\xda\xb7\xb9]\x05\xe9\x13\xa8V\xfd|\xba\xdc\xbe\xf9$\xfd\xdac\xdf.\xc7\xb9G]\xcb\x0e\xd8k3\xecO\xd7\xe7h\x8e\x1d\x01\xb1r\xe48\x8f\xe8\xbe\xcc\xd0w\xdf\x8d\xfb\xe3\xf9\xfa#&\xe9W\x1f\xfbz>\x1d\xe6\t\x12K\xb0\xe1\xca|\xf8\xefW\x95\x08\xc8\xf7\x80\xe4v\xbd\\\xcez\\.\xd7\xdb\xedGM\xd2/?\xf6\xcd\x8e\xfbS\x8d\x08\xc8\x13M\xd2o}\xec\x08H\x1c\x11\x90\x08H\xbc\xe2\x11\x90x\xbb" \x11\x908" \x11\x908" \x11\x90x\xbb" \x11\x90\x08H\x04$\x02\x12\x01\x89\x80\xc4+\x1e\x01\x89\x80\xc4+\x1e\x01\x89\x80\xc4\x11\x01\x89\x80\xc4+\x1e\x01\x89\x80\xc4+\x1e\x01\x89\xb7+\x02\x12\x01\xf9\xff\xf6\xeeh\xa7m \x88\x02\xe8\x8b%\x83\x10\x8a\x04%JR\x11\xff\xff_v\x9d@AU\x12\xbc;k\xb3\xb8\xe7\xaa\xa8j\xc1e\xd8\xf1\x81\x04\xa4^\x01\x04\x10\x01\x04\x10\xeb\x02\x04\x10@V\t$V\x90~\xe9\xc2J\t\x0cv\xbb\xca\xbbi Uz\xc8g\xddJt\xbe\x9c\xe5\xb4\x07$\xb7 \xfd\xd2\x85\xb3\x00\xc9\x1c\xecV\x95w\xe3@*\xf4\x90\xcf\xbc\x95\xe8|\xd3\x97\xd3\x12\x90\xb2\x82\xf4K\x17V\x06R\xd8\xdc~\xbd\xca\xbby \xe1\x1e\xf2\xb9\xb7\x12\xeeI\x9f\xba\x9c\x86\x80\x14\x16\xa4_\xba\xb0.\x90\xb2\xc1nUy7\x0f$\xdcC>\xf3V\xa2\xf3M_NC@\n\x0b\xd2/]X\x17H\xc9`\xb7\xab\xbc\x9b\x7f\x0e\x12\xee!\x9fu+\xd1\xf9r\x96\xd3\x10\x90\xfc\x82\xf4\x1b\x17\xd6\x04R4\xd8\x17U\xdem\x7f\x05\t\xf5\x90/\xb0\x95\xe8|\x19\xcbi\x08H~A\xfa\xcd\x0b\xeb\x01)\x1b\xec\xab*\xef\xa6\x1fbEz\xc8\x97\xd8Jt\xbe\xe9\xcbi\xea9HnA\xfa\xd5\x0bk?\x07)\x1a\xecF\x95w\xfbO\xd2\x03=\xe4\x8bl%:\xdf\xe4\xe5\xb4\x01\xa4\xb0 \xfd\xea\x855\x1f\xed\x96\r6\xdc\xac\xf2n\x1fH\xa0\x87|\xfe\xadD\xe7\xcbYN\x0b@V\x9d\tU\xde\xed\xfe$=\xd4C\xde\xc6\xe1\xd6\xbf\x1e\x90\xaa\x99P\xe5\xdd.\x90P\x0fy\x1b\x87[\xffz@\xda\xbaI\xd7\xfb\xbeW\xba.@\x00\xb1.@\x00\x11@\x00\xb1.@\x00\xb1.@\x00\x01\x04\x10@\x00\x01\xc4\x89\x03\x02\x88\x00\x02\x88\x00\x02\x88\x13\x07\xc4\xba\x00\x01D\x00\x01D\x00\x01\xc4\xba\x00\x01\x04\x10@\x00\x01\x04\x10\'\x0e\x08 N\x1c\x10@\x04\x10@\x9c8 \x808q@\xac\x0b\x10@\x04\x10@\x04\x10@\xac\x0b\x10@\x00\x01\x04\x10@\x00q\xe2\x80\x00"\x80\x00"\x80\x00Rv\xe2\x91\x12\xf2~s\x98\xf3\x08B\xfd\xe8\x9f\xfe\xbb\xf0\xf1\xcd&\x94o\xaf\xae\'\xbd\xe4\x10r\xb6\x13\x9e/t\xff|\x0f\x90\xbc\x12\xf2\xe3\xb9\ne\x19 \xd9\xfd\xe8\xcf\x9f\xff8\xa1\xd7k\x85=\xe9\xf9\x87\x90\xb7\x9d\xe0|\xa1\xfbgi E%\xe4c\xc5VzE\xbarH/\xfb\xee\x90\xfe\x99\xf4\x8a\xc3\xf3\xc3\xa1"\x90\xc2~\xf4\xbb\xb7\x9b\xe2\xe3\xca\xf3\xa4\xd7\xe6[cOz\xf6!dn\'8_\xe4\xfeY\x18Ha;z\xf7\x98>\xac}\xfa5<=\r\xbf\xba\xed\xf8[\xd7\xed~\xa7\xbf\xaf\x06\xa4\xb4\x1f}\xf7\xb9\x15\xf2\\\x19y\x9a\xf4\xda|+\xecI\xcf?\x84\xbc\xed\x84{\xdc\x03\xf7\xcf\xc2@\n\xdb\xd1\xbb~\xd8\x8c\x15\xa5\x8f\xc3\xb6?\xf6w\xf7\xc7~;\x9e\xc1\xf8\xf7\xd5\x80\x14\xf6\xa3?\x1f\xff\xbd7\xde&\xbd6\xdf\xeaz\xd2K\x0e!\xef9H\xb8\xc7=p\xff,\x0c\xa4\xb0\x1d=}\t\xef\xcf\xc7\xb2\xebv\xdd>\xbd\xec\xea=\xda\r\x8dv\xf80\xf5~e\xff\xb1\xc0K\xf3\xad\xb0\'=\xff\x10\xb2\xb6\x13\xefq\x0f\xdc?\x0b\x03)+!\x7fM\x8f!O\x9f\x016\xe3\x9b\xf6C\x7f\x7f\xfa\xea]\x17Haq{z}:\xfb\xe3\xbf\x9f<7?\x08H\xbc\'=\xfb\x10\xf2\xb6\x13\x9d/r\xff,\xfe\x1c\xa4\xa4\x84|\x9b>\xd6\xdd\xf91dzr\xf62\xbc\xa4\x8f\xbf:\x90\xb2\xd1\xc6K6\xe36\xce\xc5\xdb\xe3\xcb\xee\xfd\xe1\xf7\x8f\x01\x12\xed!\xcf?\x84\xcc\xed\x04\xe7\x8b\xdc?\xcb\x01)oG\xef\x9f\xc6\x87\x93\xe7\xefB\xa4\x83\xda\xa7O\x03\xddkM \x81\xe2\xf6\xf1[\xf4\x87\x87\xcd\xeb[\xf1\xf6\xe9\xca\xf7o\xe0\xfc\x1c \xc1\x9e\xf4\xfcC\xc8\x04\x12\x9b/t\xff\xf8Iz\x037i#\xef{\xed=\xe9M\xff\x1cD\xda\x07\xb2\xf6\x9et@\x00\x19\xac\x0b\x10@\x00\x01\x04\x10@\x00q\xe2\x80\x00"\x80\x00"\x80\x00\xe2\xc4\x01\xb1.@\x00\x11@\x00\x11@\x00\xb1.@\x00\x01\x04\x10@\x00\x01\xc4\x89\x03\x02\x88\x13\x07\x04\x10\x01\x04\x10\'\x0e\x88u\x01\x02\x88u\x01\x02\x88\x00\x02\x88u\x01\x02\x88u\x01\x02\x08 \x80\x00\x02\x08 N\x1c\x10@\x04\x10@\x04\x10@\x9c8 \xd6\x95\x05D\xb2\xf3\x9d\xdb\x96\xca\xeb\x02\x04\x10\xeb*\x07"\xf2\x7f\x07\x10\x11@D\x00\x11\x01D\x04\x10\x11@D\x00\x11\x01D\x04\x10\x11@D\x04\x10\x11@D\x00\x11\x01D\x04\x10\x11@D\x00\x11\x01D\x04\x10\x11@D\x04\x10\x11@D\x00\x11\x01D\x04\x10\x11@D\x00\x11\x01D\x04\x10\x11\xf9\x9b?[\xfc\x08\x03\xfc\x04\x987\x00\x00\x00\x00IEND\xaeB`\x82'
+
+if __name__ == "__main__":
+ app = wx.PySimpleApp()
+
+ init_joysticks()
+ frame = Frame()
+ frame.Show()
+ app.MainLoop()
diff --git a/bin/G-FSUAE/G-FS-UAE/fs-uae-netplay-server.py b/bin/G-FSUAE/G-FS-UAE/fs-uae-netplay-server.py
new file mode 100644
index 0000000..7c9086a
--- /dev/null
+++ b/bin/G-FSUAE/G-FS-UAE/fs-uae-netplay-server.py
@@ -0,0 +1,725 @@
+"""
+FS-UAE Netplay Game Server
+Copyright (C) 2012 Frode Solheim
+
+This library is free software; you can redistribute it and/or modify it
+under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or (at
+your option) any later version.
+
+This library 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 Lesser General Public
+License for more details.
+
+You should have received a copy of the GNU Lesser General Public License
+along with this library; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+"""
+from __future__ import print_function
+
+import sys
+import time
+from collections import deque
+import socket
+import traceback
+import threading
+import random
+from hashlib import sha1
+
+SERVER_PROTOCOL_VERSION = 1
+MAX_PLAYERS = 6
+
+if sys.version > '3':
+ PYTHON3 = True
+else:
+ PYTHON3 = False
+
+if PYTHON3:
+ def int_to_bytes(number):
+ return bytes([(number & 0xff000000) >> 24, (number & 0x00ff0000) >> 16,
+ (number & 0x0000ff00) >> 8, (number & 0x000000ff)])
+ def bytes_to_int(m):
+ return m[0] << 24 | m[1] << 16 | m[2] << 8 | m[3]
+ def byte_ord(v):
+ #print("byte_ord", v)
+ try:
+ return v[0]
+ except TypeError:
+ return v
+ def byte(v):
+ return bytes([v])
+ server_protocol_version = byte(SERVER_PROTOCOL_VERSION)
+
+else:
+ def int_to_bytes(number):
+ return chr((number & 0xff000000) >> 24) + \
+ chr((number & 0x00ff0000) >> 16) + \
+ chr((number & 0x0000ff00) >> 8) + \
+ chr((number & 0x000000ff))
+ def bytes_to_int(m):
+ return ord(m[0]) << 24 | ord(m[1]) << 16 | ord(m[2]) << 8 | ord(m[3])
+ def byte_ord(v):
+ return ord(v)
+ def byte(v):
+ return v
+ server_protocol_version = chr(SERVER_PROTOCOL_VERSION)
+
+max_drift = 25
+num_clients = 2
+port = 25100
+host = "0.0.0.0"
+game = None
+game_password = 0
+launch_timeout = 0
+
+def create_game_password(value):
+ # for python 2 + 3 compatibility
+ #if not isinstance(value, unicode):
+ value = value.encode("UTF-8")
+ #print(repr(value))
+ h = sha1()
+ h.update(b"FSNP")
+ val = b""
+ for v in value:
+ if byte_ord(v) < 128:
+ val += byte(v)
+ #print("update:", repr(val))
+ h.update(val)
+ return bytes_to_int(h.digest()[:4])
+
+for arg in sys.argv:
+ if arg.startswith("--"):
+ parts = arg[2:].split("=", 1)
+ if len(parts) == 2:
+ key, value = parts
+ key = key.lower()
+ if key == "port":
+ port = int(value)
+ elif key == "players":
+ num_clients = int(value)
+ elif key == "password":
+ #game_password = crc32(value) & 0xffffffff
+ game_password = create_game_password(value)
+ print("game password (numeric) is", game_password)
+ elif key == "launch-timeout":
+ launch_timeout = int(value)
+
+
+MESSAGE_READY = 0
+MESSAGE_MEM_CHECK = 5
+MESSAGE_RND_CHECK = 6
+MESSAGE_PING = 7
+MESSAGE_PLAYERS = 8
+MESSAGE_PLAYER_TAG_0 = 9
+MESSAGE_PLAYER_TAG_1 = 10
+MESSAGE_PLAYER_TAG_2 = 11
+MESSAGE_PLAYER_TAG_3 = 12
+MESSAGE_PLAYER_TAG_4 = 13
+MESSAGE_PLAYER_TAG_5 = 14
+
+MESSAGE_PLAYER_PING = 15
+MESSAGE_PLAYER_LAG = 16
+MESSAGE_SET_PLAYER_TAG = 17
+MESSAGE_PROTOCOL_VERSION = 18
+MESSAGE_EMULATION_VERSION = 19
+MESSAGE_ERROR = 20
+MESSAGE_TEXT = 21
+MESSAGE_SESSION_KEY = 22
+
+#MESSAGE_MEM_CHECK = 5
+#MESSAGE_RND_CHECK = 6
+#MESSAGE_PING = 7
+
+MESSAGE_MEMCHECK_MASK = (0x80000000 | (MESSAGE_MEM_CHECK << 24))
+MESSAGE_RNDCHECK_MASK = (0x80000000 | (MESSAGE_RND_CHECK << 24))
+
+ERROR_PROTOCOL_MISMATCH = 1
+ERROR_WRONG_PASSWORD = 2
+ERROR_CANNOT_RESUME = 3
+ERROR_GAME_ALREADY_STARTED = 4
+ERROR_PLAYER_NUMBER = 5
+ERROR_EMULATOR_MISMATCH = 6
+ERROR_CLIENT_ERROR = 7
+ERROR_MEMORY_DESYNC = 8
+ERROR_RANDOM_DESYNC = 9
+ERROR_SESSION_KEY = 10
+ERROR_GAME_STOPPED = 99
+
+def create_ext_message(ext, data):
+ return 0x80000000 | ext << 24 | (data & 0xffffff)
+
+class Client:
+
+ def __init__(self, socket, address):
+ #self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ self.socket = socket
+ self.messages = []
+ self.lock = threading.Lock()
+ self.address = address
+ self.ready = 0
+ self.tag = b"PLY"
+ self.player = 0
+ self.playing = False
+ self.frame = 0
+ self.frame_times = [0.0 for x in range(100)]
+ self.lag = 0.0
+ self.out_seq = 0
+ self.memcheck = [(0, 0) for x in range(100)]
+ self.rndcheck = [(0, 0) for x in range(100)]
+ self.ping_sent_at = 0
+ self.pings = deque([0 for x in range(10)])
+ self.pings_sum = 0
+ self.pings_avg = 0
+ #self.protocol_version = 0
+ self.emulator_version = b""
+ self.session_key = 0
+
+
+ self.temp_a = 0
+ self.temp_b = 0
+ self.temp_c = 0
+
+ threading.Thread(target=self.__thread_function).start()
+
+ def send_error_message(self, error_num):
+ print(self, "error", error_num)
+ message = 0x80000000 | (MESSAGE_ERROR) << 24 | error_num
+ self.send_message(message)
+
+ def send_message(self, message):
+ with self.lock:
+ #if message == 0x87000000:
+ # #print("queueing %08x" % (message,))
+ # #traceback.print_stack()
+ # self.temp_c += 1
+ self.__send_data(int_to_bytes(message))
+
+ def __send_data(self, data):
+ #if data[0] == '\x87':
+ # #print("queueing ping")
+ # #traceback.print_stack()
+ # self.temp_c += 1
+ self.socket.sendall(data)
+
+ def queue_message(self, message):
+ with self.lock:
+ #if message == 0x87000000:
+ # #print("queueing %08x" % (message,))
+ # #traceback.print_stack()
+ # self.temp_c += 1
+ #print("queueing %08x" % (message,))
+ #if message & 0x20000000:
+ # traceback.print_stack()
+ self.messages.append(int_to_bytes(message))
+ if len(self.messages) == 100:
+ self.__send_queued_messages()
+
+ def queue_bytes(self, message):
+ with self.lock:
+ self.messages.append(message)
+ if len(self.messages) == 100:
+ self.__send_queued_messages()
+
+ def send_queued_messages(self):
+ with self.lock:
+ self.__send_queued_messages()
+
+ def __send_queued_messages(self):
+ data = b"".join(self.messages)
+ self.messages[:] = []
+ #print("sending", repr(data))
+ self.__send_data(data)
+
+ def initialize_client(self):
+ print("initialize", self)
+ def read_bytes(num):
+ data = b""
+ for i in range(num):
+ data = data + self.socket.recv(1)
+ if not len(data) == num:
+ raise Exception("did not get {0} bytes".format(num))
+ return data
+ # check header message
+ data = read_bytes(4)
+ if data == b"PING":
+ # connection check only
+ self.__send_data(b"PONG")
+ return False
+ if data != b"FSNP":
+ print(data)
+ raise Exception("did not get expected FSNP header")
+ # check protocol version
+ data = self.socket.recv(1)
+ #print(repr(data), repr(server_protocol_version))
+ if data != server_protocol_version:
+ print("protocol mismatch")
+ self.send_error_message(ERROR_PROTOCOL_MISMATCH)
+ return False
+ # check net play password
+ password = bytes_to_int(read_bytes(4))
+ if password != game_password:
+ print("wrong password")
+ self.send_error_message(ERROR_WRONG_PASSWORD)
+ return False
+ # read emulator version
+ self.emulator_version = read_bytes(8)
+ # read player number and session key, session key is checked to
+ # make sure another client cannot hijack this player slot
+ self.session_key = bytes_to_int(b"\0" + read_bytes(3))
+ self.player = byte_ord(self.socket.recv(1))
+ self.tag = read_bytes(3)
+ # get package sequence number
+ self.resume_from_packet = bytes_to_int(read_bytes(4))
+
+ error = game.add_client(self)
+ if error:
+ self.send_error_message(error)
+ return False
+
+ message = create_ext_message(MESSAGE_SESSION_KEY, self.session_key)
+ self.queue_message(message)
+ data = (self.player << 8) | num_clients
+ message = create_ext_message(MESSAGE_PLAYERS, data)
+ self.queue_message(message)
+
+ game.send_player_tags(self)
+ self.send_queued_messages()
+ print("initialize done for", self)
+ return True
+
+ def __thread_function(self):
+ try:
+ try:
+ if not self.initialize_client():
+ print("initialize failed for", self)
+ return
+ self.receive_loop()
+ except Exception:
+ traceback.print_exc()
+ game.stop = True
+ finally:
+ try:
+ self.socket.close()
+ except Exception:
+ pass
+
+
+ def receive_loop(self):
+ data = b""
+ count = 0
+ while not game.stop:
+ data = data + self.socket.recv(4 - count)
+ count = len(data)
+ if count == 4:
+ self.on_message(data)
+ count = 0
+ data = b""
+
+ def send_ping(self):
+ with self.lock:
+ #print("ping?", self.ping_sent_at)
+ if self.ping_sent_at == 0:
+ self.temp_a += 1
+ self.ping_sent_at = time.time()
+ message = int_to_bytes(0x80000000 | (7 << 24))
+ self.__send_data(message)
+ assert self.ping_sent_at > 0
+
+ def on_ping(self):
+ # may not need lock here
+ with self.lock:
+ self.temp_b += 1
+ if self.temp_a != self.temp_b:
+ print(self.temp_a, self.temp_b, self.temp_c)
+ assert self.ping_sent_at > 0
+ t = time.time()
+ new = (t - self.ping_sent_at) / 1.0
+ #print(t, "-", self.ping_sent_at, "=", new)
+ old = self.pings.popleft()
+ self.pings.append(new)
+ #print(self.pings)
+ self.pings_sum = self.pings_sum - old + new
+ self.pings_avg = self.pings_sum / len(self.pings)
+ self.ping_sent_at = 0
+
+ def on_message(self, m):
+ message = bytes_to_int(m)
+ #with game.lock:
+ # if not self.playing:
+ # print(self, "is no longer playing/valid, ignoring message")
+
+ if message & 0x80000000:
+ # ext message
+ command = (message & 0x7f000000) >> 24
+ data = message & 0x00ffffff;
+ if command == MESSAGE_MEM_CHECK:
+ self.memcheck[self.frame % 100] = (data, self.frame)
+ elif command == MESSAGE_RND_CHECK:
+ self.rndcheck[self.frame % 100] = (data, self.frame)
+ elif command == MESSAGE_PING:
+ #print("{0:x}".format(message))
+ self.on_ping()
+ elif command == MESSAGE_TEXT:
+ print("received text command")
+ remaining = data
+ text = b""
+ while not game.stop:
+ part = self.socket.recv(remaining)
+ count = len(part)
+ text += part
+ remaining = remaining - count
+ if remaining == 0:
+ game.add_text_message(self, text)
+ break
+
+ elif message & (1 << 30):
+ frame = message & 0x3fffffff
+ #print("received frame", frame)
+ if frame != self.frame + 1:
+ print("error, expected frame", self.frame + 1, "got", frame)
+ #print("received frame", frame)
+ self.frame = frame
+ t = time.time()
+ self.frame_times[self.frame % 100] = t
+ game_t = game.frame_times[self.frame % 100]
+ self.lag = t - game_t
+
+ elif message & (1 << 29):
+ game.add_input_event(self, message & 0x00ffffff)
+
+ else:
+ print("warning: unknown command received %x" % (message,))
+
+ def __str__(self):
+ return "<Client {0}:{1} {2}>".format(self.player, self.tag,
+ self.address)
+
+def create_session_key():
+ return random.randint(0, 2**24 - 1)
+
+class Game:
+
+ def __init__(self, num_clients):
+ self.started = False
+ self.frame = 0
+ self.time = 0
+ self.clients = []
+ self.num_clients = 0
+ self.frame_times = [0.0 for x in range(100)]
+ self.lock = threading.Lock()
+ self.stop = False
+ self.session_keys = [0 for x in range(MAX_PLAYERS)]
+ self.emulator_version = b""
+ self.verified_frame = -1
+
+ def __start(self):
+ if len(self.clients) != num_clients:
+ printf("error - cannot start until all players have connected")
+ return
+ print("{0} clients connected, starting game".format(num_clients))
+ self.started = True
+ threading.Thread(target=self.__thread_function).start()
+
+ def add_client(self, client):
+ with self.lock:
+ if client.player == 0xff:
+ if client.resume_from_packet != 0:
+ return ERROR_CLIENT_ERROR
+ if self.started:
+ return ERROR_GAME_ALREADY_STARTED
+ client.player = len(self.clients)
+ if client.player == 0:
+ self.emulator_version = client.emulator_version
+ else:
+ if self.emulator_version != client.emulator_version:
+ return ERROR_EMULATOR_MISMATCH
+ client.session_key = create_session_key()
+ self.session_keys[client.player] = client.session_key
+ self.clients.append(client)
+ client.playing = True
+ if not self.started:
+ if len(self.clients) == num_clients:
+ self.__start()
+ else:
+ if client.player >= len(self.clients):
+ return ERROR_PLAYER_NUMBER
+ if self.session_keys[client.player] != client.session_key:
+ return ERROR_SESSION_KEY
+ old_client = self.clients[client.player]
+ # FIXME: must transfer settings for resuming to work
+ self.clients[client.player] = client
+ client.playing = True
+
+ if client.resume_from_packet > 0:
+ # cannot resume yet...
+ print("cannot resume at packet", resume_from_packet)
+ return ERROR_CANNOT_RESUME
+ return 0
+ # FIXME: start the game..?
+
+ def __thread_function(self):
+ try:
+ self.__game_loop()
+ except Exception:
+ traceback.print_exc()
+ self.stop = True
+
+ def __send_player_tags(self, send_to_client):
+ for i, client in enumerate(game.clients):
+ data = bytes_to_int(b"\0" + client.tag)
+ message = create_ext_message(MESSAGE_PLAYER_TAG_0 + i, data)
+ send_to_client.queue_message(message)
+
+ def send_player_tags(self, client):
+ with game.lock:
+ self.__send_player_tags(client)
+
+ def __game_loop(self):
+ with self.lock:
+ for client in self.clients:
+ self.__send_player_tags(client)
+ print("game loop running")
+ self.time = time.time()
+ while True:
+ if self.stop:
+ print("stopping game loop")
+ # try to send error message to all players
+ with self.lock:
+ for client in self.clients:
+ try:
+ client.send_error_message(ERROR_GAME_STOPPED)
+ except Exception:
+ traceback.print_exc()
+ return
+ self.__game_loop_iteration()
+
+ def __game_loop_iteration(self):
+ # FIXME: higher precision sleep?
+ target_time = self.time + 0.02
+ t2 = time.time()
+ diff = target_time - t2
+ sleep = diff - 0.001
+ if sleep > 0.0:
+ #print(sleep)
+ time.sleep(sleep)
+ self.time = target_time
+ while time.time() < target_time:
+ # busy-loop until target time
+ pass
+
+ with self.lock:
+ if self.frame % 100 == 0:
+ self.__send_status()
+ self.frame += 1
+ self.frame_times[self.frame % 100] = time.time()
+ message = (1 << 30) | self.frame
+ self.__send_to_clients(message, True)
+ if self.frame % 10 == 0:
+ for client in self.clients:
+ client.send_ping()
+ if self.frame % 200 == 0:
+ self.__print_status()
+
+ self.__check_game()
+
+ def __check_game(self):
+ oldest_frame, frames = self.__check_frame_status()
+ while oldest_frame > self.verified_frame:
+ self.check_synchronization(self.verified_frame + 1)
+ self.verified_frame += 1
+ diff = self.frame - oldest_frame
+ if diff <= max_drift:
+ # clients have not drifted too far
+ return
+ first = True
+ count = 0;
+ while diff > 0 and not self.stop:
+ if first:
+ first = False
+ print("---", self.frame, "acked", frames)
+ elif count % 100 == 0:
+ print(" ", self.frame, "acked", frames)
+ time.sleep(0.02)
+ oldest_frame, frames = self.__check_frame_status()
+ diff = self.frame - oldest_frame
+ count += 1
+ self.time = time.time() - 0.02
+
+ def __check_frame_status(self):
+ oldest_frame = self.frame
+ frames = []
+ with self.lock:
+ for client in self.clients:
+ af = client.frame
+ if af < oldest_frame:
+ oldest_frame = af
+ frames.append(af)
+ return oldest_frame, frames
+
+ def __print_status(self):
+ for i, client in enumerate(self.clients):
+ print("{0} f {1:6d} p avg: {2:3d} {3:3d}".format(i,
+ client.frame, int(client.pings_avg * 1000),
+ int(client.lag * 1000)))
+
+ def __send_status(self):
+ for i, client in enumerate(self.clients):
+ v = int(client.lag * 1000) & 0x0000ffff
+ message = 0x80000000 | MESSAGE_PLAYER_LAG << 24 | i << 16 | v
+ self.__send_to_clients(message)
+ v = int(client.pings_avg * 1000) & 0x0000ffff
+ message = 0x80000000 | MESSAGE_PLAYER_PING << 24 | i << 16 | v
+ self.__send_to_clients(message)
+
+ def add_input_event(self, client, input_event):
+ if not self.started:
+ # game was not started - ignoring input event
+ print("game not started, ignoring input event {0:08x}".format(
+ input_event))
+ return
+ with self.lock:
+ if not client.playing:
+ print("client", client, "is no longer valid, dropping msg")
+ return
+ # for now, just broadcast out again to all clients
+ message = (1 << 29) | input_event
+ self.__send_to_clients(message)
+
+ def add_text_message(self, from_client, text):
+ print("add text message")
+ with self.lock:
+ for client in self.clients:
+ #if from_client == client:
+ # continue
+ message = 0x80000000 | MESSAGE_TEXT << 24 \
+ | from_client.player << 16 | len(text)
+ message = int_to_bytes(message) + text
+ print("send", repr(message), "to", client)
+ client.queue_bytes(message)
+
+ def send_to_clients(self, message, force_send=False):
+ # using a lock here to ensure that send_to_clients can
+ # be called from multiple threads, but all clients will
+ # still get messages in expected order
+ with self.lock:
+ self.__send_to_clients(message, force_send)
+
+ def __send_to_clients(self, message, force_send=False):
+ for client in self.clients:
+ #print("send", message, "to", client)
+ client.queue_message(message)
+ if force_send:
+ client.send_queued_messages()
+
+ def check_synchronization(self, check_frame):
+ # FIXME: MOVE TO GAME CLASS
+ # FIXME: ONLY RUN ONCE PER FRAME, not once per frame * clients
+ #if self.frame == 0:
+ # return
+ with game.lock:
+ #print("received memcheck", data)
+ #check_frame = self.frame - 1
+ if check_frame < 0:
+ return
+ index = check_frame % 100
+ mem_check_data = []
+ rnd_check_data = []
+ for client in game.clients:
+ if client.frame <= check_frame:
+ # cannot check yet
+ return
+ #print(client, client.frame, client.memcheck)
+ mem_check_data.append((client, client.memcheck[index]))
+ rnd_check_data.append((client, client.rndcheck[index]))
+ check = mem_check_data[0][1]
+ for client, data in mem_check_data:
+ if check != data:
+ print("memcheck failed for frame", check_frame)
+ for c, d in mem_check_data:
+ print("* {0:08x} f {1:05d} {2}".format(d[0], d[1], c))
+ for c in game.clients:
+ c.send_error_message(ERROR_MEMORY_DESYNC)
+ c.send_queued_messages()
+ raise Exception("mem check failed")
+ check = rnd_check_data[0][1]
+ for client, data in rnd_check_data:
+ if check != data:
+ print("rndcheck failed for frame", check_frame)
+ for c, d in rnd_check_data:
+ print("* {0:08x} f {1:05d} {2}".format(d[0], d[1], c))
+ for c in game.clients:
+ c.send_error_message(ERROR_RANDOM_DESYNC)
+ c.send_queued_messages()
+ raise Exception("rnd check failed")
+
+address_map = {
+
+}
+
+def accept_client(server_socket):
+ server_socket.settimeout(1)
+ client_socket, client_address = server_socket.accept()
+ #client_socket.setsockopt(socket.SOL_SOCKET, socket.SO_SNDLOWAT, 4)
+ client_socket.setsockopt(socket.SOL_TCP, socket.TCP_NODELAY, 1)
+ client_socket.settimeout(None)
+ client = Client(client_socket, client_address)
+ #client.player = len(game.clients) + 1
+ print("Client connected", client)
+ #game.add_client(client)
+ #client.start()
+ #if game.can_start():
+ # game.start()
+
+#stop_accepting = False
+
+def accept_thread(server_socket):
+ while not game.stop:
+ try:
+ accept_client(server_socket)
+ except socket.timeout:
+ pass
+ except Exception:
+ traceback.print_exc()
+ time.sleep(1.0)
+
+def _run_server():
+ global game
+ game = Game(num_clients)
+ address = (host, port)
+ server_socket = socket.socket()
+ server_socket.bind(address)
+ server_socket.listen(4)
+ if address[0] != "0.0.0.0":
+ print("server listening on", address[0], "port", address[1])
+ else:
+ print("server listening on port", address[1])
+ print("want", num_clients, "client(s)")
+ if num_clients > MAX_PLAYERS:
+ print("ERROR: max clients are", MAX_PLAYERS)
+ threading.Thread(target=accept_thread, args=(server_socket,)).start()
+ t1 = time.time()
+ while not game.started:
+ time.sleep(0.1)
+ if launch_timeout:
+ t2 = time.time()
+ if t2 - t1 > launch_timeout:
+ print("game not started yet, aborting")
+ game.stop = True
+ return
+ #sys.exit()
+ print("game started")
+ while not game.stop:
+ time.sleep(0.1)
+
+def run_server():
+ try:
+ _run_server()
+ except Exception:
+ traceback.print_exc()
+ except KeyboardInterrupt:
+ traceback.print_exc()
+ game.stop = True
+
+if __name__ == "__main__":
+ run_server()
diff --git a/bin/G-FSUAE/G-FS-UAE/g-fs-uae.png b/bin/G-FSUAE/G-FS-UAE/g-fs-uae.png
new file mode 100644
index 0000000..d1ea933
--- /dev/null
+++ b/bin/G-FSUAE/G-FS-UAE/g-fs-uae.png
Binary files differ