# HG changeset patch # User Silverwing # Date 2019-02-24 09:17:13 # Node ID b168d439d9a0c72e7c1fba1bc7961640bf5e9242 initial commit. Version 1.0 diff --git a/LICENSE b/LICENSE new file mode 100644 --- /dev/null +++ b/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/README b/README new file mode 100644 --- /dev/null +++ b/README @@ -0,0 +1,7 @@ +GUI utility for managing your virtual CD/DVD drives +Compatible with CDEmu>=3.1.0 +Uses Qt5 https://www.qt.io/ +Uses PyQt5 https://www.riverbankcomputing.com/software/pyqt/intro +Built-in icons by http://www.fatcow.com/free-icons +This program is licensed with GNU GPL v3 (see ./LICENSE for details) +This program is available on http://silverwing.one/pages/kcdemu diff --git a/__main__.py b/__main__.py new file mode 100644 --- /dev/null +++ b/__main__.py @@ -0,0 +1,50 @@ +from config.config import ConfigFile +from gui.tray import TrayManager +from gui.dropper import KCDEmuDropperWidget +from cdemu import CDEmu +from PyQt5 import QtWidgets, QtCore +import sys, os, fcntl, signal +from dbus.mainloop.pyqt5 import DBusQtMainLoop + +# noinspection PyUnresolvedReferences +import pics + +DBusQtMainLoop(set_as_default=True) + +app = QtWidgets.QApplication(sys.argv) +app.setQuitOnLastWindowClosed(False) + + +def handle_signal_wakeup(): + notifier.setEnabled(False) + read_fd = notifier.socket() + try: + os.read(read_fd, 1) + except OSError: + print("Failed to read wakeup fd.") + notifier.setEnabled(True) + + +def destroy(a, b): + app.exit(128 + a) + + +read_fd, write_fd = os.pipe() +for fd in (read_fd, write_fd): + flags = fcntl.fcntl(fd, fcntl.F_GETFL) + fcntl.fcntl(fd, fcntl.F_SETFL, flags | os.O_NONBLOCK) + +notifier = QtCore.QSocketNotifier(read_fd, QtCore.QSocketNotifier.Read) +notifier.activated.connect(handle_signal_wakeup) +orig_wakeup_fd = signal.set_wakeup_fd(write_fd) +signal.signal(signal.SIGTERM, destroy) +signal.signal(signal.SIGINT, destroy) + +config_path = os.environ.get('XDG_CONFIG_HOME', default=os.path.expanduser('~/.config/')) +os.makedirs(os.path.join(config_path, 'silvertools'), exist_ok=True) + +config = ConfigFile(os.path.join(config_path, 'silvertools', 'kcdemu_config.cfg')) + +cdemu = CDEmu(os.path.join(config_path, 'silvertools', 'kcdemu_state.cfg'), config) +tm = TrayManager(cdemu, config, KCDEmuDropperWidget(cdemu, config)) +sys.exit(app.exec()) diff --git a/build.sh b/build.sh new file mode 100755 --- /dev/null +++ b/build.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash +VERSION="1.0.0" + +pyrcc5 pics/pics.qrc -o pics/__init__.py +rm kcdemu.zip +zip -r kcdemu.zip ./ -x \*.png \*.ui .hg\* .idea\* build.sh +mkdir build +mkdir build/temp + +# Debian/Ubuntu +mkdir build/temp/debian +cp -r packaging/debian/* build/temp/debian +cp kcdemu.zip build/temp/debian/opt/kcdemu/ +CONTROL=`cat build/temp/debian/DEBIAN/control` +CONTROL=`echo "${CONTROL//%VERSION%/$VERSION}"` +SIZE="$(du -sk --apparent-size build/temp | awk '{print $1}')" +CONTROL=`echo "${CONTROL//%SIZE%/$SIZE}"` +echo "$CONTROL" > build/temp/debian/DEBIAN/control +pushd build/temp +fakeroot dpkg-deb --build debian +popd +mv build/temp/debian.deb build/kcdemu-${VERSION}.deb +# Cleanup +rm -rf build/temp diff --git a/cdemu/__init__.py b/cdemu/__init__.py new file mode 100644 --- /dev/null +++ b/cdemu/__init__.py @@ -0,0 +1,231 @@ +from event.sync import EventDispatcher +from configparser import ConfigParser +import os +from .device import CDEmuDevice +from .proxy import CDEmuDaemonProxy, OP_DEV_ID, OP_DPM_EMU, OP_TR_EMU, OP_BS_EMU +from mount import mount + + +class CDEmuException(Exception): + """ + Exception class that encompasses exceptions happening while interacting with CDEmu class + """ + pass + + +class CDEmu(EventDispatcher): + def __init__(self, state_file=None, config=None): + """ + High-level abstraction for CDEmu daemon. Includes following features: + 1) Automatically restart daemon if it is stopped(fails?) + 2) Automatically unmount device before ejecting or removing it + 3) Automatically restore daemon state on startup + """ + super().__init__( + 'daemon_started', + 'daemon_stopped', + 'device_status_changed', + 'device_option_changed', + 'devices_changed' + ) + + self.devices = [] + + self.state_file = state_file + self.state = ConfigParser() + if state_file and os.path.isfile(state_file): + self.state.read(state_file) + + if not self.state.has_section('main'): + self.change_option('main', 'devices', '1') + self.change_option('main', 'autorestart', '1') + self.change_option('dev0', 'dpm-emulation', '0') + self.change_option('dev0', 'tr-emulation', '0') + self.change_option('dev0', 'bad-sector-emulation', '0') + self.save_options() + + self.daemon = CDEmuDaemonProxy() + self.daemon.add_handler('daemon_started', self.on_daemon_started) + self.daemon.add_handler('daemon_stopped', self.on_daemon_stopped) + self.daemon.add_handler('device_mapping_ready', self.on_device_mapping_ready) + self.daemon.add_handler('device_added', self.on_device_added) + self.daemon.add_handler('device_removed', self.on_device_removed) + self.daemon.add_handler('device_status_changed', self.on_device_status_changed) + self.daemon.add_handler('device_option_changed', self.on_device_option_changed) + if self.daemon.is_running: + self.on_daemon_started() + + def get_option(self, section, option): + if not self.state.has_section(section): + return None + if option not in self.state[section]: + return None + value = self.state[section][option] + if value.startswith('"'): + value = value[1:-1] + return value + + def change_option(self, section, option, value, save=False): + value = str(value) + if value.startswith(' ') or value.startswith('"') or value.endswith(' '): + value = '"{}"'.format(value) + if not self.state.has_section(section): + self.state.add_section(section) + self.state.set(section, option, value) + if save and self.state_file: + with open(self.state_file, 'w') as f: + self.state.write(f) + + def save_options(self): + if self.state_file: + with open(self.state_file, 'w') as f: + self.state.write(f) + + def on_daemon_started(self): + self.dispatch('daemon_started') + need_devices = int(self.get_option('main', 'devices')) + have_devices = self.daemon.get_number_of_devices() + while have_devices < need_devices: + self.daemon.add_device() + have_devices += 1 + while have_devices > need_devices: + self.daemon.remove_device() + have_devices -= 1 + for x in range(need_devices): + if len(self.devices) <= x: + self.devices.append(self._create_device(len(self.devices))) + + if self.state.has_section('dev{}'.format(x)): + sect = 'dev{}'.format(x) + else: + sect = 'main' + if self.get_option(sect, OP_DPM_EMU): + self.daemon.device_set_option(x, OP_DPM_EMU, self.get_option(sect, OP_DPM_EMU) == '1') + if self.get_option(sect, OP_TR_EMU): + self.daemon.device_set_option(x, OP_TR_EMU, self.get_option(sect, OP_TR_EMU) == '1') + if self.get_option(sect, OP_BS_EMU): + self.daemon.device_set_option(x, OP_BS_EMU, self.get_option(sect, OP_BS_EMU) == '1') + if self.get_option(sect, 'vendor'): + vendor = self.get_option(sect, 'vendor') + product = self.get_option(sect, 'product') + revision = self.get_option(sect, 'revision') + vendor_specific = self.get_option(sect, 'vendor_specific') + self.daemon.device_set_option(x, OP_DEV_ID, (vendor, product, revision, vendor_specific)) + if self.get_option(sect, 'img'): + self.load(self.devices[x], self.get_option(sect, 'img')) + else: + self.unload(self.devices[x]) + + sr_map, sg_map = self.daemon.device_get_mapping(x) + if sr_map or sg_map: + self.on_device_mapping_ready(x) + + def on_daemon_stopped(self): + self.dispatch('daemon_stopped') + if self.get_option('main', 'autorestart') == '1': + self.daemon.connect() + + def on_device_mapping_ready(self, number): + if number >= len(self.devices): + return + + device = self.devices[number] + device._mapped_sr, device._mapped_sg = self.daemon.device_get_mapping(number) + loaded, img = self.daemon.device_get_status(number) + if loaded and len(img) > 0: + device._img = img[0] + + device._dpm = self.daemon.device_get_option(number, 'dpm-emulation') + device._tr = self.daemon.device_get_option(number, 'tr-emulation') + device._bs = self.daemon.device_get_option(number, 'bad-sector-emulation') + device._vendor, device._product, device._revision, device._vendor_specific = self.daemon.device_get_option( + number, 'device-id') + + section = 'dev{}'.format(number) + self.change_option(section, OP_DPM_EMU, device._dpm) + self.change_option(section, OP_TR_EMU, device._tr) + self.change_option(section, OP_BS_EMU, device._bs) + + self.change_option(section, 'vendor', device._vendor) + self.change_option(section, 'product', device._product) + self.change_option(section, 'revision', device._revision) + self.change_option(section, 'vendor_specific', device._vendor_specific) + self.save_options() + + self.dispatch('devices_changed', number) + + def _create_device(self, id): + device = CDEmuDevice(id, self) + return device + + def load(self, device, img): + self.daemon.device_load(device._device_id, [img], {}) + + def unload(self, device): + try: + if device.mapped_sr and mount.is_mounted(device.mapped_sr): + mount.unmount(device.mapped_sr) + except mount.MountException as e: + raise CDEmuException(str(e)) + self.daemon.device_unload(device._device_id) + + def on_device_added(self): + # Add device and send event + devices = self.daemon.get_number_of_devices() + self.change_option('main', 'devices', devices, True) + while len(self.devices) < devices: + self.devices.append(self._create_device(len(self.devices))) + + def on_device_removed(self): + # Remove device and send event + devices = self.daemon.get_number_of_devices() + while len(self.devices) > devices: + self.devices.pop() + self.dispatch('devices_changed') + + def on_device_status_changed(self, id): + loaded, img = self.daemon.device_get_status(id) + if loaded and len(img) > 0: + self.devices[id]._img = img[0] + self.change_option('dev{}'.format(id), 'img', self.devices[id]._img, True) + else: + self.devices[id]._img = None + self.change_option('dev{}'.format(id), 'img', '', True) + self.devices[id].dispatch('change') + + def on_device_option_changed(self, id, option): + value = self.daemon.device_get_option(id, option) + section = 'dev{}'.format(id) + if option == OP_DPM_EMU: + self.devices[id]._dpm = value + self.change_option(section, OP_DPM_EMU, value) + elif option == OP_TR_EMU: + self.devices[id]._tr = value + self.change_option(section, OP_TR_EMU, value) + elif option == OP_BS_EMU: + self.devices[id]._bs = value + self.change_option(section, OP_BS_EMU, value) + elif option == OP_DEV_ID: + self.devices[id]._vendor, self.devices[id]._product, \ + self.devices[id]._revision, self.devices[id]._vendor_specific = value + self.change_option(section, 'vendor', self.devices[id]._vendor) + self.change_option(section, 'product', self.devices[id]._product) + self.change_option(section, 'revision', self.devices[id]._revision) + self.change_option(section, 'vendor_specific', self.devices[id]._vendor_specific) + self.save_options() + self.devices[id].dispatch('change') + + def set_device_option(self, device_number, option_name, option_value): + self.daemon.device_set_option(device_number, option_name, option_value) + + def add_device(self): + self.daemon.add_device() + + def remove_device(self): + self.daemon.remove_device() + + def get_devices(self): + return self.devices.copy() + + def is_daemon_ready(self): + return self.daemon.is_running \ No newline at end of file diff --git a/cdemu/device.py b/cdemu/device.py new file mode 100644 --- /dev/null +++ b/cdemu/device.py @@ -0,0 +1,100 @@ +from event.sync import EventDispatcher +from .proxy import OP_BS_EMU, OP_DEV_ID, OP_DPM_EMU, OP_TR_EMU + + +class CDEmuDevice(EventDispatcher): + def __init__(self, device_id, cdemu): + super().__init__( + 'change' + ) + self.cdemu = cdemu + self._device_id = device_id + self._mapped_sr = None + self._mapped_sg = None + self._img = False + self._dpm = False + self._tr = False + self._bs = False + self._vendor = None + self._product = None + self._revision = None + self._vendor_specific = None + + def get_name(self): + return 'Device {}'.format(self._device_id) + + @property + def mapped_sr(self): + return self._mapped_sr + + @property + def mapped_sg(self): + return self._mapped_sg + + def get_img(self): + return self._img + + def load(self, img): + self.cdemu.load(self, img) + + def unload(self): + self.cdemu.unload(self) + + def get_dpm(self): + """ + get current dpm emulation state + :return: True if dpm emulation is on, False otherwise + """ + return self._dpm + + def set_dpm(self, dpm): + """ + set dpm emulation state + :param dpm: True to enable emulation, False to disable + """ + self.cdemu.set_device_option(self._device_id, OP_DPM_EMU, dpm) + + def get_tr(self): + """ + get current transfer rate emulation state + :return: True if transfer rate emulation is enabled, False otherwise + """ + return self._tr + + def set_tr(self, tr): + """ + set transfer rate emulation state + :param tr: True to enable emulation, False to disable + """ + self.cdemu.set_device_option(self._device_id, OP_TR_EMU, tr) + + def get_bs(self): + """ + get current bad sector emulation state + :return: True if bad sector emulation is enabled, False otherwise + """ + return self._bs + + def set_bs(self, bs): + """ + set bad sector emulation state + :param bs: True to enable emulation, False to disable + """ + self.cdemu.set_device_option(self._device_id, OP_BS_EMU, bs) + + def get_id(self): + """ + get current device id + :return: tuple(str(vendor_id), str(product_id), str(revision), str(vendor_specific)) + """ + return (self._vendor, self._product, self._revision, self._vendor_specific) + + def set_id(self, vendor, product, revision, vendor_specific): + """ + set device id + :param vendor: vendor_id - 8 character string + :param product: product_id - 16 character string + :param revision: - 4 character string + :param vendor_specific: - 20 character string + """ + self.cdemu.set_device_option(self._device_id, OP_DEV_ID, (vendor, product, revision, vendor_specific)) diff --git a/cdemu/proxy.py b/cdemu/proxy.py new file mode 100644 --- /dev/null +++ b/cdemu/proxy.py @@ -0,0 +1,147 @@ +import dbus, dbus.service, dbus.exceptions +from event.sync import EventDispatcher + + +OP_DPM_EMU = 'dpm-emulation' +OP_TR_EMU = 'tr-emulation' +OP_BS_EMU = 'bad-sector-emulation' +OP_DEV_ID = 'device-id' + + +class CDEmuDaemonProxy(EventDispatcher): + _name = 'net.sf.cdemu.CDEmuDaemon' + _object_path = '/Daemon' + + def __init__(self, use_system=False): + """ + Low-level abstraction for communitating with cdemu daemon + :param autostart_daemon: Attempt to start daemon if it is not running + :param use_system: Not recommended. Consider this option deprecated. Left as a compatibility option + """ + super().__init__( + 'daemon_started', + 'daemon_stopped', + 'device_status_changed', + 'device_option_changed', + 'device_mapping_ready', + 'device_added', + 'device_removed' + ) + + self._is_running = False + self.use_system = use_system + + if self.use_system: + self.bus = dbus.SystemBus() + else: + self.bus = dbus.SessionBus() + + self.bus.add_signal_receiver(self.from_daemon, dbus_interface='net.sf.cdemu.CDEmuDaemon', + member_keyword='signal', message_keyword='params') + self.bus.add_signal_receiver(self.from_dbus, dbus_interface='org.freedesktop.DBus', + member_keyword='signal', message_keyword='params') + + self.obj = None + self.interface = None + + self.connect() + + @property + def is_running(self): + return self._is_running + + def connect(self, retry=True): + self.obj = self.bus.get_object(self._name, self._object_path) + self.interface = dbus.Interface(self.obj, dbus_interface='net.sf.cdemu.CDEmuDaemon') + + self.ping_daemon(retry) + + def ping_daemon(self, retry=True): + try: + self.obj.Ping(dbus_interface='org.freedesktop.DBus.Peer') + self._is_running = True + self.dispatch('daemon_started') + except dbus.exceptions.DBusException as e: + if e.get_dbus_name() == 'org.freedesktop.DBus.Error.ServiceUnknown' and retry: + self.connect(False) + + def from_dbus(self, *args, signal, params): + print('from_dbus', '|'.join([str(a) for a in args]), signal, params) + if signal == 'NameOwnerChanged': + if args[0] == self._name: + if args[2]: + self._is_running = True + self.dispatch('daemon_started') + else: + self._is_running = False + self.dispatch('daemon_stopped') + + def from_daemon(self, *args, signal, params): + print('from_daemon', '|'.join([str(a) for a in args]), signal, params) + if signal == "DeviceStatusChanged": + self.dispatch('device_status_changed', *args) + elif signal == "DeviceOptionChanged": + self.dispatch('device_option_changed', *args) + elif signal == "DeviceMappingReady": + self.dispatch('device_mapping_ready', *args) + elif signal == "DeviceAdded": + self.dispatch('device_added', *args) + elif signal == "DeviceRemoved": + self.dispatch('device_removed', *args) + + def get_daemon_version(self): + return self.interface.GetDaemonVersion() + + def get_library_version(self): + return self.interface.GetLibraryVersion() + + def get_daemon_interface_version2(self): + return self.interface.GetDaemonInterfaceVersion2() + + def enum_daemon_debug_masks(self): + return self.interface.EnumDaemonDebugMasks() + + def enum_library_debug_masks(self): + return self.interface.EnumLibraryDebugMasks() + + def enum_supported_parsers(self): + return self.interface.EnumSupportedParsers() + + def enum_supported_writers(self): + return self.interface.EnumSupportedWriters() + + def enum_supported_filter_streams(self): + return self.interface.EnumSupportedFilterStreams() + + def enum_writer_parameters(self, writer_id): + return self.interface.EnumWriterParameters('(s)', writer_id) + + def get_number_of_devices(self): + return self.interface.GetNumberOfDevices() + + def device_get_mapping(self, device_number): + return self.interface.DeviceGetMapping(device_number) + + def device_get_status(self, device_number): + return self.interface.DeviceGetStatus(device_number) + + def device_load(self, device_number, filenames, parameters={}): + return self.interface.DeviceLoad(device_number, filenames, parameters) + + def device_create_blank(self, device_number, filename, parameters): + return self.interface.DeviceCreateBlank(device_number, filename, parameters) + + def device_unload(self, device_number): + return self.interface.DeviceUnload(device_number) + + def device_get_option(self, device_number, option_name): + return self.interface.DeviceGetOption(device_number, option_name) + + def device_set_option(self, device_number, option_name, option_value): + return self.interface.DeviceSetOption(device_number, option_name, option_value) + + def add_device(self): + return self.interface.AddDevice() + + def remove_device(self): + return self.interface.RemoveDevice() diff --git a/config/config.py b/config/config.py new file mode 100644 --- /dev/null +++ b/config/config.py @@ -0,0 +1,96 @@ +import os +from configparser import ConfigParser + +from event.sync import EventDispatcher + + +class ConfigFile(EventDispatcher, ConfigParser): + POSITION_TOP_LEFT = 'tl' + POSITION_TOP_RIGHT = 'tr' + POSITION_BOTTOM_LEFT = 'bl' + POSITION_BOTTOM_RIGHT = 'br' + + ICON_TRAY_DEFAULT = ':/icon.png' + ICON_DROPPER_EMPTY_DEFAULT = ':/dropper_empty.png' + ICON_DROPPER_FULL_DEFAULT = ':/dropper_full.png' + + def __init__(self, path): + EventDispatcher.__init__(self, 'changed') + ConfigParser.__init__(self) + self.path = path + if os.path.exists(path): + self.load() + + def get_position(self): + return self.get('ui', 'position', fallback=self.POSITION_BOTTOM_RIGHT) + + def set_position(self, position): + assert position in ( + self.POSITION_TOP_LEFT, self.POSITION_TOP_RIGHT, self.POSITION_BOTTOM_LEFT, self.POSITION_BOTTOM_RIGHT + ) + + if not self.has_section('ui'): + self.add_section('ui') + self.set('ui', 'position', position) + + def get_offset(self): + return ( + self.getint('ui', 'offset_x', fallback=16), + self.getint('ui', 'offset_y', fallback=16) + ) + + def set_offset(self, offset_x, offset_y): + if not self.has_section('ui'): + self.add_section('ui') + + self.set('ui', 'offset_x', str(offset_x)) + self.set('ui', 'offset_y', str(offset_y)) + + def get_icon_size(self): + return ( + self.getint('ui', 'icon_width', fallback=32), + self.getint('ui', 'icon_height', fallback=32) + ) + + def set_icon_size(self, icon_width, icon_height): + if not self.has_section('ui'): + self.add_section('ui') + + self.set('ui', 'icon_width', str(icon_width)) + self.set('ui', 'icon_height', str(icon_height)) + + def get_tray_icon(self): + return self.get('ui', 'tray_icon', fallback=self.ICON_TRAY_DEFAULT) + + def set_tray_icon(self, icon): + if not self.has_section('ui'): + self.add_section('ui') + + self.set('ui', 'tray_icon', icon) + + def get_empty_icon(self): + return self.get('ui', 'empty_icon', fallback=self.ICON_DROPPER_EMPTY_DEFAULT) + + def set_empty_icon(self, icon): + if not self.has_section('ui'): + self.add_section('ui') + + self.set('ui', 'empty_icon', icon) + + def get_full_icon(self): + return self.get('ui', 'full_icon', fallback=self.ICON_DROPPER_FULL_DEFAULT) + + def set_full_icon(self, icon): + if not self.has_section('ui'): + self.add_section('ui') + + self.set('ui', 'full_icon', icon) + + def load(self): + self.read(self.path) + + def save(self): + with open(self.path, 'w') as f: + self.write(f) + + self.dispatch('changed') diff --git a/event/sync.py b/event/sync.py new file mode 100644 --- /dev/null +++ b/event/sync.py @@ -0,0 +1,31 @@ +import sys, traceback + + +class EventDispatcher: + def __init__(self, *event_names): + self._events = {} + self._listname = 0 + for n in event_names: + self._events[n] = {} + + def add_handler(self, name, listener, listener_name=None): + # TODO add proper checks and specific exception if event is not supported + if not listener_name: + listener_name = '__auto__name__{}'.format(self._listname) + self._listname += 1 + self._events[name][listener_name] = listener + + def remove_handler(self, name, listener_name=None): + # TODO add proper checks and specific exception if event is not supported + if listener_name: + del self._events[name][listener_name] + else: + self._events[name].clear() + + def dispatch(self, name, *args, **kwargs): + # TODO add special exceptions for StopPropagation and PreventDefault + for e in self._events[name].values(): + try: + e(*args, **kwargs) + except Exception: + sys.stderr.write(traceback.format_exc()) diff --git a/gui/__init__.py b/gui/__init__.py new file mode 100644 diff --git a/gui/about.py b/gui/about.py new file mode 100644 --- /dev/null +++ b/gui/about.py @@ -0,0 +1,9 @@ +from PyQt5.QtWidgets import QWidget + +from .ui.about import Ui_Form + + +class AboutWidget(Ui_Form, QWidget): + def __init__(self): + super().__init__() + self.setupUi(self) diff --git a/gui/device_menu.py b/gui/device_menu.py new file mode 100644 --- /dev/null +++ b/gui/device_menu.py @@ -0,0 +1,52 @@ +from PyQt5 import QtWidgets +from mount import mount +from cdemu import CDEmuException + +class QDeviceMenu(QtWidgets.QMenu): + def __init__(self, device): + super().__init__(device.get_name()) + self._id = id + self.load = self.addAction('Load') + self.load.triggered.connect(self.on_load) + self.unload = self.addAction('Unload') + self.unload.triggered.connect(self.on_unload) + self.addSeparator() + self.dpm = self.addAction('DPM emulation') + self.dpm.setCheckable(True) + self.dpm.triggered.connect(self.on_dpm) + self.tr = self.addAction('Transfer rate emulation') + self.tr.setCheckable(True) + self.tr.triggered.connect(self.on_tr) + self.bs = self.addAction('Bad sector enulation') + self.bs.setCheckable(True) + self.bs.triggered.connect(self.on_bs) + self.device = device + self.device.add_handler('change', self.on_device_change) + self.on_device_change() + + def on_device_change(self): + self.unload.setEnabled(bool(self.device.get_img())) + self.dpm.setChecked(self.device.get_dpm()) + self.tr.setChecked(self.device.get_tr()) + self.bs.setChecked(self.device.get_bs()) + + def on_load(self): + fd = QtWidgets.QFileDialog(self) + fd.setAcceptMode(QtWidgets.QFileDialog.AcceptOpen) + if fd.exec() == QtWidgets.QDialog.Accepted: + self.device.load(fd.selectedFiles()[0]) + + def on_unload(self): + try: + self.device.unload() + except Exception as e: + QtWidgets.QMessageBox.critical(None, 'Error', str(e)) + + def on_dpm(self): + self.device.set_dpm(self.dpm.isChecked()) + + def on_tr(self): + self.device.set_tr(self.tr.isChecked()) + + def on_bs(self): + self.device.set_bs(self.bs.isChecked()) diff --git a/gui/dropper.py b/gui/dropper.py new file mode 100644 --- /dev/null +++ b/gui/dropper.py @@ -0,0 +1,145 @@ +from PyQt5 import QtGui, QtCore, QtWidgets +from PyQt5.QtWidgets import QSizePolicy + +from cdemu import CDEmuException +from .device_menu import QDeviceMenu +from mount import mount +from urllib.parse import unquote + +class KCDEmuDevice(QtWidgets.QLabel): + def __init__(self, device, width=32, height=32): + super().__init__() + self.setAcceptDrops(True) + self.device = None + self.empty_icon = ':/dropper_empty.png' + self.full_icon = ':/dropper_full.png' + self.refresh(device) + self.setSizePolicy(QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)) + self.setAlignment(QtCore.Qt.AlignCenter) + self.setMinimumSize(width, height) + self.setBaseSize(width, height) + self.setMaximumSize(width, height) + self.ctx_menu = QDeviceMenu(device) + + def set_size(self, width, height): + self.setMinimumSize(width, height) + self.setBaseSize(width, height) + self.setMaximumSize(width, height) + self.adjustSize() + + def set_icons(self, empty_icon, full_icon): + self.empty_icon = empty_icon + self.full_icon = full_icon + self.refresh() + + def refresh(self, device=None): + if device is not None: + if self.device and self.device != device: + self.device.remove_handler('change') + if self.device != device: + self.device = device + self.device.add_handler('change', self.refresh) + self.setText( + ''.format( + self.full_icon if self.device.get_img() else self.empty_icon)) + if self.device.get_img(): + self.setToolTip('Click to unload "{}"'.format(self.device.get_img())) + else: + self.setToolTip('Click to load') + + def dragEnterEvent(self, event): + if event.mimeData().hasFormat('text/plain'): + text = event.mimeData().text() + if text.startswith('file://'): + event.acceptProposedAction() + + def dragLeaveEvent(self, event): + pass + + def dropEvent(self, event): + if event.mimeData().hasFormat('text/plain'): + text = event.mimeData().text() + if text.startswith('file://'): + text = unquote(text.replace('file://', '').replace('\n', '').replace('\r', '')) + self.device.load(text) + + def mousePressEvent(self, event): + if event.button() == QtCore.Qt.RightButton: + self.ctx_menu.popup(QtCore.QPoint(event.globalX(), event.globalY())) + elif event.button() == QtCore.Qt.LeftButton: + if self.device.get_img(): + try: + self.device.unload() + except Exception as e: + QtWidgets.QMessageBox.critical(None, 'Error', str(e)) + else: + fd = QtWidgets.QFileDialog(self) + fd.setAcceptMode(QtWidgets.QFileDialog.AcceptOpen) + if fd.exec() == QtWidgets.QDialog.Accepted: + self.device.load(fd.selectedFiles()[0]) + +class KCDEmuDropperWidget(QtWidgets.QWidget): + changed = QtCore.pyqtSignal() + + def __init__(self, cdemu, config): + """ + :type cdemu: cdemu.CDEmu + :type config: config.config.ConfigFile + """ + super().__init__(None, QtCore.Qt.FramelessWindowHint | QtCore.Qt.WindowStaysOnTopHint) + self.config = config + self.setWindowTitle('KCDEmu') + self.setSizePolicy(QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum)) + layout = QtWidgets.QHBoxLayout(self) + layout.setSizeConstraint(QtWidgets.QLayout.SetFixedSize) + layout.setContentsMargins(0,0,0,0) + layout.setSpacing(0) + self.setLayout(layout) + self.cdemu = cdemu + self.devices = [] + + self.cdemu.add_handler('devices_changed', self.on_devices_changed) + self.cdemu.add_handler('daemon_stopped', self.on_daemon_stopped) + self.cdemu.add_handler('daemon_started', self.on_daemon_started) + self.config.add_handler('changed', self.reload_config) + self.reload_config() + + if self.cdemu.is_daemon_ready(): + self.on_devices_changed() + + def on_devices_changed(self, number=None): + devices = self.cdemu.get_devices() + while len(self.devices) > len(devices): + dev = self.devices.pop() + self.layout().removeWidget(dev) + dev.hide() + for index, device in enumerate(devices): + if len(self.devices) <= index: + icon_size = self.config.get_icon_size() + dev = KCDEmuDevice(device, icon_size[0], icon_size[1]) + dev.set_icons(self.config.get_empty_icon(), self.config.get_full_icon()) + self.devices.append(dev) + self.layout().addWidget(dev) + else: + self.devices[index].refresh(device) + self.updateGeometry() + + def resizeEvent(self, event): + print(self.sizeHint().width()) + self.changed.emit() + + def on_daemon_stopped(self): + while len(self.devices) > 0: + dev = self.devices.pop() + self.layout().removeWidget(dev) + + def on_daemon_started(self): + self.on_devices_changed() + + def reload_config(self): + for i in range(self.layout().count()): + widget = self.layout().itemAt(i).widget() + if widget: + icon_size = self.config.get_icon_size() + widget.set_size(icon_size[0], icon_size[1]) + widget.set_icons(self.config.get_empty_icon(), self.config.get_full_icon()) diff --git a/gui/main_settings.py b/gui/main_settings.py new file mode 100644 --- /dev/null +++ b/gui/main_settings.py @@ -0,0 +1,142 @@ +import os + +from PyQt5 import QtCore +from PyQt5.QtCore import QSize +from PyQt5.QtWidgets import QWidget, QFileDialog + +from .ui.main_settings import Ui_Form + + +class MainSettingsWidget(Ui_Form, QWidget): + def __init__(self, config): + """ + + :type config: config.config.ConfigFile + """ + super().__init__() + self.config = config + self.last_directory = None + self.setupUi(self) + + self.trayDefaultB.clicked.connect(self.on_tray_default_click) + self.emptyDefaultB.clicked.connect(self.on_empty_default_click) + self.fullDefaultB.clicked.connect(self.on_full_default_click) + + self.trayCustomB.clicked.connect(self.on_tray_custom_click) + self.emptyCustomB.clicked.connect(self.on_empty_custom_click) + self.fullCustomB.clicked.connect(self.on_full_custom_click) + + position = self.config.get_position() + if position == self.config.POSITION_TOP_LEFT: + self.topLeftRB.setChecked(True) + if position == self.config.POSITION_TOP_RIGHT: + self.topRightRB.setChecked(True) + if position == self.config.POSITION_BOTTOM_LEFT: + self.bottomLeftRB.setChecked(True) + if position == self.config.POSITION_BOTTOM_RIGHT: + self.bottomRightRB.setChecked(True) + + offset = self.config.get_offset() + icon_size = self.config.get_icon_size() + + self.hOffsetSB.setValue(offset[0]) + self.vOffsetSB.setValue(offset[1]) + + self.iwidthSB.setValue(icon_size[0]) + self.iheightSB.setValue(icon_size[1]) + + self.iwidthSB.editingFinished.connect(self.resize_icons) + self.iheightSB.editingFinished.connect(self.resize_icons) + self.resize_icons() + + self.tray_icon = self.config.get_tray_icon() + self.empty_icon = self.config.get_empty_icon() + self.full_icon = self.config.get_full_icon() + + self.update_icons() + + def update_icons(self): + self.trayLabel.setText("

") + self.emptyLabel.setText("

") + self.fullLabel.setText("

") + + def on_tray_default_click(self): + self.tray_icon = self.config.ICON_TRAY_DEFAULT + self.update_icons() + + def on_empty_default_click(self): + self.empty_icon = self.config.ICON_DROPPER_EMPTY_DEFAULT + self.update_icons() + + def on_full_default_click(self): + self.full_icon = self.config.ICON_DROPPER_FULL_DEFAULT + self.update_icons() + + def on_tray_custom_click(self): + file = self.show_open_dialog() + if file: + self.tray_icon = file + self.update_icons() + + def on_empty_custom_click(self): + file = self.show_open_dialog() + if file: + self.empty_icon = file + self.update_icons() + + def on_full_custom_click(self): + file = self.show_open_dialog() + if file: + self.full_icon = file + self.update_icons() + + def show_open_dialog(self): + open_dialog = QFileDialog() + + if self.last_directory: + open_dialog.setDirectory(self.last_directory) + + open_dialog.setAcceptMode(open_dialog.AcceptOpen) + if open_dialog.exec_() == QFileDialog.Accepted: + file = open_dialog.selectedFiles()[0] + self.last_directory = os.path.abspath(file) + return file + return None + + def resize_icons(self): + icon_width = self.iwidthSB.value() + icon_height = self.iheightSB.value() + self.trayLabel.setMinimumSize(icon_width, icon_height) + self.trayLabel.setBaseSize(icon_width, icon_height) + self.trayLabel.setMaximumSize(icon_width, icon_height) + + self.emptyLabel.setMinimumSize(icon_width, icon_height) + self.emptyLabel.setBaseSize(icon_width, icon_height) + self.emptyLabel.setMaximumSize(icon_width, icon_height) + + self.fullLabel.setMinimumSize(icon_width, icon_height) + self.fullLabel.setBaseSize(icon_width, icon_height) + self.fullLabel.setMaximumSize(icon_width, icon_height) + + self.trayDefaultB.setIconSize(QSize(icon_width, icon_height)) + self.emptyDefaultB.setIconSize(QSize(icon_width, icon_height)) + self.fullDefaultB.setIconSize(QSize(icon_width, icon_height)) + + + def update_config(self): + position = None + if self.topLeftRB.isChecked(): + position = self.config.POSITION_TOP_LEFT + if self.topRightRB.isChecked(): + position = self.config.POSITION_TOP_RIGHT + if self.bottomLeftRB.isChecked(): + position = self.config.POSITION_BOTTOM_LEFT + if self.bottomRightRB.isChecked(): + position = self.config.POSITION_BOTTOM_RIGHT + + self.config.set_position(position) + self.config.set_icon_size(self.iwidthSB.value(), self.iheightSB.value()) + self.config.set_offset(self.hOffsetSB.value(), self.vOffsetSB.value()) + self.config.set_tray_icon(self.tray_icon) + self.config.set_empty_icon(self.empty_icon) + self.config.set_full_icon(self.full_icon) diff --git a/gui/settings.py b/gui/settings.py new file mode 100644 --- /dev/null +++ b/gui/settings.py @@ -0,0 +1,47 @@ +from PyQt5.QtCore import QRect, QItemSelectionModel +from PyQt5.QtWidgets import QDialog +from PyQt5 import QtCore + +from gui.about import AboutWidget +from gui.main_settings import MainSettingsWidget +from .ui.settings import Ui_Form + + +class SettingsForm(Ui_Form, QDialog): + PAGES = [ + 'main_settings', + 'about' + ] + + def __init__(self, config, index=0): + super().__init__() + self.config = config + self.setupUi(self) + self.cancel_button.clicked.connect(self.on_cancel) + self.ok_button.clicked.connect(self.on_ok) + self.apply_button.clicked.connect(self.on_apply) + for i in range(len(self.PAGES)): + self.group_list.item(i).setData(QtCore.Qt.UserRole, self.PAGES[i]) + self.group_list.currentItemChanged.connect(self.on_page_change) + self.tabs = { + 'main_settings': MainSettingsWidget(config), + 'about': AboutWidget() + } + self.group_list.setCurrentRow(index) + + def on_page_change(self, current, _): + print(current.data(QtCore.Qt.UserRole)) + self.scrollArea.takeWidget() + tab = self.tabs[current.data(QtCore.Qt.UserRole)] + self.scrollArea.setWidget(tab) + + def on_cancel(self): + self.reject() + + def on_ok(self): + self.on_apply() + self.accept() + + def on_apply(self): + self.tabs['main_settings'].update_config() + self.config.save() diff --git a/gui/tray.py b/gui/tray.py new file mode 100644 --- /dev/null +++ b/gui/tray.py @@ -0,0 +1,143 @@ +from PyQt5 import QtCore, QtWidgets, QtGui + +from gui.settings import SettingsForm +from .device_menu import QDeviceMenu + +class TrayManager: + def __init__(self, cdemu, config, dropper): + """ + + :type cdemu: cdemu.CDEmu + :type config: config.config.ConfigFile + :type dropper: gui.dropper.KCDEmuDropperWidget + """ + self.cdemu = cdemu + self.config = config + self.dropper = dropper + self.dropper.changed.connect(self.on_dropper_changed) + self.cdemu.add_handler('devices_changed', self.on_devices_changed) + self.cdemu.add_handler('daemon_stopped', self.on_daemon_stopped) + self.cdemu.add_handler('daemon_started', self.on_daemon_started) + + self.act_device_sep = QtWidgets.QAction(None) + self.act_device_sep.setSeparator(True) + self.act_add = QtWidgets.QAction('Add device', None) + self.act_add.triggered.connect(self.on_act_add) + self.act_remove = QtWidgets.QAction('Remove device', None) + self.act_remove.triggered.connect(self.on_act_remove) + self.act_dropper_show = QtWidgets.QAction('Toggle dropper', None) + self.act_dropper_show.triggered.connect(self.on_act_dropper_show) + self.act_settings = QtWidgets.QAction('Settings', None) + self.act_settings.triggered.connect(self.on_act_settings) + self.act_about = QtWidgets.QAction('About', None) + self.act_about.triggered.connect(self.on_act_about) + self.act_quit = QtWidgets.QAction('Quit', None) + self.act_quit.triggered.connect(self.on_act_quit) + + self.menus = {} + self.ctx_menu = QtWidgets.QMenu() + + icon = QtGui.QIcon(self.config.get_tray_icon()) + self.tray_icon = QtWidgets.QSystemTrayIcon(icon) + self.tray_icon.setIcon(icon) + self.tray_icon.setContextMenu(self.ctx_menu) + self.tray_icon.activated.connect(self.on_tray_icon_activated) + self.tray_icon.setToolTip('KCDEmu') + self.tray_icon.show() + self.update_menu() + + def update_menu(self): + self.ctx_menu.clear() + self.ctx_menu.addAction(self.act_device_sep) + self.ctx_menu.addAction(self.act_add) + self.ctx_menu.addAction(self.act_remove) + self.ctx_menu.addSeparator() + self.ctx_menu.addAction(self.act_dropper_show) + self.ctx_menu.addAction(self.act_settings) + self.ctx_menu.addAction(self.act_about) + self.ctx_menu.addAction(self.act_quit) + if self.cdemu.is_daemon_ready(): + self.on_devices_changed() + + def toggle_dropper_visibility(self): + # TODO move to dropper object + if self.dropper.isVisible(): + self.dropper.hide() + else: + self.dropper.show() + geo = QtWidgets.QApplication.desktop().availableGeometry() + position = self.config.get_position() + offset = self.config.get_offset() + if position == self.config.POSITION_TOP_LEFT: + self.dropper.move(offset[0], offset[1]) + elif position == self.config.POSITION_TOP_RIGHT: + self.dropper.move(geo.width() - self.dropper.width() - offset[0], offset[1]) + elif position == self.config.POSITION_BOTTOM_LEFT: + self.dropper.move(offset[0], geo.height() - self.dropper.height() - offset[1]) + elif position == self.config.POSITION_BOTTOM_RIGHT: + self.dropper.move( + geo.width() - self.dropper.width() - offset[0], geo.height() - self.dropper.height() - offset[1] + ) + + def on_tray_icon_activated(self, reason): + if reason == QtWidgets.QSystemTrayIcon.Trigger: + self.toggle_dropper_visibility() + + def on_dropper_changed(self): + if self.dropper.isVisible(): + geo = QtWidgets.QApplication.desktop().availableGeometry() + self.dropper.move(geo.width() - self.dropper.width() - 16, geo.height() - self.dropper.height() - 16) + + def on_act_quit(self): + QtCore.QCoreApplication.exit(0) + + def on_act_dropper_show(self): + self.toggle_dropper_visibility() + + def on_act_settings(self): + settings_form = SettingsForm(self.config) + settings_form.exec_() + + def on_act_about(self): + print(self.cdemu.daemon.get_daemon_version()) + settings_form = SettingsForm(1) + settings_form.exec_() + + def on_act_add(self): + self.cdemu.add_device() + + def on_act_remove(self): + self.cdemu.remove_device() + + def get_menu_for_device(self, number, device): + if number in self.menus: + return self.menus[number] + else: + self.menus[number] = QDeviceMenu(device) + for k in sorted(self.menus.keys()): + if k > number: + self.ctx_menu.insertMenu(self.menus[number], self.menus[k]) + self.ctx_menu.insertMenu(self.act_device_sep, self.menus[number]) + + def on_devices_changed(self, number=None): + devices = self.cdemu.get_devices() + for key in list(self.menus.keys()): + if key >= len(devices): + self.ctx_menu.removeAction(self.menus[key].menuAction()) + del self.menus[key] + + if number is None: + for number, device in enumerate(devices): + self.on_device_changed(device, self.get_menu_for_device(number, device)) + else: + device = self.cdemu.get_devices()[number] + self.on_device_changed(device, self.get_menu_for_device(number, device)) + + def on_device_changed(self, device, menu): + pass + + def on_daemon_started(self): + self.tray_icon.showMessage('KCDEmu', 'cdemu-daemon is ready') + + def on_daemon_stopped(self): + self.tray_icon.showMessage('KCDEmu', 'cdemu-daemon has stopped') diff --git a/gui/ui/about.py b/gui/ui/about.py new file mode 100644 --- /dev/null +++ b/gui/ui/about.py @@ -0,0 +1,29 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'gui/ui/about.ui' +# +# Created by: PyQt5 UI code generator 5.10.1 +# +# WARNING! All changes made in this file will be lost! + +from PyQt5 import QtCore, QtGui, QtWidgets + +class Ui_Form(object): + def setupUi(self, Form): + Form.setObjectName("Form") + Form.resize(427, 316) + self.horizontalLayout = QtWidgets.QHBoxLayout(Form) + self.horizontalLayout.setContentsMargins(0, 0, 0, 0) + self.horizontalLayout.setSpacing(0) + self.horizontalLayout.setObjectName("horizontalLayout") + self.label = QtWidgets.QLabel(Form) + self.label.setObjectName("label") + self.horizontalLayout.addWidget(self.label, 0, QtCore.Qt.AlignHCenter) + + self.retranslateUi(Form) + QtCore.QMetaObject.connectSlotsByName(Form) + + def retranslateUi(self, Form): + _translate = QtCore.QCoreApplication.translate + Form.setWindowTitle(_translate("Form", "Form")) + self.label.setText(_translate("Form", "

+=

KCDEmu

Version 1.0

By: Khaelenmore Thaal


GUI utility for managing your virtual CD/DVD drives
Compatible with CDEmu>=3.1.0
Uses Qt5
https://www.qt.io/
Uses PyQt5
https://www.riverbankcomputing.com/software/pyqt/intro
Built-in icons by
http://www.fatcow.com/free-icons
This program is licensed with GNU GPL v3
This program is available on
http://silverwing.one/pages/kcdemu

")) diff --git a/gui/ui/about.ui b/gui/ui/about.ui new file mode 100644 --- /dev/null +++ b/gui/ui/about.ui @@ -0,0 +1,45 @@ + + + Form + + + + 0 + 0 + 427 + 316 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + <html><head/><body><p align="center"><img src=":/dropper_empty.png"/><span style=" font-size:36pt;">+</span><img src=":/icon.png"/><span style=" font-size:36pt;">=</span><img src=":/dropper_full.png"/></p><p align="center"><span style=" font-size:22pt;">KCDEmu</span></p><p align="center">Version 1.0</p><p align="center">By: Khaelenmore Thaal</p><p><br/></p><p><span style=" font-family:'DejaVu Sans Mono'; font-size:9.1pt;">GUI utility for managing your virtual CD/DVD drives</span><span style=" font-family:'DejaVu Sans Mono'; font-size:9.1pt; color:#000000;"><br/>Compatible with CDEmu&gt;=3.1.0<br/>Uses Qt5 </span><a href="https://www.qt.io/"><span style=" text-decoration: underline;">https://www.qt.io/</span></a><span style=" font-family:'DejaVu Sans Mono'; font-size:9.1pt; "><br/>Uses PyQt5 </span><a href="https://www.riverbankcomputing.com/software/pyqt/intro"><span style=" text-decoration: underline;">https://www.riverbankcomputing.com/software/pyqt/intro</span></a><span style=" font-family:'DejaVu Sans Mono'; font-size:9.1pt;"><br/>Built-in icons by </span><a href="http://www.fatcow.com/free-icons"><span style=" text-decoration: underline;">http://www.fatcow.com/free-icons</span></a><span style=" font-family:'DejaVu Sans Mono'; font-size:9.1pt;"><br/>This program is licensed with GNU GPL v3<br/>This program is available on </span><a href="http://silverwing.one/pages/kcdemu"><span style=" text-decoration: underline; ">http://silverwing.one/pages/kcdemu</span></a></p></body></html> + + + + + + + + + + diff --git a/gui/ui/main_settings.py b/gui/ui/main_settings.py new file mode 100644 --- /dev/null +++ b/gui/ui/main_settings.py @@ -0,0 +1,201 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'gui/ui/main_settings.ui' +# +# Created by: PyQt5 UI code generator 5.10.1 +# +# WARNING! All changes made in this file will be lost! + +from PyQt5 import QtCore, QtGui, QtWidgets + +class Ui_Form(object): + def setupUi(self, Form): + Form.setObjectName("Form") + Form.resize(584, 401) + self.verticalLayout_2 = QtWidgets.QVBoxLayout(Form) + self.verticalLayout_2.setContentsMargins(0, 0, 0, 0) + self.verticalLayout_2.setSpacing(0) + self.verticalLayout_2.setObjectName("verticalLayout_2") + self.positionGB = QtWidgets.QGroupBox(Form) + self.positionGB.setFlat(False) + self.positionGB.setObjectName("positionGB") + self.horizontalLayout = QtWidgets.QHBoxLayout(self.positionGB) + self.horizontalLayout.setContentsMargins(0, 0, 0, 0) + self.horizontalLayout.setObjectName("horizontalLayout") + self.verticalLayout_3 = QtWidgets.QVBoxLayout() + self.verticalLayout_3.setSpacing(0) + self.verticalLayout_3.setObjectName("verticalLayout_3") + self.topLeftRB = QtWidgets.QRadioButton(self.positionGB) + self.topLeftRB.setObjectName("topLeftRB") + self.verticalLayout_3.addWidget(self.topLeftRB) + self.topRightRB = QtWidgets.QRadioButton(self.positionGB) + self.topRightRB.setObjectName("topRightRB") + self.verticalLayout_3.addWidget(self.topRightRB) + self.bottomLeftRB = QtWidgets.QRadioButton(self.positionGB) + self.bottomLeftRB.setObjectName("bottomLeftRB") + self.verticalLayout_3.addWidget(self.bottomLeftRB) + self.bottomRightRB = QtWidgets.QRadioButton(self.positionGB) + self.bottomRightRB.setObjectName("bottomRightRB") + self.verticalLayout_3.addWidget(self.bottomRightRB) + self.horizontalLayout.addLayout(self.verticalLayout_3) + self.verticalLayout = QtWidgets.QVBoxLayout() + self.verticalLayout.setSpacing(0) + self.verticalLayout.setObjectName("verticalLayout") + self.vOffsetLabel = QtWidgets.QLabel(self.positionGB) + self.vOffsetLabel.setObjectName("vOffsetLabel") + self.verticalLayout.addWidget(self.vOffsetLabel) + self.vOffsetSB = QtWidgets.QSpinBox(self.positionGB) + self.vOffsetSB.setObjectName("vOffsetSB") + self.verticalLayout.addWidget(self.vOffsetSB) + self.hOffsetLabel = QtWidgets.QLabel(self.positionGB) + self.hOffsetLabel.setObjectName("hOffsetLabel") + self.verticalLayout.addWidget(self.hOffsetLabel) + self.hOffsetSB = QtWidgets.QSpinBox(self.positionGB) + self.hOffsetSB.setObjectName("hOffsetSB") + self.verticalLayout.addWidget(self.hOffsetSB) + self.horizontalLayout.addLayout(self.verticalLayout) + self.verticalLayout_2.addWidget(self.positionGB) + self.sizeGB = QtWidgets.QGroupBox(Form) + self.sizeGB.setObjectName("sizeGB") + self.formLayout = QtWidgets.QFormLayout(self.sizeGB) + self.formLayout.setObjectName("formLayout") + self.label = QtWidgets.QLabel(self.sizeGB) + self.label.setObjectName("label") + self.formLayout.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.label) + self.iheightSB = QtWidgets.QSpinBox(self.sizeGB) + self.iheightSB.setObjectName("iheightSB") + self.formLayout.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.iheightSB) + self.label_2 = QtWidgets.QLabel(self.sizeGB) + self.label_2.setObjectName("label_2") + self.formLayout.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.label_2) + self.iwidthSB = QtWidgets.QSpinBox(self.sizeGB) + self.iwidthSB.setObjectName("iwidthSB") + self.formLayout.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.iwidthSB) + self.verticalLayout_2.addWidget(self.sizeGB) + self.iconGB = QtWidgets.QGroupBox(Form) + self.iconGB.setObjectName("iconGB") + self.gridLayout = QtWidgets.QGridLayout(self.iconGB) + self.gridLayout.setObjectName("gridLayout") + self.label_5 = QtWidgets.QLabel(self.iconGB) + self.label_5.setObjectName("label_5") + self.gridLayout.addWidget(self.label_5, 2, 0, 1, 1) + self.fullLabel = QtWidgets.QLabel(self.iconGB) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.fullLabel.sizePolicy().hasHeightForWidth()) + self.fullLabel.setSizePolicy(sizePolicy) + self.fullLabel.setMinimumSize(QtCore.QSize(32, 32)) + self.fullLabel.setBaseSize(QtCore.QSize(32, 32)) + self.fullLabel.setAlignment(QtCore.Qt.AlignCenter) + self.fullLabel.setObjectName("fullLabel") + self.gridLayout.addWidget(self.fullLabel, 2, 1, 1, 1) + self.label_3 = QtWidgets.QLabel(self.iconGB) + self.label_3.setObjectName("label_3") + self.gridLayout.addWidget(self.label_3, 0, 0, 1, 1) + self.emptyLabel = QtWidgets.QLabel(self.iconGB) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.emptyLabel.sizePolicy().hasHeightForWidth()) + self.emptyLabel.setSizePolicy(sizePolicy) + self.emptyLabel.setMinimumSize(QtCore.QSize(32, 32)) + self.emptyLabel.setBaseSize(QtCore.QSize(32, 32)) + self.emptyLabel.setAlignment(QtCore.Qt.AlignCenter) + self.emptyLabel.setObjectName("emptyLabel") + self.gridLayout.addWidget(self.emptyLabel, 1, 1, 1, 1) + self.trayDefaultB = QtWidgets.QPushButton(self.iconGB) + icon = QtGui.QIcon() + icon.addPixmap(QtGui.QPixmap(":/icon.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.trayDefaultB.setIcon(icon) + self.trayDefaultB.setIconSize(QtCore.QSize(32, 32)) + self.trayDefaultB.setObjectName("trayDefaultB") + self.gridLayout.addWidget(self.trayDefaultB, 0, 2, 1, 1) + self.label_4 = QtWidgets.QLabel(self.iconGB) + self.label_4.setObjectName("label_4") + self.gridLayout.addWidget(self.label_4, 1, 0, 1, 1) + self.trayLabel = QtWidgets.QLabel(self.iconGB) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.trayLabel.sizePolicy().hasHeightForWidth()) + self.trayLabel.setSizePolicy(sizePolicy) + self.trayLabel.setMinimumSize(QtCore.QSize(32, 32)) + self.trayLabel.setMaximumSize(QtCore.QSize(32, 32)) + self.trayLabel.setBaseSize(QtCore.QSize(32, 32)) + self.trayLabel.setAlignment(QtCore.Qt.AlignCenter) + self.trayLabel.setObjectName("trayLabel") + self.gridLayout.addWidget(self.trayLabel, 0, 1, 1, 1) + self.fullDefaultB = QtWidgets.QPushButton(self.iconGB) + icon1 = QtGui.QIcon() + icon1.addPixmap(QtGui.QPixmap(":/dropper_full.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.fullDefaultB.setIcon(icon1) + self.fullDefaultB.setIconSize(QtCore.QSize(32, 32)) + self.fullDefaultB.setFlat(False) + self.fullDefaultB.setObjectName("fullDefaultB") + self.gridLayout.addWidget(self.fullDefaultB, 2, 2, 1, 1) + self.emptyCustomB = QtWidgets.QPushButton(self.iconGB) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Preferred) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.emptyCustomB.sizePolicy().hasHeightForWidth()) + self.emptyCustomB.setSizePolicy(sizePolicy) + self.emptyCustomB.setIconSize(QtCore.QSize(32, 32)) + self.emptyCustomB.setObjectName("emptyCustomB") + self.gridLayout.addWidget(self.emptyCustomB, 1, 3, 1, 1) + self.emptyDefaultB = QtWidgets.QPushButton(self.iconGB) + icon2 = QtGui.QIcon() + icon2.addPixmap(QtGui.QPixmap(":/dropper_empty.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.emptyDefaultB.setIcon(icon2) + self.emptyDefaultB.setIconSize(QtCore.QSize(32, 32)) + self.emptyDefaultB.setObjectName("emptyDefaultB") + self.gridLayout.addWidget(self.emptyDefaultB, 1, 2, 1, 1) + self.trayCustomB = QtWidgets.QPushButton(self.iconGB) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Preferred) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.trayCustomB.sizePolicy().hasHeightForWidth()) + self.trayCustomB.setSizePolicy(sizePolicy) + self.trayCustomB.setIconSize(QtCore.QSize(32, 32)) + self.trayCustomB.setObjectName("trayCustomB") + self.gridLayout.addWidget(self.trayCustomB, 0, 3, 1, 1) + self.fullCustomB = QtWidgets.QPushButton(self.iconGB) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Preferred) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.fullCustomB.sizePolicy().hasHeightForWidth()) + self.fullCustomB.setSizePolicy(sizePolicy) + self.fullCustomB.setIconSize(QtCore.QSize(32, 32)) + self.fullCustomB.setObjectName("fullCustomB") + self.gridLayout.addWidget(self.fullCustomB, 2, 3, 1, 1) + self.verticalLayout_2.addWidget(self.iconGB) + + self.retranslateUi(Form) + QtCore.QMetaObject.connectSlotsByName(Form) + + def retranslateUi(self, Form): + _translate = QtCore.QCoreApplication.translate + Form.setWindowTitle(_translate("Form", "Form")) + self.positionGB.setTitle(_translate("Form", "Position")) + self.topLeftRB.setText(_translate("Form", "Top &Left")) + self.topRightRB.setText(_translate("Form", "Top Ri&ght")) + self.bottomLeftRB.setText(_translate("Form", "Bo&ttom Left")) + self.bottomRightRB.setText(_translate("Form", "Bo&ttom Right")) + self.vOffsetLabel.setText(_translate("Form", "Vertical offset")) + self.hOffsetLabel.setText(_translate("Form", "Horizontal offset")) + self.sizeGB.setTitle(_translate("Form", "Size")) + self.label.setText(_translate("Form", "iconHeight")) + self.label_2.setText(_translate("Form", "iconWidth")) + self.iconGB.setTitle(_translate("Form", "Icons")) + self.label_5.setText(_translate("Form", "Full drive")) + self.fullLabel.setText(_translate("Form", "

")) + self.label_3.setText(_translate("Form", "Tray icon")) + self.emptyLabel.setText(_translate("Form", "

")) + self.trayDefaultB.setText(_translate("Form", "Default")) + self.label_4.setText(_translate("Form", "Empty drive")) + self.trayLabel.setText(_translate("Form", "

")) + self.fullDefaultB.setText(_translate("Form", "Default")) + self.emptyCustomB.setText(_translate("Form", "Choose...")) + self.emptyDefaultB.setText(_translate("Form", "Default")) + self.trayCustomB.setText(_translate("Form", "Choose...")) + self.fullCustomB.setText(_translate("Form", "Choose...")) diff --git a/gui/ui/main_settings.ui b/gui/ui/main_settings.ui new file mode 100644 --- /dev/null +++ b/gui/ui/main_settings.ui @@ -0,0 +1,384 @@ + + + Form + + + + 0 + 0 + 584 + 401 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Position + + + false + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 0 + + + + + Top &Left + + + + + + + Top Ri&ght + + + + + + + Bo&ttom Left + + + + + + + Bo&ttom Right + + + + + + + + + 0 + + + + + Vertical offset + + + + + + + + + + Horizontal offset + + + + + + + + + + + + + + + Size + + + + + + iconHeight + + + + + + + + + + iconWidth + + + + + + + + + + + + + Icons + + + + + + Full drive + + + + + + + + 0 + 0 + + + + + 32 + 32 + + + + + 32 + 32 + + + + <html><head/><body><p><img src=":/dropper_full.png"/></p></body></html> + + + Qt::AlignCenter + + + + + + + Tray icon + + + + + + + + 0 + 0 + + + + + 32 + 32 + + + + + 32 + 32 + + + + <html><head/><body><p><img src=":/dropper_empty.png"/></p></body></html> + + + Qt::AlignCenter + + + + + + + Default + + + + :/icon.png:/icon.png + + + + 32 + 32 + + + + + + + + Empty drive + + + + + + + + 0 + 0 + + + + + 32 + 32 + + + + + 32 + 32 + + + + + 32 + 32 + + + + <html><head/><body><p><img src=":/icon.png"/></p></body></html> + + + Qt::AlignCenter + + + + + + + Default + + + + :/dropper_full.png:/dropper_full.png + + + + 32 + 32 + + + + false + + + + + + + + 0 + 0 + + + + Choose... + + + + 32 + 32 + + + + + + + + Default + + + + :/dropper_empty.png:/dropper_empty.png + + + + 32 + 32 + + + + + + + + + 0 + 0 + + + + Choose... + + + + 32 + 32 + + + + + + + + + 0 + 0 + + + + Choose... + + + + 32 + 32 + + + + + + + + + + + + + + diff --git a/gui/ui/settings.py b/gui/ui/settings.py new file mode 100644 --- /dev/null +++ b/gui/ui/settings.py @@ -0,0 +1,94 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'gui/ui/settings.ui' +# +# Created by: PyQt5 UI code generator 5.10.1 +# +# WARNING! All changes made in this file will be lost! + +from PyQt5 import QtCore, QtGui, QtWidgets + +class Ui_Form(object): + def setupUi(self, Form): + Form.setObjectName("Form") + Form.resize(715, 466) + self.verticalLayout = QtWidgets.QVBoxLayout(Form) + self.verticalLayout.setObjectName("verticalLayout") + self.horizontalLayout_2 = QtWidgets.QHBoxLayout() + self.horizontalLayout_2.setObjectName("horizontalLayout_2") + self.group_list = QtWidgets.QListWidget(Form) + self.group_list.setMaximumSize(QtCore.QSize(150, 16777215)) + self.group_list.setTabKeyNavigation(True) + self.group_list.setProperty("showDropIndicator", False) + self.group_list.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectItems) + self.group_list.setMovement(QtWidgets.QListView.Static) + self.group_list.setFlow(QtWidgets.QListView.TopToBottom) + self.group_list.setResizeMode(QtWidgets.QListView.Adjust) + self.group_list.setUniformItemSizes(True) + self.group_list.setSelectionRectVisible(True) + self.group_list.setObjectName("group_list") + item = QtWidgets.QListWidgetItem() + icon = QtGui.QIcon() + icon.addPixmap(QtGui.QPixmap(":/setting_tools.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + item.setIcon(icon) + item.setFlags(QtCore.Qt.ItemIsSelectable|QtCore.Qt.ItemIsEnabled) + self.group_list.addItem(item) + item = QtWidgets.QListWidgetItem() + icon1 = QtGui.QIcon() + icon1.addPixmap(QtGui.QPixmap(":/information.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + item.setIcon(icon1) + item.setFlags(QtCore.Qt.ItemIsSelectable|QtCore.Qt.ItemIsEnabled) + self.group_list.addItem(item) + self.horizontalLayout_2.addWidget(self.group_list) + self.widget = QtWidgets.QWidget(Form) + self.widget.setObjectName("widget") + self.horizontalLayout_3 = QtWidgets.QHBoxLayout(self.widget) + self.horizontalLayout_3.setContentsMargins(0, 0, 0, 0) + self.horizontalLayout_3.setSpacing(0) + self.horizontalLayout_3.setObjectName("horizontalLayout_3") + self.scrollArea = QtWidgets.QScrollArea(self.widget) + self.scrollArea.setFrameShape(QtWidgets.QFrame.NoFrame) + self.scrollArea.setFrameShadow(QtWidgets.QFrame.Plain) + self.scrollArea.setWidgetResizable(True) + self.scrollArea.setObjectName("scrollArea") + self.scrollAreaWidgetContents = QtWidgets.QWidget() + self.scrollAreaWidgetContents.setGeometry(QtCore.QRect(0, 0, 539, 413)) + self.scrollAreaWidgetContents.setObjectName("scrollAreaWidgetContents") + self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.scrollAreaWidgetContents) + self.verticalLayout_2.setObjectName("verticalLayout_2") + self.scrollArea.setWidget(self.scrollAreaWidgetContents) + self.horizontalLayout_3.addWidget(self.scrollArea) + self.horizontalLayout_2.addWidget(self.widget) + self.verticalLayout.addLayout(self.horizontalLayout_2) + self.horizontalLayout = QtWidgets.QHBoxLayout() + self.horizontalLayout.setObjectName("horizontalLayout") + spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + self.horizontalLayout.addItem(spacerItem) + self.ok_button = QtWidgets.QPushButton(Form) + self.ok_button.setObjectName("ok_button") + self.horizontalLayout.addWidget(self.ok_button) + self.cancel_button = QtWidgets.QPushButton(Form) + self.cancel_button.setObjectName("cancel_button") + self.horizontalLayout.addWidget(self.cancel_button) + self.apply_button = QtWidgets.QPushButton(Form) + self.apply_button.setObjectName("apply_button") + self.horizontalLayout.addWidget(self.apply_button) + self.verticalLayout.addLayout(self.horizontalLayout) + + self.retranslateUi(Form) + self.group_list.setCurrentRow(-1) + QtCore.QMetaObject.connectSlotsByName(Form) + + def retranslateUi(self, Form): + _translate = QtCore.QCoreApplication.translate + Form.setWindowTitle(_translate("Form", "Settings")) + __sortingEnabled = self.group_list.isSortingEnabled() + self.group_list.setSortingEnabled(False) + item = self.group_list.item(0) + item.setText(_translate("Form", "Main Settings")) + item = self.group_list.item(1) + item.setText(_translate("Form", "About")) + self.group_list.setSortingEnabled(__sortingEnabled) + self.ok_button.setText(_translate("Form", "Ok")) + self.cancel_button.setText(_translate("Form", "Cancel")) + self.apply_button.setText(_translate("Form", "Apply")) diff --git a/gui/ui/settings.ui b/gui/ui/settings.ui new file mode 100644 --- /dev/null +++ b/gui/ui/settings.ui @@ -0,0 +1,171 @@ + + + Form + + + + 0 + 0 + 715 + 466 + + + + Settings + + + + + + + + + 150 + 16777215 + + + + true + + + false + + + QAbstractItemView::SelectItems + + + QListView::Static + + + QListView::TopToBottom + + + QListView::Adjust + + + true + + + true + + + -1 + + + + Main Settings + + + + :/setting_tools.png:/setting_tools.png + + + ItemIsSelectable|ItemIsEnabled + + + + + About + + + + :/information.png:/information.png + + + ItemIsSelectable|ItemIsEnabled + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + QFrame::NoFrame + + + QFrame::Plain + + + true + + + + + 0 + 0 + 539 + 413 + + + + + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Ok + + + + + + + Cancel + + + + + + + Apply + + + + + + + + + + + + diff --git a/mount/__init__.py b/mount/__init__.py new file mode 100644 diff --git a/mount/mount.py b/mount/mount.py new file mode 100644 --- /dev/null +++ b/mount/mount.py @@ -0,0 +1,28 @@ +import subprocess, json + + +class MountException(Exception): + pass + + +def mount(source, target, fs, options=''): + NotImplementedError('Not yet') + + +def is_mounted(device): + proc = subprocess.Popen(['findmnt', '-J', device], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + result = proc.wait() + if result == 1: + return False + output = proc.stdout.read().decode('utf') + if output: + output = json.loads(output) + return len(output['filesystems']) > 0 + else: + return False + + +def unmount(device): + proc = subprocess.Popen(['umount', device], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + if proc.wait() != 0: + raise MountException(proc.stderr.read().decode('utf8')) diff --git a/packaging/debian/DEBIAN/control b/packaging/debian/DEBIAN/control new file mode 100644 --- /dev/null +++ b/packaging/debian/DEBIAN/control @@ -0,0 +1,16 @@ +Package: kcdemu +Version: %VERSION% +Section: utils +Architecture: all +Maintainer: Khaelenmore Thaal +Depends: python3 (>=3.5.0), python3-pyqt5, python3-dbus, python3-dbus.mainloop.pyqt5, cdemu-daemon (>=3.1.0) +Installed-Size: %SIZE% +Priority: optional +Origin: http://silverwing.one/pages/kcdemu +Description: QT5 application to control CDEmu daemon + This is KCDEmu, a QT5 application for controlling CDEmu daemon. It was written + in order to create a user-friendly way to interact with CDEmu. + . + It provides a graphic interface that allows performing the key tasks related to + controlling the CDEmu daemon, such as loading and unloading devices, displaying + devices' status and modifying device settings. diff --git a/packaging/debian/DEBIAN/copyright b/packaging/debian/DEBIAN/copyright new file mode 100644 --- /dev/null +++ b/packaging/debian/DEBIAN/copyright @@ -0,0 +1,7 @@ +Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: KCDEmu +Source: http://silverwing.one/pages/kcdemu +Comment: Incorporates icons licensed by CC-BY 3.0 http://www.fatcow.com/free-icons +Files: * +Copyright: Copyright 2018, Khaelenmore Thaal +License: GPL-3+ \ No newline at end of file diff --git a/packaging/debian/usr/bin/kcdemu b/packaging/debian/usr/bin/kcdemu new file mode 100755 --- /dev/null +++ b/packaging/debian/usr/bin/kcdemu @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +python3 /opt/kcdemu/kcdemu.zip diff --git a/packaging/debian/usr/share/applications/kcdemu.desktop b/packaging/debian/usr/share/applications/kcdemu.desktop new file mode 100644 --- /dev/null +++ b/packaging/debian/usr/share/applications/kcdemu.desktop @@ -0,0 +1,21 @@ +[Desktop Entry] +Version=1.0.0 +Name=KCDEmu +Comment=KCDEmu GUI +Comment[no]=KCDEmu GUI +Comment[ru]=Графический интерфейс KCDEmu +Comment[sl]=KCDEmu grafični vmesnik +Comment[sv]=KCDEmu-GUI +Icon=kcdemu +Terminal=false +Type=Application +StartupNotify=false +Categories=System; +TryExec=kcdemu +Exec=kcdemu +X-GNOME-Autostart-Phase=Applications +X-GNOME-Autostart-Notify=false +X-GNOME-Autostart-enabled=false +X-KDE-autostart-phase=2 +X-KDE-autostart-after=panel +X-KDE-StartupNotify=false diff --git a/packaging/debian/usr/share/icons/hicolor/32x32/apps/kcdemu.png b/packaging/debian/usr/share/icons/hicolor/32x32/apps/kcdemu.png new file mode 100644 index 0000000000000000000000000000000000000000..e59f7a69ea22ce0cdf946be3c24faffd6f0dc1ec GIT binary patch literal 2189 zc$@)^2y*v{P)P%|JT#6Bs`h!xnF&Awz zPNOERY0|_J8>(X)EydIU6(}|W1;ikNu*-6Xg@wJH?fabzE-OUsne)zf_Ut*|^SsY{ z`M#sTJs!V!=R|N+!lLI?D(z+{RcRi0z)s;%uu}mK?{47Tj^n*u-yD1Er5}ym%e;!a z{lDkv*@gOu=yHu#otk0@M{IZ?66X0ss}x_B%|T>t_1n-rFpkE~K{)KLrqKa&e#w?K zH*lNkeGyO{_{){UI?iwFV-Lo`7$1ZQ7cguG#vBCb2Gm4CO*5#-{MA6P76{XdleU?y zID4fVcKg(kq750((=GS?5YW6`eznsOt&h%GmVj{rzBLXE5xAD#(5mPG0@o-+66~6d zbRs$A_M|-7sk$!QFpZdB$$!W|Kb`kNK=bAo|GBv`)evJegreOFm?uO~P9*|qy@3_t zs8Wh5L~=}T4~2k-+pUCFr@+;YVVrO1G8L^)PxK|>OF;A17hiX+cpzcEAwCpsqx3Wh zr(xi;fU12|2qAElo5v)9GXi!858mxTaEJ~q<{?xy-|BrOKRv;lgf{`z{?Dr4PfCb+ z_E93*OyDZd9CCXTkbVo#5%`qd4F>_+83`Jvrg%7=JmM2WP}z7B-*lTlDE{%I+vJ>( z2`IMhd2eaz@>R`0T>T(AZGg+;0!w zy$4FA0vt&cK2L{!$0WYG($=!GAUmB^uRvhcQyXInw(aW4&rL(`Buxl$^4SQGBk)&= zrRRN&69LPB9X1<*69ETF&yy-z0@ji@TAI+0EF>}jpM2GTlfT;^d*RHdCN_;~&(Sl* zS()j-N?sHrCfyv;`>8~8^Bz=p+F>PcSrCJ$ES5!Sg(_)jwPMujgqmK^(!(5qYH1=3 zjqF+%z@etyjLY>`e_66+?LJ08x9`)c`qkNK#<0jBC_S@hIwvJ#$B+Y+*T>w7LD5EZOJ;loid_dCG(iNK7oxMD&ez-sTE&ufW5 zc9cM1--*kXU4_}Vkw{aahNW7boOHtw=a0{7Oz5+U1nMfwpZUYy9jDmiKiz%k2g`Hv zKV}LdO(JwNyIjjOQcRN)1M&B! zUbI;zC?}L~JMA4qmLAs85Ed2tzEZ<2a1ugFToBei6a{(}JTEphmjv#Taz&{D{zwFN z$^`bGxMU$Va8Ynyxue;LKuna5%1VI`t8NHhsVU3lB;*AzdFqD13u_lsuTa9~ z6!!=+@H7F4$2jJ5*q?51LyAqXCsS$Z~quBUaY(R+NRuqC1iJ%R&H)c4 zcw1W+sw>XE{oCDJONB;cSe$CyvF})Y;lt?|vpEEh*^Dp^jSIU9^3fy z-KVpnmyp*A0Z8lB$EQhfq7p7%F)gdpb9Vx6Nu}nhi}=&wy?M3g&s5Q^k?G}_Ec(@h zpByqSPJKE%F$oHiN=-y$V7Z&A3Eb+qi?_2#&y&tK8(Y!dSpV1Gy!6Zt!ZS}V$I`|m zkZi%;k1Hy(mnMfBW21%OXsBjcCu5$gDb?*%sv{0@QePnFECiV6Zkm12)C*H{bTkrN_^m{_ZkkL}qfFu+>$m#Ru53V3`6aM}_WoH}4rRv*8{F z-Y`(Sc}*To!M+m)eYa%On^Lj}rbOxSv!|CN#)Yk4l_IW$5GU;zlbXr50ZY41M$&Ta zzZfxh4;62EGEee+V6IK6FA2lTZ~XrCSVPiNYqE`azz~5!dd+0R%TExApy+3%^mnH(_*?{J5}~X-(;iu!y8hW0e;pj6Pc$UNA}BN*!C`tKU{0^t zJ18&4hDTr<9fZ}~LmSkQn`OrizIOG}`6gCwWW%_}4-@a4@R#x;G9o%I>gi1{Z~aI4@pn)6o4YM^yI+d84fpwJ=Ka4$A`(CrOhzAH&`CMz zeX!J^EiLGa0_@9pLIP%9|6d=cvnj&*g&)L?v+uhMgl%@HPT-i!W8dR{x5+0Nkhla} P00000NkvXXu0mjf;9@3Z diff --git a/packaging/debian/usr/share/icons/hicolor/scalable/apps/kcdemu.png b/packaging/debian/usr/share/icons/hicolor/scalable/apps/kcdemu.png new file mode 100644 index 0000000000000000000000000000000000000000..e59f7a69ea22ce0cdf946be3c24faffd6f0dc1ec GIT binary patch literal 2189 zc$@)^2y*v{P)P%|JT#6Bs`h!xnF&Awz zPNOERY0|_J8>(X)EydIU6(}|W1;ikNu*-6Xg@wJH?fabzE-OUsne)zf_Ut*|^SsY{ z`M#sTJs!V!=R|N+!lLI?D(z+{RcRi0z)s;%uu}mK?{47Tj^n*u-yD1Er5}ym%e;!a z{lDkv*@gOu=yHu#otk0@M{IZ?66X0ss}x_B%|T>t_1n-rFpkE~K{)KLrqKa&e#w?K zH*lNkeGyO{_{){UI?iwFV-Lo`7$1ZQ7cguG#vBCb2Gm4CO*5#-{MA6P76{XdleU?y zID4fVcKg(kq750((=GS?5YW6`eznsOt&h%GmVj{rzBLXE5xAD#(5mPG0@o-+66~6d zbRs$A_M|-7sk$!QFpZdB$$!W|Kb`kNK=bAo|GBv`)evJegreOFm?uO~P9*|qy@3_t zs8Wh5L~=}T4~2k-+pUCFr@+;YVVrO1G8L^)PxK|>OF;A17hiX+cpzcEAwCpsqx3Wh zr(xi;fU12|2qAElo5v)9GXi!858mxTaEJ~q<{?xy-|BrOKRv;lgf{`z{?Dr4PfCb+ z_E93*OyDZd9CCXTkbVo#5%`qd4F>_+83`Jvrg%7=JmM2WP}z7B-*lTlDE{%I+vJ>( z2`IMhd2eaz@>R`0T>T(AZGg+;0!w zy$4FA0vt&cK2L{!$0WYG($=!GAUmB^uRvhcQyXInw(aW4&rL(`Buxl$^4SQGBk)&= zrRRN&69LPB9X1<*69ETF&yy-z0@ji@TAI+0EF>}jpM2GTlfT;^d*RHdCN_;~&(Sl* zS()j-N?sHrCfyv;`>8~8^Bz=p+F>PcSrCJ$ES5!Sg(_)jwPMujgqmK^(!(5qYH1=3 zjqF+%z@etyjLY>`e_66+?LJ08x9`)c`qkNK#<0jBC_S@hIwvJ#$B+Y+*T>w7LD5EZOJ;loid_dCG(iNK7oxMD&ez-sTE&ufW5 zc9cM1--*kXU4_}Vkw{aahNW7boOHtw=a0{7Oz5+U1nMfwpZUYy9jDmiKiz%k2g`Hv zKV}LdO(JwNyIjjOQcRN)1M&B! zUbI;zC?}L~JMA4qmLAs85Ed2tzEZ<2a1ugFToBei6a{(}JTEphmjv#Taz&{D{zwFN z$^`bGxMU$Va8Ynyxue;LKuna5%1VI`t8NHhsVU3lB;*AzdFqD13u_lsuTa9~ z6!!=+@H7F4$2jJ5*q?51LyAqXCsS$Z~quBUaY(R+NRuqC1iJ%R&H)c4 zcw1W+sw>XE{oCDJONB;cSe$CyvF})Y;lt?|vpEEh*^Dp^jSIU9^3fy z-KVpnmyp*A0Z8lB$EQhfq7p7%F)gdpb9Vx6Nu}nhi}=&wy?M3g&s5Q^k?G}_Ec(@h zpByqSPJKE%F$oHiN=-y$V7Z&A3Eb+qi?_2#&y&tK8(Y!dSpV1Gy!6Zt!ZS}V$I`|m zkZi%;k1Hy(mnMfBW21%OXsBjcCu5$gDb?*%sv{0@QePnFECiV6Zkm12)C*H{bTkrN_^m{_ZkkL}qfFu+>$m#Ru53V3`6aM}_WoH}4rRv*8{F z-Y`(Sc}*To!M+m)eYa%On^Lj}rbOxSv!|CN#)Yk4l_IW$5GU;zlbXr50ZY41M$&Ta zzZfxh4;62EGEee+V6IK6FA2lTZ~XrCSVPiNYqE`azz~5!dd+0R%TExApy+3%^mnH(_*?{J5}~X-(;iu!y8hW0e;pj6Pc$UNA}BN*!C`tKU{0^t zJ18&4hDTr<9fZ}~LmSkQn`OrizIOG}`6gCwWW%_}4-@a4@R#x;G9o%I>gi1{Z~aI4@pn)6o4YM^yI+d84fpwJ=Ka4$A`(CrOhzAH&`CMz zeX!J^EiLGa0_@9pLIP%9|6d=cvnj&*g&)L?v+uhMgl%@HPT-i!W8dR{x5+0Nkhla} P00000NkvXXu0mjf;9@3Z diff --git a/pics/__init__.py b/pics/__init__.py new file mode 100644 --- /dev/null +++ b/pics/__init__.py @@ -0,0 +1,774 @@ +# -*- coding: utf-8 -*- + +# Resource object code +# +# Created by: The Resource Compiler for PyQt5 (Qt v5.9.5) +# +# WARNING! All changes made in this file will be lost! + +from PyQt5 import QtCore + +qt_resource_data = b"\ +\x00\x00\x09\x0b\ +\x89\ +\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ +\x00\x00\x20\x00\x00\x00\x20\x08\x06\x00\x00\x00\x73\x7a\x7a\xf4\ +\x00\x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\x74\x77\x61\x72\x65\ +\x00\x41\x64\x6f\x62\x65\x20\x49\x6d\x61\x67\x65\x52\x65\x61\x64\ +\x79\x71\xc9\x65\x3c\x00\x00\x08\xad\x49\x44\x41\x54\x78\xda\xac\ +\x57\x0b\x50\x54\xe7\x15\x3e\x7b\xef\xee\xb2\x20\xe1\xbd\x08\x88\ +\x20\x88\xca\xf2\xa8\x0a\xd4\xa6\x51\xb1\x6a\xad\x71\xd4\xea\xc6\ +\x47\x68\xc7\x41\x6d\x6a\x3a\x99\x69\x13\x8d\xa1\x2a\xf8\x4c\xc0\ +\x21\x16\x93\x31\x26\xa9\x9d\x38\x65\xf2\xb0\x51\x51\x30\x9d\xc6\ +\xb4\x4d\xc2\x63\x51\xa4\xa0\x93\xa2\xf2\x5e\x41\x81\x7d\x2f\xbb\ +\xec\xee\xdd\xf7\xab\xe7\xbf\xb9\x9b\xac\x88\xc8\xb4\xbd\x33\x67\ +\x76\xf7\xde\xff\xfe\xdf\x77\x5e\xdf\xf9\x97\x07\x53\xbc\xb6\x6d\ +\xdf\x11\x99\x98\x90\x78\x42\x24\x12\x15\x27\x24\x24\x88\x92\x67\ +\x24\xc1\xb4\x69\xd3\x80\xa6\x69\x90\xdf\xbb\x07\x3d\x3d\xbd\xb2\ +\x93\x7f\x78\x73\x25\x2e\xf5\x1d\x7d\xbd\x62\x2f\x45\x51\xbb\xbd\ +\x5e\x6f\x92\x58\x2c\x06\x8d\x46\x03\x3c\x1e\x3e\xf0\x7a\x6b\xed\ +\x0e\xc7\x01\x5c\xd7\x8f\xeb\xfc\x64\x5f\xde\x14\xb0\xe9\x03\x65\ +\x87\xfe\x28\x10\x08\x76\x49\x37\x6e\x80\xf9\xb9\x39\xc0\xa3\x28\ +\x50\x28\x95\xd0\xda\xfa\x2f\xfc\x54\x41\x5f\x5f\x5f\xe3\x7b\xa7\ +\x4f\xed\xc0\xb5\x86\xf2\xe3\x6f\xf6\x25\x25\x24\x26\x2c\x5b\xbe\ +\x14\xc4\x71\x71\xe0\xf7\xfb\x11\x9c\x07\x4e\xa7\x13\xfa\xfb\xef\ +\xc1\xd7\xf5\x0d\xa0\x50\x0c\x67\x9e\x79\xff\xbd\x3e\x42\x82\xff\ +\x24\xf0\x7d\x07\xca\x6a\xf2\xf3\xf2\x10\x7b\x3d\x0b\x6c\x65\xac\ +\x50\xdf\xd8\x04\xb2\xe6\x66\xe8\xea\xec\xac\x64\x18\xcb\x70\xb3\ +\xac\xa9\x05\xd7\xba\xdf\xa8\xa8\xec\x9b\x37\x77\x6e\x42\x61\xe1\ +\x12\xe8\xb8\x7d\x17\xae\x5f\x6f\x05\x8c\x04\x6b\xf1\xd3\xe3\x21\ +\x37\x3b\x0b\x92\x93\x93\xe0\x83\xb3\x7f\xee\xc1\xf5\x22\x34\xe7\ +\x64\x04\xe8\xfd\xa5\x07\x6b\xf2\x0b\xf2\xa4\x9b\xa4\x1b\x61\x74\ +\xd4\x00\x06\xa3\x11\x1a\x10\xbc\xb7\xb7\x57\xff\x56\xd5\x89\x2d\ +\xb8\x46\x87\x66\x46\x63\xf6\x96\xec\x7f\x7e\x46\x62\x12\x82\x2f\ +\x46\x82\x32\xf0\x7a\x3c\x80\x51\x03\xb3\xd9\x4c\x3c\x86\xae\xee\ +\x2e\x68\xa8\xaf\x67\x83\x4e\xd3\x14\xd9\x3f\x6c\x32\x02\x24\xec\ +\xe8\x79\x3e\x82\x6f\x80\x07\x43\x23\x60\xb5\x59\xa1\xa9\x49\x06\ +\x72\xb9\x5c\x81\xe0\x5b\x71\xcd\x10\x09\x39\x9a\x0b\x4d\x18\x11\ +\x11\x71\x94\x84\xbd\xe3\x76\x27\x0b\x4e\x51\x34\x5b\x1f\x26\x93\ +\x09\x5e\x3f\x7a\x64\x0d\x89\x50\xd0\xfe\xc6\xc0\x17\x6a\x62\xf0\ +\xc3\x35\x3f\x2c\x28\x90\x6e\x7a\x6e\x03\xdc\x7f\x30\x84\x9b\x8c\ +\x01\x85\x79\x8c\x8d\x8d\x01\xc6\x62\xf9\x86\x03\xd7\xa0\xd9\xd0\ +\x3c\x68\x21\x98\xeb\xe9\xf1\x71\x62\x50\xab\xd5\x6c\xce\x29\x8a\ +\x07\x7c\x3e\x9f\x2d\x3e\xbc\x06\xd1\xee\x04\x19\xf9\x6d\x25\x0f\ +\xc6\x47\x80\x7f\xf0\xf0\xb1\xda\xfc\xbc\x85\xeb\x49\xce\x07\x06\ +\xef\x63\x08\x19\x70\xb9\x9c\xe0\xf1\x7a\x61\xeb\xe6\x4d\x40\x53\ +\xd4\xba\x88\xc8\x48\xa8\x3a\x51\x29\x0d\x7a\x4f\x40\x8a\xcd\x6c\ +\xb1\x00\x41\x24\x04\x88\xf7\x42\xa1\x20\xd8\x63\xed\x44\xa1\x1e\ +\x1f\x81\x50\x1e\x8f\xe2\xc0\x07\x11\xdc\x84\xd5\xeb\x00\xa7\xc3\ +\x09\x7a\xfd\x28\x16\xde\x75\xd8\xf4\x9c\x94\x6c\xbe\x8e\xac\x0d\ +\x7e\x91\x10\xe0\xd3\x7c\xb6\xad\xbe\x05\xe7\xb3\x35\xf0\xa4\x46\ +\xa3\xc6\x15\x5d\x75\x7e\xde\x02\x50\xaa\xd4\x30\x36\x66\x06\xbb\ +\xc3\x05\x0e\x87\x03\xec\x84\x04\xb6\x91\x46\xab\x83\xaf\xea\x1b\ +\x59\x30\xae\x8a\xbf\xbb\xbc\x1e\x97\x6a\x70\x70\x00\xe2\xb1\xef\ +\x05\x02\x3e\x92\x40\xe3\xd3\xe0\xf7\xf9\x26\x25\x40\xe8\xf1\xcb\ +\x8f\x57\x5e\x11\x8b\xe3\xd7\xa6\xa6\xcc\x84\x8c\xd9\xe9\x6c\xde\ +\xc3\xc3\xc3\x11\xd8\x09\x2e\x34\x07\x12\x21\x69\x40\x61\x81\x81\ +\x81\x41\xc0\x64\x83\x52\xa1\xa8\x3b\x74\xa8\x8c\x74\x82\x17\x2d\ +\x76\xf7\x9e\xbd\x75\x45\x45\x45\x4b\xa3\xa3\xa3\xa1\x5f\x2e\x67\ +\xf3\x2f\x0a\x09\x01\x35\x8a\x50\x7b\x7b\x3b\xdb\x8a\x2c\x20\x5b\ +\x1f\x14\x54\x1e\x2f\x0f\x21\x05\xcc\xe7\xc2\xbe\xf6\xc5\x5f\xef\ +\x64\x17\xd4\x37\x34\xc1\x20\x86\x9f\x14\x4f\xea\xac\x34\x04\x76\ +\x81\xc7\xed\x62\x9f\x0d\x0d\x0d\x83\x48\x24\x64\xd3\x30\x66\xd0\ +\x49\x9f\xa2\x7d\xe7\x77\x97\x1e\xda\xf6\x46\xe9\x81\x33\xab\xd6\ +\xae\x5f\x3a\x6f\x6e\x06\xfc\x1b\xfb\x9f\x44\x08\x15\x93\x25\x91\ +\x9e\x9e\x0e\x59\x12\x09\xfb\x5b\x20\xa0\x41\xa5\x54\xc3\xe5\xba\ +\xba\x01\xae\x0d\x59\x02\x42\x8b\xc5\xd4\x54\x73\xa9\x76\x99\x04\ +\x17\x86\x86\x8a\xd8\xc0\x58\x2c\x0c\xf6\x7b\x1f\xe8\x74\x1a\xf6\ +\x37\xd9\x34\x32\x22\x02\x16\xcc\x9f\x0f\xb7\xda\xdb\x20\x2b\x27\ +\x1b\x3e\x0e\x5f\xb6\x39\xe6\xc8\xe7\x52\xcf\x1c\xa0\xf3\x7f\x90\ +\x05\x6d\x1d\x9d\x60\xb7\xdb\xd0\xec\xac\xf7\x31\x51\x51\xec\x7b\ +\x6c\x81\xa2\x1e\x74\xf7\xf4\x40\x77\x77\x37\xea\xc1\xd7\x7b\x83\ +\x53\xf0\x14\x5a\xda\xde\x92\x7d\xa7\x33\xe7\x49\x0a\x67\xcf\x49\ +\x07\x37\x7a\x2d\x97\x0f\x40\x6f\x4f\x77\x7d\x5b\x5b\xeb\x25\x01\ +\x5f\xc0\xc3\x28\xf8\xd3\x52\x66\x6e\xd9\xb8\x61\xc3\xf2\xa8\xb8\ +\xe9\xf0\xea\x37\x6e\xd8\xb3\x76\x25\xb4\xa9\x18\x68\x1f\x52\x41\ +\x8a\x5b\x05\xaf\x66\xf8\x60\x48\xa3\x87\xe6\xeb\x2d\x8a\xc8\xc8\ +\xc8\x18\xac\x94\xd0\x69\x61\x61\xe8\x8c\x85\x05\xc3\xa2\x6e\xf9\ +\xe0\x4f\x67\xf6\xe1\xd7\x07\x5c\x1b\xbb\x78\x5c\x2b\x46\xa1\xa5\ +\x20\x89\xb7\x67\xcf\xce\x28\x4c\x4e\x4e\x86\x8e\x8e\x0e\x38\x54\ +\xb6\xff\x69\xbc\xaf\xe7\x44\x84\x94\x74\xcc\xd6\xcd\x9b\x8f\xf5\ +\x2e\xda\xb5\xe6\xb7\xcf\x2e\x83\xbb\x3a\x3b\xa8\x19\x07\xb8\xfd\ +\x14\xdc\xbc\x37\x08\xeb\x68\x39\xf8\xee\x36\x6a\xcf\x9c\x3d\xfb\ +\x4b\xae\x36\x28\x1c\x5c\xe1\xa8\x0d\x56\x4e\x2f\x4c\x1c\x70\x40\ +\xc0\xfc\x34\x99\x5e\x44\x12\xc9\x8d\x1b\x2d\xd7\x5b\x25\x59\x39\ +\xb9\x18\xae\x99\x8c\xc5\xd4\x72\xed\x5a\x73\x35\xde\x57\x05\xe4\ +\x36\x74\x45\x71\x8c\x75\xc5\x4b\x55\x25\x6b\x16\xc3\x6d\xad\x1d\ +\x54\x16\x3b\xb8\x7d\x7e\xf0\xa0\x45\x84\x47\x81\x6c\xd8\x00\x8d\ +\x6f\x97\xfc\x0c\xd7\xde\x47\x53\x12\xf2\x0c\xc3\x28\x39\x0d\xd0\ +\x70\xd2\x6d\x0a\x56\xc5\xe0\x26\x25\x64\xa2\xd1\x92\xd0\x22\x38\ +\xf1\x20\x8a\x47\xe2\xc7\x8b\x79\xe9\xdd\xd9\xe9\xd9\x79\xfd\x15\ +\xeb\x7f\x0c\x57\xe5\x46\x50\x5a\x1c\x2c\xb0\x1b\xfd\x74\xfb\x79\ +\xa0\xc0\x39\x11\xe6\x35\xc1\x26\xfd\x97\x9f\xed\x3b\x7c\xa4\x08\ +\xdf\x71\x04\x61\xf8\x1f\x3b\x70\x82\xb5\x84\x0b\x0b\xc3\x81\x93\ +\x30\xd9\xc9\xfd\x98\xdf\xbc\x93\x86\xe0\xf2\xf2\xf5\xcf\xb0\xe0\ +\x2a\xc6\x8e\xe0\xc0\x82\x7b\x10\x5c\x89\xe0\x5e\xfd\x00\x7c\xb5\ +\x2d\x1b\x24\x92\x79\x99\xb3\xe2\xc5\x99\x7f\xaf\x6f\xa8\x9d\x0c\ +\x78\xa2\x08\x4c\x28\x54\xb1\xfb\xce\x17\x2f\xca\x9c\x5b\xfd\xf2\ +\xf2\x85\xf0\xc5\x38\x70\x2f\x82\xab\x71\x4e\x78\x0c\x0f\xe0\x54\ +\x9e\x00\x68\x9e\x0f\x7e\x94\xbf\x00\x76\xfe\xb5\x0f\xda\xf5\x3e\ +\x30\x99\xcd\x57\x74\xc7\xd6\x6d\xe1\xf2\x0f\x53\x91\xe2\x87\x9e\ +\xc5\x94\x9c\xdb\xbe\x7c\x7e\x4e\x75\xd9\xb3\x0b\xe1\x6a\xbf\x81\ +\xcd\x79\x30\xb8\xce\x6c\x84\x61\xad\x12\x24\xb7\xaa\xff\x69\xd0\ +\xa9\x41\x80\x02\xb3\xf1\xc3\x76\x18\xa3\x22\xe0\x2f\xdb\x97\xc2\ +\xb2\x79\x69\x1b\xe3\x0e\xd6\x5d\x1c\x17\xe9\xc7\xa6\xe0\x21\xf0\ +\xe8\xd7\xce\x15\xaf\x58\x38\xbf\x7a\xf7\x8a\x6c\xb8\x70\xd7\xc0\ +\x56\xbb\xd7\xff\x3d\xb8\xde\x6c\x00\xa5\x7a\x04\x8c\x1f\x96\x15\ +\xdd\x6e\xfe\xf2\x6a\x98\x50\x30\xeb\x6f\x82\xec\x34\x41\x7c\x1a\ +\xfc\x7e\x55\x0e\xfc\x43\x6e\x81\x5f\x2c\x48\x06\xb5\xd5\x2b\xd1\ +\x49\x56\xe5\xda\x64\x17\x2e\x4d\x94\x92\x89\x52\xc0\x8f\x2e\xab\ +\xbb\xf8\x62\x61\x81\x74\x6d\x6e\x32\x5c\xec\x1c\x45\x70\xe7\x04\ +\xe0\xc3\x60\xbc\x50\xf9\xbc\xed\x4e\xd3\x4d\xae\xe0\x12\xe7\xbe\ +\x73\xe3\xe6\x27\x3b\x9e\x86\xb7\x1a\xb5\x60\x45\x2d\x99\x15\x15\ +\x06\x5b\xb3\x63\xe0\x54\x43\x27\x34\x76\xf5\xd7\xe9\xcb\xa5\x01\ +\xe9\x7e\x6c\x04\xe8\xe8\xd2\xda\x9a\x3d\xab\x17\x4b\x7f\x9a\x99\ +\x08\x17\xbb\x46\x41\xf3\x38\xf0\x4f\x2b\xb6\xd8\xee\x36\xb7\xe1\ +\x3b\xea\x40\xa7\xf0\x72\x56\x14\x5c\xe9\xb5\xa6\x7a\xa9\x70\x20\ +\x87\x1e\x87\xdb\x03\x23\x16\x0f\xfc\x6a\x51\x2a\x68\x30\x12\xda\ +\x09\x22\x41\x3f\xe4\x79\x69\xed\x25\x02\x5e\x98\x11\x0f\x35\xe8\ +\xb9\xc6\x3a\x31\xb8\xb3\xab\xa5\xc2\x54\xff\xc9\xe7\x1c\xb8\x93\ +\xd3\x12\xbf\xad\xa5\xf6\x46\x68\xee\x92\x6c\x0f\x5f\x98\x1a\x22\ +\x8a\x64\xe3\x6b\x77\x4d\x4e\x22\x40\x80\x16\x97\xd5\x9e\xdd\xf5\ +\x93\x45\x45\x2b\xd1\xf3\x47\xc1\xb1\x2f\x19\x0b\x28\x54\x0f\xc0\ +\xd9\x79\xed\xb8\xfe\xd3\xf2\x8f\x38\x81\x72\x04\x79\x43\x48\xb8\ +\x99\x1b\x9f\xb5\x86\xe4\x2c\x91\xb8\x69\x61\xaa\x30\xf4\xc9\x24\ +\x02\x04\xa6\xc5\xae\x79\xe1\x62\xe9\xea\x05\x58\x70\xc1\xe0\x7e\ +\xd6\x73\x06\x0f\x24\x23\xca\x01\x70\xdc\x91\x55\xe8\xcf\x1f\xff\ +\x98\x03\xb7\x8f\x2b\xaa\xef\x14\x75\x32\x12\x2f\x20\x09\xad\xcd\ +\x27\xd1\x67\xaf\xce\xb3\x36\x9d\xaf\x09\xb4\xa1\x68\x8e\x38\x0a\ +\x1f\xe0\xce\x98\xf3\x6f\x5b\xed\x7b\xf0\xfb\x23\xfd\xe0\x36\xa8\ +\x65\x08\x7e\x8e\x93\x58\x2b\x07\x38\xfe\xf2\x06\x14\x54\x75\x72\ +\xe7\x1e\x46\xd1\x23\xd3\x6a\x87\xc1\x8a\xc2\x6b\x45\x25\x18\x36\ +\x39\xe0\x72\xb7\x11\x4a\x56\x65\xe1\x1f\x02\x01\x7b\xaa\x0a\x10\ +\xa0\xd4\x16\x1b\x50\x38\x96\x08\x38\xb1\x60\x70\xcf\xa8\x4a\xa6\ +\xaa\xda\xfe\x0a\x97\xf3\xc7\x81\x4f\x4a\xc2\x86\x24\x6c\x48\x42\ +\xc9\xb8\xe0\x95\xda\x5b\xc0\x63\x8c\x4d\x64\xc0\x05\x52\x10\xc6\ +\x2b\xf8\xf9\x96\xac\x99\x71\x51\x1a\x3b\x1f\x9c\xb8\x85\x19\x8f\ +\xe1\x43\x8a\x7b\xa8\x72\xaa\x66\xd5\xc9\x1d\x7b\x70\xcd\x30\x37\ +\x94\xbc\x53\xf8\x37\xe5\x7f\x24\x1d\xfc\x90\x54\x51\x58\x34\xb2\ +\x1a\x82\x11\x79\xa7\x4c\x53\x55\xfc\x32\x3e\x1f\x0d\xe8\x00\x26\ +\x0a\x32\x12\xca\x2e\xd7\xf2\x44\x61\x29\x78\xa2\x66\x8f\x4e\x5e\ +\xed\xf0\x17\xaa\x53\xbb\x0e\x72\x43\xc9\x38\x45\x70\x98\x60\xc0\ +\xa5\x24\xbe\xf6\xd1\x69\xa1\x38\xf9\x19\x97\x6e\xa4\x45\x55\x55\ +\xfc\x3b\x6e\xcf\xb1\x00\x01\x32\xeb\x63\xd1\x66\x70\x93\x90\xe2\ +\x46\xa6\x91\x2b\xb8\xb1\xc9\xf4\x7c\x8a\x24\x02\x53\xd6\xcc\xd5\ +\x11\xeb\x50\xb0\x12\x0a\xb8\x73\x9a\x90\x53\x48\x1f\xd7\x66\xf6\ +\xff\xc2\xf3\x47\xd4\x95\x3b\xc6\x0b\x38\xc7\xec\xff\x83\x43\xff\ +\xdf\xeb\x3f\x02\x0c\x00\x74\x6a\x81\xfc\x7e\x57\x10\xb3\x00\x00\ +\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\ +\x00\x00\x08\x8d\ +\x89\ +\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ +\x00\x00\x20\x00\x00\x00\x20\x08\x06\x00\x00\x00\x73\x7a\x7a\xf4\ +\x00\x00\x08\x54\x49\x44\x41\x54\x78\xda\x9d\x57\x6b\x50\x54\xe7\ +\x19\x7e\xf6\x7a\x58\xae\xcb\x4d\xe4\xba\x28\xa2\x2e\x21\x04\x21\ +\x42\x8c\xa9\xad\x84\xd4\x8c\x91\x06\xeb\x40\xeb\x4c\xaa\xed\x24\ +\xea\x4c\x6a\xc6\x5c\x8a\x11\x93\xfa\x83\x52\xb5\x31\x17\x6d\x32\ +\x4e\xa3\x26\xad\x69\xd2\xc4\x12\x1b\xaa\x63\x1b\x2d\xc5\xd4\x01\ +\x15\x28\x36\x02\x05\xc4\x20\x82\xb0\xcb\x72\x87\x85\x85\xbd\x9d\ +\xed\xfb\x9d\x0b\x2e\x2b\x44\xed\x99\xf3\xce\x77\xf6\xec\x39\xdf\ +\xf3\xbc\xcf\x7b\xf9\xbe\xa3\xc0\x3d\x1e\xbf\x78\xe7\x44\x70\x54\ +\xc2\xa2\xe7\x54\x2a\xed\x66\x28\x55\x69\x1e\x78\xc0\x4e\xe1\x50\ +\xb0\x53\x01\x0f\xef\x6e\xe0\xdd\x8e\xe3\xbd\x5d\xdf\x1c\x7b\xeb\ +\xa5\x1f\x8d\xdd\xcb\xbc\x8a\xbb\xfd\xbf\xe7\xe8\xd9\x85\xfa\x88\ +\xe8\x72\x8d\x56\x9d\x9a\x62\x08\x47\x6c\x78\x20\x12\xe6\xf9\x43\ +\xab\x14\x5f\x96\xcd\x41\x64\x6e\xf5\xdb\xd0\x3d\x30\x8e\xc6\xce\ +\x41\x38\xec\xae\xa6\xd1\x01\x73\x7e\xc9\x96\x35\x37\x70\x9b\xea\ +\x7d\x11\x50\x1d\xf8\xcb\x95\xc3\x3a\xce\x6f\xeb\x63\x0f\xc6\xc1\ +\x18\x1f\x84\x09\x17\x30\x6c\x07\xc6\x1c\x04\xe8\x06\xd4\x44\x42\ +\x4d\x33\xa8\xc8\xfc\xd5\x40\xb0\x16\x08\xd3\x8a\x93\xb6\x99\xad\ +\x38\x7b\xa5\x1b\x76\xfb\xd4\x91\xa2\x0d\x19\xcf\xd3\x2d\xf7\xfd\ +\x10\xd0\xbc\x5d\x7e\xb5\xd3\x10\xad\x8f\xce\x59\x96\x80\x71\x02\ +\xbe\x35\x0e\x0c\x11\xb8\x96\xde\xd0\xaa\xe8\x01\x02\xd7\x28\x44\ +\x12\xec\x9a\x8d\x74\x22\x39\xe4\xf6\xa4\x3c\xd9\xa9\xba\x2e\xdc\ +\x30\x8d\x98\x5f\xc9\x7f\xc8\x40\x3f\x9d\xf7\x42\x40\xf3\xe6\x17\ +\xff\xb9\xb9\x32\xd5\x10\x63\x34\x84\xa2\xdd\x0a\x98\x27\x44\x50\ +\x4e\x25\x02\x69\xbd\x81\x15\xe2\xa8\x52\x8a\x2a\x44\x72\x4c\x6f\ +\x0f\x85\x80\x87\xdb\xad\x84\x56\xa7\xc0\xd5\x8e\x61\x9c\x6f\xe8\ +\x32\x15\xad\x4f\x4f\xf4\x25\xe1\x4b\x40\xf3\xd6\x17\x5f\x77\xad\ +\x78\x20\x61\xbe\x21\x3e\x14\x6d\xa3\xf4\x34\x09\xa7\x61\xe0\xb3\ +\x80\xaa\x7d\x54\x08\x21\x70\x95\x9b\xc7\x24\x81\x33\x02\x76\x07\ +\x0f\xde\xed\x41\x70\x88\x0e\x2d\xe6\x21\x54\x35\xdf\xea\x7d\x25\ +\x3f\x3d\xc1\x9b\x84\x37\x01\xd5\xfe\xcf\xaa\xdf\x4f\x4a\x88\x79\ +\xf6\x51\x22\xda\x4c\xe0\x2a\xcf\x1c\x72\x7b\x13\x90\x7e\x0b\xcf\ +\x11\xf8\x94\xdd\x0d\x07\x01\xdb\x19\x09\x1a\xa7\xa6\x78\x38\x9d\ +\x3c\xe2\x13\x43\x50\xd9\x78\x13\xdf\x74\x9b\x3f\x28\xfe\xf1\xa3\ +\xdb\xe4\x9c\x90\x09\x28\xb6\xed\x79\x6f\x69\xea\xf2\x55\xcd\x3f\ +\x5c\xfd\x20\x3a\x6d\x80\xcb\xe3\xe3\xf5\xb7\x90\x10\x2a\xc2\xc3\ +\xc3\x66\x75\x09\x5e\xcb\xe0\x6c\x64\x84\xa6\xa6\xdc\xe0\x38\x35\ +\x12\x92\x82\xf1\xe7\xea\x66\x34\xd5\xfe\x2b\xe5\x48\xc9\xf6\x56\ +\x56\x1d\x32\x01\xed\x81\xb2\x9a\xaf\xbf\x93\xb1\xc4\x18\x18\x1e\ +\x22\x64\x39\x37\x07\xe8\x0c\xaf\x95\x62\x78\xd8\xf5\xe8\x98\x13\ +\x56\x22\xc0\xa4\x77\x38\x45\xf9\xa7\xa6\x5c\xe4\xbd\x07\x4a\xa5\ +\x02\x1c\x49\x14\x3e\x4f\x87\x7e\xc7\x24\xbe\xba\xd2\xd6\xb2\xb3\ +\x20\x3b\x9d\x55\xaf\x40\x60\xd5\x53\x1b\x63\x0a\xb6\xed\xea\xc9\ +\xcf\x4d\x43\xef\x24\x4d\x08\x71\xf2\xd9\x08\x90\x23\xf0\x57\x89\ +\xa5\xe7\x7d\x8c\x13\x01\xcb\x80\x1d\x36\x1b\x81\x13\x01\x07\x49\ +\xcf\x93\x2a\x5a\x02\xd6\x92\x37\x5a\x9a\xd0\x8f\x2c\x24\x32\x00\ +\x9f\x7c\xd5\x80\x93\xbf\xdb\x1f\x7b\xe1\xcc\xa7\x26\x36\x8d\x6a\ +\xcf\xd1\x33\xc5\x59\x99\xe9\xbf\x4a\x5e\x14\x23\x26\xdd\x1c\xd2\ +\xfb\xa9\x44\x73\xf3\x1e\x54\x77\xda\x61\x25\x6f\x59\x10\x83\xa8\ +\x2c\x96\x45\x69\x85\x2a\x69\x69\xb5\x62\xd4\xea\x84\x9a\x5e\xd0\ +\xd2\xc3\x1c\x81\x6a\x69\x22\x0d\x8d\xec\x5a\x17\xc0\xa1\xa6\xdd\ +\x8c\xcb\xf5\x57\x7f\x59\xb2\x65\xed\x3e\x46\x40\xb7\xef\xd3\xaa\ +\xfa\xd5\xd9\x69\xc6\xb0\xc8\x20\x28\x3d\xb3\x67\x3a\x27\x25\x63\ +\xc7\x90\x03\x95\xd7\xc7\xe1\xa6\xe8\x29\x15\xa2\x0c\xc4\x87\x14\ +\xf1\x60\x45\x9c\x0e\x89\x7a\x35\x9a\xaf\x59\x49\x76\xf2\xd8\x4f\ +\x04\xd6\x7a\x59\x00\xd5\x6a\xb3\x79\x1c\xe7\x2e\x35\xb6\x14\x6f\ +\x5c\x99\xc9\x66\x08\x7d\xf3\x64\xed\xd0\xfa\x27\x96\x0b\xf1\xa4\ +\xf3\x0e\xe9\xe5\xfb\xcc\xf3\xc3\x17\x06\xc0\xd1\x4d\x16\x02\x09\ +\x1f\x1e\x8f\x48\x02\x0a\x1e\x85\x69\xa1\x70\x52\xd2\xf5\x0d\x3a\ +\x88\x80\x4a\x00\xd5\x48\x2a\x70\x9c\xa8\xc2\x28\x35\xb4\x0f\xff\ +\x56\x87\xa2\x0d\x59\x61\x6c\x8a\xc8\x03\x65\xb5\x7d\x3f\xc9\x5b\ +\x2e\x78\xaf\xf2\x06\x96\xae\xe5\xa3\xa2\x65\x04\xd7\x2c\x93\xd0\ +\xd0\x4d\xfb\x84\xd5\x54\x51\x76\xf4\x20\xbb\x9f\x5b\xb0\xe5\x45\ +\x2e\x20\x28\xc6\x49\x35\x9f\x1c\xce\xe1\xf1\xa5\x7a\x74\xf6\x4c\ +\xce\x20\xc0\xc0\x59\x32\xd2\x09\x6a\xac\x78\xf7\x64\x1d\x76\x16\ +\x64\xcd\x63\x04\xe6\xbd\x51\x56\x63\xd9\x92\x9f\x25\xc8\xae\x94\ +\x6a\x5f\x3d\x4b\x8f\xfc\xbc\xde\x02\xf3\x08\xc5\x97\x58\xfe\xb3\ +\xec\xc8\xf6\x93\x87\x4b\x2a\xd9\xfd\x0d\xcf\xef\xc9\x79\xbc\x60\ +\xeb\x7b\x2e\x22\x10\x15\xac\x41\xe1\xc3\x51\x30\xf7\x3b\xc0\xf9\ +\x89\xc0\x8c\xb0\xb8\x62\x42\x20\xc0\x6a\xef\x9d\xcf\x6b\x89\x40\ +\x76\x94\x40\x60\xdf\x89\xcb\x96\x5d\x85\xd9\xb7\x91\x48\x53\xa1\ +\x86\xa5\x5a\x9e\x9c\x74\xc3\x10\xe7\x8f\xcf\x6a\x4c\xe8\xb3\x8a\ +\x04\xea\x2a\xcb\x9f\xf9\xc3\xde\x1d\xa7\xd8\xe3\x3f\xdd\x7d\xe8\ +\x07\xcb\x73\xf2\x3f\x66\x0a\x44\x87\x68\x51\x98\x1d\x2d\xf4\x11\ +\x39\x3c\xd4\x9f\x40\xd5\x08\x17\x2f\x76\x1f\x05\xf1\xf9\xa0\xbc\ +\x06\xaf\x16\x3e\x72\x27\x01\x37\x4d\x22\x74\x33\xbb\x5c\xcb\x34\ +\x52\x4c\x93\x12\x03\xf1\xf7\xa6\x5e\xb4\x59\x26\x28\x27\x94\x70\ +\x3b\xed\x1d\x43\x96\x1e\xd6\xd1\x10\x16\x15\xfb\xbe\x52\xc3\x2d\ +\x70\x12\x42\x4a\x5c\x10\xd6\x3e\x14\x05\x7a\x15\x3c\x2f\x36\x34\ +\x97\x04\xee\x92\x72\x45\xa9\x01\xfe\x48\x04\x76\xca\x04\xf6\x9f\ +\xb8\x64\x21\x36\x70\x51\x70\x6c\x93\xce\x69\x70\xb9\xa3\xd9\x88\ +\x40\x4c\x94\x8e\xca\x4a\x81\x0f\xab\x6e\x08\x5e\xa9\x29\xcb\xe5\ +\x24\xe4\x05\x2f\x79\xea\x86\xc0\x0b\x6b\x17\x53\xaf\x50\xc2\xe6\ +\x14\xf7\x08\x32\xf0\x34\x01\x88\xe3\xa9\x2f\x2f\xa3\xa8\x70\x85\ +\x40\x20\xb2\xe4\xa3\x8a\xea\x8d\xdf\xcb\x48\x9e\xaf\x0f\xa6\x46\ +\xe2\x14\xa5\x77\x88\x0b\xca\x14\x33\x0a\x81\x92\x64\xcf\x48\xd7\ +\xa3\xb5\x67\x14\x5f\x36\x98\xc8\x3b\xb1\xc3\x41\xaa\x0e\x96\x33\ +\xeb\xb2\xe2\x91\x12\x1b\x02\x2b\x03\xe7\xef\x04\x67\x23\x8b\x7f\ +\x6f\xff\x18\x2e\x5e\xac\xbb\xfe\xda\xa6\xdc\x95\x42\x19\x6e\xdf\ +\x7b\xe4\xf5\x75\xb9\xb9\x2f\x67\x2e\x88\xc5\xf8\xb8\x48\x40\x06\ +\x77\xd0\xce\x83\x01\xb1\x4c\x0e\x0a\xd2\x20\x39\x31\x00\x36\x87\ +\x8b\x16\x16\x33\x46\x6c\x76\x81\x80\x3e\x40\x8b\x27\x33\xe2\x85\ +\xf2\x64\x6d\x9c\x04\xbb\x03\x58\xce\x01\x0f\x25\x78\x5b\x5b\x17\ +\xaa\x2b\xce\xbe\xfd\xdb\xdd\x5b\x4b\x85\x46\x64\x58\x9c\x6a\xdc\ +\xb1\xef\x58\x7d\xe1\xc3\xe9\x18\xb3\x39\x04\x8f\xd9\x92\xca\x93\ +\xac\x1a\x6a\x02\x2c\x93\x59\x1d\xab\xc8\xfc\xa9\x0f\x47\x84\x71\ +\xd0\xf9\xcd\x2c\x13\x06\xc0\xc0\xed\xee\xd9\x3d\x67\xa3\x9b\x25\ +\x26\xb5\xf2\xaa\xca\x1a\x1c\x2a\x7e\x2e\xb3\xb3\xad\xa9\x45\x68\ +\xc5\x64\xf3\x4b\x3f\x3a\x77\x21\x7b\x91\x71\x61\x38\x47\x5b\x2f\ +\x5a\x44\x14\x14\x60\x3f\x56\x42\xdc\xcc\x4e\xa6\x66\x3d\x9d\xda\ +\xa2\x1f\x11\x71\x49\x32\x3b\xf9\xb9\x41\x5d\x5e\x89\xc8\xe4\xef\ +\xb3\x0c\xa1\xa5\xfe\xdf\x37\x76\x6f\x5e\xb3\x8a\x70\x7b\x65\x37\ +\xf4\x59\x39\xeb\x1e\xdb\xf8\xf3\xdd\xa7\xb3\xa2\x97\x90\xd7\x0a\ +\x01\x48\xeb\xd5\xc7\xa7\x49\x70\xa2\x12\x2e\x5e\x31\x2d\xab\xd3\ +\x73\x77\x02\x6e\x49\xa5\xe6\xaa\x8b\xf8\xd3\xe1\xbd\x79\xb5\xe7\ +\xcf\x54\xd1\xad\x91\xe9\xe5\x98\x2c\xfa\xd5\x83\x9f\x1c\x32\x2e\ +\x4e\x7d\x3a\x3b\x31\x09\x0a\x92\x4a\x4d\x44\x64\x60\xb9\x9b\xa9\ +\x09\xdc\xea\xb8\x8b\xb7\xb3\x48\xcf\x93\xce\x37\x1b\x5b\xd1\xde\ +\x58\xff\xd7\xdf\xbc\xf4\xcc\x0e\xc2\x33\x4f\x2f\xc7\xd2\xc6\x24\ +\x90\x6c\xc1\xde\x8f\x2b\x2a\xb3\x97\x26\x87\x1b\x63\xa3\x85\xe0\ +\x68\xa8\x66\x59\x27\x63\x9e\xab\x29\xd5\xed\x54\xaa\x23\x0e\x71\ +\x52\x5f\x20\xe7\x2c\x04\x98\xe7\x6e\x9a\x7d\xd0\xd4\x0b\x53\x73\ +\xe3\xe0\xae\x4d\xdf\xcf\xa1\x5b\x1d\x6c\x05\xf7\xde\x90\x40\xca\ +\x85\x70\xb2\x85\xa5\xc7\xcf\x9d\xfe\xee\x32\x63\x44\x66\x72\x9c\ +\xb0\xd5\x55\xa9\xc5\x07\xd8\xb2\x60\x99\x02\x28\x47\x85\xee\x76\ +\x37\xef\x19\x30\xb3\xe1\x1e\x06\xde\x30\x50\xbc\x79\x4d\x1e\x4d\ +\xc1\xbe\x13\x06\x7d\xb7\x64\xd3\x9b\x52\xb2\x08\xa6\x44\xe9\xf1\ +\xb3\xa7\x97\x24\xc6\x85\xad\x5f\x95\x22\xae\x84\x10\x27\xed\x18\ +\x93\x9a\xc9\xb7\x01\x4b\xbb\x4e\x46\xd2\x72\xed\xbf\x18\x31\x77\ +\x0f\x15\x6f\x7a\x32\x4f\xf2\x7c\x60\xae\x4d\xa9\x2f\x09\xc3\xcb\ +\x6f\xfc\xfe\xf5\x58\x43\xd2\x53\x6b\xb2\x8d\x78\xc0\x10\x81\x41\ +\x7a\xcd\x64\xc3\xcc\xec\xf7\x02\x95\xbd\x66\x9d\x71\xb4\x6f\x00\ +\x83\xd7\x5b\x60\xea\x6c\x3f\x73\xa0\xe8\x67\xa5\xf4\x77\xa7\x2f\ +\xf8\x5c\x04\x64\x12\xa1\xac\x3c\xd3\x1e\x59\x9d\xba\xfe\xd9\x17\ +\x7f\x1d\x1c\xa2\x4f\x34\x24\xc4\x22\x28\x34\x1c\xc1\x61\x7a\x21\ +\x60\x4e\xaf\xd8\x3b\x28\x2f\xc6\x86\x47\x60\x1b\x1d\x84\xd5\xdc\ +\x43\x1b\xd4\x91\x9b\x65\xc7\x0e\xbe\x76\xf5\xd2\xf9\x26\x56\x6e\ +\x64\xc3\xb8\xc7\x0f\x13\xef\x9c\xf0\x97\xf2\x22\x32\x22\x3a\x2e\ +\xea\xe9\x4d\x2f\xe4\xc5\x27\x2d\x7d\xc2\x3f\x20\x70\x81\xef\x24\ +\x6c\x5d\xb0\x4d\x8c\x77\xdc\x6a\x6f\xfd\x47\xf9\xf1\x77\x4f\xf7\ +\x9b\xbb\x2d\x74\xbb\x5f\x8a\xb7\x0d\xf7\xf9\x69\xe6\xfd\xbf\x46\ +\x22\x12\x40\x16\x4c\x46\x1f\x5f\xd0\x49\x39\xe9\x7d\xb0\xd4\xa0\ +\x2d\x2d\xe8\x8b\x02\xec\xcb\x78\x42\x02\x66\x5e\xff\x5f\x1f\xa7\ +\xb3\x29\xc2\xfa\x85\x1f\xc4\x8d\xb3\xef\xbb\x0c\x84\x6d\x76\xa8\ +\x4e\xe0\x98\xcb\x63\xdf\xe3\x7f\xb7\xc9\x27\x19\x90\xb8\x04\x5b\ +\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\ +\x00\x00\x08\x40\ +\x89\ +\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ +\x00\x00\x20\x00\x00\x00\x20\x08\x06\x00\x00\x00\x73\x7a\x7a\xf4\ +\x00\x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\x74\x77\x61\x72\x65\ +\x00\x41\x64\x6f\x62\x65\x20\x49\x6d\x61\x67\x65\x52\x65\x61\x64\ +\x79\x71\xc9\x65\x3c\x00\x00\x07\xe2\x49\x44\x41\x54\x78\xda\x9c\ +\x57\x6d\x70\x54\xe5\x15\x7e\xde\xfb\xb1\x1f\xd9\xec\x26\x9b\x6c\ +\x42\xb2\x31\x1f\x7c\x19\x13\xbe\x0a\x56\x61\xb0\xb6\x63\x47\xb1\ +\x9a\x52\x70\x54\x50\x7e\x28\x52\xdb\x19\xfb\xa3\xed\xaf\xb6\x33\ +\xf5\x67\xdb\x1f\x76\x3a\x53\xfb\xa3\xda\x0e\x65\x5a\x07\xb0\xa0\ +\x19\x0b\x0c\xc5\xa9\x95\x4e\x8b\xe2\x80\x88\x40\x02\x92\x44\xf2\ +\x41\xe2\x26\x21\x9b\xcd\xee\x26\xd9\xbb\xd9\xbd\x1f\x3d\xe7\xde\ +\xdd\xb8\x2c\x9b\x94\x72\x67\x4e\xb2\xf3\xbe\xf7\x3d\xe7\x39\xe7\ +\x3d\xe7\x39\xe7\x0a\xdc\xe6\x53\xf5\xf8\x2b\x01\xe1\xbf\xeb\x45\ +\x53\x28\xcf\x9b\x96\xb4\xd6\xb2\xac\xf9\x3d\x21\x04\x09\x20\x09\ +\xf3\x92\x64\x19\x7f\xb1\x92\xc3\x7b\x27\xff\xfe\x93\xe4\xed\xe8\ +\x15\xff\x6b\x3f\xf4\xc4\x6b\xcb\x4c\x77\xf5\xdf\x14\x55\x5d\xbd\ +\x79\xc3\x52\xac\x68\x0a\x61\x45\x73\x08\x2e\x55\x86\x61\x58\x30\ +\x4d\x03\x86\x69\x61\x56\xcb\xe2\xda\x50\x14\x03\x23\x93\xf8\xf4\ +\x6a\x04\xa6\x9e\xed\x16\x5a\x74\xfb\xc4\x3b\x2f\xf5\x93\x1e\xeb\ +\x4e\x00\xc8\xd5\xcf\x1c\xfa\xbd\xac\xba\xbe\xff\xcc\xe3\xeb\xb1\ +\x79\x43\x33\xbc\x2e\xa0\xdc\x0b\x78\x3d\xe4\x2d\x6b\xb5\x1c\x21\ +\xfb\xd0\x0d\x20\x31\x6b\x22\x9a\xc8\x20\x46\x72\xfe\xca\x17\x38\ +\x79\xba\x87\x81\xfc\x71\xe2\xcd\x1d\x3f\xa0\xd7\x8d\xff\x07\x80\ +\x1a\xdc\xf9\xd6\xd0\xaa\x95\xe1\xfa\x17\x9e\xbc\x1f\x0d\x35\x0a\ +\xaa\x2a\x80\x33\x9f\xc5\x71\xe6\xf2\x38\x2e\xf7\x4f\x62\x22\x9e\ +\xb6\xc3\x8e\x9c\x7b\xa1\x4a\x0f\xda\x9b\xaa\x70\x6f\x6b\x2d\xd6\ +\x2c\xaf\xc2\xf0\x0d\x0d\x3d\x43\x71\xfc\xe3\x54\x0f\x46\x22\xb1\ +\xd1\xe8\xc1\x27\x9b\xe9\xb5\xec\xed\x00\x50\x2b\x76\x1c\x1e\x7c\ +\xfa\xb1\xf5\xe1\x2d\x0f\xac\x40\x43\x08\xb8\x16\x49\xe1\xb7\x6f\ +\x5f\x40\x7c\x3a\x03\x55\x96\x20\xc9\x82\xee\x5b\xcc\x1f\xb6\xec\ +\x68\x58\xf6\x55\xe8\x86\x89\x0a\x9f\x1b\x7b\x3a\xda\x51\x5f\xe5\ +\xc3\xc5\xbe\x18\x3e\xee\x1a\xc1\xd9\x4f\xfb\x23\x93\x6f\x3e\xdd\ +\x52\x0c\x42\xbe\xd5\xf3\xc3\xd7\x9f\x7a\x6c\x7d\x3d\x1b\x6f\xa9\ +\x07\xf6\xbf\xd7\x8f\x7d\xc7\xaf\xd8\x56\xbc\x2e\x05\xaa\x22\x43\ +\x91\x08\x84\x94\x4f\x3e\x01\x59\x12\xf6\x1a\x83\x73\x29\x92\x0d\ +\xe2\x74\xf7\x28\xb2\xba\x89\x87\x36\xdc\x45\x57\x24\xc1\x94\x24\ +\xff\x74\xed\xa3\xdf\x4b\x75\x1d\xfe\x1d\xd9\x31\x4b\x01\x90\xab\ +\x77\xec\x7f\xbd\xbd\xb5\xf1\xeb\x3b\x3b\xd6\x61\x59\x58\xe0\x0f\ +\x47\xfa\x70\xea\xe2\x28\xca\xdc\x2a\x19\x56\x20\xb3\xf7\x64\x8c\ +\x4f\x67\x74\x0b\x5a\xd6\x44\x86\x12\x91\x63\xa1\xd0\x1e\x8b\x2c\ +\x39\xc2\x40\x87\xc6\xa7\x31\x95\x4c\xe3\xe1\xfb\x9a\xa0\xcd\x59\ +\x18\x9f\x9c\x2d\xb7\x5a\xbe\x15\x4e\x75\x77\x1e\xcf\x27\x66\x1e\ +\x80\xa8\xfc\xe6\xcb\xad\x6a\xb0\xf9\x4f\x3f\xde\xfd\x0d\x2c\x6f\ +\x50\x70\xe8\xe4\x10\x3e\xb8\x14\xa1\x84\x63\xe3\x8e\x61\x16\xbe\ +\xf8\x0c\x79\xf6\xd3\x8e\xa5\xd8\xb7\x67\x35\x7e\xf4\x48\x33\xfc\ +\x5e\x05\x1f\xf4\xc5\xed\xf7\xe4\xdc\x7b\xb2\xec\x80\x8a\x4c\xa6\ +\xc8\x78\x16\x1b\xdb\xeb\x30\xab\x0b\xaa\x92\xa9\x0d\xee\xd0\xca\ +\xbf\x6a\x03\xff\x99\x64\xc3\x52\x3e\xf4\x52\xcd\xea\xce\xa7\xb6\ +\xac\xa3\x3b\x77\x63\x68\x4c\xc3\xbf\xce\x8f\x90\xe7\x8a\x1d\x52\ +\x56\x9a\x17\xc6\xc0\x77\xfd\xdc\xe6\xf0\x7c\xe8\xf8\x37\xaf\xf1\ +\x5e\xe1\xbb\x0c\xc8\xe3\x92\xf1\xf1\xd5\x71\xc4\xa6\xd3\x68\x6b\ +\x09\xa2\x7d\x65\x18\x72\xed\xda\x4e\xb6\x39\x0f\xa0\xec\x9e\x8e\ +\x90\xac\x7a\xda\x36\x7e\xa5\x09\x35\x94\xed\x7c\xe7\x3e\xf2\xdc\ +\x45\x77\x9e\x0f\x6d\x5e\x54\x59\xb6\xff\x17\x3f\x85\x7b\x85\xe2\ +\x26\xbe\xf0\x92\x23\x47\x4f\xf7\x63\x79\xb8\x02\x0d\xe1\x4a\x48\ +\xaa\xab\xcd\x47\x36\xf3\x00\x64\x4f\xdb\xb6\x3d\x1b\xd7\x36\x92\ +\xc7\xc0\xc5\x81\x24\x91\x8a\x6e\x7b\xae\x14\x78\x33\x2f\x14\x5a\ +\x2f\x79\x75\xf0\xcc\xe8\xbc\x71\xfe\xcd\x6b\xbc\x27\x97\x38\xc3\ +\x91\x48\xcf\xe9\x18\x18\x4b\xa0\x36\xe8\x45\x5d\x6d\x00\x65\xab\ +\xb6\xef\x61\xdb\x0a\xfd\x71\x59\x6a\xf9\xae\x96\xc6\x2a\xf8\x7d\ +\x32\x4e\x5d\x9a\x22\xd4\x0e\x7a\x51\xa2\x48\x25\x4a\x1d\x0f\xed\ +\xbf\xfa\xcf\x61\xfc\xfa\xdd\xeb\xf6\x9a\x9b\x0c\x30\x00\x45\x16\ +\x25\xeb\x5a\x86\xb3\x7e\x2d\x12\x47\x6b\x63\x08\x3e\x1f\x31\x99\ +\xcb\xbf\x8b\x96\x7e\xc3\x00\x3c\x96\xe4\x6a\x6b\x6a\x08\x52\x32\ +\x01\x03\xa3\x71\xa2\x59\xc5\xf6\xa6\xf8\xe1\xb4\xd5\x29\xeb\x75\ +\x8b\x2b\x21\x97\x94\x9c\xc1\x9c\x1b\xec\x3d\x83\x5e\x90\x73\x05\ +\xc6\x62\x29\x3c\xb8\xd6\x8b\x32\xe2\x09\x21\xbb\xdb\xd8\x36\x03\ +\x50\x98\x4e\x55\x0a\x06\x7b\x9c\x4c\x65\xed\x7a\xcf\x2b\x2f\x7c\ +\x4c\xcb\x21\x9c\x0f\x7f\x76\xef\x2d\x7b\x0f\xbe\x72\x1e\x6e\x26\ +\x28\x69\x21\xfb\x82\xaa\x41\xb7\xf9\xc2\xa6\xf0\x5c\xea\x48\x79\ +\x36\xcc\x64\x0d\x6a\x2c\xb4\xa2\x90\x27\x8a\x73\xd7\xc5\xc2\x7b\ +\xaa\x2a\x70\xdf\xaf\xce\x95\x88\x0e\x55\x01\x15\x75\xa9\x73\x4e\ +\x49\x3a\xe7\xd9\x01\x5d\x37\xf3\x91\x12\x4a\x9e\x8f\x0d\x62\x2f\ +\x46\xa6\x90\x16\x26\x12\x49\x2c\x10\x4c\x52\xe2\x51\x6f\x6d\x6e\ +\x92\x94\x27\xa1\xd2\xe7\x9c\xa6\x65\xd9\x51\xcc\x9a\xe6\x97\xea\ +\xf2\x1d\x81\x17\x4d\x1b\x00\x21\x15\xa2\xe4\x15\xe4\xc1\xba\x4a\ +\xe4\x87\x6a\xd3\xb1\x58\x14\x00\x2c\x27\x02\x86\x61\xdc\x0c\x80\ +\xbd\xd5\xb4\x0c\xe8\x16\x10\x0c\xb8\x91\xce\x18\x0b\x2a\x62\x04\ +\xa5\x12\x74\x3e\xdc\x0b\x9c\x63\xef\xfd\x6e\xd6\xad\x23\x43\xfa\ +\xf3\x01\xb6\xdb\x3a\x0c\xad\x6f\x30\x32\x85\x68\x3c\x8b\xa6\x25\ +\x7e\x1b\x90\x2c\x49\x0b\x8a\x22\xcb\x25\x88\x48\x5e\xf4\x8c\x2c\ +\x24\xd4\x54\x7a\x31\x4c\xfd\x21\x95\xca\x50\x39\x69\x7d\x6c\x9b\ +\x01\xe8\x34\x42\x1d\xfb\x62\x74\x0a\x91\x68\x0a\xcb\xea\x03\x4e\ +\x59\x49\x62\xc1\x84\x5a\x34\x02\x25\xc4\x6e\xdf\x84\x79\x49\xb0\ +\x1c\xbd\xc3\x31\xcc\xa5\xb3\xd0\xe3\xc3\xc7\xd8\x36\x03\x48\xcf\ +\x7c\xf2\xe7\x03\xbd\x03\x13\x36\x80\x70\xc8\x0f\x7f\x99\x6a\x87\ +\xa8\x14\xab\x39\xcd\xa6\x04\x41\x49\x8b\xbc\x4f\xba\x7c\xd4\x51\ +\xfd\x65\x2e\xf4\x5e\x8f\xd1\x75\xa7\x31\x7d\x6e\xdf\x01\xb6\x2d\ +\xd9\x15\x38\xd1\x33\x6e\xa4\x67\xfb\xaf\x7c\x3e\x46\x2f\x24\xf0\ +\xb5\x35\x0d\x36\x77\x89\xa2\xab\x90\x9c\x21\x80\x12\x49\x2a\x91\ +\x64\xce\x9e\x54\x14\x7a\x49\x38\xe1\x6f\xa5\x59\xf2\xdc\x67\xa3\ +\x48\x24\x34\x88\x4c\xaa\x3f\x43\x36\xd9\xb6\x9c\x23\x38\xd9\x9a\ +\x4b\x5c\x89\xa9\xf7\xec\xf2\x07\x7c\xb8\xbb\xa9\xd2\x46\x1e\x9f\ +\x99\x2b\xf0\xda\x99\x82\xb8\x52\x0e\x7d\xb7\xf5\x16\x00\xcf\x7e\ +\xb5\x06\x47\x2e\xc5\xa8\x42\xbe\x6c\xc9\x7c\x95\x1c\xc9\xfa\xea\ +\x72\x08\x02\xf8\xce\xbf\x7b\xa1\x25\x66\x91\x3c\xfb\xfa\x73\xd9\ +\xd8\xc0\x35\x3a\xa6\x29\xb9\xf3\x29\xed\xf3\xf7\xbb\xbc\xed\xdb\ +\x8e\x9c\xbd\xe0\xdb\xe6\x26\x26\x7c\x74\x53\x23\x74\xb2\x16\x99\ +\x98\x76\xc6\x2f\xe1\x20\x75\x93\x37\xbb\xf7\xf7\xd9\x43\xa8\x95\ +\xe3\x33\x67\x20\x81\xdd\x43\xd8\xb8\x28\x18\xd3\x82\xe4\x50\x5d\ +\xd0\x8f\xbd\x47\x2f\xd2\x5d\xa7\x60\x24\x46\x8e\xa4\xfa\xde\xef\ +\x62\x9b\x85\x03\x09\x33\x83\xa1\xf5\x9c\xb8\x60\x2d\xfb\xf6\x0e\ +\xe2\xb4\xb2\x84\x66\x60\xd3\xaa\x3a\xdb\xe3\xa4\x36\x47\xde\x48\ +\xb9\xc4\xe4\xb1\x4b\xb6\xfb\xbc\x47\x75\x84\x1b\x11\xaf\x29\x39\ +\xef\x45\x2e\x4f\x6a\x2a\xca\xc9\x78\x39\xde\x38\xd1\x85\x1b\x13\ +\x49\x32\x30\x3b\x79\xe3\xad\x17\x76\x93\x2d\x6e\xa5\xe9\xe2\x91\ +\x8c\xd9\xc1\x4c\x75\xbf\x7d\x22\x55\xf7\xf0\xf6\xac\x29\x95\x8d\ +\x27\xe6\xb0\xa6\x25\x84\xfa\x50\x39\x12\x33\x1a\x51\xb5\xe5\x24\ +\x67\x8e\xa8\x94\x9b\x12\x53\xcc\x47\x8a\x67\x80\xa6\xda\x4a\xd0\ +\x27\x03\xde\x38\xde\x4d\xc6\xa7\xa1\xcf\x24\xa3\x63\x07\x76\x6e\ +\x25\x1b\xc3\x24\xc9\xfc\x5c\x28\x17\x35\xbb\x0c\x4f\xad\xa9\xcb\ +\x9d\xef\xce\x85\xb7\x3c\x31\x95\x98\xf3\x8e\x4c\x69\xa4\x50\xc1\ +\xba\xe5\xb5\x76\x16\x73\x62\x99\x96\x69\x13\x8b\xf3\x35\xc4\xdd\ +\x10\x76\x24\x02\xd4\x66\x6b\xc9\xeb\x50\xc0\x4b\x09\x37\x86\xce\ +\x93\x3d\x98\x49\xcc\xc0\xd4\x66\x62\x63\x07\x77\x7e\x87\x74\x0f\ +\x92\xc4\xb8\xfc\x16\x1d\xcb\x79\xcc\x27\x69\x0e\x6d\x7d\xf5\x65\ +\x25\xd8\xdc\x51\x4f\xad\x3a\x54\x15\xb0\x49\xaa\x85\x78\xa2\xa1\ +\xc6\x47\xe1\x56\xec\x3b\xb6\x72\x1d\x52\xcb\xea\x18\x8c\x24\xe9\ +\x5b\x20\x8a\xab\x43\x31\xcc\xd2\x08\xa6\xa7\x33\x30\xe2\x43\xc7\ +\x27\x8e\xfe\xf0\x17\xa4\x6f\x88\x24\x5a\x3c\x96\x2f\xf8\x61\x42\ +\x12\x24\xa9\xf3\xb4\x3c\xb0\xda\xbf\xe9\xa5\x5f\x4a\xaa\xbf\x25\ +\x50\xe1\x85\x9b\x27\x64\x1a\xd7\x38\x37\x98\xd3\x39\x51\xf9\x6a\ +\x4c\x6a\x66\x19\xa2\x59\x93\xf8\x5c\xa7\x21\xd4\xca\x4e\x0f\x26\ +\x3f\x7a\xed\xe7\xda\xe0\x87\xdd\xa4\x67\x8c\x64\xea\x76\x3f\x4c\ +\x0a\x47\xf6\x32\x92\x6a\x92\x1a\x25\x10\x5e\x12\xb8\xff\xc5\xad\ +\x4a\xf5\xdd\x8f\x08\x97\x6f\xe9\xcd\x4a\x84\xad\xc9\xca\xcc\x0c\ +\x64\xa3\xbd\xef\x25\xcf\xee\x3d\xa6\x27\x23\x5c\xe7\x13\x24\x93\ +\xb9\x8c\x37\xee\xe8\xe3\x34\x17\x0d\x06\xe2\x23\x09\x90\xd0\xd8\ +\x0a\x6f\xc1\x44\x8d\x82\x4a\xd2\x48\x12\xb9\x24\x9b\xcd\x19\xce\ +\xde\xe9\xc7\x69\xa9\x88\xb8\x78\x8c\xca\x75\x51\x51\x6a\x62\xcb\ +\x95\x57\x66\x21\x8f\x8b\x9f\xff\x0a\x30\x00\x9f\xd5\x07\x3c\x37\ +\x4c\x9a\x0f\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\ +\x00\x00\x07\xa0\ +\x89\ +\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ +\x00\x00\x20\x00\x00\x00\x20\x08\x06\x00\x00\x00\x73\x7a\x7a\xf4\ +\x00\x00\x07\x67\x49\x44\x41\x54\x78\xda\xbd\x57\x59\x6c\x1c\x55\ +\x16\x3d\x55\xbd\xd8\xee\xcd\x1b\x71\xe4\x84\x71\x16\x9c\x10\xcb\ +\x20\xc2\x1a\x26\x21\x16\x0c\xe0\xe0\x21\x06\x2c\x43\xc4\x12\x08\ +\xfc\x19\x04\x12\x8b\x88\x12\x88\x04\xe4\x0b\xf1\x35\x1f\x44\x42\ +\x33\x41\x64\x45\x21\x90\xed\x63\xfe\x22\x06\x46\x71\xb0\x83\x93\ +\xb6\xe3\x04\x3b\xb1\xe9\xd8\x8e\xb7\x6e\xb7\xd3\xfb\x56\xbd\x14\ +\xf7\xbe\xee\x6a\x77\xec\x72\x06\x10\x4c\xa9\x9f\x5e\x57\xbd\x57\ +\xef\x9c\x7b\xee\x7d\xf7\xdd\x92\xf0\x07\x5f\xbb\x76\xed\x2a\xb5\ +\x5a\xad\x75\x26\x93\xa9\x81\x6e\x57\x1a\x8d\xc6\xb5\x06\x83\xa1\ +\x4e\x92\x24\xb4\xb6\xb6\x16\xd1\x33\xa5\x70\xbe\xf4\x3b\x71\xa4\ +\xfd\xfb\xf7\xdf\xa2\x28\x4a\xa5\xc5\x62\x69\x95\x65\x69\x85\x2c\ +\x1b\x56\x10\x50\x3d\x35\x70\xcb\x64\x32\x50\x81\x44\x38\x14\xba\ +\x92\x51\x11\xb7\x59\x2d\xab\x5b\x5a\x5a\x2a\xe8\x5d\xdf\x6f\x21\ +\x20\x1d\x38\x70\x60\x4d\x3a\x9d\xae\x30\x9b\xcd\xeb\x72\xd6\xac\ +\x90\x25\x69\xb1\x4c\x20\xd4\x23\x96\x48\x64\x92\x8a\xe2\x0f\x87\ +\x23\x13\xb1\x78\x2c\x18\x8b\xc5\x82\xe3\x63\xe3\x57\x69\x9c\xa6\ +\x1b\x25\xa3\xd1\xa4\x66\xd4\x8c\xd4\xb0\x6e\xed\x96\x8d\xcd\xcd\ +\x0b\x69\x4d\x8f\x1e\x01\x69\xef\xde\xbd\x0d\xb4\xf8\x32\x59\x96\ +\x6b\x49\xbe\x66\xfa\x5f\x4b\xff\x2d\x2c\x1d\x37\x9f\xcf\x87\x44\ +\x22\x91\x22\x00\x9f\xdf\x1f\xf0\x44\x22\xe1\xe0\x94\xd7\xeb\x35\ +\x1a\x18\xc9\x44\x40\x06\x95\x00\x65\xa3\xc9\xac\x9a\x8c\x46\xd0\ +\x3d\x18\xdc\x40\xcf\x53\xa9\x34\xee\xbd\xfb\xae\xc7\x3f\xda\xb9\ +\x73\x6d\x6d\x6d\xad\x7a\xf0\xe0\xc1\x4e\xc2\x54\x0b\x09\x14\x1f\ +\x39\x72\x24\xc6\xb2\x85\xc3\x61\x84\x42\x21\x90\xd5\xf0\xfb\xfd\ +\x88\xc7\xe3\xa2\xd1\xe2\x82\x08\xcb\x4b\xc4\x72\x3d\x37\x22\x48\ +\xf7\xac\xc6\xf5\xbd\x0c\xc8\x0c\x20\x41\x55\x55\xdc\x56\x5f\x0f\ +\x32\x12\xa4\x24\xe3\xfd\x9b\x94\x7d\x8a\xfa\x94\x46\xa0\xe2\xe8\ +\xd1\xa3\xd3\xc3\xc3\xc3\x79\x20\x06\xe1\xa6\x29\x60\x20\x30\x49\ +\x96\x66\xc6\xa4\xdc\x98\x78\x36\x33\x8f\x7e\xa2\x17\xd0\x92\x80\ +\x17\x04\x2c\x56\x2b\xfa\x2e\xf6\x81\xd4\x45\xd7\xb9\x2e\x50\x0c\ +\x39\x68\x52\x48\x23\x50\x75\xec\xd8\x31\xf7\xc8\xf0\x08\xfe\xf3\ +\xed\xb7\x78\xac\xa9\x09\x27\x4f\x9e\xcc\x5b\x2b\x88\xc8\x59\x12\ +\xac\x8c\x12\x4f\x20\x95\x4e\x89\x17\xb5\x71\x6e\x2c\x7d\x51\x71\ +\x31\x2c\x36\x3b\x59\x6a\x82\x90\x00\x19\x11\x90\x1b\x36\x34\xe2\ +\x8d\xd7\xdf\x40\x5b\x5b\x1b\x9c\xdd\x4e\x26\x50\x45\x83\x53\x79\ +\x02\xc7\x8f\x1f\x77\x0f\x0d\x8d\xe0\xd4\x7f\xbf\xc7\x23\x8d\x8d\ +\xf8\xfe\xbb\xef\xb2\x04\x72\x51\xcd\x56\x84\x03\x41\xa8\x99\x34\ +\xac\x76\xbb\x00\x32\x92\x35\x79\x02\x3c\x87\x80\xd2\xa9\x14\x92\ +\x89\x84\x18\xb3\x97\x96\x0b\x15\xd2\x44\xb6\xb1\xf1\x11\xbc\xf5\ +\xe6\x5b\x78\xf9\x95\x57\xd0\xd3\xd3\xcd\x04\x44\x40\x5e\x47\xc0\ +\xe5\xba\x82\x8e\x8e\xd3\x78\xf0\xc1\xbf\xa1\xbd\xfd\x14\x4c\x06\ +\x23\x64\xb2\x2a\x95\x54\x04\xb8\x85\x80\x19\x3c\xeb\x67\xb2\x4f\ +\xa2\x71\x96\xbc\x40\x05\x8d\xb4\x42\x71\x13\x8b\x46\x61\x77\xd8\ +\x41\x11\x8a\x47\x1f\x7e\x18\xef\xbc\xfd\x0e\x5e\xd8\xbc\x19\x17\ +\x2e\x9c\xc7\xbe\x7d\x3a\x04\x06\x07\x5d\xe8\xfa\xf1\x0c\x1e\x58\ +\xbf\x1e\x9d\x1d\x1d\xc2\x5f\x2c\x79\x84\x82\xd2\x51\x5e\x0e\x33\ +\xdd\xa3\x20\x3e\xe4\xc2\xa0\x9b\xdd\x38\x17\xd0\xbb\xe1\x50\x10\ +\x25\x25\x16\x34\x92\x0b\xb6\xbe\xbb\x15\xcf\x6c\xda\x84\xbe\xbe\ +\x9f\x88\xc0\xbe\xb9\x04\x06\x06\x06\xe0\x3c\xdb\x8d\xbf\xae\xbb\ +\x1f\x5d\x5d\x67\x85\x35\xd1\x48\x08\x56\xab\x1d\x26\x8a\x5e\x0e\ +\x29\x8e\x85\x42\x20\x0e\x40\x39\xf7\x8c\xe7\x4b\xd4\x8c\x14\x2b\ +\x29\x72\x95\xd8\x41\x91\x08\xbc\x53\x53\x68\x7b\xed\x55\x6c\xdb\ +\xba\x1d\x2d\xad\x4f\xa1\xbf\xbf\x5f\x9f\xc0\xe5\xcb\x03\x38\xdf\ +\xd3\x83\xfb\xd6\xac\x81\xd3\xe9\x44\x4a\x51\x04\xb0\xa9\xc8\x2c\ +\xa2\x5e\xb4\x5c\x40\xe6\x25\x67\x05\x0a\xe2\x24\x11\x8b\x21\x44\ +\xa0\x94\x25\x73\xbb\x01\x88\x45\xa2\x44\xa0\x0d\xdb\xb7\xbd\x8f\ +\x8d\xcd\x8f\x63\x60\xe0\xb2\x3e\x81\x4b\x97\xfa\x71\xa1\xb7\x0f\ +\x77\xdf\x73\x27\x11\xe9\x15\x12\x16\x5b\x2d\x14\xcb\xb3\x2c\xe7\ +\x6d\x27\x33\x68\x36\x2f\x50\x82\x42\x94\xfc\xcd\xa0\x22\xb9\xe4\ +\xb6\x24\x5f\x4c\x2a\x99\x4c\x52\xf0\x6d\xc1\x7b\xdb\x76\xa0\xe9\ +\xef\x4d\x70\xb9\x06\xf5\x09\xf4\xf5\xf5\x0b\x79\xee\x58\xbd\x1a\ +\x3f\xf5\xf6\x8a\x9c\xc0\x41\x98\xb5\x3a\xeb\x6f\xcd\xbf\x02\x98\ +\x24\x66\x60\x8e\x93\xd9\xc0\xda\xa5\x11\xd8\xb2\xe5\x25\xec\xd8\ +\xbe\x03\x8d\x4d\x1b\x70\xe5\x8a\x6b\x2e\x81\x13\x27\x4e\xb8\x7b\ +\x7b\x2f\x62\x70\xf0\x67\xdc\x7e\x7b\x3d\x2e\x5f\xba\x04\x63\xce\ +\xa7\x5a\x42\xe2\x9e\x49\xb1\xa5\x94\x92\x05\x30\x03\xcc\x24\xa1\ +\xf9\x09\xbc\xf8\xe2\x66\xec\xfc\xe0\x43\xac\x7f\xe8\x21\x8c\x0c\ +\x0f\xcd\x47\xa0\x97\xd8\x0d\xa1\xae\x6e\x15\x46\x28\x27\xf0\x79\ +\xa6\xa5\x5b\xb6\x58\xf8\x53\x00\xa7\x68\x61\xa0\x66\xc9\x52\xdc\ +\x54\x59\x29\x9e\x4f\x4f\x4f\x63\x98\x16\x66\x92\x7a\x04\x9e\x7f\ +\xfe\x39\x7c\xf2\xf1\x27\xb8\x77\xcd\x7d\xb8\x7a\x75\x44\x9f\x40\ +\x0f\xf9\x9d\x07\x57\xac\xbc\x15\x9e\xc9\x09\x28\xf4\xa2\x96\xf3\ +\x19\x94\x7d\xad\xaa\xd9\x74\xcb\xd9\xed\x96\xe5\xcb\xd1\xdd\xe3\ +\x14\x0b\xac\xbe\xe3\x4e\xfc\xec\x72\xcd\x43\x20\x85\x67\x9f\x7b\ +\x06\x9f\xfe\xe3\x53\xac\xba\xad\x1e\x13\xe3\xa3\xf3\x11\x38\x8f\ +\xd1\xd1\x71\xd4\xd6\x2e\x87\x9f\x4e\xbf\x28\x59\xcb\x92\xb3\x05\ +\xdc\x66\xcb\x0b\x11\xa4\x36\xf1\x37\x1e\x09\x03\x39\x95\xf4\x08\ +\x6c\xda\xd4\x8a\x7f\xfd\xf3\x73\x2c\x5d\xb6\x0c\x93\x93\xe3\xfa\ +\x04\x9c\xce\x6e\x78\x3c\x1e\x2c\x59\xb2\x84\xb2\x58\x0c\x41\x4a\ +\x40\x7c\x94\xb2\xf5\x73\xc0\x69\x67\x04\xfd\x3e\x4c\x4f\x65\x8f\ +\xf7\xca\x05\x55\x70\x94\x95\x6b\xa7\xec\x1c\x02\x4f\x3f\xdd\x82\ +\x83\x07\xbe\xc4\x82\xaa\x2a\xc2\x98\xd4\x27\x70\xee\x5c\x37\xf9\ +\xf2\x1a\x6e\xbe\x79\x31\xa5\xdf\x24\x7c\x04\xa0\x28\x3a\x96\x6b\ +\x14\xc4\x29\xa8\x01\x71\xcb\xe8\x2a\x90\xa2\xf3\xa1\xa5\xe5\x09\ +\x7c\xf5\xd5\x37\x70\x94\x96\x62\xda\xeb\x99\x8f\x80\x53\xd4\x00\ +\xd5\xd5\xd5\xb9\x7d\x9d\xa4\xa0\x8b\x08\xa0\xdf\x7b\x69\x04\x9e\ +\x7c\xb2\x19\x87\x0e\x7d\x8d\x52\x26\x30\x3d\x35\x3f\x81\x60\x28\ +\x8c\xca\x8a\x72\xf8\x03\x01\x8a\xf0\x05\x98\x98\x18\xd7\xdd\x5e\ +\xbf\xf6\xca\x64\x98\x40\x92\x08\x6c\xc4\xee\xdd\x5f\x60\xd1\xa2\ +\x45\x54\x5d\x79\xa9\x38\x99\xc7\x05\x5c\x11\x39\x1c\x0e\x51\x82\ +\x69\x4a\x04\x83\xc1\x39\xd1\xfd\x6b\xad\xe7\xdd\x42\x55\x32\x1a\ +\x1a\x1e\xc0\x67\x9f\xed\x46\x4d\xcd\x5f\x48\xe5\x1b\x10\x60\xb0\ +\xb2\xb2\x32\x04\x48\x01\xde\x82\x35\x35\x35\x18\x1b\x1b\xa3\x2d\ +\xa9\x88\x7c\x70\xa3\x3a\x56\xe5\x00\x54\x67\x7a\x2a\x46\x61\x36\ +\x98\x51\xbd\xb8\x1a\x75\xab\x56\x92\x02\x7b\x84\x02\x81\xc0\xb4\ +\x3e\x81\x33\x67\xbb\x10\x0b\xc7\x28\x50\x1c\x74\x8c\x86\x85\x05\ +\x36\xbb\x0d\x8b\x29\x28\x87\x5c\x43\x48\x14\x1c\x30\x33\x56\xe6\ +\xa0\xd5\x99\xc6\x19\xd4\x4e\x75\x43\x05\xb9\x72\xe1\xc2\x2a\x14\ +\x15\x15\xa1\x98\x0a\x98\xcf\xbf\xd8\x87\x85\x55\x0b\x10\x0c\xf8\ +\xb8\x3e\x9c\x4b\xa0\xa3\xf3\x8c\xd8\x32\x56\x6b\x09\x49\x1f\xcf\ +\xcb\x68\xa3\x12\x8b\xa5\x73\x4f\x7a\xe0\xa5\x00\x62\x12\x1a\x98\ +\x41\x80\xd9\x50\x41\xf5\x82\x95\x0e\x2e\x9e\x6b\x29\x29\x16\x75\ +\x43\x22\x11\x27\xf5\x26\x84\x3b\x7d\xfe\x00\xa6\xa6\xbc\xa4\x6e\ +\x29\x42\x41\xbf\x3e\x81\xd3\x3f\x74\x22\x4d\x3e\x2b\x26\xc6\x9c\ +\xf5\x34\x02\xfc\x8c\xeb\xbd\xe5\x94\x44\x2c\x04\xc2\xee\xb1\xdb\ +\x6c\x04\x66\x15\x73\x59\xf2\x6b\xd7\x7c\x94\x2f\x32\xe2\xd0\x32\ +\x9b\x8b\xe0\x76\xbb\x71\xe1\xe2\x45\x28\x09\x25\x5f\x37\x30\x71\ +\x26\x19\x0e\xe9\x10\xe0\xd3\xb0\xbd\xbd\x53\x2c\xc6\x60\xac\xac\ +\x9d\x00\xca\xca\xcb\x60\xa3\x20\xb2\xdb\xad\x24\x65\x71\xfe\x1b\ +\x81\xbe\x06\x60\xa6\x7b\x3e\x90\x38\x59\x31\x40\x34\x16\xa5\x2d\ +\xe6\xc5\x10\x9d\x27\x1c\xcc\xa2\x40\x99\xb5\x83\xb8\xbe\x88\x86\ +\x03\x73\x09\x70\x55\x3c\x3a\x3a\x26\x7c\xe7\xa0\x3a\xae\xa4\xa4\ +\x44\xbc\xe8\xf7\x05\x44\x42\xf2\x51\xef\xa7\xde\xed\xf1\x66\x17\ +\x94\x70\x5d\xd2\xcb\x62\x48\xd7\x7f\x27\xe8\x6c\xdd\x6c\x95\x15\ +\xc0\x9e\x3d\xb3\x08\xd0\x77\x81\x9b\xa5\xe5\x44\xc4\x16\x72\xcf\ +\x32\x1a\x0c\x72\xbe\xce\xcf\x4a\x89\x1b\xee\x84\xff\x39\x4e\x8d\ +\xce\x98\xf6\xc3\x87\x0f\xb7\xa0\xa0\x2c\xe7\x24\xbe\x2c\xd7\xff\ +\xd9\x17\x7f\x50\x78\xa9\x8d\xa0\xe0\xc3\x84\xbf\x97\xac\xec\xa2\ +\xff\x03\x01\x76\x1c\x6f\xb1\x18\x93\xf9\x05\x9d\xa2\x0a\x5d\xc8\ +\x27\x1c\x01\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\ +\x00\x00\x09\x64\ +\x89\ +\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ +\x00\x00\x20\x00\x00\x00\x20\x08\x06\x00\x00\x00\x73\x7a\x7a\xf4\ +\x00\x00\x09\x2b\x49\x44\x41\x54\x78\xda\xbd\x57\x6b\x6c\x14\xd7\ +\x15\xfe\x66\x76\x76\x76\xbd\x6b\xaf\x1f\x60\x63\x1b\x1b\x4c\x58\ +\x03\x0e\x84\x18\xb0\x03\x18\xec\xa2\x3e\x30\x34\xa1\xa9\x21\x4d\ +\x2a\x25\x52\x1b\x29\x3f\x50\xd5\x48\x8d\x94\x4a\x51\xa5\x48\x25\ +\xea\xdf\xf6\x1f\xca\x9f\xb6\xbc\xdb\x8a\x8a\x57\x25\x92\x96\x12\ +\x5e\xc1\xc1\x06\x83\x6d\x0c\xd8\x60\x63\x9b\xb5\x01\x2f\x5e\x7b\ +\xdf\x3b\xfb\x9e\x7e\x77\x76\xed\x38\xb0\xfc\x68\xd5\x76\x57\x57\ +\x77\x66\x77\xe6\x7e\xdf\x39\xe7\x3b\xe7\x9e\x2b\xe1\xbf\xfc\xd9\ +\xbb\x77\x6f\xa1\xdd\x6e\xaf\x33\x9b\xcd\x2d\xbc\x5d\xa6\x28\x4a\ +\x93\xc9\x64\xaa\x93\x24\x09\xbb\x76\xed\xb2\xf0\xb7\xf8\xdc\xe7\ +\xa5\xff\x10\x47\x3a\x74\xe8\xd0\xd2\x78\x3c\x3e\xcf\x66\xb3\xed\ +\x92\x65\xa9\x56\x96\x4d\xb5\x04\x5a\xc9\x01\x31\xd2\xe9\x34\x74\ +\x20\x16\x0a\x06\x47\xd2\x3a\xa2\xf9\x76\x5b\x7d\x5b\x5b\x5b\x09\ +\xdf\xf5\xfe\x3b\x04\xa4\xc3\x87\x0f\xaf\x4f\xa5\x52\x25\xaa\xaa\ +\x6e\xca\x5a\x53\x2b\x4b\xd2\x42\x99\x20\x9c\xa1\xc5\x62\xe9\x44\ +\x3c\xee\x0b\x85\xc2\x8f\xb5\xa8\x16\xd0\x34\x2d\xf0\xe8\xe1\xa3\ +\x31\xfe\xcf\xc7\x15\x49\x51\xcc\x7a\x5a\x4f\x4b\x2d\x9b\x9a\x7e\ +\xf2\xda\x8e\x1d\x0b\xb8\xe6\x93\x5c\x04\xa4\x03\x07\x0e\xb4\x70\ +\xf1\x25\xb2\x2c\x3b\xe9\xbe\x1d\xbc\x76\xf2\xda\x26\x5c\x27\x86\ +\xd7\xeb\x45\x2c\x16\x4b\x12\xc0\xeb\xf3\xf9\x9f\x84\xc3\xa1\xc0\ +\xa4\xc7\xe3\x51\x4c\x02\xc9\x4c\x20\x93\x4e\x40\x59\x31\xab\xba\ +\x59\x51\xc0\x7b\x08\x70\x13\x7f\x4f\x26\x53\x68\x5c\xb7\xf6\xd5\ +\x3d\x9f\x7c\xd2\xe4\x74\x3a\xf5\x23\x47\x8e\x74\x12\x53\x9f\x4b\ +\xc0\x7a\xec\xd8\x31\x4d\xb8\x2d\x14\x0a\x21\x18\x0c\x82\x56\xc3\ +\xe7\xf3\x21\x1a\x8d\x1a\x83\x8b\x1b\x44\x84\x7b\x33\xb3\x4c\x0f\ +\xd0\x0b\x26\x12\x94\x65\xc3\x1b\x92\x6c\xca\xce\x32\x4c\x1c\xbc\ +\x81\xf8\xea\xba\x8e\x55\x2b\x57\x82\x46\x82\x9e\x14\x78\xa7\xe9\ +\xd9\x1f\x72\x4e\xce\x10\x28\x39\x7e\xfc\xf8\xd4\x83\x07\x0f\x66\ +\x81\x64\xb1\x28\xc7\x8c\x07\x64\x49\x5c\x93\xb6\x9e\x36\x86\xa4\ +\x67\x5d\x27\x40\x24\x79\xf6\x1d\xba\xc3\x20\x29\x9b\xcc\x19\x32\ +\xc6\x3b\x3a\x6c\x76\x3b\xfa\x6f\xf7\x83\xde\x45\xd7\x8d\x2e\x50\ +\x43\x0e\xbe\x1e\x9c\x21\x50\x76\xe2\xc4\x09\xb7\xeb\x81\x0b\xe7\ +\xcf\x9d\xc3\xb6\xed\xdb\x71\xf6\xec\xd9\xcc\x42\x59\x8b\x74\x10\ +\x98\xae\x2c\xa9\x58\x8c\xaa\xe5\xf5\x28\xa9\xac\x41\x5e\x41\x51\ +\x86\x04\x57\x31\x91\x44\x2c\x12\x80\xdf\xfd\x10\x8f\x06\x6f\x50\ +\x7d\x7e\x30\x1c\x06\xa9\x34\x09\xb4\xb6\x6e\xc5\xfb\x3f\x7f\x1f\ +\xbb\x77\xef\x46\x77\x4f\xb7\x20\x50\xc6\x57\x27\x67\x09\x9c\x3c\ +\x79\xd2\x3d\x3a\xea\xc2\xe5\x4b\x17\xf1\xdd\xad\x5b\x71\xf1\xc2\ +\x85\x59\x77\x0b\x8b\x2d\x79\x76\xac\xde\xb2\x13\x15\xd5\x35\x58\ +\x5c\x6a\x43\x49\xbe\x0a\x87\xdd\x8c\x44\x9a\x9e\xa6\x37\xcc\x0c\ +\x45\x30\x9a\x44\x40\x4b\x60\xcc\xa3\xe1\x09\x89\xdc\xef\xfc\x02\ +\xf1\x58\x84\xcb\xcb\x68\xdd\xf6\x3d\x7c\xf0\x8b\x0f\xf0\xd3\x77\ +\xdf\x45\x6f\x6f\x8f\x20\x60\x08\xf2\x1b\x04\x86\x87\x47\xd0\xd1\ +\xf1\x15\xb6\x6c\xf9\x36\x2e\xb7\x7f\x09\x85\x31\xa6\x82\x51\x56\ +\xed\xc4\xaa\x2d\x6d\x58\x56\x3d\x0f\x35\x04\x9f\xd2\x74\xf8\xe3\ +\x69\x82\xeb\x30\x33\x04\xaa\x89\x04\x18\x72\x3b\x49\x2c\xb0\x8b\ +\x70\x98\xe0\x9a\xd6\xd0\xef\x9a\xc2\x50\xd7\x39\x4c\x4f\x8c\x62\ +\x5b\xeb\x36\x7c\xf8\xe1\x2f\xf1\xf6\x3b\xef\xe0\xd6\xad\x9b\x38\ +\x78\x30\x07\x81\xa1\xa1\x61\x74\x5d\xbb\x8a\xcd\xcd\xcd\xe8\xb8\ +\x72\x45\xc8\x07\xa5\x55\x4e\xac\xdd\xfa\x26\xd6\x3a\xe7\x23\x4a\ +\x4b\x26\xb5\x34\x14\xbe\x20\x40\x15\x01\x2e\x67\xc0\x89\x0d\xbb\ +\x59\x42\x81\x0a\x43\x78\x69\x3e\x1b\x21\xc9\xee\x11\x2f\x6e\xb5\ +\x9f\x46\xc3\x4b\xcb\xf1\xd1\x47\xbf\xc2\x9b\x6f\xbd\x85\xfe\xfe\ +\x3b\x24\x70\xf0\x59\x02\x83\x83\x83\xe8\xbe\xde\x83\x8d\x9b\xd6\ +\xa3\xb3\xa3\x13\x56\x5b\x01\x9a\xda\xde\xc3\x2b\x2b\xaa\x11\x48\ +\xcb\x08\x53\x03\x6a\x16\x54\x31\x80\xa5\xec\xcc\x7b\xae\x54\xa0\ +\x4a\x86\x66\x44\xda\xc5\xe3\x3a\x67\xdd\x28\x7b\x97\xfb\xc7\xb1\ +\x10\x6e\x7c\xfc\xf1\xaf\xb1\xf3\x8d\x9d\x18\x18\x18\xc8\x4d\xe0\ +\xde\xbd\x41\xdc\xec\xed\x45\x43\x63\x23\xae\x75\x76\xa0\x61\xfb\ +\xdb\xa8\xaf\x5f\x05\x7b\xbe\x1d\x21\x06\x5b\x65\xb0\xd5\x39\xa0\ +\x82\x80\x01\x4e\x6f\xa8\x14\xa1\xc2\xd4\xd0\xa2\x09\x68\x5a\x0a\ +\xe1\x48\x12\xe1\x50\x12\x66\x8b\x19\x61\x25\x0d\x6d\xbc\x0f\x7b\ +\xf6\xfc\x06\x3b\x5e\xff\x01\x86\x86\x06\x73\x13\xb8\x7b\x77\x00\ +\xb7\xfa\xee\x60\xcd\x9a\xd5\xb8\x7b\xdf\x85\x96\x5d\xef\xe1\xe5\ +\xda\x85\xf0\x52\x5c\x4a\x16\xdc\x9c\xb5\x5e\x35\x49\xc6\xbd\xc5\ +\x24\xac\xa6\xb5\x09\x82\x86\x13\x88\xc6\xd2\x88\x08\x02\xe1\x24\ +\xcb\xb1\x0e\xab\x55\xc1\xfc\x4a\x07\x06\xba\x2f\xe1\xd3\x3f\xfe\ +\x05\xeb\xd7\xd4\x61\x64\x74\x24\xb7\x06\xfa\xfb\x07\x18\x9f\x7e\ +\xbc\xb4\xf2\x45\xc4\x6d\x15\xd8\xd8\xdc\x82\x7c\x87\x9d\x2a\x4f\ +\x7d\xed\x72\x5a\x6b\xe1\xb5\x85\x17\xf1\x78\x02\x2e\x77\x10\x16\ +\x8b\x84\x12\x8b\x95\x96\xa7\x59\xc4\x12\x88\xc4\xf8\x3c\x63\x62\ +\xb3\x29\x24\x60\x82\xbd\xc0\x8a\xde\x6b\x17\xd0\x3e\x12\x83\xd5\ +\xdb\x0f\xd7\xd8\xf8\xb3\x59\x70\xea\xd4\x29\x77\x5f\xdf\x6d\x08\ +\x1d\xd4\x2d\x77\xc2\xb6\x74\x23\x36\x90\xad\xca\x80\x2b\x14\xe3\ +\x0c\x01\x33\x09\x58\x59\x05\x47\x9f\x04\xd0\x39\x38\x65\x88\x4d\ +\xd4\x53\xfe\x8c\x6a\x7b\x1e\x1c\x8a\x19\x2a\x41\xad\x56\x19\x16\ +\xce\x79\x1c\x36\xbb\x05\x97\xcf\x9f\xc1\xb0\xec\xc4\xf4\xd5\xa3\ +\x78\x34\x31\x41\x02\x87\x73\x11\xe8\xc3\xf0\xfd\x61\xd4\x3a\x97\ +\xa0\x64\x75\x2b\x5a\xea\x97\xb2\xe2\x25\xa9\x70\x7d\x16\x5c\x31\ +\xde\x48\xe3\xd8\xd5\xc7\x46\x75\x0c\xdc\xef\x30\x16\x28\x74\x6e\ +\x40\x8a\x29\xbb\xae\xbc\x98\xe0\x0c\x0f\xd3\xc4\x42\x57\xa9\x66\ +\x85\xd5\x4f\xc1\xc5\xb3\x9f\xc3\x3d\x6f\x03\x5c\x7f\xdf\x8b\x89\ +\xc9\xc9\xdc\x04\x7a\x7b\xfb\x30\xe6\x7a\x80\x9a\xc5\x8b\x50\xb9\ +\x6e\x3b\x9a\xeb\x97\x40\xd1\x13\x06\x28\x6b\x12\x43\xa1\x1b\xa5\ +\xd7\xe5\x89\xe2\xda\x68\x10\xc1\xc1\x76\x74\x5d\x3a\x63\x2c\xd0\ +\xd0\xb2\x15\x8e\x65\x9b\xb0\x6c\x9e\xd5\x28\x54\x66\x55\x9a\x2d\ +\xe3\x66\x7a\xe5\xc2\x3f\x3f\x83\xb6\x68\x0b\x6e\xfe\xf5\xb7\x98\ +\x9c\xf6\x3c\x8f\xc0\x4d\x8c\x33\x3e\x8b\x16\x2d\x44\x65\xc3\x76\ +\xbc\xde\xf8\x02\x85\x94\xa4\x65\x2c\x75\x14\x54\x9c\x99\x90\x88\ +\xe9\x2c\x32\x31\x0c\x05\x12\x18\xbc\x70\x14\x9a\x77\x22\xb3\x9b\ +\x15\x97\xa3\xf6\x5b\x3f\xc2\x9a\x2a\x3b\xaa\x4a\x54\xa4\xf8\x3c\ +\xf7\x36\x24\x8d\xf8\xb0\xfe\x9f\xff\x0c\xbe\x8a\x66\xf4\x9f\xf8\ +\x1d\x3c\xde\xe9\xdc\x04\xba\xbb\x7b\xe0\x76\xbb\x51\x55\x59\x8e\ +\xd2\xfa\x56\xfc\xb8\xb9\x96\xaa\x8e\x23\x91\x4c\xb3\xa4\xa6\xb9\ +\x1d\x33\x9d\xa2\x99\x14\xbb\xc7\x99\x1b\x3d\xda\x8f\x7d\x2a\x76\ +\x1b\x34\xbd\xf1\x33\x63\xcf\x68\xad\x2b\x84\xd8\xa9\x12\x49\x20\ +\x41\xde\x29\x9d\xe5\x8c\x04\x6e\x7f\xf9\x39\x1e\x17\x37\x62\xf8\ +\xf4\x5e\x4c\xfb\xbc\x38\x98\x4b\x84\x37\x6e\xf4\x60\x6a\x6a\x0a\ +\xe5\x65\xa5\x28\xae\xdb\x84\xd7\xd6\x2f\x37\xb6\xd5\x28\xd3\x50\ +\x28\x5c\xa4\x57\x84\xa9\x26\xdc\x1a\xa3\x38\x27\x58\x65\x74\x6e\ +\xc1\x62\x63\x94\xd2\x29\xac\x2c\xb7\x60\x41\x81\x19\x31\x12\x13\ +\x7b\x44\x52\x8c\x2c\x81\xd1\x8e\x7f\x60\x48\x5a\x82\x87\x97\xfe\ +\x04\x9f\xdf\xf7\x3c\x02\xdd\xf0\xb1\xf1\x28\x23\x01\xd3\xbc\x1a\ +\x34\xad\x7f\x05\x0b\x99\x86\x81\x50\x9c\x7d\x42\x0a\xb1\x78\x8a\ +\x82\x92\x90\xc7\xf4\x12\xea\xb6\xe6\x99\x10\x48\x8a\xcd\x0a\x28\ +\xcd\x17\xfb\x06\x32\xe0\x29\x01\x9c\x25\x20\x0a\xba\xac\xc2\x7d\ +\xfd\x0c\x3a\xc6\x92\x88\x8e\x5c\x85\x57\x10\x78\xba\x10\xcd\x10\ +\x08\x04\x43\x28\x29\x72\x20\x40\x17\xbf\xb8\xf9\x55\xac\xad\xac\ +\x20\xe3\x28\x92\xa9\x34\x01\x09\x9c\xc7\xf4\xb2\x50\xe1\x4c\x33\ +\xab\x45\x61\x3e\xc0\x88\x37\xcb\xbe\x31\x52\x59\xcb\x0d\xf7\x8b\ +\x99\x69\xaa\x9b\x54\x84\x6e\x7e\x81\x3f\xff\xed\x2c\xca\x1c\x56\ +\x78\x7d\xd3\xcf\x23\xd0\x63\x74\x44\x05\x05\x05\x98\xf6\x4c\xa2\ +\xa6\xf1\x3b\x58\x52\xf1\x02\x4a\xd9\x4c\xa8\x2c\x36\x22\xad\x66\ +\xc0\xcd\x2c\x89\xf1\x64\x06\x74\xd6\xda\xa7\x67\x5a\x9f\x62\x85\ +\xf0\x4e\xf9\x90\xef\xb9\x85\xdf\xef\x3b\x84\xc5\x8b\x6b\xd8\x69\ +\x79\xd8\x1d\x3d\x87\x40\x20\x10\x40\x51\x51\x11\x1f\xf2\x42\x56\ +\x54\xac\xd8\xfc\x7d\xac\xae\xae\x42\x61\xbe\x85\xa0\xac\x03\x0c\ +\x81\x89\xa9\x98\x22\x48\x28\xf1\x34\xb0\x34\x0b\x6c\x5c\xd3\xfa\ +\x68\x2c\x86\xf1\x3b\xb7\xe1\x74\x44\xb1\x6f\xff\x11\x54\x56\x56\ +\xc2\xef\x9f\xca\x4d\xe0\xea\xf5\x2e\x68\x21\x0d\x8e\x42\x07\x82\ +\x81\x20\x55\x9e\x82\xad\xa4\x0c\x8b\x5e\xde\x8c\x96\x55\x35\x28\ +\x66\x45\x4b\x1a\xed\xb6\x6e\x80\x87\x13\x33\x60\x19\x70\xa1\xf8\ +\x04\x32\xbb\x61\x32\xc2\xf7\xc3\x3e\xc4\x98\xa6\x16\x29\x85\x3c\ +\x8b\x8a\x3f\xec\x3b\x8c\x05\x0b\x4a\x11\xf0\x7b\x45\x7f\xf8\x2c\ +\x81\x8e\xce\xab\x48\x30\x7f\xec\x2c\xa9\x91\x48\xd4\x68\x5c\xd3\ +\x5c\x2c\xaf\xb8\x14\xa5\xb5\xeb\xd0\xb0\xbc\x1a\xb5\xd5\xf3\xd9\ +\xb0\xea\x18\x8b\x88\xd2\x40\xc0\x24\x37\xa0\x70\x00\x31\xff\x34\ +\x92\x5a\x00\x29\x2d\x08\x29\x11\x63\xe1\x52\x10\xd5\x15\xb8\x1f\ +\xba\xa8\x21\x3f\x85\x17\xc4\xe4\xa4\x87\xde\x2d\xa4\x71\xbe\xdc\ +\x04\xbe\xba\xd2\x49\xe1\x50\x6c\x16\x8b\x68\xc1\x8d\x3f\x74\xde\ +\xa7\x52\xb4\x93\xa9\x54\xbe\x6c\xad\x61\xc1\x8a\x8a\x42\x4c\x7a\ +\x7d\x48\x68\x7e\xa4\x63\x51\xa3\xb5\x96\x0b\xcb\x10\x97\x54\x68\ +\x3a\xd3\x90\xe9\xe9\xf6\x4c\x61\xf8\xca\x69\x24\xa2\x11\xf6\x85\ +\xa6\x6c\x55\x94\x69\x9c\x0d\xa1\x60\x0e\x02\x62\x37\x6c\x6f\xef\ +\x34\xdc\x2b\xfa\x7a\x91\xdb\x05\xec\x03\x8a\x8a\x8b\x90\x4f\x11\ +\x16\xe4\xdb\x28\x44\xd5\x28\x3e\x5e\x56\xb2\x94\xd9\x01\xc9\x5e\ +\xca\x2e\x89\x96\xc2\x6a\x34\xa6\x71\xba\x3d\x34\x39\x8e\x89\x7b\ +\x37\x10\x0d\x4c\x1b\x5e\x90\xb2\x9d\xf5\xcc\xc7\xcc\xb6\x3c\xc2\ +\x86\xf5\x19\x02\xa2\x2b\x1e\x1f\x7f\x68\x64\x80\xc3\x51\xc0\x74\ +\xcb\x33\x5e\xf4\x79\xe9\x3e\x0a\xd2\xcb\x59\x08\xd3\xfd\x64\x72\ +\x76\x41\xc9\xa0\xa9\x7f\x7d\xba\xc9\x5a\x29\xce\x07\x06\xb0\xf8\ +\xe7\xa9\xb3\x97\x68\x74\x23\x61\x3f\xf6\xef\x7f\x8a\x00\xcf\x05\ +\x6e\xbf\xdf\x6f\x1c\x46\xc4\x29\x48\xcc\xa2\x2c\x8b\x03\x48\x06\ +\x50\xca\x9e\x13\xe6\x9e\x67\x66\x3e\x7a\x06\xec\x1b\xe7\xad\xdc\ +\xa7\x3e\xf1\x6b\x44\xd3\xda\x8f\x1e\x3d\xda\x36\xb7\x2d\x2f\xe6\ +\x58\x92\x9d\xff\xd7\x1f\x56\x0f\x78\x38\x5c\x98\x73\x30\x11\xe7\ +\x25\xbb\x08\xd1\xff\x81\x80\x88\x9b\x48\x31\x4d\x90\xf9\x17\xe9\ +\x7c\xa3\x5d\xbc\x46\x6c\x27\x00\x00\x00\x00\x49\x45\x4e\x44\xae\ +\x42\x60\x82\ +" + +qt_resource_name = b"\ +\x00\x11\ +\x07\x4b\xaf\xc7\ +\x00\x73\ +\x00\x65\x00\x74\x00\x74\x00\x69\x00\x6e\x00\x67\x00\x5f\x00\x74\x00\x6f\x00\x6f\x00\x6c\x00\x73\x00\x2e\x00\x70\x00\x6e\x00\x67\ +\ +\x00\x08\ +\x0a\x61\x5a\xa7\ +\x00\x69\ +\x00\x63\x00\x6f\x00\x6e\x00\x2e\x00\x70\x00\x6e\x00\x67\ +\x00\x0f\ +\x06\xb2\xe7\x27\ +\x00\x69\ +\x00\x6e\x00\x66\x00\x6f\x00\x72\x00\x6d\x00\x61\x00\x74\x00\x69\x00\x6f\x00\x6e\x00\x2e\x00\x70\x00\x6e\x00\x67\ +\x00\x11\ +\x0b\x9e\xa6\x07\ +\x00\x64\ +\x00\x72\x00\x6f\x00\x70\x00\x70\x00\x65\x00\x72\x00\x5f\x00\x65\x00\x6d\x00\x70\x00\x74\x00\x79\x00\x2e\x00\x70\x00\x6e\x00\x67\ +\ +\x00\x10\ +\x06\xed\x39\x67\ +\x00\x64\ +\x00\x72\x00\x6f\x00\x70\x00\x70\x00\x65\x00\x72\x00\x5f\x00\x66\x00\x75\x00\x6c\x00\x6c\x00\x2e\x00\x70\x00\x6e\x00\x67\ +" + +qt_resource_struct_v1 = b"\ +\x00\x00\x00\x00\x00\x02\x00\x00\x00\x05\x00\x00\x00\x01\ +\x00\x00\x00\x3e\x00\x00\x00\x00\x00\x01\x00\x00\x11\xa0\ +\x00\x00\x00\x8a\x00\x00\x00\x00\x00\x01\x00\x00\x21\x88\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\ +\x00\x00\x00\x28\x00\x00\x00\x00\x00\x01\x00\x00\x09\x0f\ +\x00\x00\x00\x62\x00\x00\x00\x00\x00\x01\x00\x00\x19\xe4\ +" + +qt_resource_struct_v2 = b"\ +\x00\x00\x00\x00\x00\x02\x00\x00\x00\x05\x00\x00\x00\x01\ +\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x3e\x00\x00\x00\x00\x00\x01\x00\x00\x11\xa0\ +\x00\x00\x01\x22\xba\x21\x72\x70\ +\x00\x00\x00\x8a\x00\x00\x00\x00\x00\x01\x00\x00\x21\x88\ +\x00\x00\x01\x61\xb9\xd9\xf8\x8e\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\ +\x00\x00\x01\x28\x8a\xc1\x17\xf0\ +\x00\x00\x00\x28\x00\x00\x00\x00\x00\x01\x00\x00\x09\x0f\ +\x00\x00\x01\x61\xb9\xdd\xc0\x31\ +\x00\x00\x00\x62\x00\x00\x00\x00\x00\x01\x00\x00\x19\xe4\ +\x00\x00\x01\x61\xb9\xd9\xd0\x22\ +" + +qt_version = QtCore.qVersion().split('.') +if qt_version < ['5', '8', '0']: + rcc_version = 1 + qt_resource_struct = qt_resource_struct_v1 +else: + rcc_version = 2 + qt_resource_struct = qt_resource_struct_v2 + +def qInitResources(): + QtCore.qRegisterResourceData(rcc_version, qt_resource_struct, qt_resource_name, qt_resource_data) + +def qCleanupResources(): + QtCore.qUnregisterResourceData(rcc_version, qt_resource_struct, qt_resource_name, qt_resource_data) + +qInitResources() diff --git a/pics/dropper_empty.png b/pics/dropper_empty.png new file mode 100644 index 0000000000000000000000000000000000000000..757f3b133ad23fa1e07e485b2eafc1100ba02899 GIT binary patch literal 1952 zc$@*C2VeM!P)l2H-2SuLwuT3RaTUEgmZARRbxU`;xmt}vU; z>vGntkR@rcOd!-FmPllhisBE4BNMTBEEtQ$g5zW3S81H48zrOBq-NBN)Ua;tmW|EL z3u#S{9sz0Z-n|X9_%d0RYe~Ll`d>@`Ei4^3pU;OxB9S5hpWh$wL?Yqfl-KJu8W_n$ zKn9fnWuwWgnv6!!Js8|T&r_*1?!Ei2$3NP+b8T&Ht$Og_!A`& zMxf&8(WBGD!^3$X2H~bDU?4C_mS)DJ+?aSaGmn0v7toS`7X&O;EBY?>fz(CUxh_x) zIcQQyBT#kh*fICW@Cg3%?YDTWsR<`fp3GY-h{#zItc(&*AeByu7qxM2EPYXNF)TJa zS!#li02vXG8#XlJjo05mTU#5P-A*P@MT1j$0>_UZcMlDX;M9M=$0Lo6`2M@^vIKHb z%~pg1L8zHDtaiHqGLkiU0z8L+(y0`ZiG&!lmzE0^(nPkg@e#cB=399Al~>Ty)6E1# zKwd|{<+_57j?;MX!M~xs{S-_FBV@8Fl~e?{g@746>u0i0qH&hIKn0DoLU9v|Mqzi@ zK@o~aAAT6`y!{Sd*t{7RE}X}{eL4gN2VLm;@eCeVzaE_(9l~EMd4z(}QC8r zATDUO9gAPzt|_l)OD)Ql7>DRPzAL6Hc&Q&VVr?Nw~sx*aX8PoclRU!TC` z%L6#y(}Vlgtbx<%L`qQvu&Kx_qF%IIK;|W91q$LM5sM*2pp+$Zy8(zrqC}t#+qb=s zjm?i^VBoSoflHVAaiO;lci(d-5$P2Y7F#V8%d;%!Z6`BmHZM?uLX`Z?pdz{Cwj^In zDw#}D@NL0++jgMo?@e&I2K5Q__4NyR?pVDVKlS#CoWMbtwL0r>&A$SeBxYcali9%9 zr4iV&bdo=LH#l#&H7 z1I#3$`3b!E;%4mp;6to`=pl>@56wrQm)H%Vu5J}ZC@1iFrlni9Sol)_r)XiIX30|A zR9PvWPft%{m=?nJ9uwoE^AR|Y(a~|#)-H#i zPQNHw%aXGN+-A!Q5u`TpHy#NCIhFJXY}(X{fBo}wEM2w?lau561f0%pcsw2~S+az( zSPVf*Kzx+4^>e^!X9WE|OiyKx_sRuTaFms6r|cX8Pe0v)g9pCEf~qRQnA9h5?p!zd z@)~Zwc`>EmBzy#*C_0vF6vQd00ddRdDv+Y{p{3;seD&2gaFmu}+UuE*fK$`})z#Ix zeVwGBiwL0Kduaroe6ksb4tpw*ei8Pra7Lj}2V;le5u6&0bF z3YgEo*oTEx3lI$WxIUaqpriAQ*fLgY2}vK%%V?t#i=}fO|2a;L&Y1SYhYz=)pe*MkzAV@O*%yCMc&Uw`;KPK! mm#plf{{sPb9B;80llcXmq6%HeCmaC)0000fiPs%AdBp+lq=;C6bmBUctczgKZ4hU}KxL0WV&A-#h!Bes^~5II#R^)pl1`cV>6y ze&0FgJLjG&;lKRZyLWG)-EOZlo6Rk}U8X2XlgVVNlOzedcI~q8xA@%WrT-8|I&|pJ z(s(>R&t|jjl4Yq@mQA$;TFC)1(&;pGAQlP+N76c?`F2};@aW;AG5*iT6-iOddRk9QEt{Ht z^4g9a3%KT!Tm+;82M)Aw@g=e>JL$e1yms>1CR`_m*Xu+|`agu|i0q{rh? zOajSFAd^l&Suv}+Sy8}y2yhdBPbO1nT(|bsJ)eBiuZ`z2L$BYMLNnDe*o;HT0XI&>iBzg87y2BtTo6H$9 z#ydJ}c02lS_k-R=*NrYvLk_+koFPzl;=~EJ>wydB&z-}z?b~to?AgphS&MWKn650T zSd6OW^(d*VL0&;oh6GnJkwh#KfZzQPj|Oj0eSR<;2B|a&w6<==`#*mlyLazKcTcw< zP{vm$X9=7cZtq7xCS#TXFHig;{qCi!FI}tlqK{71cFZTxvr}z6yukj6|BA zrZ>zCMKGE~KuciEqu~jO-0%DX@mPe*%V^#9J$&$s5Af4>-$ie4k04+O@*4z(hey!S z@fEgg`8F=Me+h*#rh-vc?Zm1rZCF-44>bgz(scOaX@cu8GX$!M1Q>EVNi497aG2nl z*3j>o!cf;aOizqrTkAG__~9?{_B-$3)~%a3crXWnp`l@PUAv0S&CTezaz)4orBzO> z-TEfhI_D!wOD4566uuxp6#*`1z=;jJSwevdLwIRE8zIT=5if4FpGLz=%kj}izrveu zy@me%J0x)MSptKDgXq5AgN>WkqqCzER$Bp@+TOz}E2?@@vpJ-jhz@67|2cF-n~KG?CnKEVSWZ!qm{B320gh;YbqUP!eW~8DS+2ZM+YA_WTArUjG4x zh6ZyJxO;a1xBBj2&6?G?d*6kYUGL%L+J*2^TuK()oV8vxNyfd!B&?>j34(?bh{n>0 z2m;|G%drltRl)p92L`$?;nP3<1?$(;VPte9w+8zA2N<7zytMK~#BCMW*xZ7Ahn*@< zeeH5uYvHwp7x8!kE_VeMNBU*ByA9n>v%!nSlHHju5#fGZT^3;{C^+VbnU8pk#F`TI!dw;w7nY zeHL6X-bmxv)yGD44BYQ97ASOXK#D25uDqDw3=h-9LRYC~h1E~N#j~HoJ#PbCf8UFV z$;sRVdVBjY=6Zmd#fwn6ZabRmmq5`I1}JM1L!ld`*X4=g+Gr5L!FF_AI%6!*(6SYd zWt*^Up4BK+W>uOk<7Rs0!aqNQwrC4({`I$*oc27Az)g%ZK8qGDL}kNvyx#Z%g_NXa zEOiuAoSBe_=`1oa3o>!D3^S(kZMgw_rhm88H$Qdqh%IE ze_%*jf`^y>1fSpcJObVDvGFJ)0n@x1G_8LH3z>$2P@H{23bA;Kf|8KO^b+c}=9v&k zip>fr%@_LyGTKsWbrzoX7OCP~l87t3_cxz=$`?zA;i+%MJI7nQ5wmIWi)+|vdDtC2hd>|@Kv7W=XjX!kWr&6?&sZ_pftzp56 zib70!eMo42q+?N|wU!IZ5SLVFI@6Hp>Gn)v_{wP{q7n9mrfgM|X`BtAU=9LeH*9b3 zG~6rtdXd`&>;sC5i;zzd7UbI~MAaxhURJV{*#XI3$}WkP!fG_8ak@P;IgW{YHxLa> z8(xsIo%PJe%(gtjhV>Z&qAMOBe`sWY!%@I=%QIf|dFeh1(#u&0)BR*}c0tLYsegUC z$yzDS2L`bC=c%)oXmlfCKlbl`iU7|AZol7e97e<`NX()vivvgoP|BVY=ALI}K6(c1 z8|SUur#|s(MAO=j9zEJNw=FN`SdyLp*B@37csN{`b1)I-CA$dG|AByrn@BNDkogzs We4|~wMrP%|JT#6Bs`h!xnF&Awz zPNOERY0|_J8>(X)EydIU6(}|W1;ikNu*-6Xg@wJH?fabzE-OUsne)zf_Ut*|^SsY{ z`M#sTJs!V!=R|N+!lLI?D(z+{RcRi0z)s;%uu}mK?{47Tj^n*u-yD1Er5}ym%e;!a z{lDkv*@gOu=yHu#otk0@M{IZ?66X0ss}x_B%|T>t_1n-rFpkE~K{)KLrqKa&e#w?K zH*lNkeGyO{_{){UI?iwFV-Lo`7$1ZQ7cguG#vBCb2Gm4CO*5#-{MA6P76{XdleU?y zID4fVcKg(kq750((=GS?5YW6`eznsOt&h%GmVj{rzBLXE5xAD#(5mPG0@o-+66~6d zbRs$A_M|-7sk$!QFpZdB$$!W|Kb`kNK=bAo|GBv`)evJegreOFm?uO~P9*|qy@3_t zs8Wh5L~=}T4~2k-+pUCFr@+;YVVrO1G8L^)PxK|>OF;A17hiX+cpzcEAwCpsqx3Wh zr(xi;fU12|2qAElo5v)9GXi!858mxTaEJ~q<{?xy-|BrOKRv;lgf{`z{?Dr4PfCb+ z_E93*OyDZd9CCXTkbVo#5%`qd4F>_+83`Jvrg%7=JmM2WP}z7B-*lTlDE{%I+vJ>( z2`IMhd2eaz@>R`0T>T(AZGg+;0!w zy$4FA0vt&cK2L{!$0WYG($=!GAUmB^uRvhcQyXInw(aW4&rL(`Buxl$^4SQGBk)&= zrRRN&69LPB9X1<*69ETF&yy-z0@ji@TAI+0EF>}jpM2GTlfT;^d*RHdCN_;~&(Sl* zS()j-N?sHrCfyv;`>8~8^Bz=p+F>PcSrCJ$ES5!Sg(_)jwPMujgqmK^(!(5qYH1=3 zjqF+%z@etyjLY>`e_66+?LJ08x9`)c`qkNK#<0jBC_S@hIwvJ#$B+Y+*T>w7LD5EZOJ;loid_dCG(iNK7oxMD&ez-sTE&ufW5 zc9cM1--*kXU4_}Vkw{aahNW7boOHtw=a0{7Oz5+U1nMfwpZUYy9jDmiKiz%k2g`Hv zKV}LdO(JwNyIjjOQcRN)1M&B! zUbI;zC?}L~JMA4qmLAs85Ed2tzEZ<2a1ugFToBei6a{(}JTEphmjv#Taz&{D{zwFN z$^`bGxMU$Va8Ynyxue;LKuna5%1VI`t8NHhsVU3lB;*AzdFqD13u_lsuTa9~ z6!!=+@H7F4$2jJ5*q?51LyAqXCsS$Z~quBUaY(R+NRuqC1iJ%R&H)c4 zcw1W+sw>XE{oCDJONB;cSe$CyvF})Y;lt?|vpEEh*^Dp^jSIU9^3fy z-KVpnmyp*A0Z8lB$EQhfq7p7%F)gdpb9Vx6Nu}nhi}=&wy?M3g&s5Q^k?G}_Ec(@h zpByqSPJKE%F$oHiN=-y$V7Z&A3Eb+qi?_2#&y&tK8(Y!dSpV1Gy!6Zt!ZS}V$I`|m zkZi%;k1Hy(mnMfBW21%OXsBjcCu5$gDb?*%sv{0@QePnFECiV6Zkm12)C*H{bTkrN_^m{_ZkkL}qfFu+>$m#Ru53V3`6aM}_WoH}4rRv*8{F z-Y`(Sc}*To!M+m)eYa%On^Lj}rbOxSv!|CN#)Yk4l_IW$5GU;zlbXr50ZY41M$&Ta zzZfxh4;62EGEee+V6IK6FA2lTZ~XrCSVPiNYqE`azz~5!dd+0R%TExApy+3%^mnH(_*?{J5}~X-(;iu!y8hW0e;pj6Pc$UNA}BN*!C`tKU{0^t zJ18&4hDTr<9fZ}~LmSkQn`OrizIOG}`6gCwWW%_}4-@a4@R#x;G9o%I>gi1{Z~aI4@pn)6o4YM^yI+d84fpwJ=Ka4$A`(CrOhzAH&`CMz zeX!J^EiLGa0_@9pLIP%9|6d=cvnj&*g&)L?v+uhMgl%@HPT-i!W8dR{x5+0Nkhla} P00000NkvXXu0mjf;9@3Z diff --git a/pics/information.png b/pics/information.png new file mode 100644 index 0000000000000000000000000000000000000000..93c67f2bf0e6e07da700bc508b7b9f55e5c6f89a GIT binary patch literal 2112 zc$@)12*3Ak7RCwB)S8Z@q`L{>?WIR zLb5R*d>IqI3RYpTwqr-Jno@97P<|*<+Zp?#?XR{o^=I23b~;o0quLH-S_iP884C=> zsg+KP;(&-i0+K}XLEe`@}mh^^+d! zP5{W&&m8)_th&Dc%;W2^c|$Y2E}+~Ccs=ATYqcC9`_K&_F>4~4$RI{?{>m3PdAf&z z{Bh#V9Y3JgjsFLL8r=ET(5jW;`WK$O9}P7Msw#jppT%+J@;EN{PhujTw!@B`d!b4X z+MB9yZ)+{qEU&`w4Gld*as1;{4@M%f(dfY^o9VUe?Ep|Kb{rmj`my!lEf1_fLkPGQ zN#VC|UqF01167tF$%5`%%pbPwXjt}D=!S_3pBsBR+EHKS!^OTBKJFU9*-!f;lW#uV zQVc@A)$_yGpL%S4JsWSS2ll_$kA26l*jBtQ1*#%LkqCs8Pd))z680K{b19My;&k^Y zvbu?fHrz#5B*By<|8(u6-%E8J{vFvdO97CpckF+?y|wY{J3H24Syo8{Il@4P#-}lrOylAEnxM^D7@wRiw_3gxPIY%4 z&nIRcfT;ZD%dKj#`AjnPkIKKJ-$}>dvROtfM=3 zV`xmnzs`@4=ZYOtR+<;az<2%J=J0&z_&2ZdUg5h8$m5dn@zrs}rqgI^38H;v7;^2} zPPRD@pzNNGkgR&zHh!fEHI(gq=RWe>MR%SLZC+Gaw*MDDg?okKCcf~JBS%m7V|lm& z4dF^is;kXMHuC^vPusRV8`n0H$H2vbB$0}4d#)6CGcOccFL`zF%;>yv{>^K=Czm+G zkx0+!7#K^SHt0oNZ2)Dfw(nuv3jJ|eYWc2~#wz%IGEQBZBGe9gQKCpCN*xdFzxvN% z{QBMN_L`dvFaSjsCF@#-vwkfSM{8pUJ|8ie%fE}3e!~DfmgH({Y6wy?0Ry9PqFKfE zrugBs)hX+Biz^|Ql$^nw8|*!UUXXJI#$qWvxYmm@;t3(U+t@Y(P?%0t3I=L+zXsz@BMDpts_JF^8vQoIt;CEFfxT`Jd36!ejAW1NehZ1%O!|N zUi6GCB-B)TF--j-mC8^#X?<*)0q8VB9s6Q*3Xy0E%jyG8SxI8SgjzHLCB$yugn3D@ z9KFLa%%!u?%XHMlU4rwKWYkcZs5_kenfa5=knNWX_&c6naTG z;cul`^4w>mNF+3fOsYRaHrckEBRJWbOI(Sm_w4cqe7L)*(ncPinJe(xnS!`d4t=-v z768wFwFXBn#i&BQY{`6;9Lf6XatH$W&cA!1C1#O4`}%X)*uXV9rYX7eDed$3yS(k& zj-0*V+vX-Fd~{PIbg~win0Cf5;UIOBH@o-u*+Zz6<1=Rq_xy0nLOSb#G zM|Ff75w9sZ??o<8YAVXfcsbrU(S;imNir}ydE>1Yc9Sh`rRn04jM>3Vb-#TgRrm1r ztSOa^C+4uGC4~A=ITAA(QMKhHXO5_pn-WvZM~Y{FrrJu-Cj;I%-c81*q0c0vV+VF_ zB^$#e$^2X>7d5*KPpzrTo$t&YGnDdTIX{1oWy5+TY7IR{mmT41<^EJfX5OyR2 z)MjhR>rlW;2{(q$lRcP8%)r!UVq*t)eus1qlEk?D79GoZ%qM9MZGH7+CD`0iPpvvs z6|hOFg?OT&#z)~+>^rt{9a78c7>p$8TOf+7Lov+K2&ShqFyceUCyxH`7o=~9Bw9R{ zFZf|X5+tefv^=odzv-o4N~*slP~nA}CuADeI5(KnIZ^p)OloEsqFIypT&E$_%1#d^ zKYH!w+Te%Xq-Ts|>UKX&3P<)bl4_C~B@kW`xc|kiO7-232$yf^%}Ru=$;!+OWTU0000 + + + dropper_empty.png + dropper_full.png + icon.png + information.png + setting_tools.png + + \ No newline at end of file diff --git a/pics/setting_tools.png b/pics/setting_tools.png new file mode 100644 index 0000000000000000000000000000000000000000..2a1e72744479f7980973cc388a14300bb0a4bef4 GIT binary patch literal 2315 zc$@(X3H0`fP)Ran$O@M`*`EZE8B1X%mg1D)>ymAr_M{CaIk`W%965Q81mxv`xZe zQKX=gqVirrfqgH#?C#z7tLMMDo2-b)w7oNDcK6=@{@-_A-}#pZQ@plq-w~OZka$8Q z5)~&TB#4q{B*C<4(}1RFkl(upJw3g$lYe+~B`)0EEC!8613Vr+ z@Oo?Q4#xpp*N^M`WMAGCu;~M3hyExK2zE&~Zh-l@Il!YRP$-qq+WIg2R6$=~U*mhH zPwl|9hVtWY_9Z1GCM;XN6vT0H;P?9xoCi*)6Z-q_!PoT-pim5Fo_YVhK0<>3B=CI) z%4+iS@(_DH3v6YqP~X@DvgT&!>g=p!7|U?8tf>Xpy5Fd%?3*`tZbD(<66m;n7p`A# z1&jo1@zdiWH#-ZGlat_svw!Zv^&&LqWCZB_r3Y*B3-Y9!q#I#$)Bpyf5gHJ@x3^dS zR@I3uxJHL&Mq~Dt?0(Ei527iwbg!!*?`v zVoe6N1c>)Sp&-~}?L?s-AAwLFkK(`w5!{5;>j0<2 z33~k~7T$H(^qiDzzK*O7evuI1(|oqAz6IyAHSnZHqsA3ob$;FgP^#20Hf9F9!vzk9 z1MGwZwvk4wgRklvSun1Ozq`C1muhfu0OGOl1p+>jK;zTk_xUCzKo9~4v`FApIF%;&G0||ZrHPSCA6s+XdhBRidzM*X82%8qlf0}EsDs<$QZ1Y&}m^|=!}I0!=i5a;7@1v z;nxRT8@qU1D@LKEAc?nU&(0`JPELl7jt)3fw*P6oujfRBlyuD2&6|()F50!`x6dwv zyE;3l83(xi6x_Tw2h{R2Dn9eSYwuMTAFzY}f#e zZj~ZC1j$}lF%YJ@gRyv;>U}5>D}4s*?IjH>wgSa z2D#niBz=C@aT#4Xb{Q!KmEgK|~d6~1~#hHJvZ>XINc%leQC{(fg&KEDr zoLl|!@`dolIB+k5t8q?v3=iN`o&ac`4}O%t0CxVh58CxUFqzF)bjQ|h;qnhtk>ZD* z#gv@ezI=X8b=k8E;cCBu&3Q1m&TNEXtr8@+s{d)wsX>4Ov+=`r7^5QKFWZ;GvUzD6 z;||tc92e=wrr;s)=-18_uUI(0diRQKxNz6NR=d~#J-FU%0Ht~aj31U2-ERK!YM4+k z>u-YWG(mhC{C-sq{6lVmUo1!lwbd)pNmk|BWEU3z>hAgyZB;Zfpl$dGFC2@7 z>3(bk=;+egnmwx*OMjY~2p79XLGw89Pb;?A?l!mKJL*8E@N!lZT{Ay7uXfP7m2_iv1 z?viW|Uzi#qie%$!cTG+rK!6sP)t=q;lSM@<(ep>au`d}5C{z!?*?H}_{?qc0IDv8m zc|%CJnH!(AhU6@fxM^XkF!aeJ0tBYTu6f~N>FNboaKYey)^i3ABqPcJaNLnq=+7Vj zm;<+uE2{4lbutn!APKF_C(&oG&a*b1uL-oFXpT4vAK=cI^KV=2d+_@s{oppJWe9$b zlUOSUIR5em6jXI`=N?AUBhzY!!HU9bMI{(EIpA`)5lU8N;U5A4d#VZ$pwt!{P&k$d zII&=G&!|eKs@nbvm-EMgPfEguBy31R$++N^+FQV5j7?;~1yT^kEBN`AteJ698ap4H zxP=*y;X{ghsB)@i)yW-uaLq6`l)O{;H|2jM9sH0~QCKvx8iEmdr%Y2-{4zct4d?(S z3NjMPF4yuzVJUc`W{*x@?eG_>Q@ajwL&-QraF}4gr6j)g(P^PL`5BjPq(xO#{5x*X zu>b)w>toSoaFdYYMy8P~xUtFfe2OFjQ`XGY5$jN}5(>C;nvhdSAJ=B~|HAV})RjBN l3pmH@|ARyS-|IgD3;=X$f&6|~5VHUP002ovPDHLkV1lF~V^078