\
+K\x81\xe2\xbe\xd3\x95\xd4v\x06\x84\x84\xd2(\x9e\xdf\xe0\
+\xf5\x91\xcf\xfb\x80#i\xac#5\x0f\xc0\x1aF\xa5\xe2\
+\xbb\x8b5F\xd3\xec\xf3/(\xcb\xb3\xa6\x9al.\x87\
+\xa9\x08\xbf\x00\xc20\xb6\xb2I\xa1\xe4\xa5\xb3\x00\xba\xd6\
+^kx\xea\x8c\xe0\xa3\x9f\x8e\xad\xfd\xbb\xff\xc6\xe5\xde\
+\x07$\x0bKq\x9d\xc2z\xad\xfd\xc5X\x0bc5\xcb\
+\x81\xdd\xe9X\xff\x0b>\xbf\x80[&Z}\xaf\xef\xb8\
+\x1an\x09\x0a\x15n\x13\x92o\x0d[\x884\xbc\x80\xc4\
+=\x80\xd5\xd6\xdf+\xf1u\xc5\x14[}\x05pp\xbc\
+\xcd\xbe\xb1t_\x84 \x14D:\x99#t\xad\x05\xa5\
+\xecJ'`\xf7\x92\xdd\xd8\xfe\x8b\x8fJ\x1e=\x16\xc7\
+\xf6\xe6\x06\xac\xfdZ\xd7\x9d\xd9i\x98\x1cK\xbfw\xbf\
+\xbb=\xf8\xac\xa9&\x0bm\x87y\xbf?U\x89WC\
+\x08(\x8d\xe2\xf8\x0d^c\x0d\x7f\x0e\x1cOz\x0d\xa9\
+x\x00FS\x92\x8ao/\x8e0&S\x8a\xfd-\xb0\
+\xa5\x12r\xeb\xa6\x16J\xa4\xfbJ\xb6\x83N+pB\
+\xd7s\x9cx\x16@dbk\xff\xb1\xcf(\xfe\xe8\xfd\
+\x0e\xef\xfa\x90\xcb\xa7>\xaf8\xbb\xd0\xfb\xaaD\xcf\x8d\
+\xdd\x7f7#S\x9d,0Q\x8cx\xd6t\xba\x03E\
+\xdc\x22xe\xee@p\x17\x22\xf9F\xa1D\xc5\xaf\xfb\
+\xe1\x84\xe0\xab\x9c\x02/M\xab\xe4\xb7\xdb\xe0s\xe7t\
+\x83\x9a\x9b\xfe \x89\xb6\x9fl'\xa0\x14\xf0\xe81\xc9\
+?\xde+x\xf8\xa8d\xe1\x1a2\xf9\xd7\x83\xb505\
+a\x99\xd9\x91\xbe\xf5\xbf\x98\xdd#\x01\xa7\x1am\x0e\xcf\
+\x97R\xb9\xbe\x90P\xacQ\xf0\xeb\xbcF\x87\xbc_H\
+\xe6\x92\xbc~\xe2\x1e\x80\xd1\xb8R\xf1\xbab\x8d-i\
+5\xfct\x87H\xa4\x15\xf7\xaf\xc6\x92l'\xa0\x10\xb0\
+T\x17|\xf0\xe3\x0e\xffr\x7fl\xed\xaf%\x93\x7f\xbd\
+\x9f\xf1\xc0n\xc3h-{\x0a\xc0\x11\x96\xdb'[\xa9\
+\x0e\x13\xf1\xca\xe0\x95\xf8j\x04_\x0b\xc9z\x01\x89+\
+\x00!\xd8\xaf<\xee*\xa6\x94\xf9\xb7\xc0\xb6J\xc0\xc1\
+\x84\x8b}\xaeD\xdbO\xae\x13\x10\xe2\xd2\xe3 \xec\x9f\
+\xc5_\x8d\x05\xca\x85\xd8\xfd\xcfT\xeb\xe9\xaa\xf5\x8dx\
+\x9a\xdb6\xb5(\xa8tT\x80TP\xa81\x22\x15\xaf\
+2\x9aD\x8b\xe1\x13{&3\x87f\x91\x0a\x84\xe0\xdf\
+\x14*\xecN\xebd\x9f\x82\xb2\xdc\xba\xa9\xb5\xee\x03;\
+\xfa\x8d%\x0e\x01\x92T\x00\x89~>\x03\xdb7\x1bv\
+n\xce\xc6\xfd\xbe\x1c\xbbG|\xf6\x8c\xf8\xa9\xad\xb1P\
+\x01\xc7\xe3eBpK\x92\xd7MT)\xeb\x90-\xd2\
+\xe5\x9b\x0b\xd5\xf4\x8c\xc1\xdeQ\x9f\x1d\xd5\xf4]\xff.\
+\xd6\xc6!@V\xd6\xd3k\xa4\x84[g\x0c\xe5b\xb6\
+?\xa3\xdb\x99\xf5\x98V-\x88r\xa1Pa\xbb\x90|\
+\xfd\xd8\xd6\xe4\xc2\x80D\x04q\xd5\xd6\xdf\xd7xe\x9e\
+\xed\xa5\x90o\xe9N\x8b\xb9e\xbc\x85\x93r\xd6\x7f5\
+q#P\x06\x87\xdb\xf7\x00k\xa1V\xb1\x1c\xdcc\xb2\
+0\xdc\xe9\xcak\x056\x95\x22\xf6\x8f\xa57D\xa4P\
+E)\x87\x7f;\x7f\x82\xc9\xa4\xae\x99\x98%6\x9a\x82\
+T|c\xb1B%\x8d~\x7f\x01\xdc<\xe6\xb3)c\
+\x93c\xb5\x89\xb7\x01\x87\x11ka\xcfv\xcb\xe6M6\
+\xd5\xca\xbf\xf5\x12\xbf#m\xb6\xa4\x94\x1cv\x0a\xe0\x16\
+y\xa6\x80\xe7A2^@b\xa2(\x04{\x1c\x8f\x17\
+\xa5q\xb0g|\x90D\xc4\xcch;S\x96\xa8{&\
+`\xdb'\x13\xf3\x0f{\x8d\xe3\xc4\xc9\xbfB\xea=\xa7\
+\xeb\xa3\xbb=|p\xbc\x85+\x93W\x01R\x81We\
+L*\xee\xd2a2[\xf4}W\x003\x87f\x91\x0e\
+\x08\xc1+\xbd\x12\xbb\xd3(\xfcQ\x02\x0eL\xb4S\x8b\
+\xef\xae\x84\xd6\x02?H\xeeL\xc0\xa4\xb0\x166\x8dZ\
+n\xde\x95\xed\xe4\xdf%\xeb\x06v\xd6\x02\xb6U\xd2\xf1\
+\x02\xbc2(\x8f\x97\x0a\x99\xcc\xf0\xd0D<\x80\xa8M\
+M\xba\xbc\xb2P\xc5I\xfa\x90\x0f\x0b\x8c\x15\xa2\xf8\x04\
+\x9fd/\xbd\xbe{\xa3\xe3s\x01\x87\x0d\x0b\xec\xbb\xc9\
+01\x9a\xbd\xbd\xff\xabQP\x96\xfd\xe3\xedT\xb6\x05\
+\x1d\x0f\xdc\x22{\x85L&\x0c\xe8\xab\x02X\xa9\xfc\x93\
+\xecw<\xeeLc\xdc\x97\x14q\xec_\xcb\xd8\xd1Q\
+]\xc2h8\x15@\xc1\x85\xdbf\x0cN\x16\xb5\xeeU\
+\xb0\xc0\xf6j\xc0\xd6J\x90\xf8;#\x04\x14*T\xa4\
+\xe2\x95:\xa4\xef\xa5r}\xf7\x00\x84\x04!x\xaeW\
+b:i\xf7\xbf\x1b\xfb\xef\xce\xd0A\x11\x17\xdc\x1b\xe2\
+\xf8\x7f\xd8\x14\x80\xb1\xb0u\xca\xb2{\xfb\xe0Y\xff.\
+\x9e\xb4\xdc<\xea\xe3\xa5\x90\x0bp\x8b\xa0\x5c\x9e+$\
+\xdb\xa0\xbf^@\xdf\x15\x80\x0e(I\x87\x97\xba\xa5\xe4\
+;\x0f\x05q\x81G\xd6\x0e\x8e\x5cM;H\xf6P\xd0\
+$\x10\xc0-\x09\x8f\xfd\xea5\x16\xd8Z\x0d\x99,E\
+\x89\x17i\xa98\x0c\xd8'$\xcf\xee\xf7\xb5\xfa\xa6\x00\
+V\xed\xfd\xeft<\x9e\x9d\xb4\xfb\x1fgt5\xbb2\
+\x1a\xfbw\x09\x02V\x86l\x0c\x03\xd6B\xb5l\xb9e\
+&\xfb{\xffW\xa3\xa8\x0c\xbbG|T\xc2\xdb\xd6B\
+\x80W\xa2,%_\xa7C\xfa\xda?\x99D\x08\xf0\x1c\
+\xa7\xc8\xce4\xb2\xff;k\x01\x13=<\xc1\xb7\x1f\x04\
+\x11\x89v\x02\xf6\x1bkagg\xec\xd7\xa0\x7f,\x01\
+\xec\xa8\x06TS\xe8\x18uK\xa0\x5c\x9e'$\x9b\xfa\
+y\x9d\xbe*\x80\x85\xa7\x10R\xf2\x1c\xafH1i\x0b\
+WT\x96=#\x01*\xe3f\xa8\xd5\x16\x89\x9e\x0a\xdc\
+o\x94\x8a\x93\x7f\xa5\x0c\x1c\xedv\xa3t\xabGwT\
+\x93\xf7\x22\x95\x07N\x81\xfdBp+\xf4/\x0f\xd0W\
+\x050:\xcd\x98t\xb8\xd3Ix\x14\xbb\xb50Y\x0a\
+\x99,e\xa7\xe6\x7f\xcdu\xd29\x11(\xcb\x8b\xbc\x96\
+\xcf\x93\xa1\xb1_\xbdB\x89\xd8\x93\xf4\x12\xde\x12\x94\x12\
+\xdc\x12\xe3B\xf2\xdcR\x1f;g\xfb\x1b\x02\x08v9\
+\x1e\xfb\x92\x9e\xf7/e:\x0f\xedZ\xb1\xc4\x1e\xc00\
+)\x80\xbd;m&\xc6~\xf5\x92\xa9r\xc4\xa6b\xf2\
+\xc9@\xb7\x88\x94\x0e/h,\xd0\xb7\xfa\xd9\xbe(\x80\
+\x99C\xb3\xdd\xa3\xa0\x9e\xed\x14\xd8\x9c\xe4\xd0\xcfn\xf2\
+/\x0d\xb7\xed\x9a\xd7:d\x9d\x80\x9e\x0b\xcf\xb8Yg\
+f\xecW/\xe8\x9e,\xb4\xbd\x1a$\x9e\xa8u\xe3\
+\xd0\xf2\xaf/\x07!\x80\x83{\xec@\x8f\xfd\xba^*\
+\x8e\xa1\xa0\x0c\x81Q\xc9X9\x01RQ\x80\xd8\x03\xe8\
+%=W\x00\x02J\xd2a*\xd1\x1d\x00\x11\x0fnP\
+\x03P\xff\xdf\xc5\x00-\xbf\xbf\xafO\xd7\xda\x97K\xb0\
+k\x8b\xe1\x19\xfb\x0c\x07\xf6\x18\xa6\xc7-\x87\x8fH\xcc\
+u& \xe3\xb1_\xb1\xfb/\x18\xee\xe2\x9f\xb5(:\
+\x86\xa2cYN\xb0$@*\x5c)\xe3\xceZ\xdd\xc3\
+\x14D\xef\x15\x80\xa4&%SI\x96\x00K\x015o\
+\xb0^Fc\xa1\xd5\x8e\xd7\xdbK5\xd0\xb5\xf6\x8e\x82\
+\xc9\xb1x8\xc7\xed\xfb\x0c\xbb\xb6\x1a*\x9d\xac\x8c\x00\
+Z\x9dY\x84\xd7u\x0d\x0b\xbb\xb6\x1a\xb6M\x0d\xfe\xd8\
+\xafk\xfe\xec\xc4\x05g\x15\xc7p\xa6\xd7\x0f\xef\x0aH\
+\x85\x14\x92\xe9_{5\xfc\xc4=\xbd\xfb\xb9\xbd\xdf\xbd\
+\x15T\xa5C%\xc9\x12`GX\xaa\x09o\xcd\xdc(\
+\xc6t<\x80\x1e\xbdD]k_,\xc0\xce-\x86;\
+\xf6\x1bn\xd9k\x98\x1c\xb38\xb2\xa3\x18V}\x7f\xb3\
+%\xae;\xff\xd0\x1d\xfbU\xf4\x06G\xe1\xf6\x12%-\
+%7\xc1\xfd\xdb8\x07\x80\x90l\xfa\xe1\xdfGV'\
+{\xa7w{\xa6\x00V\x0e\x01\x11\x8c\x0aI%\xa9\x9b\
+c\x01GZ\xca\xee`\xd9\xa2\xf8T\xe0\x1b\xff\xec\xd6\
+\x82#a\xbc\xb3\x1f\x7f\xc7~\xc3\x9e\xed\x86j\xe9\xfc\
+\xf7\x985\xfe]\xbdu}\xa3\xc8\xac\x85\x89Q\xcb\xfe\
+\xdd\x83u\xbf{\x89\x00*\x8eN.\xcb\xdd\xcd\x13\x09\
+\xc6\xbd\x0a\x0e\xd0\xb3\xe3d\xfb\xe3\x01H\xbc$=\x80\
+\x82\xb2\xa9\x1c\xe0p#X\x0b\xc1u\xc6\x90\x17\xc7\xf6\
+\xb7\xddl8\xb0\xdb05nq\xd5\xa5\xd6~\xad\x7f\
+\xdf\x1dEv\xad\xcf\xc9Z\xd8\xbb\xc309\xbe\xf1\x92\
+\x7f]\x04\xe0)\x9bh\xc8)\x14H\xc9\x88\x94xd\
+U\x01t^\xa6\x11\xa1\xfa\x7f\xa4\xd1\x0a\x16\x0a\xca\x0c\
+\xd4\x0e\x80 \x8e\xbf\x83p\xfde\xc0\xd6v\xbc\x9dN\
+l\x7fpO'\xb6\xdf\x12w\xe1un\xc5\xba|C\
+\xcb\xf9Qd\xd7\xaa\xa7=\x17n\xdfgp\xe5\xfa\xae\
+5\xac\x94\x1c\x83\x14\x16m\x93\xb1tB\xc4\xf95!\
+)\x00\xf5^\xfd\xdc\xdez\x00\xb1J,\x0a\x91|\x17\
+\xe0 \xed\x00@,\xfc~xu\x01\xecZ\xfbJ\xd9\
+\xb2{\x9b\xe5\xf6}\x86\x037\x196\x8dZ\x94\xbc\xba\
+\xb5_\x0b\xa3\xa1}\x1d\xf9\x07\xd3\x1d\xfb5\xc0G~\
+\xf5\x8a\x82\xb2H\x01:\xa1\x1b\xd1\xa9\xb0-Ao\x8d\
+kO\x15\x80T`,U!\xfb{\x9a\xc9\xc5\xb8\xca\
+\x0e\xdc\xc9:~\x10\x9f\x0a\xb4\x16]k\xef\xaaX\xe0\
+n\xd9k\xb8\xfdf\xc3\xce-q\xe2\x0d\xd6o\xed/\
+\xa6\xdb\x85\xd8ls]I\xac\x83{\x86s\xec\xd7\xb5\
+\xe2J\x8b\x92\x96\xd0$\xf4\xe2\xc5\x0a\xa0\x8c\xa0\xa7=\
+\x81\xbd\x0d\x01$ pE\x92CnD\xfc0\xc4\x00\
+\xbd\x92\x02h\x07\xf1H\xb0\xd5t\xad}\xb5\x1c\x1f\xac\
+y\xc7~\xc3\xfe\x9b\x0c\xe3#\x16%b\x81\xef\xc5\xa7\
+\x0c\x22A\xdb\xbf\xf6\x87T.\xc4{\xff\x92\x8d\xed\xfe\
+C\x9cx\x96\xf4~\x1b\xf7r\x08\x01H\x5cAo\x8d\
+ko=\x808.tIx\xca\x95\x12\x83\xe7\x01h\
+-V\x1a\x81\x8c\x89\xdbj7OXn\x9d\x89c\xfb\
+\x1d\x9b\x0dE\xf7\xbc\x8b\xdfK\x81\x8b\xa2\xb8\x0c\xf9Z\
+0\x06\xb6o\x1e\xee\xb1_\xd7\x82\x12\x16\x99p\xd8)\
+b\xf7\xbf\xa7\xb3\x01{\xeb\x01(@$\xdf\x1b\x92\xf4\
+\xe1\x8d\xbd\xc0\x98\xce\xd1S\x958\xb6\xbf\xe3@\xc7\xda\
+\xd7\xe2\xd8\xd2\xd0?+\x1bF\x10^\xa3\x02\x902\xde\
+\xfb\x1f\xf6\xb1_\xebE\x88\x1b\xeb\xa5\xb8\xf6\x0b\x82\x90\
+\x08z\x9c_\xeb\xa9\x02(T l\xe1$\xed\x01\x08\
+\x06\xab\xa7\xde\x12+\xadW\xbc \xe2\xe0\x1e\xc3\xd6I\
+K\xa1O\xd6\xfeb\x04q\x01Rx\x0d\xe5\xa4\xd6\xc2\
+h\xd5r`\xcf`U[\xf6\x13)R9\xd1Y\xd1\
+c\x99\xed\xe9\x0f\xeb\xc4\xb0\x89\xdf\x96A\x8a\xff!\x16\
+\xa0\x1d[\x0c\xbb\xb7\xb3\x12\xdb'\x19S7\xdb\x10i\
+\xb1\xee\x17\xd8Z\xd8\xb3cc\x8c\xfdZ?6\xf9\x11\
+\xe8\xa2\xf7\xb6\xae\xa7\x9f\xc1\xc41m\xe2\xd28\x88\xe3\
+(]'\xb6 i\x08T\xab-\xaei\x10\x89\xe3\xc4\
+\xee\xffF\x19\xfb\xb5>R1;\x19W\x00\xb1[\xa9\
+m\xc2\xbbD\xddm\xb3\x9c\xf5\xd1\x1dE\xb6\x1e6\xe2\
+\xd8\xaf\xf5a\xd31;=\xbehO\x15\x80\x8e\xfb\xdb\
+\x13?\x93;\xa9j\xaca`\xa5\x0ap\x9d\xcf\xc8\xda\
+\x8d5\xf6k\xbdX\x9b\xc24'\x8b\xa1\xc7QXo\
+\x15@\xecV\xb6m\xc2\xa1\xa26\xd7\xd7\xd8\xb2\x111\
+@\xa3\xb5\xfe#\xc9\x8b\x85\xd8\xfd\xdfHc\xbf\xd6\xc3\
+\xf5T`\xde\xe8\x05;r\xd5\xd3\xb6\xd7\x9e*\x00\xab\
+\xc1Z|\x924\x16\x16B#\xd2r\xc8\x06\x0ec\xa0\
+\xd9Z\xe7\xf7Z\xd8:i\xb9i\x03\x8e\xfd\xba\x1a\xc6\
+\xa6\xe2yFXz:\x86\xa4\xb7\x0a .UkX\
+\xd3[-u5B3\xd8\xc35\x93\xa4;\x87`=\
+\xb7K\x00\x07\xf7\x9a\x0d9\xf6\xebj\xe8\x84\xdf9k\
+\xc1\x1aB\x0b=\x1dI\xdc[\x05\x10\xff\xc7O4\x04\
+\x10\xb1\x02\xd0f\xf0\x0f\xd9\xec7\xdd.\xc4\xb6\x7f\xf5\
+\xef\x8d\xc7~\xd9\x95\xb1_9\x17\xe2k\x99\xf8;g\
+->d\xd8\x03\xe8\xc4)K\xc6\xf4\xae_y=\x04\
+Z\x10\xe5&j]\x84\xa1\xa0\x1d\x5c\xfd\xb5\x8d\xc7~\
+Y\xb6Nn\xbc\xb1_\xeb\xc1\xd7\x22\xb1N@\x88\xbd\
+kkh`Yg\x00\xb7>\xfaQ\xcb\xd0\xb0\x86 \
+\xc9\xa4\x5cd\x04QR]Y\x03N\x10\xc5\x9d\x88W\
+\xbb[\xdd\xb1_\xa5\xbe\x9cJ?\xf8\xb4\xb4\xc4$\x98\
+\x03\xb0\xb1qm\xda\x1e\x1b\xd7\x9e+\x00k\xa9[C\
+3\xa9\xa0Q\x00\xa1\x91\xb4\xa2\x01l\x08H\x18A\xdc\
+\x82|\xb5Qd\xd6\xc2X-\x1e\xfb\x95;V\x97b\
+\x81F(\x13\xbd7\xd6\x80\xd5\x9c\xb36\xa3\x0a\xe0\xc8\
+\xddo\x8e\x17j\xa9\x9b\x88f\xa2\x1e\x80\x85v\xae\x00\
+\xd6\x85\x1f^\xda\x86|1\xd6\xc2\xcc\xce\x8d=\xf6\xeb\
+J\x18\x0b\xf5@%\xba\xf5\xdc\x09\x01\xceEA\x96s\
+\x00\x00\x86\x86\xb5,\xda\x04\x03Gm\x04\x8d\x5c\x01\xac\
+\x8b\xb6/\x88\xf4\x95\x1bY<\x17\x9e\xd1\x19\xfb\x95s\
+)\xa1\x914B\x99h\xd7\x8b\x89\xc0ZN\x9d\xf8\xbe\
+\x0c\xd7\x01\x00X\xf0\x8df>\xc9#\xafb\x97L\xe5\
+\xd6j\x1d\xb4\xdaW>\x90\xd4Z\x98\x9e\xb0\xec\xc9\xc7\
+~\xad\x89 N\x006\x1368Z\xa3\x8d\xe1\xe4\x81\
+\xf7\xf7\xf6\xe7\xf6\xfcS\x98\x88\xb65\x9c\xb2IV\x02\
+XX\x0ad^\x12\xbc\x0e\x9a\xed\xab\x9f\x07\x90\x8f\xfd\
+\xba2\xcdP\xe2k\x99\xe8\x16\xa0\x89\xf0\xad\xe1\xc9^\
+\x9e\x0a\x04}P\x00\xdbo'4\x9a\x13\xbd^\xe8\x15\
+\x11\xb1\x07\x10\xe8\xbc\x16\xe0JXb\x0f\xe0r\x05,\
+\x16(\x15\xcf\x8f\xfd\xcaY\x9b\xe5P%7\x0b\x90\xd8\
++\xd3!M,\xa7z\xfd\xb3{\xfa\x9c\x8f\xdc\xfdf\
+\xe6\x9e\x00k8a\xa2d\xab\x01\x1b\xa1\xa4\x19\xe6\xaf\
+\xed\x95\xb0\xc4\x1e\xc0\xe5\x92W\xd6\xc0\x8e\xcd\x86\x1d[\
+\xf2\xec\xff\xe5\xb0\xc0B[%^\x03`\x22\xe6\xac\xc9\
+\xb8\x02\x80x&\x80\xb5\x9c\xd0\x11\xeddnOg{\
+K\x0b\x16\x83D\x87\x11\x0f\x1c\xc6\xc4\xc3@.\xf7\xee\
+J\x09\xb7\xee5\x94\x0by{\xf5\xe5\x88\x8c\xe0\x9c\xef\
+$z\x83\x8c\x06\xa39k-\xe7\xe0\xfc\x8e[/\xe8\
+\xbd\xc9\x8c\xdb\xa4N\x99\x88F\x92;\x01\x91\x15\xcc\xb5\
+\x9d\xfc\xc5\xbd\x02\xda\xc4\xc3@\xd6\xc2\xdax>aw\
+\xecW\xce\xa5\x08\xa0\x19I\xce\xf9*\xf1\x1d\x00\xa39\
+jt\xef\x0e\x04\xe9\xd2\x17\x9f\xd9\x1aN\xe9\x90SI\
+*\x00\x80\xb3-\x07_\xe7\xaf\xefZ\x08\xba\x8d@k\
+\xff}|\xe4W>\xf6\xebj,\x07\x8aV\x94l\x02\
+P\x87`4O|\xe3\x1bz_b\xdf\x1f\x05`9\
+g4'\x13M\x04\x02\x8b\xbeC#T\xb9\x05\xbb\x0c\
+a\xe7<\x80\xb5p\x1c\xb8\xed\xe6|\xec\xd7\xd58\xdb\
+r\x12M\x00\x02\xe8\x80\xd0\x1a\x1e\xfa\xc8{{\xeb\xfe\
+C\x9f\x14\x80\x89h\x98\x88#\xba\xa75KWF\x00\
+\xadH0\xd7\xca\xdf\xe0\xcb\x11\x84\xacy\x1c\x99\xb5\xf1\
+y\x83\xf9\xd8\xaf+\x13Y\xc1|\xdbI\xbc\x020\x0a\
+X\xb2\x86\xc7\xfa\xe1Q\xf7E\x01\x94F\x89\x8c\xe6\xe1\
+(Hv' \xb2\x82\xd3-7\x9f\x0dp\x19\xba\x0a\
+\xe0b,\xb0\xef&\xc3\xc4H\xbe\xf7\x7f9\x04\xf1\xfe\
+\xff\xd9\xb6\x93l\xfc\xafA\x87\
+\xfe\xd7\x11\x8fY\xcb\x5c?~~\x7f6\xce\xe3\xe9%\
+\xc7\x22\x9f\xa7M\x82y\x00\x01,\x05\x8a\xf9v\x1e\x06\
+\xac\x85\x1f\xc4\xe7\x01\xac~\x83W\xc6~m\xcd\xad\xff\
+\x950\xc0\xc9\x86\x9b|\xfc\x1f'\x00\x0f\x1f}\x0f\xcd\
+~\xfc\xfc\xbeU\xceX\xc3\x09\x1d\xf1P\x92y\x00\x80\
+\xc0\x08\x9ejx\xf9\xcb\xbc\x06\xad6q#\xd0\xaa?\
+\xcb\xc7~\xad\x0f?\x92\x9cN!\xbf\x14\xb6\x09\x8c\xe6\
+\xf0\xcdo\xe8\xcf\xcf\xef\x9b\x02\xf0\xeb4u\xc4\xe7C\
+?\xf9\xf7\xea\xe9\x86K3aWm\x10h\xb4.\xec\
+\x038?\xf6K\xe7\xf7\xea\x0a\x08`\xae\xedp\xcew\
+\x92\xad\xff\xd7\x10\x05\xccay\xd0\xda\xde\xef\x00@\x1f\
+\x15@y\x1ck\x0d\x9f\x8b\xda\xc9\xce\x06\x10\xc09_\
+q&\xdf\x0d\xb8\x80\x95>\x80\x8b\x14\xc0\xae\xad\x96m\
+S\xf9\xde\xff\x950\xc0\x93u\x8f \xe1\x1a\x13\x1d\x82\
+\x0ex\xd4Z\x8e\xf6\xeb\x1a}Q\x00+\xc3A\x0c\x87\
+C\x9f\xe3I\xe6\x01 \x0e\x03\x9e\xac{\x89\xd6k\x0f\
+\x02-\xff\xc2>\x00\xa5\xe2\xe4_\xd1M{e\xd9E\
+\x10\x0f\xff8\xbe\x9c|X\x19\xf9\xa0#\xee\xd7!\x0b\
+\xfd\xbaF\x7f\xbbg,'u\xc8\x03\xd1:\xa6\xd0\xf6\
+\x9a\x93\x0d\x8f\xe5 /\x0a\xeab\xec\x85}\x00\xd6\xc2\
+\xf8\x88\xe5\xc0M\xf9\xde\xff\xd5x\xba\xe9\xb2\x1c$\x1f\
+R\x86m|\xa3\xf9\xac[\xea\x9f\x83\xd6W\x05\xe0\x16\
+i\x9a\x88O\x84\xedd\xeb\x01\xba\xbb\x01'\x1a\xf9D\
+\xcb.\xda@\xb3%V4@w\xec\xd7\xa6|\xec\xd7\
+\x15\x09\x8c\xe0\xe8R\x81(\xe1Y\x13FC\xe4s\xd6\
+\x1a\xbeL\x9f\xe2\x7f\xe8\xb3\x02\xd0\x11X\xc3\xbdA\x8b\
+9\x93\xa8\x0a\x00m\xe1\x89%/\xef\x0d\xe8\x10\xe9N\
+\x1f@\xe7vxn\xec\xfe\xe7c\xbf.\x8f\x00N7\
+]\x9en$\x9b\xfc\x83\xd8\xfd\x8f\x02\x1e\xa0\x8f\xf1?\
+\xf4;\x04 \xce\x03D\x01\x0f\xe8DO\x0a\xe8<\xbc\
+\x96\xcb\xa9\xa6\xbb\xe1\xc3\x00\x01h}\xfe<\x80\xee\xd8\
+\xaf\xbd\xf9\xd8\xaf+\xa2-<\xb6X\xa0\xad\x93\xd7\x92\
+A\x0bt\xc8\xa7\x1e\xffE\x16\xfby\x9d\xbe}\xb2\xae\
+\xcb\x12\xfa,\x98\x90\x7f\x0dzz\x9c\xc1\xfa\xf0u:\
+\xee[\x16\x89\xf4\x85\xe7\x01\x1c\xc8\xc7~]\x11\x01,\
+\xf8\x0e'\xea\xc9gH\xad\x81\xb0\xc5\xbc5\xfc\xe3\xcc\
+/\xf6\xf7Z}Wm\x85\x0a\xd6h\xfe1h%;\
+)\xb8\xcb\x93u\x8f\xf9v\x9e\x0c\xf4\x83\xf8L\x80\xee\
+\xd8\xaf\xdb\xf2\xb1_W\xc4\x10[\xffz\x0a\xdd\xa5Q\
+\x00\x91\xcfW\xac\xe50\xf4/\xfe\x87\x04\x14\x00\x80\xb5\
+|!l\xf3`\x94B\x18\xd0\x08%G\x16\x8b\x1bz\
+KP\x10\xc7\xffA\xf6\xebr\
+X\xe0\xf1\xa5\x02K)l#\x1b\x03~\x83ec\xf8\
+\xb8\xd7\xc7\xed\xbf.\x89x\x00n\x11c4\x1f\xf1\x1b\
+\xc9\xef\x06\x08\xa0\x1e*\x1e^(n\xe8\x5c@\x10\xc4\
+y\x80\xd1\x8a\xe5\xc0\xee\xbc\xee\xefr\x08\xe2\xae\xbf#\
+\xe7\xd2\xb1\xfeQ\x1b\xc26\xf7[\xc3\xbfB\x7f\xdd\x7f\
+H@\x01\xac:2\xec\xf3\x91\xcf\x97\xa3\xc4F\x85\x9e\
+\xc7\x02\xc7\x96\x0a\x9cin\x5c/\xa0\x1d\x08\xb4\x86=\
+;,\x9b7\xe5\xc9\xbf\xcb\xa1-<\xbcPL\xc5\xfa\
+\x03\xf8\x0d\xd0!\x7f\xff\xf8\xcf\xf4\xdf\xfd\x87\x84<\x00\
+\x80\x93\x0fpV\x87\xfc\x8d\xdfH\xb6(\x08:\xb9\x80\
+H\xf2\xc0|iC\xd6\x05X\xe2a \xdd\x13\x7f\xf3\
+\xb1_k#\x803-\x97\xa3K\xe9X\x7f\x13A\xd0\
+\xe4\xa45\xfc\xdd\xcc[\xfbo\xfd!!\x05p\xe4\xee\
+7\xb3\xfd\x0e\xb0\x86\xbf\xf7\x9b<\x95t\x8b0\xc4\x0f\
+\xf7\xc9e\x8f'\x96\x0b\x1b\xd2\x0b\x10\x02&\xc7\xf3\xb1\
+_W\x224\x82\x87\xe6\x8b\xd4S\xea$\xf5\x1b\x10\xb6\
+\xf9\x14\xf0@R\xd7Lt'\xc8Z\x0eG>\xff\x90\
+F2\x10\xe2\xb2\xce\x07\xe6J,o\xc0V\xe1\xa2g\
+\xb9e\x8fa|4w\xff\xd7B\x00O,{\x1c]\
+J\xc7@X\x03~\x9d\x86\xd1\x1c\x12\xb2\xf7\xe3\xbf/\
+G\xa2\x0a@*\xdaF\xf3\xa1\xf62\xf54j\x02\x04\
+p\xb6\xed\xf0\xd0|i\xc3m\x0bN\x8e[\x9euP\
+\xe7c\xbf\xd6\xa0\x1b\x22>8_\x22Hx\xe2O\x97\
+\xb0\x0dA\x8b\x07\xb0\xfc3$\xe3\xfeC\x82\x0a`U\
+\x8b\xf0?\x85m\xbe\x10\xa6\x90\x0c\x84\xb8+\xee\xe1\x85\
+\x22'\x1b\xde\x86\xf1\x02,q\xe9\xef\xf6\xe9\xdc\xfa\xaf\
+\x85\xb1\xf0\xd0|)\xbd\xb2q\x0b\xed:F\x87|\xf0\
+5\xdf\xc6SI^:\xf1b\xb0'?\xcfi\x1d\xf2\
+\xc1\xf62Q\x1aoc\xf7t\x97/\x9c)\xd3\xd8@\
+\xc3C\x0b^<\xfb?\xe7B\x04p\xaa\xe9\xf2\xd0B\
+z\xc5b\xa1\x0f\xfe2G\xac\xe1\x83\xef\xbd'\xd9k\
+'\xae\x00v\xdd\x09\xd6\xf0!\xbf\xc1ca\x0as\x02\
+ ~\xe8'\x9b.\x0f\xcf\x17\xf3\x11\xe2\x1b\x98\xb8F\
+D\xf2\xf93\xe5\xc4\xa7\xfd\xae\xc6o@\x14\xf0W\xd6\
+\xf4\xbf\xf4\xf7b\x12U\x00\xabj\x02\x1e\x8d|>\xe0\
+\xd7\xd3\xf3H\x8d\x85\xc3\xf3%\x9e\xa8o\x9cP \xe7\
+Bt\xe7\x1d8\x91\xe2;\xa0\x03h/\xf3\xb45|\
+@:\xc9o\x91'\xee\x01\x1c\xb9\xfb\xcdHEd\x0d\
+\xefo/\xf3T\xd2\xfd\x01]V\x87\x02\x8b\xf9\xe4\xa0\
+\x0d\xc9\xd1\xa5B\xec\x05\xa6\xb8\x86v\x1d\xa26\xff\x1b\
+\xf8\x1c$k\xfd!\x05\x05\xb0\x82\xe5\xfe\xc8\xe7\xaf\xdb\
+\xcb\xa9\xad`e\xe0\xc3\x17\xce\x94S\xcb\xfe\xe6$\x8f\
+ >\xe3\xef\xbe\xd3\x15Z:=\xd7_\x87\xd0Z\xe2\
+\x94\xd1\xfc\x89\x90\xa4\x92\x16OM\x01\x08\x85o\x0c\xef\
+n/q\x22-/\xa0\xcb\x91\xc5\x02\x0f\xcd\x17\x13=\
+\xf3-'\x1d\xbaq\xff\xe7NWX\xf0\xd3\xf5\xfc\xda\
+\xcb\x10\xb5\xf9\x08\xf0iH\xde\xfaCJ\x0a`\xe5\x83\
+Z>\x13\xfa|\xa0\xbd\x94\xc6*\xce\x13\x1a\xc1\x17\xcf\
+\x96y2\xcf\x07\x0c5\x82\xb8\x18\xec\xfe3\x15\x9e\x5c\
+N\xf7YG\x01\xb4\x169e\x0c\xef\x12\x92\x14\xc6\xe5\
+\xc4\xa4:\x13BH|\xabyWk\x89\xc7\xd3\xaa\x0b\
+\x80\xf3\xf9\x80\xcf\x9e\xaap\xa6\xb5q\x1b\x86\x86\x1dm\
+\x05\x87\xe7J<\xba\x90N\xad\xffj\xda\xcb\x10\xfa|\
+ \xe9\xc2\x9f\x8bIM\x01\xac\xfa\xc0\xf7E>\xf7\xb4\
+\x16I\xd5\x05\xef\x9e\xfe\xf2\xd9S\x95\xd4:\xc1r\xfa\
+\xcb\xa3\xe7\x0a|\xe1l9\xf5\xb6\xf0\xc8\x87\xf6\x22'\
+\xac\xe6\x9dB\x92\xd2fxL\xeaS\xa1\x84$\xb2\x86\
+w\xb4\x97\xf9B\x98R\x8f\xc0\xcaZ\x80\x13u\x8f\xcf\
+\x9e\xaa\xe4'\x0c\x0f\x19\xc7\x96<\xee;]\xc1\xd7\x22\
+\xd5\xe7j-4\x17\xb1\xa1\xcf!\xe0^H\xcf\xfaC\
+\xca\x0a`Uy\xf0C:\xe4\x1d\x8d\x05\xfc\xa4\x07\x86\
+\xac\xc5\xe3K\x05\xee\xcd\x95\xc0P\xd0U\xea\xff\xfat\
+5\xb5.\xbf\xd5\x84-h/\xf1\xa05\xfc\xa1\x90\xa4\
+\xd0\x17{!\xa9{\x00G\xee~3\xd2\xc1Z\xc3\xbb\
+\xfd\x06\x1f\xf7\x13\xeb\x83\xba<\xd6\xc2#\x0bE>w\
+\xbaB;e\x8b\x91sc<\xd5p\xf9\xf4\xd3\xd9\x08\
+\xeb\xac\x81\xe69\xb4\x0e\xf9\x93(\xe0AH\xd7\xfaC\
+\x06\x14@\x17!9c4oo,0\x97\xf6\xb6 \
+\xc4\x0d4\x8f,\x14\xf9\xec\xa9*\xad\xdc\x13\x188\xba\
+5\x1e\x9f:Ye.#\xf3 \xdb\xcb\xd0\xae\xf3\xcf\
+\xd6\xf2n\xb7\x88M[\xf8!#\x0a`\xd5\xb6\xe0\xdf\
+\x86m\xfe\xa2y\x0e\x9b\x85=yc\xe1\xe1\xf9b\x9e\
+\x13\x180\xba\xbd\x1e\x9f\xcc\x90\xf0\xeb\x10\x9a\xe7X4\
+\x11o\x13\x82\xe3i\xaf\xa7K&\x14@\x17!i[\
+\xc3\xffh-\xf2\x85\xa0\x91\xf6jb,\xf0\xc8\xb9\x22\
+\x9f:Y\xcd\x0f\x1b\x1d\x10N6\x5c>\xf9T5;\
+[\xba\x16\x9a\xe7 h\xf1>\xe0\xef }\xd7\xbfK\
+f\x14@\xf7\x86\x08\xc1a\x1d\xf2\xb6\xc6\xc2\xc4k\xef\x02x\xccD\xec\x16\x82g\x16\xca\x90\
+\x95'\xb9\x18(\xce\xb6\x1d\xc6\x0a\x9a\x9a\x9b\x8f\xd6\xce\
+\x0a\x828\x5c{\xf4\x5c\x91\xcf<]\xcdTs\x97\x89\
+`\xf9\x0c\xad\xa0\xc5/\x0b\xf8\x1bDv\xac?dL\
+\x01@G\x09\xdc}\x97o-GM\xc8\x8bU\x81)\
+'C\xa7|\xd7C\xc5\xe9\xa6K\xc95\x8cz\x1a\x99\
+\x957m\x83\x22\x80\xb6\x16<0W\xe6\xbe\xd3\x19\xcb\
+\xd5X\xa8\xcfC{\x91\xf7Y\xcb\xaf\x0bA+K\xc2\
+\x0f\x19\x0a\x01.\xa62\xc6\xfdQ\xc8\x7fk\xcc\xb3\x9c\
+\xc6\x14\xe1\xcb\x11\x1f\x1a\xa9\xf8\xe4SU\x0e\xcf\x97\x08\
+M\xbeM\x98\x16\x82\xd8+\xfb\xe4\xc9\x1a\xf7ep\xcb\
+\xb6\xdd\x80\xd69\xbel4\xbf*`>\xed\xf5\xacE\
+\xe6<\x00\x88\xbd\x80\xda\xab\xee\x028bBv`y\
+\x96WF\x88\x8c<]A\xdc@t\xaa\xe9\xd2\x8c\x14\
+\x13\x05MAe`\xdbb\x83 \x88\xf32\xc7\xeb\x1e\
+\x9f~\xba\xca\xf1e\x0fKf\x22E .\xf7]>\
+\xcdR\xe4\xf3\xf3&\xe0o\xa5\x9b-\xd7\xbfK&\x15\
+\x00\xc0\xc4k\xefB\x08\x02kyP\x87\xcdB\xe4\xf33\x02>,2\xb6\xe5\xb7\
+\x16Y\xbd\x97\x970sh\x16k\xd9\xa7\x1c\xdeQ\x9d\
+\xe4\x85\x95\xf1\xec\xae\xde\x02J\xc0t9\xe4\xf6M-\
+vT\x03\x5c\x99\x1f\xcaq\xad\xd4C\xc5#\x0bE\x1e\
+Y\x88\xcf\xeb\xcb2:\x84\xc5\x93\xb4\xfc:\xff\x15\x98\
+E\xe0g]\xf8a\x00<\x80.\x9d\x02\xa1yk8\
+\xa2\x03^\xa4<69\x85\xb4W\xb56\x82X\x09,\
+\x87\x8a\x13u\x8f\xc5@QT\x96\x92k\xf3\xa3\xb9\xae\
+\x82\x00\x02-xl\xa9\xc8g\x9e\xae\xf0\xd8R1\xf5\
+\x1e\xfe\xaba4\xd4\xcf`\xdau\xdem-\xbf$\x04\
+\x8dA\x10~\x18 \x05\xd0)\x10B\x08\x8e\x1b\xcd\xbc\
+\x0ex\xb1S\xa4\x92\xc5\xa4`\x17A<\x86j\xae\xed\
+\xf0d\xdd\xa3\x11JJ\x8e\xa1\xe8\x98\xbc\x80\xe8\x22\x04\
+\x10j\xc1\x93\x0d\x8f{OUyp\xbe\xc4R\xa8V\
+\xfe.\xabX\x0b\x8d9h.\xf0\xd7\xd6\xf0\x93\x02N\
+#\xe2\xf7u\x10\x18\x18\x05\x00+;\x03\x16x\xd0D\
+X\x13\xf1\x22\xb7\x88+3~\xe4U\xb7n\xe0l\xcb\
+\xe5x\xbd\xc0r\xa0(8\x86\xd2\x06W\x04b\xd5\xbd\
+9Q\xf7\xf8\xdc\xe9\x0a_\x9a+3\xd7v2\x9b\xe4\
+\xbb\x80N\x93Oc\x8e\xcf\x19\xcd\x8f\x0ax4k\xa5\
+\xbeWc\xa0\x14\x00\xac$\x05\x8d\x85/\xea\x90\x92\x89\
+\xf8*\xaf\x84#3\xfeI\xba/s\xd0Q\x04O\xd6\
+\x0b,\x05\x0eJ@\xc918\x22\xdb\x96\xae\x1f\xf7\xa2\
+\x19I\x9eX\xf6\xb8\xffL,\xf8g\x07E\xf0;\xb4\
+\x96a\xf9\x0c\x87u\xc4\x9bt\xc0g\xa43X\xc2\x0f\
+\x03\xfc\xceu\x92\x82\x13B\xf2\x1b\xa5\x11\xbe\xab6\x8d\
+R\x19\xf7\x04V\xd3M\x08\x16\x95ek%`\xcf\xa8\
+\xcf\xd6rH\xd95+9\x84a\xa2\xfb\xa2EVp\
+\xceW\x1c_\xf6xb\xd9c\xae\xe5\x10\x0d\x90\xd0w\
+i/\xc3\xf2i\x9e\x0c}\xfe\x13\xf0\xfeA\xc8\xf8\xaf\
+\xc5\x00\x89\xcc\xa5\x888)\xf8\x96\xd6\x22\x8e\x10|G\
+m\x1a\x95uO`\xd5\xda\x01\xf0\xb5\xe0\xf1\xa5\x02\xc7\
+\xeb\x1e\xe3\x05\xcd\x8ej\xc0\xaeZ\xc0D1\xc2\xedl\
+!\x0e\xaa2\xe8~Fm\xa1\x19)N5\x1d\x9eX\
+*p\xaa\xe9P\x0f\xd5J\xf9\xee\xa0\x09\xbf\xdf\x80\xe5\
+\xd3<\x1d\xfa\xfc\x94\x10|\x10\x06S\xf8a\xf0\xee\xfd\
+\x05\xcc\x1c\x9a\x05\x0b\x16vJ\xc9o\x96Fyum\
+\x0a\x99\xf5\x9c\xc0\xe5\xe8\x0aD\xd91L\x96\x22\xb6U\
+\x03\xa6K\x11c\x85\x08O\xd9\x95\x87\x95e\x85\xb0\xda\
+\xd27C\xc9\x99\x96\xc3S\x0d\x8fS\x0d\x87\xa5@\xad\
+\x8c\xe4\x1e\xd4\x17/h\xc2\xd2)\xe6\xc2\x16oA\xf0\
+\x87\x90\xdd2\xdf\xf50\xa8\xcfa\x85N(\x00\xb0K\
+J\xfe{i\x8cW\xd7&\x07W\x09@G\xc0-H\
+\x01\x05\xc70Q\x8c\x98.EL\x97CF\x0b\x9a\x8a\
+cp\xa4\xbd\xe0\xe1\xa5\xa1\x14.\xbe\xbe\xaf\x05\xf5P\
+q\xa6\xe9p\xaa\xe9r\xa6\xe5R\x0f%\xa1\x19l\xa1\
+\xef\xd2\x11\xfe\x85\xa0\xc5\xcf\x09\xf8\xff\x10\x04\x83,\xfc\
+0\xf8\xcf\x04\xb8\xc0\x13\xd8-$o-\x8f\xf2-\xb5\
+)\xd4 +\x81.]\xc1\x16\x80+-E\xc70^\
+\xd0L\x14#6\x95\x22F\xbcX!\xb8\xca\xa2\x84\xbd\
+\xe4\x81\xde\x88b\x10k\xfc\xdev\xbe\x22#\xf0\xb5\xa0\
+\x19I\x16\xda\x0e\x0b\xbe\xc3\xd9Vl\xe5[\x91D\xdb\
+\xb5\x7f\xc6\xa0\xd2\x15\xfe\xb0\xc5/\x02\xbf?(\x85>\
+WcX\x9e\x0f{\xdf3\xdb}AwJ\xc9[\x8b\
+#\xbc\xa66\x85\x93\xe5:\x81\xeb\xa1\xeb\x1d\x08\x01J\
+Z\x8a\xcaRv\x0c5OS\xf34#\x9e\xa6\xea\xc6\
+[\x8cE\x15{\x0aJ\x80\x14\x16q\x85\x9d\x86\xae\xa2\
+\xb06.i66\x9e\xb2\x13\x1aI` \xd0\x92v\
+$\xa9\x87\x92\xe5@\xc5\xff\x0fca\xf7\xf5y\x81\x87\
+!z\xa9:tb\xfe\xf9\xb0\xc5\x7f\xb5\xf0?\xc4\x90\
+\x08?\x0c\xd9\xb3Z\x15\x0el\x15\x82_(\xd6\xf8\xf7\
+\xb5i\xdc,M\x14\xea5v\xf5/D\x5c\x82\xac\x84\
+\xc5S\x96\x822\x14\x94\xc5S\x86\xa2\xb2\xb8\xd2\xe2*\
+\x8b\xc0\x22\xc5\xf9\xa3\xd8\x8c\x15h\x13\xc7\xed\x91\x16\x04\
+F\x10h\x11\x0b\xbf\x16\x84V\x10\x19A\xd4Q\x0ev\
+\xb5[\xc2\x90\xbdD\x17\xe17`\xf9\x14g\x826?\
+'\xe0\x8f\x87\xc5\xf2w\x19\xbag\xb7*\x1c\x98\x12\x82\
+\xb7x\x15\xfe\xc3\xc84\xe5,\xcd\x12H\x02{\xc9/\
+\xb8\xe0i_\xb2\xd5\xb8V\xac \xd6\xfc\xe5\x86\xa1\xb5\
+\x04\xf53<\x19\xfa\xfc\x82\x80w\x22\x08\x87I\xf8a\
+\x88\x9f\xeb\xcc{f\xb1P\x15\xf0&\xaf\xccO\xd6\xa6\
+\xa8y\x95\xb4W\x953\x08X\x0b\xadE\xa8\x9f\xe5\xb1\
+(\xe0\xc7;[}z\xd8\x84\x1f\x06\xb0\x12p\xbd,\
+\xfc\xe5G\x98\xb8\xfb\xae\x00\xb8W\x87\x9c\x0d\xdb\
+\xabC\xfe\xb3\x10|\x180\xc3(\xfc0\xc4\x0a\x00V\
+z\x07B\xe0>\xady\xff\x9d\xd8\xe5_\xda(.\xff\xc5lX\x05\x00\
+\x9d\x90\xc0\x00\x82M\xc0w\xbb\x05\xdeT\x1eggi\
+4\xf7\x06\x86\x91\xa0\x05\x8dy\x02\xbf\xce\xdf\x19\xcd\xaf\
+\x09\xc1\xa7\x19\xd2\xfd\xfd\xf5\xb2\xa1\x15@\x97\x99C\xb3\
+\x00\xcaZ\xbeV*~\xa2P\xe5\xe5\x95q\xbc\xdc\x1b\
+\x18\x0e\x8c\x8e\x0b{\x9a\xe78\x11\xf9\xfc\x9e\xb5\xfc\x11\
+\x96S\x1b!\xcb\x7f5r\x05\xd0a\xe6=\xb3\x98\xb8\
+\xc9fZ\xc0w;\x05~\xa44\xc6\xee\xd2\x08\x0c[\
+C\xd1\x86\xc1\xaeX\xfd\xb6_\xe7\xc3\xc6\xf0;\xc0'\
+\x80h\xa3\xc5\xfa\x97#W\x00\x17\xb1\xf7=\xb17\x00\
+\xa3#\xfe\x02\xcb\xc7\x80\xa7 w\xf5o\x94\
+\x5c\x01\xf4\x90\x8e\x22\x90\xc0v\x04/S\x8a\xff\xcb)\
+\xf2\xb5\xc5\x1a\x9b\xbc28\x85\xdc+X\x0f:\x8a\xdd\
+|\xbfA\x104\xf9\x8a\x0e\xf8\x98\xd1\xfc\x15\xf09`\
+\xc1Zx\xfcu\xb9\xe0\xf7\x82\xfcu\xec\x03{\xfel\
+\x16\xe5\x82\x8e\xa8\x09\xc1\xf3\x84\xe2\xdf:.\xafpK\
+\xec+T(w\xbd\x82\x5c\x19\x9c\xc7h\x88\xda\xe07\
+1A\x93\xa7#\x9f\xcf\x98\x88\xbf\xb1\x96\x8f[\xc3Q\
+!\x88\x16\x16a\xe1\x87r\xc1\xef%\xf9+\xd8g\xf6\
+\xfcy\xbc\x85(\x04\xdb\x11\xbcP*^\xe5\x14x\xae\
+[d\x97W\xc6s\x8bq\xa9\xf1FT\x06&\x82\xd0\
+\x87\xb0\x85\x0e\x9a\xcc\x87>\xf7\x9b\x88\x7f\xb0\x86\xbf\xb7\
+\x96\x07\xdd2\xf5\xe5\x13p\xf2Gr\xa1\xef\x17\x1b\xf0\
+\xb5K\x87\xdd\xef\x9e\xc5-A\xe4S\x02v\x09\xc9\x0b\
+\xa4\xe2\xc5\x8e\xc7W;\x05vy%\xaaN\x11\x1c\xb7\
+\xd3\x898\x84O\xc6\x9a\xd8\xd2\x87m\x08\xdbDA\x8b\
+S\x91\xcf\x97;B\xff/\xd6\xf2\x80\xd1,\x08\x09G\
+\xbf#\x17\xfa$\x106\x1e\x91\xb3r\xee\xa4\xd8\x88\xa6\
+(av\xfe\xd1,\xc5\x09\x08\x9bxB\xb0]H\x9e\
+!%\xcf\x97\x0e\xcfV.\xb7\xba\x056\xbb%\x8a\x8e\
+\xd7\xf1\x0e\xd4`z\x08\xd6\xc6V>\x0a \xf21a\
+\x9bF\xe4sL\x87|\xd1h>a\x0d\xf7Z\xcb\xc3\
+X\x96\x11\xd8<\xaeO\x9e\xae\x02\xe8\xf6\xbb\x85\xb9\x02\
+H\x9e\x9d\xef\x98E\x87\x88B\x85*\x82]B\xf2\x0c\
+\xa9\xb8S*nW.{\x95\xcbf\xb7@Uy(\
+\xc7\x8b=\x84\xac)\x05k\xe2/\x1d\xc5\xa3\xb6\x22\x9f\
+(\x0a\xa8\xeb\x80\x13:\xe4Q\xa3\xb9\xd7\x18\xee\xb7\x86\
+\xc3X\x9e\x1e\xdfE\xfb\xe4\x03p\xf2\x07s\xa1O\x92\
+K\x0c\xbe\xb5\x17\x1e\x09\x93+\x80\xf4\xb9\xe9]\xb3\x04\
+MD\xa1FU\x08\xa6\x84dFH\x0eJ\xc53\xa4\
+\xc3\x8ct\xd8\xa5\x1c\xc6\x94KM\xb9x\xcaAH\xa7\
+\xa3\x18d\xe7\x0b.\x7f\xba'k\xfc9\x5c\xfe$Q\
+\xdb9\x14\xd4\xc4V\xbd\xeb\xca\x9b\x08L\x84\xd5\x11-\
+\x1d\xd0\xd0\x11gu\xc4\x13V\xf3\x15\xa3y\xd8\x1a\xbe\
+l-\x8fY\xc3\x19kh\x02\x1c{C.\xf0i\xd2\
+\x91w\x8f\xf8\x91\x86\xb9\xb4\x0f\x00\x9b\x7fw\x96\xe9\xdb\
+`\xf9\x04.\x82\xb2\x90L\x0a\xc1\xb4\x10\xdc$$;\
+\x84d\xafTl\x93\x8a-R1&\x14\xa3RQ\x90\
+\x0aOH\x1c)QH\xa4\x10H!8\x7f\xa6\xdf\xaa\
+\xf3\x01\xbbB\x8e\xc5X\x83\xb5\xf1\xff\xb55DF\x13\
+ZC\xdbh\xeaF\xb3d5\xf3\xc62g5'\xac\
+\xe1)c8j-'0\x85@`\xe3\x83\x80\x01\
+\x1f\x8b\x01\x9a\xd6\xd2\xc6\xb2d\xa1\x8e\xe1\x9c\xb5\x9c\x03\
+\xceY\xcb\x925,Z\xcb\xb25\xf8: \xd8\xf7\x0a\
+\xcc\xa7\xff'\xb4~9\x17\xf4A\xe4\xff\x00!\xbf\xbf\
+E\xaax=\x13\x00\x00\x00\x00IEND\xaeB`\
\x82\
"
qt_resource_name = b"\
\x00\x09\
-\x0a\x6c\x78\x43\
-\x00\x72\
-\x00\x65\x00\x73\x00\x6f\x00\x75\x00\x72\x00\x63\x00\x65\x00\x73\
-\x00\x10\
-\x0a\x2c\xb7\x07\
-\x00\x64\
-\x00\x6f\x00\x6e\x00\x61\x00\x74\x00\x65\x00\x62\x00\x75\x00\x74\x00\x74\x00\x6f\x00\x6e\x00\x2e\x00\x70\x00\x6e\x00\x67\
+\x0alxC\
+\x00r\
+\x00e\x00s\x00o\x00u\x00r\x00c\x00e\x00s\
+\x00\x13\
+\x0f\xce\x16\xa7\
+\x00b\
+\x00t\x00n\x00_\x00d\x00o\x00n\x00a\x00t\x00e\x00C\x00C\x00_\x00L\x00G\x00.\x00p\
+\x00n\x00g\
\x00\x08\
-\x0a\x61\x42\x7f\
-\x00\x69\
-\x00\x63\x00\x6f\x00\x6e\x00\x2e\x00\x69\x00\x63\x00\x6f\
+\x0aaB\x7f\
+\x00i\
+\x00c\x00o\x00n\x00.\x00i\x00c\x00o\
"
qt_resource_struct = b"\
\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x01\
+\x00\x00\x00\x00\x00\x00\x00\x00\
\x00\x00\x00\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00\x02\
+\x00\x00\x00\x00\x00\x00\x00\x00\
+\x00\x00\x00D\x00\x00\x00\x00\x00\x01\x00\x00\x0a\x91\
+\x00\x00\x01}\x07\xf8'\x92\
\x00\x00\x00\x18\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\
-\x00\x00\x00\x3e\x00\x00\x00\x00\x00\x01\x00\x00\x3b\x83\
+\x00\x00\x01}\x07\xf7U\x91\
"
def qInitResources():
- QtCore.qRegisterResourceData(0x01, qt_resource_struct, qt_resource_name, qt_resource_data)
+ QtCore.qRegisterResourceData(0x03, qt_resource_struct, qt_resource_name, qt_resource_data)
def qCleanupResources():
- QtCore.qUnregisterResourceData(0x01, qt_resource_struct, qt_resource_name, qt_resource_data)
+ QtCore.qUnregisterResourceData(0x03, qt_resource_struct, qt_resource_name, qt_resource_data)
qInitResources()
diff --git a/src/screen_region.py b/src/screen_region.py
index 9c9d382a..8bb50e9c 100644
--- a/src/screen_region.py
+++ b/src/screen_region.py
@@ -1,16 +1,15 @@
-from PyQt4 import QtGui, QtCore, QtTest
-import ctypes
-import ctypes.wintypes
-import win32gui
-import cv2
+from PyQt6 import QtCore, QtGui, QtTest, QtWidgets
+from win32 import win32gui
+from typing import cast, Callable
import capture_windows
-from PyQt4 import QtGui, QtCore, QtTest
import ctypes
import ctypes.wintypes
-import win32gui
import cv2
import numpy as np
+import error_messages
+
+
def selectRegion(self):
# Create a screen selector widget
selector = SelectRegionWidget()
@@ -22,7 +21,7 @@ def selectRegion(self):
# return an error if width or height are zero.
if selector.width == 0 or selector.height == 0:
- self.regionSizeError()
+ error_messages.regionSizeError()
return
# Width and Height of the spinBox
@@ -30,11 +29,11 @@ def selectRegion(self):
self.heightSpinBox.setValue(selector.height)
# Grab the window handle from the coordinates selected by the widget
- self.hwnd = win32gui.WindowFromPoint((selector.left, selector.top))
+ self.hwnd = cast(int, win32gui.WindowFromPoint((selector.left, selector.top)))
# Want to pull the parent window from the window handle
# By using GetAncestor we are able to get the parent window instead
# of the owner window.
- GetAncestor = ctypes.windll.user32.GetAncestor
+ GetAncestor = cast(Callable[[int, int], int], ctypes.windll.user32.GetAncestor)
GA_ROOT = 2
while win32gui.IsChild(win32gui.GetParent(self.hwnd), self.hwnd):
@@ -75,6 +74,7 @@ def selectRegion(self):
# check if live image needs to be turned on or just set a single image
self.checkLiveImage()
+
def selectWindow(self):
# Create a screen selector widget
selector = SelectWindowWidget()
@@ -85,10 +85,9 @@ def selectWindow(self):
QtTest.QTest.qWait(1)
# Grab the window handle from the coordinates selected by the widget
- self.hwnd = None
- self.hwnd = win32gui.WindowFromPoint((selector.x, selector.y))
+ self.hwnd = cast(int, win32gui.WindowFromPoint((selector.x, selector.y)))
- if self.hwnd is None:
+ if self.hwnd == 0:
return
del selector
@@ -96,7 +95,7 @@ def selectWindow(self):
# Want to pull the parent window from the window handle
# By using GetAncestor we are able to get the parent window instead
# of the owner window.
- GetAncestor = ctypes.windll.user32.GetAncestor
+ GetAncestor = cast(Callable[[int, int], int], ctypes.windll.user32.GetAncestor)
GA_ROOT = 2
while win32gui.IsChild(win32gui.GetParent(self.hwnd), self.hwnd):
self.hwnd = GetAncestor(self.hwnd, GA_ROOT)
@@ -122,90 +121,94 @@ def selectWindow(self):
self.checkLiveImage()
+
def alignRegion(self):
- # check to see if a region has been set
- if self.hwnd == 0 or win32gui.GetWindowText(self.hwnd) == '':
- self.regionError()
- return
- # This is the image used for aligning the capture region
- # to the best fit for the user.
- template_filename = str(QtGui.QFileDialog.getOpenFileName(self, "Select Reference Image", "",
- "Image Files (*.png *.jpg *.jpeg *.jpe *.jp2 *.bmp *.tiff *.tif *.dib *.webp *.pbm *.pgm *.ppm *.sr *.ras)"))
-
- # return if the user presses cancel
- if template_filename == '':
- return
-
- template = cv2.imread(template_filename, cv2.IMREAD_COLOR)
-
- # shouldn't need this, but just for caution, throw a type error if file is not a valid image file
- if template is None:
- self.alignRegionImageTypeError()
- return
-
- # Obtaining the capture of a region which contains the
- # subregion being searched for to align the image.
- capture = capture_windows.capture_region(self.hwnd, self.rect)
- capture = cv2.cvtColor(capture, cv2.COLOR_BGRA2BGR)
-
- # Obtain the best matching point for the template within the
- # capture. This assumes that the template is actually smaller
- # than the dimensions of the capture. Since we are using SQDIFF
- # the best match will be the min_val which is located at min_loc.
- # The best match found in the image, set everything to 0 by default
- # so that way the first match will overwrite these values
- best_match = 0.0
- best_height = 0
- best_width = 0
- best_loc = (0, 0)
-
- # This tests 50 images scaled from 20% to 300% of the original template size
- for scale in np.linspace(0.2, 3, num=56):
- width = int(template.shape[1] * scale)
- height = int(template.shape[0] * scale)
-
- # The template can not be larger than the capture
- if width > capture.shape[1] or height > capture.shape[0]:
- continue
-
- resized = cv2.resize(template, (width, height))
-
- result = cv2.matchTemplate(capture, resized, cv2.TM_SQDIFF)
- min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
-
- # The maximum value for SQ_DIFF is dependent on the size of the template
- # we need this value to normalize it from 0.0 to 1.0
- max_error = resized.size * 255 * 255
- similarity = 1 - (min_val / max_error)
-
- # Check if the similarity was good enough to get alignment
- if similarity > best_match:
- best_match = similarity
- best_width = width
- best_height = height
- best_loc = min_loc
-
- # Go ahead and check if this satisfies our requirement before setting the region
- # We don't want a low similarity image to be aligned.
- if best_match < 0.9:
- self.alignmentNotMatchedError()
- return
-
- # The new region can be defined by using the min_loc point and the
- # height and width of the template.
- self.rect.left = self.rect.left + best_loc[0]
- self.rect.top = self.rect.top + best_loc[1]
- self.rect.right = self.rect.left + best_width
- self.rect.bottom = self.rect.top + best_height
-
- self.xSpinBox.setValue(self.rect.left)
- self.ySpinBox.setValue(self.rect.top)
- self.widthSpinBox.setValue(best_width)
- self.heightSpinBox.setValue(best_height)
+ # check to see if a region has been set
+ if self.hwnd == 0 or win32gui.GetWindowText(self.hwnd) == '':
+ error_messages.regionError()
+ return
+ # This is the image used for aligning the capture region
+ # to the best fit for the user.
+ template_filename = str(QtWidgets.QFileDialog.getOpenFileName(
+ self,
+ "Select Reference Image",
+ "",
+ "Image Files (*.png *.jpg *.jpeg *.jpe *.jp2 *.bmp *.tiff *.tif *.dib *.webp *.pbm *.pgm *.ppm *.sr *.ras)"))
+
+ # return if the user presses cancel
+ if template_filename == '':
+ return
+
+ template = cv2.imread(template_filename, cv2.IMREAD_COLOR)
+
+ # shouldn't need this, but just for caution, throw a type error if file is not a valid image file
+ if template is None:
+ error_messages.alignRegionImageTypeError()
+ return
+
+ # Obtaining the capture of a region which contains the
+ # subregion being searched for to align the image.
+ capture = capture_windows.capture_region(self.hwnd, self.rect)
+ capture = cv2.cvtColor(capture, cv2.COLOR_BGRA2BGR)
+
+ # Obtain the best matching point for the template within the
+ # capture. This assumes that the template is actually smaller
+ # than the dimensions of the capture. Since we are using SQDIFF
+ # the best match will be the min_val which is located at min_loc.
+ # The best match found in the image, set everything to 0 by default
+ # so that way the first match will overwrite these values
+ best_match = 0.0
+ best_height = 0
+ best_width = 0
+ best_loc = (0, 0)
+
+ # This tests 50 images scaled from 20% to 300% of the original template size
+ for scale in np.linspace(0.2, 3, num=56):
+ width = int(template.shape[1] * scale)
+ height = int(template.shape[0] * scale)
+
+ # The template can not be larger than the capture
+ if width > capture.shape[1] or height > capture.shape[0]:
+ continue
+
+ resized = cv2.resize(template, (width, height))
+
+ result = cv2.matchTemplate(capture, resized, cv2.TM_SQDIFF)
+ min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
+
+ # The maximum value for SQ_DIFF is dependent on the size of the template
+ # we need this value to normalize it from 0.0 to 1.0
+ max_error = resized.size * 255 * 255
+ similarity = 1 - (min_val / max_error)
+
+ # Check if the similarity was good enough to get alignment
+ if similarity > best_match:
+ best_match = similarity
+ best_width = width
+ best_height = height
+ best_loc = min_loc
+
+ # Go ahead and check if this satisfies our requirement before setting the region
+ # We don't want a low similarity image to be aligned.
+ if best_match < 0.9:
+ error_messages.alignmentNotMatchedError()
+ return
+
+ # The new region can be defined by using the min_loc point and the
+ # height and width of the template.
+ self.rect.left = self.rect.left + best_loc[0]
+ self.rect.top = self.rect.top + best_loc[1]
+ self.rect.right = self.rect.left + best_width
+ self.rect.bottom = self.rect.top + best_height
+
+ self.xSpinBox.setValue(self.rect.left)
+ self.ySpinBox.setValue(self.rect.top)
+ self.widthSpinBox.setValue(best_width)
+ self.heightSpinBox.setValue(best_height)
# widget to select a window and obtain its bounds
-class SelectWindowWidget(QtGui.QWidget):
+class SelectWindowWidget(QtWidgets.QWidget):
def __init__(self):
super(SelectWindowWidget, self).__init__()
user32 = ctypes.windll.user32
@@ -226,17 +229,18 @@ def __init__(self):
self.setWindowTitle(' ')
self.setWindowOpacity(0.5)
- self.setWindowFlags(QtCore.Qt.FramelessWindowHint)
+ self.setWindowFlags(QtCore.Qt.WindowType.FramelessWindowHint)
self.show()
- def mouseReleaseEvent(self, event):
+ def mouseReleaseEvent(self, event: QtGui.QMouseEvent):
self.close()
- self.x = event.pos().x()
- self.y = event.pos().y()
+ self.x = int(event.position().x())
+ self.y = int(event.position().y())
+
# Widget for dragging screen region
# https://github.com/harupy/snipping-tool
-class SelectRegionWidget(QtGui.QWidget):
+class SelectRegionWidget(QtWidgets.QWidget):
def __init__(self):
super(SelectRegionWidget, self).__init__()
user32 = ctypes.windll.user32
@@ -259,36 +263,36 @@ def __init__(self):
self.begin = QtCore.QPoint()
self.end = QtCore.QPoint()
self.setWindowOpacity(0.5)
- QtGui.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.CrossCursor))
- self.setWindowFlags(QtCore.Qt.FramelessWindowHint)
+ QtWidgets.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.CursorShape.CrossCursor))
+ self.setWindowFlags(QtCore.Qt.WindowType.FramelessWindowHint)
self.show()
- def paintEvent(self, event):
+ def paintEvent(self, event: QtGui.QPaintEvent):
qp = QtGui.QPainter(self)
qp.setPen(QtGui.QPen(QtGui.QColor('red'), 2))
qp.setBrush(QtGui.QColor('opaque'))
qp.drawRect(QtCore.QRect(self.begin, self.end))
- def mousePressEvent(self, event):
- self.begin = event.pos()
+ def mousePressEvent(self, event: QtGui.QMouseEvent):
+ self.begin = event.position().toPoint()
self.end = self.begin
self.update()
- def mouseMoveEvent(self, event):
- self.end = event.pos()
+ def mouseMoveEvent(self, event: QtGui.QMouseEvent):
+ self.end = event.position().toPoint()
self.update()
- def mouseReleaseEvent(self, event):
- QtGui.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.ArrowCursor))
+ def mouseReleaseEvent(self, event: QtGui.QMouseEvent):
+ QtWidgets.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.CursorShape.ArrowCursor))
self.close()
# The coordinates are pulled relative to the top left of the set geometry,
# so the added virtual screen offsets convert them back to the virtual
# screen coordinates
- self.left = min(self.begin.x(), self.end.x()) + self.SM_XVIRTUALSCREEN
- self.top = min(self.begin.y(), self.end.y()) + self.SM_YVIRTUALSCREEN
- self.right = max(self.begin.x(), self.end.x()) + self.SM_XVIRTUALSCREEN
- self.bottom = max(self.begin.y(), self.end.y()) + self.SM_YVIRTUALSCREEN
+ self.left = int(min(self.begin.x(), self.end.x()) + self.SM_XVIRTUALSCREEN)
+ self.top = int(min(self.begin.y(), self.end.y()) + self.SM_YVIRTUALSCREEN)
+ self.right = int(max(self.begin.x(), self.end.x()) + self.SM_XVIRTUALSCREEN)
+ self.bottom = int(max(self.begin.y(), self.end.y()) + self.SM_YVIRTUALSCREEN)
self.height = self.bottom - self.top
- self.width = self.right - self.left
\ No newline at end of file
+ self.width = self.right - self.left
diff --git a/src/settings_file.py b/src/settings_file.py
index 6e7e270d..b4d20769 100644
--- a/src/settings_file.py
+++ b/src/settings_file.py
@@ -1,8 +1,13 @@
+from win32 import win32gui
+from PyQt6 import QtWidgets
+import glob
import keyboard
-import win32gui
import pickle
-import glob
-from PyQt4 import QtGui
+import logging
+
+from hotkeys import _hotkey_action
+import error_messages
+
def getSaveSettingsValues(self):
# get values to be able to save settings
@@ -21,16 +26,6 @@ def getSaveSettingsValues(self):
self.undo_split_key = str(self.undosplitLineEdit.text())
self.pause_key = str(self.pausehotkeyLineEdit.text())
- if self.custompausetimesCheckBox.isChecked():
- self.custom_pause_times_setting = 1
- else:
- self.custom_pause_times_setting = 0
-
- if self.customthresholdsCheckBox.isChecked():
- self.custom_thresholds_setting = 1
- else:
- self.custom_thresholds_setting = 0
-
if self.groupDummySplitsCheckBox.isChecked():
self.group_dummy_splits_undo_skip_setting = 1
else:
@@ -48,12 +43,27 @@ def getSaveSettingsValues(self):
def haveSettingsChanged(self):
self.getSaveSettingsValues()
- self.current_save_settings = [self.split_image_directory, self.similarity_threshold, self.comparison_index, self.pause,
- self.fps_limit, self.split_key,
- self.reset_key, self.skip_split_key, self.undo_split_key, self.pause_key, self.x, self.y, self.width, self.height,
- self.hwnd_title,
- self.custom_pause_times_setting, self.custom_thresholds_setting,
- self.group_dummy_splits_undo_skip_setting, self.loop_setting, self.auto_start_on_reset_setting]
+ self.current_save_settings = [
+ self.split_image_directory,
+ self.similarity_threshold,
+ self.comparison_index,
+ self.pause,
+ self.fps_limit,
+ self.split_key,
+ self.reset_key,
+ self.skip_split_key,
+ self.undo_split_key,
+ self.pause_key,
+ self.x,
+ self.y,
+ self.width,
+ self.height,
+ self.hwnd_title,
+ 0,
+ 0,
+ self.group_dummy_splits_undo_skip_setting,
+ self.loop_setting,
+ self.auto_start_on_reset_setting]
#one small caveat in this: if you load a settings file from an old version, but dont change settings,
#the current save settings and last load settings will have different # of elements and it will ask
@@ -68,33 +78,62 @@ def saveSettings(self):
self.saveSettingsAs()
else:
self.getSaveSettingsValues()
- self.last_saved_settings = [self.split_image_directory, self.similarity_threshold, self.comparison_index,
- self.pause,
- self.fps_limit, self.split_key,
- self.reset_key, self.skip_split_key, self.undo_split_key, self.pause_key, self.x,
- self.y, self.width, self.height,
- self.hwnd_title,
- self.custom_pause_times_setting, self.custom_thresholds_setting,
- self.group_dummy_splits_undo_skip_setting, self.loop_setting, self.auto_start_on_reset_setting]
+ self.last_saved_settings = [
+ self.split_image_directory,
+ self.similarity_threshold,
+ self.comparison_index,
+ self.pause,
+ self.fps_limit,
+ self.split_key,
+ self.reset_key,
+ self.skip_split_key,
+ self.undo_split_key,
+ self.pause_key,
+ self.x,
+ self.y,
+ self.width,
+ self.height,
+ self.hwnd_title,
+ 0,
+ 0,
+ self.group_dummy_splits_undo_skip_setting,
+ self.loop_setting,
+ self.auto_start_on_reset_setting]
# save settings to a .pkl file
with open(self.last_successfully_loaded_settings_file_path, 'wb') as f:
pickle.dump(self.last_saved_settings, f)
+
def saveSettingsAs(self):
- # user picks save destination
- self.save_settings_file_path = str(QtGui.QFileDialog.getSaveFileName(self, "Save Settings As", "", "PKL (*.pkl)"))
+ # User picks save destination
+ self.save_settings_file_path = QtWidgets.QFileDialog.getSaveFileName(self, "Save Settings As", "", "PKL (*.pkl)")[0]
- #if user cancels save destination window, don't save settings
- if self.save_settings_file_path == '':
+ # If user cancels save destination window, don't save settings
+ if not self.save_settings_file_path:
return
self.getSaveSettingsValues()
- self.last_saved_settings = [self.split_image_directory, self.similarity_threshold, self.comparison_index, self.pause,
- self.fps_limit, self.split_key,
- self.reset_key, self.skip_split_key, self.undo_split_key, self.pause_key, self.x, self.y, self.width, self.height,
- self.hwnd_title,
- self.custom_pause_times_setting, self.custom_thresholds_setting,
- self.group_dummy_splits_undo_skip_setting, self.loop_setting, self.auto_start_on_reset_setting]
+ self.last_saved_settings = [
+ self.split_image_directory,
+ self.similarity_threshold,
+ self.comparison_index,
+ self.pause,
+ self.fps_limit,
+ self.split_key,
+ self.reset_key,
+ self.skip_split_key,
+ self.undo_split_key,
+ self.pause_key,
+ self.x,
+ self.y,
+ self.width,
+ self.height,
+ self.hwnd_title,
+ 0,
+ 0,
+ self.group_dummy_splits_undo_skip_setting,
+ self.loop_setting,
+ self.auto_start_on_reset_setting]
# save settings to a .pkl file
with open(self.save_settings_file_path, 'wb') as f:
@@ -106,21 +145,28 @@ def saveSettingsAs(self):
def loadSettings(self):
- if self.load_settings_on_open == True:
+ # hotkeys need to be initialized to be passed as thread arguments in hotkeys.py
+ self.split_hotkey = ""
+ self.reset_hotkey = ""
+ self.skip_split_hotkey = ""
+ self.undo_split_hotkey = ""
+ self.pause_hotkey = ""
+
+ if self.load_settings_on_open:
self.settings_files = glob.glob("*.pkl")
if len(self.settings_files) < 1:
- self.noSettingsFileOnOpenError()
+ error_messages.noSettingsFileOnOpenError()
self.last_loaded_settings = None
return
elif len(self.settings_files) > 1:
- self.tooManySettingsFilesOnOpenError()
+ error_messages.tooManySettingsFilesOnOpenError()
self.last_loaded_settings = None
return
else:
self.load_settings_file_path = self.settings_files[0]
else:
- self.load_settings_file_path = str(QtGui.QFileDialog.getOpenFileName(self, "Load Settings", "", "PKL (*.pkl)"))
+ self.load_settings_file_path = str(QtWidgets.QFileDialog.getOpenFileName(self, "Load Settings", "", "PKL (*.pkl)"))
#
if self.load_settings_file_path == '':
@@ -132,25 +178,53 @@ def loadSettings(self):
#v1.5 settings
if self.settings_count == 20:
with open(self.load_settings_file_path, 'rb') as f:
- self.last_loaded_settings = [self.split_image_directory, self.similarity_threshold, self.comparison_index, self.pause,
- self.fps_limit, self.split_key,
- self.reset_key, self.skip_split_key, self.undo_split_key, self.pause_key, self.x, self.y, self.width, self.height,
- self.hwnd_title,
- self.custom_pause_times_setting, self.custom_thresholds_setting,
- self.group_dummy_splits_undo_skip_setting, self.loop_setting, self.auto_start_on_reset_setting] = pickle.load(f)
- #v1.3-1.4 settings. add a blank pause key.
+ self.last_loaded_settings = [
+ self.split_image_directory,
+ self.similarity_threshold,
+ self.comparison_index,
+ self.pause,
+ self.fps_limit,
+ self.split_key,
+ self.reset_key,
+ self.skip_split_key,
+ self.undo_split_key,
+ self.pause_key,
+ self.x,
+ self.y,
+ self.width,
+ self.height,
+ self.hwnd_title,
+ 0,
+ 0,
+ self.group_dummy_splits_undo_skip_setting,
+ self.loop_setting,
+ self.auto_start_on_reset_setting] = pickle.load(f)
+ # v1.3-1.4 settings. add a blank pause key.
elif self.settings_count == 18:
with open(self.load_settings_file_path, 'rb') as f:
- self.last_loaded_settings = [self.split_image_directory, self.similarity_threshold, self.comparison_index, self.pause,
- self.fps_limit, self.split_key,
- self.reset_key, self.skip_split_key, self.undo_split_key, self.x, self.y, self.width, self.height,
- self.hwnd_title,
- self.custom_pause_times_setting, self.custom_thresholds_setting,
- self.group_dummy_splits_undo_skip_setting, self.loop_setting] = pickle.load(f)
+ self.last_loaded_settings = [
+ self.split_image_directory,
+ self.similarity_threshold,
+ self.comparison_index,
+ self.pause,
+ self.fps_limit,
+ self.split_key,
+ self.reset_key,
+ self.skip_split_key,
+ self.undo_split_key,
+ self.x,
+ self.y,
+ self.width,
+ self.height,
+ self.hwnd_title,
+ 0,
+ 0,
+ self.group_dummy_splits_undo_skip_setting,
+ self.loop_setting] = pickle.load(f)
self.pause_key = ''
self.auto_start_on_reset_setting = 0
elif self.settings_count < 18:
- self.oldVersionSettingsFileError()
+ error_messages.oldVersionSettingsFileError()
return
self.split_image_directory = str(self.split_image_directory)
@@ -166,101 +240,68 @@ def loadSettings(self):
self.hwnd = win32gui.FindWindow(None, self.hwnd_title)
# set custom checkbox's accordingly
- if self.custom_pause_times_setting == 1:
- self.custompausetimesCheckBox.setChecked(True)
- else:
- self.custompausetimesCheckBox.setChecked(False)
-
- if self.custom_thresholds_setting == 1:
- self.customthresholdsCheckBox.setChecked(True)
- else:
- self.customthresholdsCheckBox.setChecked(False)
-
- if self.group_dummy_splits_undo_skip_setting == 1:
- self.groupDummySplitsCheckBox.setChecked(True)
- else:
- self.groupDummySplitsCheckBox.setChecked(False)
-
- if self.loop_setting == 1:
- self.loopCheckBox.setChecked(True)
- else:
- self.loopCheckBox.setChecked(False)
-
- if self.auto_start_on_reset_setting == 1:
- self.autostartonresetCheckBox.setChecked(True)
- else:
- self.autostartonresetCheckBox.setChecked(False)
+ self.groupDummySplitsCheckBox.setChecked(self.group_dummy_splits_undo_skip_setting == 1)
+ self.loopCheckBox.setChecked(self.loop_setting == 1)
+ self.autostartonresetCheckBox.setChecked(self.auto_start_on_reset_setting == 1)
+ self.autostartonresetCheckBox.setChecked(self.auto_start_on_reset_setting == 1)
+ # TODO: Reuse code from hotkeys rather than duplicating here
# try to set hotkeys from when user last closed the window
try:
- try:
- keyboard.remove_hotkey(self.split_hotkey)
- except AttributeError:
- pass
- self.splitLineEdit.setText(str(self.split_key))
- self.split_hotkey = keyboard.add_hotkey(str(self.split_key), self.startAutoSplitter)
- self.old_split_key = self.split_key
+ keyboard.unhook_key(self.split_hotkey)
# pass if the key is an empty string (hotkey was never set)
- except ValueError:
+ except (AttributeError, KeyError):
pass
- except KeyError:
+ try:
+ if not self.is_auto_controlled:
+ self.splitLineEdit.setText(self.split_key)
+ self.split_hotkey = keyboard.hook_key(str(self.split_key), lambda e: _hotkey_action(e, self.split_key, self.startAutoSplitter))
+ except (ValueError, KeyError):
pass
try:
- try:
- keyboard.remove_hotkey(self.reset_hotkey)
- except AttributeError:
- pass
- self.resetLineEdit.setText(str(self.reset_key))
- self.reset_hotkey = keyboard.add_hotkey(str(self.reset_key), self.startReset)
- self.old_reset_key = self.reset_key
- except ValueError:
+ keyboard.unhook_key(self.reset_hotkey)
+ except (AttributeError, KeyError):
pass
- except KeyError:
+ try:
+ self.resetLineEdit.setText(self.reset_key)
+ self.reset_hotkey = keyboard.hook_key(self.reset_key, lambda e: _hotkey_action(e, self.reset_key, self.startReset))
+ except (ValueError, KeyError):
pass
try:
- try:
- keyboard.remove_hotkey(self.skip_split_hotkey)
- except AttributeError:
- pass
- self.skipsplitLineEdit.setText(str(self.skip_split_key))
- self.skip_split_hotkey = keyboard.add_hotkey(str(self.skip_split_key), self.startSkipSplit)
- self.old_skip_split_key = self.skip_split_key
- except ValueError:
+ keyboard.unhook_key(self.skip_split_hotkey)
+ except (AttributeError, KeyError):
pass
- except KeyError:
+ try:
+ self.skipsplitLineEdit.setText(self.skip_split_key)
+ self.skip_split_hotkey = keyboard.hook_key(self.skip_split_key, lambda e: _hotkey_action(e, self.skip_split_key, self.startSkipSplit))
+ except (ValueError, KeyError):
pass
try:
- try:
- keyboard.remove_hotkey(self.undo_split_hotkey)
- except AttributeError:
- pass
- self.undosplitLineEdit.setText(str(self.undo_split_key))
- self.undo_split_hotkey = keyboard.add_hotkey(str(self.undo_split_key), self.startUndoSplit)
- self.old_undo_split_key = self.undo_split_key
- except ValueError:
+ keyboard.unhook_key(self.undo_split_hotkey)
+ except (AttributeError, KeyError):
pass
- except KeyError:
+ try:
+ self.undosplitLineEdit.setText(self.undo_split_key)
+ self.undo_split_hotkey = keyboard.hook_key(self.undo_split_key, lambda e: _hotkey_action(e, self.undo_split_key, self.startUndoSplit))
+ except (ValueError, KeyError):
pass
try:
- try:
- keyboard.remove_hotkey(self.pause_hotkey)
- except AttributeError:
- pass
- self.pausehotkeyLineEdit.setText(str(self.pause_key))
- self.pause_hotkey = keyboard.add_hotkey(str(self.pause_key), self.startPause)
- self.old_pause_key = self.pause_key
- except ValueError:
+ keyboard.unhook_key(self.pause_hotkey)
+ except (AttributeError, KeyError):
pass
- except KeyError:
+ try:
+ self.pausehotkeyLineEdit.setText(self.pause_key)
+ self.pause_hotkey = keyboard.hook_key(self.pause_key, lambda e: _hotkey_action(e, self.pause_key, self.startPause))
+ except (ValueError, KeyError):
pass
self.last_successfully_loaded_settings_file_path = self.load_settings_file_path
self.checkLiveImage()
except Exception:
- self.invalidSettingsError()
- pass
+ logging.error(logging.traceback.format_exc())
+ error_messages.invalidSettingsError()
diff --git a/src/split_parser.py b/src/split_parser.py
index 2b6d9bc1..74d9fe42 100644
--- a/src/split_parser.py
+++ b/src/split_parser.py
@@ -6,7 +6,7 @@ def threshold_from_filename(filename):
@param filename: String containing the file's name
@return: A valid threshold, if not then None
"""
-
+
# Check to make sure there is a valid floating point number between
# parentheses of the filename
try:
@@ -97,7 +97,6 @@ def flags_from_filename(filename):
"""
List of flags:
'd' = dummy, do nothing when this split is found
- 'm' = mask, use a mask when comparing this split
'b' = below threshold, after threshold is met, split when it goes below the threhsold.
'p' = pause, hit pause key when this split is found
"""
@@ -110,17 +109,14 @@ def flags_from_filename(filename):
return 0
DUMMY_FLAG = 1 << 0
- MASK_FLAG = 1 << 1
BELOW_FLAG = 1 << 2
PAUSE_FLAG = 1 << 3
flags = 0x00
-
+
for c in flags_str:
if c.upper() == 'D':
flags |= DUMMY_FLAG
- elif c.upper() == 'M':
- flags |= MASK_FLAG
elif c.upper() == 'B':
flags |= BELOW_FLAG
elif c.upper() == 'P':
@@ -134,7 +130,7 @@ def flags_from_filename(filename):
# For instance, we can't have a dummy split also pause
if (flags & DUMMY_FLAG == DUMMY_FLAG) and (flags & PAUSE_FLAG == PAUSE_FLAG):
return 0
-
+
return flags
def is_reset_image(filename):
@@ -144,4 +140,4 @@ def is_reset_image(filename):
@param filename: String containing the file's name
@return: True if its a reset image
"""
- return ('RESET' in filename.upper())
\ No newline at end of file
+ return ('RESET' in filename.upper())