summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndreas Brachold <vdr07@deltab.de>2005-07-19 15:09:05 +0000
committerAndreas Brachold <vdr07@deltab.de>2005-07-19 15:09:05 +0000
commitf897f2aa7055c493db6391c50c8d19da970078e8 (patch)
treed13a515b24c149d7da4e9828cc9e9c73d4916f00
downloadvdr-plugin-image-f897f2aa7055c493db6391c50c8d19da970078e8.tar.gz
vdr-plugin-image-f897f2aa7055c493db6391c50c8d19da970078e8.tar.bz2
Initial import with release 0.2.3
-rw-r--r--COPYING340
-rw-r--r--HISTORY217
-rw-r--r--Makefile130
-rw-r--r--README148
-rw-r--r--README.DE152
-rw-r--r--TODO7
-rw-r--r--commands.c239
-rw-r--r--commands.h64
-rwxr-xr-xcontrib/image_pregen.sh46
-rwxr-xr-xcontrib/install-scripts.sh35
-rw-r--r--control-image.c1137
-rw-r--r--control-image.h169
-rw-r--r--data-image.c266
-rw-r--r--data-image.h87
-rw-r--r--data.c508
-rw-r--r--data.h128
-rw-r--r--examples/imagecmds.conf22
-rw-r--r--examples/imagecmds.conf.DE16
-rw-r--r--examples/imagesources.conf21
-rw-r--r--i18n.c625
-rw-r--r--i18n.h30
-rw-r--r--image.c129
-rw-r--r--image.h67
-rw-r--r--libimage/Makefile51
-rw-r--r--libimage/error.svg2167
-rw-r--r--libimage/error.xpm454
-rw-r--r--libimage/error_small.xpm183
-rw-r--r--libimage/img1.xpm56
-rw-r--r--libimage/img2.xpm56
-rw-r--r--libimage/img3.xpm56
-rw-r--r--libimage/img4.xpm56
-rw-r--r--libimage/img5.xpm56
-rw-r--r--libimage/img6.xpm56
-rw-r--r--libimage/img7.xpm56
-rw-r--r--libimage/img8.xpm56
-rw-r--r--libimage/img9.xpm56
-rw-r--r--libimage/pnm.c512
-rw-r--r--libimage/pnm.h218
-rw-r--r--libimage/xpm.c224
-rw-r--r--libimage/xpm.h65
-rw-r--r--liboutput/Makefile58
-rw-r--r--liboutput/encode.c369
-rw-r--r--liboutput/encode.h92
-rw-r--r--liboutput/stillimage-player.c61
-rw-r--r--liboutput/stillimage-player.h55
-rw-r--r--liboutput/stillimage.c210
-rw-r--r--liboutput/stillimage.h54
-rw-r--r--list.c221
-rw-r--r--list.h60
-rw-r--r--menu-commands.c163
-rw-r--r--menu-commands.h60
-rw-r--r--menu-image.c115
-rw-r--r--menu-image.h41
-rw-r--r--menu.c402
-rw-r--r--menu.h75
-rw-r--r--player-image.c478
-rw-r--r--player-image.h148
-rwxr-xr-xscripts/imageplugin.sh160
-rwxr-xr-xscripts/mount.sh35
-rw-r--r--setup-image.c145
-rw-r--r--setup-image.h73
61 files changed, 12036 insertions, 0 deletions
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..5b6e7c6
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) 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
+this service 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 make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. 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.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute 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 and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+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
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the 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 a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, 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.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE 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.
+
+ 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
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision 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, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This 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 Library General
+Public License instead of this License.
diff --git a/HISTORY b/HISTORY
new file mode 100644
index 0000000..27e3f31
--- /dev/null
+++ b/HISTORY
@@ -0,0 +1,217 @@
+VDR Plugin 'image' Revision History
+-----------------------------------
+
+2005-01-09 Version 0.2.3
+- Bounce to vdr-1.3.18
+- Add -fPIC to CXXFLAGS (suggest by Thomas Schmidt)
+- plugin: add reload image after commands execution, if image changed
+
+2004-12-06
+- Fix missing environ variable for examples/imagecmds.conf (Reported by Thomas Günther)
+- Fix wrong folder flags on distribute archives (Reported by Thomas Günther)
+
+2004-12-05
+- Use now threadsafe functions replacement of strerror
+
+2004-12-02
+- Add missing -lz for static build with ffmpeg-0.4.9-pre1
+
+2004-11-03 Version 0.2.2a
+- bugfix release
+
+2004-10-18
+- plugin: include support for ffmpeg-0.4.9-pre1
+
+2004-09-03
+- plugin: enlarge encode memory-areas, avoid encode failing (Reported by Christan Jacobsen)
+
+2004-09-01 Version 0.2.2
+- plugin: remove unused code on BuildPesPacket, avoid SIGSEGV on some cases (Reported by Tobias Grimm)
+
+2004-08-29
+- plugin: add header and format description on imagesources.conf/imagecmds.conf
+
+2004-08-24
+- plugin: change internal handling of delay for slideshow
+- plugin: add DeviceFreeze/DevicePlay control, for better image change
+
+2004-08-22 Version 0.2.1
+- plugin: fix typo on housekeeping tmp-files
+
+2004-08-21
+- plugin: Updated finnish translations. (Thanks to Rolf Ahrenberg)
+- plugin: include DVBDIR on libout/Makefile (Suggest by Rolf Ahrenberg)
+- script: enable PATH by default (Suggest by Ronny Kornexl)
+
+2004-08-19 Version 0.2.0
+- plugin: check temporary file and try it to remove if it also existed.
+- plugin: call of OSD_ErrorMsg only from OSD thread
+- plugin: add new default housekeeping mode
+ ( use temporary filenames and remove this files at exit,
+ for use image_pregen.sh your must disable housekeeping at setup )
+- plugin: rewrite zoom
+- plugin: remove option "Playback mode 'use DeviceStillPicture'"
+- script: now error messages dumped to syslog, (/var/log/messages)
+- Move functions to encode MPEG Frame from Script to Pluginssource
+ ( now plugin only depends package libavcodec1-dev to compile,
+ and package netpbm and libavcodec1 to run )
+- script: full rewrite to simple single script which only depends anytopnm / pnmscale, pnmfile, pnmcut, pnmflip
+ convert.sh/convert_jump.sh/convert_zoom.sh merged to one file, called imageplugin.sh
+- plugin: rewrite generation of indexview
+- plugin: use xpm-files for numbers as overlay and errorimage
+- plugin: read ppm/pbm/pgm Files direct (see libimage)
+- plugin: encode images with ffmpeg to playbackloop inside plugin (see liboutput)
+
+2004-07-31 Version 0.1.1
+- new release
+
+2004-07-30
+- plugin: add parser for placeholder %s on imagecmds.conf
+- load imagecmds.conf now on menu open
+- Exif State Viewer (see examples/imagecmds.conf with jpegtopnm -dumpexif %s > /dev/null ... )
+
+2004-07-27
+- plugin: remove blocking umount, (thanks to Peter Juszack)
+ reorder slideshow management, outside player-image.c
+
+2004-07-26
+- scripts: change mkdir -v -p to mkdir -p, for work with busybox
+- convert.sh: fix mkdir order before first use this folder
+
+2004-07-17
+- convert_jump.sh: Show if used, number on indexview inside image
+
+2004-07-07
+- scripts: prevent some zerosize files, e.g. vdr-image.pnm if pbmmake fail
+
+2004-07-03 Version 0.1.0
+- new release
+
+2004-07-01
+- some script bugfixes (thanks to Ronny Kornexl)
+
+2004-06-28
+- fix slow browser starting
+- new faster threadmode, without polling on ProcessKey
+ + prepared for housekeeping and image pre-generation
+- remove blocking key on converting, for commadostacking
+- remove anymore necessary parts for create black.mpg, e.g. uuencode, gunzip
+
+2004-06-27
+- Implement a precompiled image, which show on detected error
+- Include a used sample, other images possible with edit Makefile and "make errorimage"
+ + artwork be inspired on two icons from Nuvola vector icon theme which created by David Vignoni <dvgn-at-libero-dot-it>
+- Plugin use now the real generated file, image.mpg is now out of use
+- Little Scripts speedup
+-Remove the last script step copy xxx.mpg to image.mpg, nocopy is now default
+ + update: contrib/image_pregen.sh
+ + update: all scripts/convert.sh,convert_functions.sh,convert_jump.sh,convert_zoom.sh
+-Remove legacy around ImageMagick, to speedup scripts
+ + update: all scripts/convert.sh,convert_functions.sh,convert_jump.sh,convert_zoom.sh
+
+2004-06-20 Version 0.0.9
+- Redesign OSD for vdr-1.3.7+, thats work with skins
+ Hide Setup for ShowDate, therefore is'nt work anymore for vdr-1.3.7+
+- Change text around progressbar on part vdr-1.3.7+
+
+2004-06-08
+- Added finnish translations. (Thanks to Rolf Ahrenberg)
+
+2004-06-06
+- Fix my mistake on zoom, (thanks to Thosten Schnebeck)
+
+2004-06-02 Version 0.0.9-rc2
+- Fix index view
+
+2004-06-02 Version 0.0.9-rc1
+- Fix plugin hangs on reassign files from browser, thanks <steffx-at-vdr-portal-de> to reportings this
+- Added playback live audio from primary device, since VDR-1.3.8 support this
+
+2004-06-01 Version 0.0.9-pre6
+- Fix unquoted strings on scripts, thanks <steffx-at-vdr-portal-de> to reportings this
+- Added check inside scripts for requirement external files
+
+2004-05-16 Version 0.0.9-pre5
+- update to VDR 1.3.7
+
+2004-04-01
+- protect plugin before hang vdr on DeviceStillImage, if image.mpg has zero size
+- extend error messages
+- control environent from Plugin, to set ASPECT_RATIO,SHOW_NUMBERS,CONVERT_TEMPDIR
+
+2004-03-28: Verison 0.0.9-pre1
+- integrated scripts from Andreas Holzhammer, now no more ImageMagick !!!!!!!!!!
+- changed zoom code, to start at 768x576 and Zoom level 1x
+- fixup on some small issues in the scripts
+- example scripts handling changed
+
+2004-03-27:
+- make statusmessage for graphlcd-plugin recognizable, with prefix [image]
+
+2004-03-17:
+- handle more error's
+- add menu with commandos for images (userdefined imagecmd.conf)
+
+2004-03-14:
+- changed if single file selected,
+ generate a slideshow for this folder start at selected file
+- cleanup OSD/RC-Layouts for more usability
+
+2004-03-13: Verison 0.0.8
+- fixed to install.sh
+- online readme is now the readme
+- made history avail online
+- readme and faq updated
+- removed debian stuff, debian port now maintained by Tobias Grimm, see donwload area for more info
+
+2004-03-04: Version 0.0.8-rc2
+- changed background file, now generated using netpbm tools and placed in the cache folder
+- enhanced slideshow time to max. 300 sec., no need to limit to 60 sec....
+
+2004-03.03: Version 0.0.8-rc-1
+- Fix for commandline parsing (thx to Hollymolly and Henning from vdr-portal.de)
+- README now has a DOUBLE NOTE if installing tools from source, use "./configure --prefix=/usr" !
+
+2004-02-23: Version 0.0.8-pre-5
+- Added other File-Filterimplemation with more feature see examples/imagesources.conf.example
+
+2004-02-22: Version 0.0.8-pre-4 (onno <kreuzinger.biz>)
+- Reworked convert* scripts for better aspect scaling
+- Started to optionally replace ImageMagick by netpbm tools, check README.SCRIPTS
+- Cleanup obsolete code from scripts
+- Added image_pregen.sh to pre generate a file or folder (cron/at/..)
+
+
+2004-02-22: Version 0.0.8-pre-3
+
+- Add Filter for filetype (by Onno Kreuzinger)
+- Implement Interface to Statusmonitor (this use e.g. GLCD-Plugin)
+- Fix "Error: unknow config paramater ..."
+- Make the most text translatable
+- Change setupmenu form Integer 0/1 to yes/no Selection
+- Remove some old file form mp3-plugins, and reformated the source with indent
+- Fix Header with correct copyright
+- Cleanup errorlog (esyslog)
+- Remove some possible bufferoverflows throw sprint()
+
+2004-02-20: all versions between 0.0.5a and 0.0.8-pre are by Interpohl@vdr-portal.de
+
+Updated by Interpohl
+
+ Extensions in V0.0.6
+ - Autorepeat => default 0 => setup-parameter for endless loop of pictureshow
+ - remotelayouts => default 0 => setup-parameter for switching remote layout (see below)
+ - Using of function DeviceStillPicture or function send_pes_packet
+ => default 1 => setup-parameter
+ => DeviceStillPicture seems to be not works in VDR1.2.2(-6ct)
+ - Implementation of jumps (like as MP3- bzw. MPlayer-Plugin)
+ - Implementation of zooms (1..10) with possibility of navigation
+
+ V0.0.6_1
+
+ Reworks on Jumpmenu (red button)
+ Now you get a 3x3 Matrix to navigate
+
+2003-01-31: Version 0.0.4 First Version
+
+- Initial revision.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..24b86d4
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,130 @@
+#
+# Makefile for a Video Disk Recorder plugin
+#
+# $Id$
+
+# The official name of this plugin.
+# This name will be used in the '-P...' option of VDR to load the plugin.
+# By default the main source file also carries this name.
+#
+PLUGIN = image
+
+### The version number of this plugin (taken from the main source file):
+
+VERSION = $(shell grep 'static const char \*VERSION *=' $(PLUGIN).c | awk '{ print $$6 }' | sed -e 's/[";]//g')
+
+### The C++ compiler and options:
+
+CXX ?= g++
+CXXFLAGS ?= -fPIC -O2 -Wall -Woverloaded-virtual
+
+### The directory environment:
+
+DVBDIR = ../../../../DVB
+VDRDIR = ../../..
+LIBDIR = ../../lib
+TMPDIR = /tmp
+FFMDIR = ../../../../ffmpeg
+
+### Allow user defined options to overwrite defaults:
+
+-include $(VDRDIR)/Make.config
+
+### The version number of VDR (taken from VDR's "config.h"):
+
+VDRVERSION = $(shell grep 'define VDRVERSION ' $(VDRDIR)/config.h | awk '{ print $$3 }' | sed -e 's/"//g')
+
+### The name of the distribution archive:
+
+ARCHIVE = $(PLUGIN)-$(VERSION)
+PACKAGE = vdr-$(ARCHIVE)
+
+### Includes and Defines (add further entries here):
+
+INCLUDES += -I$(VDRDIR)/include -I$(DVBDIR)/include -I.
+DEFINES += -DPLUGIN_NAME_I18N='"$(PLUGIN)"'
+DEFINES += -D_GNU_SOURCE
+
+
+LIBS += liboutput/liboutput.a libimage/libimage.a
+
+ifdef FFMPEG_STATIC
+ INCLUDES += -I$(FFMDIR)
+ DEFINES += -DHAVE_FFMPEG_STATIC
+ LIBS += -L$(FFMDIR)/libavcodec -lavcodec -lz
+else
+ LIBS += -ldl -rdynamic
+endif
+
+### The object files (add further files here):
+
+OBJS = ${PLUGIN}.o
+OBJS += i18n.o
+OBJS += data.o
+OBJS += menu.o
+OBJS += data-image.o
+OBJS += menu-image.o
+OBJS += setup-image.o
+OBJS += player-image.o
+OBJS += control-image.o
+OBJS += commands.o
+OBJS += menu-commands.o
+OBJS += list.o
+
+### The subdirectories:
+
+SUBDIRS = liboutput libimage
+
+### Implicit rules:
+
+%.o: %.c
+ $(CXX) $(CXXFLAGS) -c $(DEFINES) $(INCLUDES) $<
+
+# Dependencies:
+
+MAKEDEP = $(CXX) -MM -MG
+DEPFILE = .dependencies
+$(DEPFILE): Makefile
+ @$(MAKEDEP) $(DEFINES) $(INCLUDES) $(OBJS:%.o=%.c) > $@
+
+-include $(DEPFILE)
+
+### Targets:
+
+all: subdirs libvdr-$(PLUGIN).so
+
+libvdr-$(PLUGIN).so: $(OBJS)
+ $(CXX) $(CXXFLAGS) -shared -export-dynamic $(OBJS) $(LIBS) -o $@
+ @cp $@ $(LIBDIR)/$@.$(VDRVERSION)
+
+
+subdirs:
+ @for i in $(SUBDIRS) ;\
+ do \
+ ( cd $$i;\
+ $(MAKE) all;\
+ ) \
+ || exit 1;\
+ done
+
+dist: clean
+ @-rm -rf $(TMPDIR)/$(ARCHIVE)
+ @mkdir $(TMPDIR)/$(ARCHIVE)
+ @cp -a * $(TMPDIR)/$(ARCHIVE)
+ @chmod 644 -R $(TMPDIR)/$(ARCHIVE)/*
+ @find $(TMPDIR)/$(ARCHIVE) -name "*.sh" -exec chmod 755 {} \;
+ @find $(TMPDIR)/$(ARCHIVE) -type d -exec chmod 755 {} \;
+ @chown root.root -R $(TMPDIR)/$(ARCHIVE)/*
+ @tar czf $(PACKAGE).tar.gz -C $(TMPDIR) $(ARCHIVE)
+ @-rm -rf $(TMPDIR)/$(ARCHIVE)
+ @echo Distribution package created as $(PACKAGE).tar.gz
+
+clean:
+ @-rm -f $(OBJS) $(DEPFILE) *.so *.tar.gz core* *~ contrib/*~ examples/*~ scripts/*~
+ @for i in $(SUBDIRS) ;\
+ do \
+ ( cd $$i;\
+ $(MAKE) clean;\
+ ) \
+ || exit 1;\
+ done
diff --git a/README b/README
new file mode 100644
index 0000000..eedeced
--- /dev/null
+++ b/README
@@ -0,0 +1,148 @@
+This is a "plugin" for the Video Disk Recorder (VDR).
+
+Originaly written by: Kai Tobias Burwieck <kai-at-burwieck.net>
+ "Interpohl" <interpohl-at-vdr-portal.de>
+ Onno Kreuzinger <o.kreuzinger-at-kreuzinger.biz>
+ Andreas Brachold <vdr04-at-deltab.de>
+
+Former project's homepage: http://www.burwieck.net/vdr (dead?)
+ http://vdr-image.kreuzinger.biz>
+
+Maintainer: Andreas Brachold <vdr04-at-deltab.de>
+
+Project homepage: http://deltab.de/vdr/image.html
+
+Some files are from the mp3/MPlayer Plugin. Thanks to Stefan Hülswitt
+
+
+Required:
+-----------
+- fullfeatured dvb card
+- vdr 1.2.6+, series vdr 1.3.0 work too
+- to compile plugin depends package libavcodec1-dev (tested with ffmpeg-0.4.8)
+- to run your will need package netpbm and libavcodec1*
+- netpbm 10.0+ (check for "anytopnm", if it does not exist upgrade/install netpbm)
+- not necessary but useful utils/file (file) for better imagetyp detection, used by anytopnm
+
+*) it's use libavcodec.so, also static linking possible with compile #> make plugin FFMPEG_STATIC=1
+
+Important Note:
+if tools are installed by source absolutely also for it provide that the
+programs (anytopnm pnmscale pnmfile pnmcut pnmflip) are inside the search-path,
+otherwise adapt the variable "PATH" within the file imageplugin.sh.
+
+Install:
+------------
+Install the plugin part as usual (see vdr docs if you not know already).
+
+Compile depends package libavcodec1-dev, libavcodec1 or also know ffmpeg.
+later for run your will only need libavcodec1(libavcodec.so) and netpbm.
+("apt-get install libavcodec1-dev libavcodec1 netpbm" on Debian)
+
+In the ./examples directory you can find a sample file for
+imagesources.conf, change this file if needed and place it in
+the plugins folder in your config folder (the video folder by default).
+
+The syntax is :
+<path>;<name in vdr menu>;<0/1>;<pattern to filter file display>
+
+examples:
+/media/cdrom;CDROM;1;*.jpg *.jpeg *.png *.tif* *.bmp
+/archive/photos/holiday;Holiday pictures;0;DSC10*.jpg
+
+0/1 is for media that need to be mounted (uses the same mount.sh
+as mplayer/mp3 plugin).
+
+
+The file ./scripts/imageplugin.sh could go to /usr/bin, or any other folder in your path.
+You can also use the plugin options (vdr -help after installing the plugin) to specify
+the path's to the files, if you don't want to those files in you system.
+[ e.g.: ./vdr '-Pimage -C /path/imageplugin.sh -m /path/mount.sh' ...]
+The install.sh in the examples folder will try a automatic install, give it a try if it fails else.
+
+Manual:
+------------
+Start the plugin, than select a file (or folder) and press OK.
+
+
+At all modi
+ OK toggle OSD informations
+ Play/Pause begin/halt slide
+ Stop/Blue stop plugin
+ Red open menu with imagecommands
+
+While watching pictures
+ Back stop plugin
+
+ Left previous picture
+ Right next picture
+ Down jump three pictures back
+ Up jump three pictures forward
+
+ 7 jump five pictures back
+ 9 jump five pictures forward
+
+ 0 view original image
+ 1 Rotate 90 Grad counter clockwise
+ 3 Rotate 90 Grad clockwise direction
+
+ 4 slide show - decrease the time (seconds) each picture shows
+ 6 slide show - increase the time (seconds) each picture shows
+ 5 Zoom inside image
+ 8 Zoom outside image and call Jumpmenu
+
+'Zoom menu'
+ Back Zoom outside image
+
+ Left scroll zoomedframe inside picture to left
+ Right scroll zoomedframe inside picture to right
+ Down scroll zoomedframe inside picture to left
+ Up scroll zoomedframe inside picture to up
+
+ 0 view original image
+ 5 Zoom inside image
+ 8 Zoom outside image
+
+'Jump menu' (3x3 preview):
+ Back view original image
+
+ Left previous picture group (go 9 pictures back)
+ Right next picture group (go to the next 9 pictures)
+
+ 0 view original image
+ 1 ... 9 pick the corresponding image
+
+(thx for the english readme/keymapping description to Kai Steinbach)
+
+image commands
+------------
+The file imagecmds.conf can be used to define commands that can be applied to the
+currently viewed image. The syntax is exactly the same as described for the
+file commands.conf, see "man 5 vdr". If only one instruction is indicated,
+the filename of the image will be appended to the command string,
+separated by a blank and enclosed in single quotes.
+
+Format> Menutext ?: command %s
+? - Confirmation request (optionally)
+%s - placeholder for filename (optionally)
+
+See for a sample at examples/imagecmds.conf .
+
+
+Diagnosis of the scripts and installation
+-----------------------------------------
+If the script does not work like it should, start the Script to test from the console.
+
+imageplugin.sh [infile] [outfile] [WIDTH] [HEIGHT] [ZOOMFACTOR] [LEFTPOS] [TOPPOS] {FLIPCMD}
+e.g. imageplugin.sh myimage.png outfile.pnm 720 576 0 0 0 original
+
+and please examine the produced syslog messages at /var/log/message or messages on console
+
+if that does not help repeats the procedure with > export DEBUG=yes;
+
+export DEBUG=yes;imageplugin.sh myimage.png outfile.pnm 720 576 0 0 0 original
+
+and examine the expenditure on the screendump
+
+*) if script work for your, it's could reduce the message on syslog,
+if your change on imageplugin.sh inside from VERBOSE=yes to VERBOSE=no
diff --git a/README.DE b/README.DE
new file mode 100644
index 0000000..39600b5
--- /dev/null
+++ b/README.DE
@@ -0,0 +1,152 @@
+This is a "plugin" for the Video Disk Recorder (VDR).
+
+Originaly written by: Kai Tobias Burwieck <kai-at-burwieck.net>
+ "Interpohl" <vdr-portal.de>
+ Onno Kreuzinger <o.kreuzinger-at-kreuzinger.biz>
+ Andreas Brachold <vdr04-at-deltab.de>
+
+Former project's homepage: http://www.burwieck.net/vdr (dead?)
+ http://vdr-image.kreuzinger.biz>
+
+Maintainer: Andreas Brachold <vdr04-at-deltab.de>
+
+Project homepage: http://deltab.de/vdr/image.html
+
+
+Einige Dateien stammen vom mp3/MPlayer Plugin, der danke geht an Stefan Hülswitt
+
+
+
+Benötigt:
+-----------
+- Full-Featured DVB Karte
+- vdr 1.2.6+
+- das Plugin ist abhängig vom Paket libavcodec1-dev zu übersetzen, (getestet mit ffmpeg-0.4.8)
+- und Paket netpbm sowie libavcodec1* zur Ausführung
+- netpbm 10.0+ (prüfe ob das Tool "anytopnm" existiert)
+- nicht notwendig, aber sinnvoll utils/file (file) für bessere Bildtyperkennung, mit anytopnm
+
+*) es wird libavcodec.so nachgeladen, aber auch statisches Linken ist mit möglich mit Kompilierung per #> make plugin FFMPEG_STATIC=1
+
+WICHTIG,
+wenn Tools von Source installiert werden, unbedingt auch dafür sorgen das
+die Programme (anytopnm pnmscale pnmfile pnmcut pnmflip) innerhalb des Such-Pfade liegen,
+ansonsten die Variable "PATH" innerhalb der Datei imageplugin.sh anpassen.
+
+Installation:
+------------------
+Das plugin wird wie alle plugins installiert (siehe vdr Dokumentation).
+
+Das Kompilieren ist abhängig vom Paket libavcodec1-dev, libavcodec1, aber auch bekannt als ffmpeg.
+Später zum Ausführen wird nur noch libavcodec1(libavcodec.so) und "netpbm" benötigt.
+("apt-get install libavcodec1-dev libavcodec1 netpbm" in Debian)
+
+
+Im ./examples Ordner liegt eine Beispiel imagesources.conf, dies ggf.
+anpassen und in das plugins Verzeichnis des config Ordners legen.
+
+Die Syntax der Datei ist:
+<Pfad>;<Name im vdr Menu>;<0/1>;<Suchmuster für Dateien die angezeigt werden sollen>
+
+Beispiele:
+/media/cdrom;CDROM;1;*.jpg *.jpeg *.png *.tif* *.bmp
+/archive/photos/holiday;Urlaubs Bilder;0;DSC10*.jpg
+
+0/1 ist für Quellen die gemountet werden müssen (benutzt das gleiche mount.sh wie
+das mplayer/mp3 plugin).
+gefolgt mit einer Liste des Suchmuster für Dateien welche angezeigt werden sollen,
+welche dann jeweils mit Leerzeichen getrennt werden.
+
+Die Datei imageplugin.sh sollte nach /usr/bin kopiert werden, bzw. in einen anderen
+gemeinsamen Ordner innerhalb Such-Path kopieren werden,
+alternativ können als Kommandozeilenoption die gewählten Pfade zu dem Skript
+beim vdr Start übergeben werden.
+[z.B.: ./vdr '-Pimage -C /pfad/imageplugin.sh -m /pfad/mount.sh' ...]
+
+
+Bedienung:
+-----------------
+Nach dem Aufrufen entweder eine Datei oder einen Ordner auswählen und mit OK wählen.
+
+At all modi
+ OK toggle OSD informations
+ Play/Pause Starte Diaschau/ Stoppe Diaschau
+ Stop/Blue Stoppe Plugin
+ Red Öffne Menu mit Bildbefehle
+
+Während der Bildbetrachtung:
+ Back Stoppe Plugin
+
+ Left vorheriges Bild
+ Right nächstes Bild
+ Down Springe drei Bilder zurück
+ Up Springe drei Bilder vorwärts
+
+ 7 Springe fünf Bilder zurück
+ 9 Springe fünf Bilder vorwärts
+
+ 0 Zeige Originalbild
+ 1 Rotatiere 90 Grad gegen den Uhrzeigersinn
+ 3 Rotatiere 90 Grad im Uhrzeigersinn
+
+ 4 Diaschau-WeiterschaltwertinSekundenverkleinern
+ 6 Diaschau-WeiterschaltwertinSekundenerhöhen
+
+ 5 "Zoomwert erhöhen" und damit Zoom-Bedienung starten
+ 8 "Zoomwert verkleinern" und damit Jump-Bedienung starten
+
+'Zoom-Bedienung'
+ Back Zoomwert verkleinern
+
+ Left Bildausschnitt nach Links verschieben
+ Right Bildausschnitt nach Rechts verschieben
+ Down Bildausschnitt nach Unten verschieben
+ Up Bildausschnitt nach Oben verschieben
+
+ 0 view original image
+ 5 Zoomwert erhöhen
+ 8 Zoomwert verkleinern
+
+Jump-Menu (3x3 Vorschau):
+ Back Zeige Originalbild
+
+ Left vorherige Bildgruppe (9 Bilder weiter in der Vorschau)
+ Right nächste Bildgruppe (9 Bilder vorher in der Vorschau)
+
+ 0 Zeige Originalbild
+ 1 ... 9 Wählt ensprechendes Bild
+
+Bildbefehle
+------------
+Die Datei imagecmds.conf kann dazu genutzt werden, um definierbare Befehle auf
+auf das an gezeigte Bild anzuwenden. Der Syntax ist extakt der gleiche der
+für die Datei commands.conf unter "man 5 vdr" beschrieben wurde.
+Wenn nur ein Befehl angegeben wird, wird der Datei des Bildes angehängt an den Befehlstext,
+getrennt durch ein Leerzeichen und einschlossen in Anführungszeichen (single quotes).
+
+Format> Menutext ?: Kommando %s
+? - Bestätigung anfordern, (optional)
+%s - Platzhalter für Dateinamen (optional)
+
+Schaue für ein Beispiel nach examples/imagecmds.conf.DE .
+
+
+Diagnose des Script
+-------------------
+Wenn der Script nicht arbeitet wie er sollten, starte den Script zum Testen von der Konsole.
+
+imageplugin.sh [infile] [outfile] [WIDTH] [HEIGHT] [ZOOMFACTOR] [LEFTPOS] [TOPPOS] {FLIPCMD}
+z.B. imageplugin.sh myimage.png outfile.pnm 720 576 0 0 0 original
+
+und bitte überprüfe die erzeugten Nachrichten im Syslog in /var/log/message bzw.
+die Ausgaben auf dem Bildschirm
+
+
+wenn das nicht hilft wiederhole die Prozedur mit > export DEBUG=yes;
+
+export DEBUG=yes;imageplugin.sh myimage.png outfile.pnm 720 576 0 0 0 original
+
+und überprüfe die Ausgabe auf dem Bildschirm.
+
+*) Wenn der Script für dich arbeitet, es können die Ausgaben im Syslog reduziert werden,
+wenn Du innerhalb von imageplugin.sh die Zeile von VERBOSE=yes zu VERBOSE=no geändert wird.
diff --git a/TODO b/TODO
new file mode 100644
index 0000000..72fcea4
--- /dev/null
+++ b/TODO
@@ -0,0 +1,7 @@
+TODO
+
+Proper Scale on 16:9
+Exif automated rotation
+
+Change BLUE/EXIT > Stop to BLUE > Stop / EXIT > reshow Browser
+reload Image after commands execution, if image changed
diff --git a/commands.c b/commands.c
new file mode 100644
index 0000000..f1649ea
--- /dev/null
+++ b/commands.c
@@ -0,0 +1,239 @@
+/*
+ * Image plugin to VDR (C++)
+ *
+ * (C) 2004 Andreas Brachold <vdr04-at-deltab.de>
+ *
+ * This code is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This code is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <malloc.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "commands.h"
+#include "setup-image.h"
+
+
+// --- cImageCommand -------------------------------------------------------------
+
+char *cImageCommand::m_szLastResult = NULL;
+
+cImageCommand::cImageCommand(void)
+: m_szTitle(NULL)
+, m_szCommand(NULL)
+, m_bConfirm(false)
+{
+
+}
+
+cImageCommand::~cImageCommand()
+{
+ if(m_szTitle)
+ {
+ free(m_szTitle);
+ m_szTitle = NULL;
+ }
+ if(m_szCommand)
+ {
+ free(m_szCommand);
+ m_szCommand = NULL;
+ }
+}
+
+bool cImageCommand::Parse(const char *s)
+{
+ const char *p = strchr(s, ':');
+ if(p)
+ {
+ int l = p - s;
+ if(l > 0)
+ {
+ m_szTitle = MALLOC(char, l + 1);
+ stripspace(strn0cpy(m_szTitle, s, l + 1));
+ if(!isempty(m_szTitle))
+ {
+ int l = strlen(m_szTitle);
+ if(l > 1 && m_szTitle[l - 1] == '?')
+ {
+ m_bConfirm = true;
+ m_szTitle[l - 1] = 0;
+ }
+ m_szCommand = stripspace(strdup(skipspace(p + 1)));
+ return !isempty(m_szCommand);
+ }
+ }
+ }
+ return false;
+}
+
+
+
+const char *cImageCommand::Execute(const char *szFileName)
+{
+ char *szCmdBuf = NULL;
+
+ if(m_szLastResult)
+ {
+ free(m_szLastResult);
+ m_szLastResult = NULL;
+ }
+
+ // Combine command and filename
+ if(szFileName && m_szCommand) {
+
+ if(NULL == strstr(m_szCommand, "%s")) {
+ /// Merge command and filename e.g : identify 'my_image.png'
+ asprintf(&szCmdBuf, "%s \'%s\'", m_szCommand, szFileName);
+ }
+ else {
+ /// Replace Mode, replace any %s with filename
+ /// e.g. : jpegtran -rotate 90 %s > $CONVERT_TEMPDIR/tmp.jpg && mv $CONVERT_TEMPDIR/tmp.jpg %s
+ /// would to jpegtran -rotate 90 'my_image.png' > $CONVERT_TEMPDIR/tmp.jpg && mv $CONVERT_TEMPDIR/tmp.jpg 'my_image.png'
+ char *szF;
+ asprintf(&szF, "\'%s\'", szFileName);
+ if(szF) {
+ szCmdBuf = (char*)calloc(PATH_MAX,1);
+ if(szCmdBuf)
+ {
+ char* d = szCmdBuf;
+ char* s = m_szCommand;
+ for(;d-szCmdBuf < PATH_MAX-1 && *s != '\0';++s,++d)
+ {
+ if(*s == '%' && *(s+1) == 's')
+ {
+ strncat(szCmdBuf,szF,PATH_MAX-1);
+ d += strlen(szF)-1;
+ ++s;
+ }
+ else
+ *d = *s;
+ }
+ }
+ }
+ free(szF);
+ }
+ }
+
+ const char *szCmd = szCmdBuf ? szCmdBuf : m_szCommand;
+ dsyslog("imageplugin: executing command '%s'", szCmd);
+
+ // Set environment
+ ImageSetup.SetEnv();
+
+ FILE *p = popen(szCmd, "r");
+ if(p)
+ {
+ int l = 0;
+ int c;
+ while((c = fgetc(p)) != EOF)
+ {
+ if(l % 20 == 0) {
+ m_szLastResult = (char *)realloc(m_szLastResult, l + 21);
+ if(!m_szLastResult)
+ break;
+ }
+ m_szLastResult[l++] = c;
+ }
+ if(m_szLastResult)
+ m_szLastResult[l] = 0;
+ pclose(p);
+ }
+ else
+ esyslog("imageplugin: can't open pipe for command '%s'", szCmd);
+
+ if(szCmdBuf)
+ free(szCmdBuf);
+
+ return m_szLastResult;
+}
+
+cImageCommands::cImageCommands(void)
+: m_szFileName(NULL)
+{
+
+}
+
+cImageCommands::~ cImageCommands()
+{
+ Clear();
+}
+
+void cImageCommands::Clear(void)
+{
+ if(m_szFileName)
+ free(m_szFileName);
+ m_szFileName = NULL;
+ cList < cImageCommand >::Clear();
+}
+
+
+bool cImageCommands::Load(const char *szFileName/* = NULL*/, bool bAllowComments /*=true*/, bool bMustExist /*= false*/)
+{
+ Clear();
+ if(szFileName)
+ {
+ m_szFileName = strdup(szFileName);
+ m_bAllowComments = bAllowComments;
+ }
+
+ bool bRet = !bMustExist;
+ if(m_szFileName && access(m_szFileName, F_OK) == 0)
+ {
+ isyslog("imageplugin: loading %s", m_szFileName);
+ FILE *f = fopen(m_szFileName, "r");
+ if(f)
+ {
+ int n = 0;
+ char szBuf[8192];
+ bRet = true;
+ while(fgets(szBuf, sizeof(szBuf), f) > 0)
+ {
+ ++n;
+ if(m_bAllowComments)
+ {
+ char *p = strchr(szBuf, '#');
+ if(p)
+ *p = 0;
+ }
+ stripspace(szBuf);
+ if(!isempty(szBuf))
+ {
+ cImageCommand *l = new cImageCommand;
+ if(l->Parse(szBuf))
+ {
+ Add(l);
+ }
+ else
+ {
+ esyslog("imageplugin: error in %s, line %d\n",m_szFileName, n);
+ delete l;
+ bRet = false;
+ break;
+ }
+ }
+ }
+ fclose(f);
+ } else
+ {
+ esyslog("imageplugin: error %s: %m", m_szFileName);
+ bRet = false;
+ }
+ }
+ if(!bRet)
+ esyslog("imageplugin: error while reading '%s'\n", m_szFileName);
+ return bRet;
+}
diff --git a/commands.h b/commands.h
new file mode 100644
index 0000000..39f7646
--- /dev/null
+++ b/commands.h
@@ -0,0 +1,64 @@
+/*
+ * Image plugin to VDR (C++)
+ *
+ * (C) 2004 Andreas Brachold <vdr04-at-deltab.de>
+ *
+ * This code is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This code is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+// --- cImageCommands ---------------------------------------------------------
+
+#ifndef __COMMANDS_H
+#define __COMMANDS_H
+
+#include <vdr/tools.h>
+
+class cImageCommand
+: public cListObject
+{
+ char *m_szTitle;
+ char *m_szCommand;
+ bool m_bConfirm;
+ static char *m_szLastResult;
+public:
+ cImageCommand(void);
+ virtual ~ cImageCommand();
+
+ bool Parse(const char *s);
+
+ const char *Title(void) const { return m_szTitle; }
+ bool Confirm(void) const { return m_bConfirm; }
+ const char *Command(void) const{ return m_szCommand;}
+
+ const char *Execute(const char *szFileName = NULL);
+};
+
+class cImageCommands
+: public cList < cImageCommand >
+{
+ char *m_szFileName;
+ bool m_bAllowComments;
+protected:
+ void Clear(void);
+public:
+ cImageCommands(void);
+ virtual ~ cImageCommands();
+
+ const char *FileName(void) const { return m_szFileName; }
+ bool Load(const char *szFileName = NULL, bool bAllowComments = true, bool bMustExist = false);
+};
+
+#endif //__COMMANDS_H
diff --git a/contrib/image_pregen.sh b/contrib/image_pregen.sh
new file mode 100755
index 0000000..8a89fd6
--- /dev/null
+++ b/contrib/image_pregen.sh
@@ -0,0 +1,46 @@
+#!/bin/bash
+# image_pregen.sh
+#
+# call with file or dir to pregenerate the file or dir
+# if you set first param. to -at, the script will use at to start a job for
+# atd, so it runs asychonous in the background
+#
+# by Onno Kreuzinger, onno_AT_kreuzinger_DOT_biz
+# 2004-06-27 a.brachold - remove nocopy method
+# 2004-08-14 a.brachold - update for imageplugin.sh version 0.2.0
+#
+[ "z$DEBUG" = "zyes" ] && set -xv
+
+[ "z$1" = "z" ] && echo "start atleast with file or dir as argument, to pregenerate that file or dir." && exit 1
+[ "z$1" = "z-at" ] && echo "starting at job mode.." && echo "$0 $2 $3"|at now && exit 0
+[ "z$2" = "z-at" ] && echo "starting at job mode.." && echo "$0 $1 $3"|at now && exit 0
+[ "z$1" = "z-follow" ] && FOLLOWMODE="true" && shift
+[ "z$2" != "z" ] && shift
+
+CONVERT_SCRIPT="imageplugin.sh"
+CACHEFOLDER=${CACHEFOLDER:-"/tmp/image"}
+
+[ ! -e $CONVERT_SCRIPT ] && echo "Can't find our worker $CONVERT_SCRIPT, please adjust path or variable CONVERT_SCRIPT" && exit 1
+
+if [ -d "$1" ]
+ then
+ if [ "z$FOLLOWMODE" = "ztrue" ]
+ then
+ echo "$0 : recursive dir mode "$1""
+ find "$1" -follow -type f \
+ -name "*\.[jJbBtTpPmM][pPmMiInN][gGpPeEfFmM]" \
+ -exec $CONVERT_SCRIPT "{}" "$CACHEFOLDER{}.pnm" 688 544 0 0 0 \; 2> /dev/null
+ else
+ echo "$0 : dir mode "$1""
+ find "$1" -maxdepth 1 -follow -type f \
+ -name "*\.[jJbBtTpPmM][pPmMiInN][gGpPeEfFmM]" \
+ -exec $CONVERT_SCRIPT "{}" "$CACHEFOLDER{}.pnm" 688 544 0 0 0 \; 2> /dev/null
+ fi
+elif [ -e "$1" ]
+ then
+ echo "$0 : single file mode "$1""
+ $CONVERT_SCRIPT "$1" "$CACHEFOLDER$1.pnm" 688 544 0 0 0 2> /dev/null
+else
+ echo -e "$1 is no file or directory\n syntax: $0 [-at] [-follow] <file|folder>"
+fi
+echo "done"
diff --git a/contrib/install-scripts.sh b/contrib/install-scripts.sh
new file mode 100755
index 0000000..8f2d34d
--- /dev/null
+++ b/contrib/install-scripts.sh
@@ -0,0 +1,35 @@
+#!/bin/sh
+#set -xv
+pushd $(dirname $0) &>/dev/null
+
+BIN_FILES="../scripts/imageplugin.sh ../contrib/image_pregen.sh"
+BIN_DEST="/usr/bin"
+
+echo -e "Warning: this script will install these files:\n$BIN_FILES\nto $BIN_DEST,\npress CTRL-C to abort, or anykey to begin."
+read ANYKEY
+
+#installing bin files
+echo "Installing $BIN_FILES to $BIN_DEST....."
+for FILE in $BIN_FILES
+do
+ echo "copying $FILE to $BIN_DEST ..."
+ rm -f $BIN_DEST/$FILE
+ cp -f $FILE $BIN_DEST/$FILE;RV=$?
+ [ $RV -ne 0 ] && echo -e "Errorcode $RV on file: $BIN_DEST/$FILE.\n"
+done
+
+#check and install mount.sh
+if $(which mount.sh &>/dev/null)
+ then
+ OLD_MOUNT=$(which mount.sh)
+ echo -e "Should the existing mount.sh ("$OLD_MOUNT")\nbe overwriten? (y/N)"
+ read -n 1 -t 90 $KEYPRESSED
+ [ "z$KEYPRESSED" = "zy" -o "z$KEYPRESSED" = "zY" ] && rm -f $OLD_MOUNT && cp -f ../scripts/mount.sh $OLD_MOUNT
+ else
+ cp ../scripts/mount.sh $BIN_DEST/mount.sh
+fi
+
+which mount.sh &>/dev/null || echo -e "Error could not execute mount.sh, please check.\n"
+
+popd &>/dev/null &>/dev/null
+echo -e "Remember to copy/create imagesources.conf and imagecmds.conf\nfiles in your plugins config directory."
diff --git a/control-image.c b/control-image.c
new file mode 100644
index 0000000..b109c50
--- /dev/null
+++ b/control-image.c
@@ -0,0 +1,1137 @@
+/*
+* Image plugin to VDR (C++)
+*
+* (C) 2004 Andreas Brachold <vdr04 -at- deltab.de>
+* based on (C) 2003 Kai Tobias Burwieck <kai -at- burwieck.net>
+*
+* This code is free software; you can redistribute it and/or
+* modify it under the terms of the GNU General Public License
+* as published by the Free Software Foundation; either version 2
+* of the License, or (at your option) any later version.
+*
+* This code is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+* Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+*/
+
+#include <time.h>
+
+#include "player-image.h"
+#include "control-image.h"
+#include "data-image.h"
+#include "setup-image.h"
+#include "i18n.h"
+#include "menu-commands.h"
+#include "list.h"
+
+#include <vdr/status.h>
+#include <vdr/tools.h>
+#include <vdr/plugin.h>
+
+
+#if VDRVERSNUM < 10307
+// --- cProgressBar ------------------------------------------------------------
+class cProgressBar:public cBitmap {
+ public:
+ cProgressBar(int nWidth, int nHeight, int nProgressBegin, int nProgressEnd, int nProgressRange)
+ :cBitmap(nWidth, nHeight,2)
+ {
+ if(nProgressRange > 0)
+ {
+ if(nProgressBegin>0)
+ {
+ int b = nProgressBegin * width / nProgressRange;
+ int p = nProgressEnd * width / nProgressRange;
+ Fill(0, 0, b, nHeight - 1, clrWhite);
+ Fill(b + 1, 0, p, nHeight - 1, clrGreen);
+ Fill(p + 1, 0, nHeight - 1, nHeight - 1, clrWhite);
+ }
+ else
+ {
+ int p = nProgressEnd * width / nProgressRange;
+ Fill(0, 0, p, nHeight - 1, clrGreen);
+ Fill(p + 1, 0, nWidth - 1, nHeight - 1, clrWhite);
+ }
+ }
+ }
+};
+#endif
+
+
+// --- cImageControl ---------------------------------------------------------
+
+char* cImageControl::m_szLastShowStatusMsg = 0;
+
+void cImageControl::SetSlideShow(cSlideShow * pNewSlideShow)
+{
+ // Remove old session
+ if(cControl::Control())
+ cControl::Shutdown();
+
+ if(pNewSlideShow) {
+ cImageControl* pControl = new cImageControl(pNewSlideShow);
+ cControl::Launch(pControl);
+ }
+}
+//////////////////////////////////////////////////////////////////////////////
+/** C-tor Create for control-object to control the image-player
+*/
+cImageControl::cImageControl(cSlideShow * pNewSlideShow)
+ : cControl(player = new cImagePlayer(pNewSlideShow))
+ , m_pImageMenu(NULL)
+#if VDRVERSNUM >= 10307
+ , m_pDisplayReplay(NULL)
+#endif
+{
+ // Notity all cStatusMonitor
+ cStatus::MsgReplaying(this, "[image]");
+
+ m_tStarted = time(NULL);
+
+ m_eOSDStatusIsOpen = eDisplayNothing;
+ // Depends the Setup of ShowReplayMode is ModeOnly visable
+ m_eOSDStatusVisable = Setup.ShowReplayMode?eDisplayModeOnly:eDisplayNothing;
+
+ m_ePlayMode = ePlayModeNormal;
+ m_bSlideShowActiv = ImageSetup.SlideShow;
+ m_bSlideShowBefore = false;
+
+ // Support for Zoom
+ m_nZoomFactor = 0;
+ m_nRealImageWidth = 0;
+ m_nRealImageHeight = 0;
+}
+
+
+//////////////////////////////////////////////////////////////////////////////
+/** D-tor Destroy control-object to control the image-player
+*/
+cImageControl::~cImageControl()
+{
+ if(m_pImageMenu)
+ delete m_pImageMenu;
+ m_pImageMenu = NULL;
+ // Notity cleanup all cStatusMonitor
+ cStatus::MsgReplaying(this, NULL);
+ // Hide OSD
+ HideOSD();
+#if VDRVERSNUM >= 10307
+ if(m_pDisplayReplay) {
+ delete m_pDisplayReplay;
+ m_pDisplayReplay = NULL;
+ }
+#endif
+ // Stop Playback
+ Stop();
+ if(player)
+ delete player;
+ player = NULL;
+
+ if(m_szLastShowStatusMsg)
+ free(m_szLastShowStatusMsg);
+ m_szLastShowStatusMsg = 0;
+}
+
+void cImageControl::Show(void)
+{
+ ShowOSD();
+}
+
+void cImageControl::Hide(void)
+{
+ m_eOSDStatusVisable = eDisplayNothing;
+ HideOSD();
+
+ if(m_szLastShowStatusMsg)
+ free(m_szLastShowStatusMsg);
+ m_szLastShowStatusMsg = 0;
+}
+
+void cImageControl::ShowOSD()
+{
+ switch(m_eOSDStatusVisable) {
+ case eDisplayNothing: HideOSD(); break;
+ case eDisplayModeOnly: ShowMode(); break;
+ case eDisplayProgress: ShowProgress(); break;
+ }
+}
+
+void cImageControl::HideOSD(void)
+{
+ if(eDisplayNothing != m_eOSDStatusIsOpen) {
+#if VDRVERSNUM < 10307
+ Interface->Close();
+#else
+ if(m_pDisplayReplay) {
+ delete m_pDisplayReplay;
+ m_pDisplayReplay = NULL;
+ }
+#endif
+ m_eOSDStatusIsOpen = eDisplayNothing;
+ }
+
+ if (m_pImageMenu) {
+ delete m_pImageMenu;
+ m_pImageMenu = NULL;
+ }
+}
+//////////////////////////////////////////////////////////////////////////
+/** Send Message if changed to any statusmonitor
+*/
+void cImageControl::ShowStatusMsg()
+{
+ char* sz = 0;
+ if(IsConvertRunning()) // Display that convert is running
+ {
+ asprintf(&sz,"[image] %s",tr("Convert..."));
+ }
+ else
+ {
+ switch(m_ePlayMode)
+ {
+ case ePlayModeNormal: asprintf(&sz,"[image] %s",FileName());break;
+ case ePlayModeJump: asprintf(&sz,"[image] %s",tr("Select picture via key 1..9!"));break;
+ case ePlayModeZoom: asprintf(&sz,"[image] %s %dx",tr("Zoom"),m_nZoomFactor);break;
+ }
+ }
+ if(sz)
+ {
+ if(!m_szLastShowStatusMsg
+ || 0 != strcmp(m_szLastShowStatusMsg,sz))
+ {
+ if(m_szLastShowStatusMsg)
+ free(m_szLastShowStatusMsg);
+ m_szLastShowStatusMsg = sz;
+ cStatus::MsgReplaying(this, m_szLastShowStatusMsg);
+ }
+ else
+ free(sz);
+ }
+}
+
+void cImageControl::ShowMode(void)
+{
+ if(eDisplayModeOnly != m_eOSDStatusIsOpen)
+ {
+#if VDRVERSNUM < 10307
+ Interface->Open(0, -1);
+#else
+ m_pDisplayReplay = Skins.Current()->DisplayReplay(m_eOSDStatusVisable==eDisplayModeOnly);
+#endif
+ m_eOSDStatusIsOpen = eDisplayModeOnly;
+ }
+
+#if VDRVERSNUM < 10307
+ bool bFixFont = false;
+ int nMaxCharacter = Interface->Width(); //Get OSD-Display width
+#else
+ int nMaxCharacter = m_pDisplayReplay->EditableWidth(); //FIXME Get OSD-Display width
+#endif
+
+ char *sz = MALLOC(char, nMaxCharacter + 1);
+
+ if(IsConvertRunning()) // Display that convert is running
+ {
+ strn0cpy(sz, tr("Convert..."), nMaxCharacter);
+ }
+ else // else Display depends on current play the status
+ {
+ switch(m_ePlayMode)
+ {
+ // Display normal playstatus
+ default:
+ case ePlayModeNormal:
+ {
+#if VDRVERSNUM < 10307
+ if(m_bSlideShowActiv)
+ { strn0cpy(sz, " > ", nMaxCharacter); bFixFont = true; }
+ else
+ { strn0cpy(sz, " || ", nMaxCharacter); bFixFont = true; }
+#else
+ // Get the current activ filename
+ const char* szFileName = FileName();
+ if(!szFileName)
+ return;
+ snprintf(sz, nMaxCharacter, "%s", szFileName);
+#endif
+ break;
+ }
+ // Display jumpmode playstatus
+ case ePlayModeJump:
+ {
+ strn0cpy(sz, tr("Select picture via key 1..9!"), nMaxCharacter);
+ break;
+ }
+ // Display zoommode playstatus
+ case ePlayModeZoom:
+ {
+ snprintf(sz, nMaxCharacter, "%s %dx",tr("Zoom"),m_nZoomFactor);
+ }
+ }
+ }
+
+#if VDRVERSNUM < 10307
+ if(bFixFont) {
+ eDvbFont OldFont;
+ OldFont = Interface->SetFont(fontFix);
+ DisplayAtBottom(sz);
+ Interface->SetFont(OldFont);
+ }
+ else {
+ DisplayAtBottom(sz);
+ }
+#else
+ m_pDisplayReplay->SetMode(m_ePlayMode == ePlayModeNormal && m_bSlideShowActiv, true, 1);
+ if(m_eOSDStatusVisable!=eDisplayModeOnly)
+ m_pDisplayReplay->SetTitle(sz);
+#endif
+ free(sz);
+}
+
+#if VDRVERSNUM < 10307
+void cImageControl::DisplayAtBottom(const char *s)
+{
+ const int p = (eDisplayModeOnly == m_eOSDStatusVisable) ? 0 : 2;// ???? Unused
+ if(s) {
+ const int d = std::max(Width() - cOsd::WidthInCells(s), 0) / 2;
+ if(eDisplayModeOnly == m_eOSDStatusVisable)
+ Interface->Fill(0, p, Interface->Width(), 1, clrTransparent);
+ Interface->Write(d, p, s);
+ }
+ else
+ Interface->Fill(12, p, Width() - 22, 1, clrBackground);
+}
+#endif
+
+void cImageControl::ShowProgress(void)
+{
+ // Get the current activ filename
+ const char* szFileName = FileName();
+ if(!szFileName)
+ return;
+
+ if(eDisplayProgress != m_eOSDStatusIsOpen) {
+ HideOSD();
+#if VDRVERSNUM < 10307
+ Interface->Open(Setup.OSDwidth, -3);
+#else
+ m_pDisplayReplay = Skins.Current()->DisplayReplay(m_eOSDStatusVisable==eDisplayModeOnly);
+ m_pDisplayReplay->SetMarks(&m_Marks);
+#endif
+ m_eOSDStatusIsOpen = eDisplayProgress;
+ }
+
+#if VDRVERSNUM < 10307
+ Interface->Clear();
+ int nMaxCharacter = Interface->Width(); //Get OSD-Display width
+#else
+ int nMaxCharacter = m_pDisplayReplay->EditableWidth(); //FIXME Get OSD-Display width
+#endif
+ //********************************************************************
+ // build first Line
+ char *sz = MALLOC(char, nMaxCharacter + 1);
+
+ if(IsConvertRunning()) // Display that convert is running
+ {
+ snprintf(sz, nMaxCharacter, "%s", tr("Convert..."));
+ }
+ else
+ {
+#if VDRVERSNUM < 10307
+ int n = strlen(szFileName);
+ if(n > nMaxCharacter) { //Clip long filenames
+ n = n - Width() + 4;
+ if(n < 0) n = 0;
+ snprintf(sz, nMaxCharacter, "... %s", szFileName + n);
+ }
+ else {
+ snprintf(sz, nMaxCharacter, "%s", szFileName);
+ }
+#else
+ switch(m_ePlayMode) {
+ case ePlayModeJump:
+ snprintf(sz, nMaxCharacter, "%s",
+ tr("Select picture via key 1..9!"));
+ break;
+ case ePlayModeZoom:
+ snprintf(sz, nMaxCharacter, "%s: %dx (%dx%d)",tr("Zoom"),
+ m_nZoomFactor,
+ m_nRealImageWidth * m_nZoomFactor,
+ m_nRealImageHeight * m_nZoomFactor);
+ break;
+ case ePlayModeNormal:
+ snprintf(sz, nMaxCharacter, "%s", szFileName);
+ break;
+ }
+#endif
+ }
+
+#if VDRVERSNUM < 10307
+ Interface->Write(0, 0, sz);
+#else
+ m_pDisplayReplay->SetTitle(sz);
+#endif
+
+ //********************************************************************
+ // show on second line the progressbar
+#if VDRVERSNUM < 10307
+ {
+ int nProgressBegin = 0;
+ int nProgressEnd = ImageCurrent();
+ int nProgressRange = ImageTotal();
+
+ if(ePlayModeJump == m_ePlayMode) // Show on Jump mode only the selected Range
+ {
+ nProgressBegin = std::max(0, nProgressEnd - 1);
+ nProgressEnd = std::min(nProgressBegin + 9,nProgressRange);
+ }
+
+ cProgressBar ProgressBar(Width() * cOsd::CellWidth(),
+ cOsd::LineHeight(), nProgressBegin, nProgressEnd, nProgressRange);
+ Interface->SetBitmap(0, cOsd::LineHeight(), ProgressBar);
+ }
+#else
+ m_pDisplayReplay->SetProgress(ImageCurrent(), ImageTotal());
+
+ snprintf(sz, nMaxCharacter, "%3d", ImageCurrent());
+ m_pDisplayReplay->SetCurrent(sz);
+ snprintf(sz, nMaxCharacter, "%3d",
+ m_ePlayMode==ePlayModeJump?
+ std::min(ImageCurrent()+9,ImageTotal())
+ :ImageTotal());
+ m_pDisplayReplay->SetTotal(sz);
+ // Remember to me : SetTotal/SetCurrent need clean Screen if changed stringformat
+#endif
+
+#if VDRVERSNUM < 10307
+ //********************************************************************
+ // show on third line a more information
+ switch(m_ePlayMode)
+ {
+ case ePlayModeJump:
+ {
+ snprintf(sz, nMaxCharacter, "%s",
+ tr("Select picture via key 1..9!"));
+ // center the Message
+ const int d = std::max(Width() - cOsd::WidthInCells(sz),0) / 2;
+ Interface->Write(d, 2,sz);
+ break;
+ }
+ case ePlayModeZoom:
+ {
+ snprintf(sz, nMaxCharacter, "%s: %dx (%dx%d)",tr("Zoom"),
+ m_nZoomFactor,
+ m_nRealImageWidth * m_nZoomFactor,
+ m_nRealImageHeight * m_nZoomFactor);
+ // center the Message
+ const int d = std::max(Width() - cOsd::WidthInCells(sz),0) / 2;
+ Interface->Write(d, 2,sz);
+ break;
+ }
+ case ePlayModeNormal:
+ {
+ char szSlideShowInfo[32];
+ szSlideShowInfo[0] = '\0';
+
+ if(m_bSlideShowActiv && !IsConvertRunning())
+ {
+ int t = time(NULL) - m_tStarted + 1;
+ snprintf(szSlideShowInfo, sizeof(szSlideShowInfo), " | %d/%d",
+ t, ImageSetup.SSsec);
+ }
+ snprintf(sz,nMaxCharacter, "(%3d/%3d)%s",
+ ImageCurrent(), ImageTotal(), szSlideShowInfo);
+ Interface->Write(0, 2, sz);
+
+ if(ImageSetup.ShowDate && !IsConvertRunning())
+ {
+ struct stat stFile;
+ if(0 != stat(szFileName, &stFile)) {
+ char szErr[128];
+ int nErr = errno;
+ szErr[sizeof(szErr)-1] = '\0';
+ if(0 != strerror_r(nErr,szErr,sizeof(szErr)-1)) {
+ szErr[0] = '\0';
+ }
+ esyslog("imageplugin: can't get filestate %s (%d)%s.\n",szFileName,nErr,szErr);
+ }
+ else
+ {
+ struct tm *timestr;
+ if((timestr = localtime(&(stFile.st_mtime))))
+ {
+ snprintf(sz,nMaxCharacter,
+ "(%2.2d.%2.2d.%2.2d-%2.2d:%2.2d)",
+ (int)timestr->tm_mday,
+ (int)timestr->tm_mon + 1,
+ (int)timestr->tm_year % 100,
+ (int)timestr->tm_hour,
+ (int)timestr->tm_min);
+
+ Interface->Write(nMaxCharacter - strlen(sz), 2, sz);
+ }
+ else {
+ char szErr[128];
+ int nErr = errno;
+ szErr[sizeof(szErr)-1] = '\0';
+ if(0 != strerror_r(nErr,szErr,sizeof(szErr)-1)) {
+ szErr[0] = '\0';
+ }
+ esyslog("imageplugin: can't get mtime from %s (%d)%s.\n",szFileName,nErr,szErr);
+ }
+ }
+ }
+ }
+ }
+#endif
+
+#if VDRVERSNUM < 10307
+ Interface->Flush();
+#else
+ m_pDisplayReplay->Flush();
+#endif
+ free(sz);
+}
+
+
+
+
+
+//////////////////////////////////////////////////////////////////////////////
+/** VDR-Callback entry for Key was processed
+@return eOSState
+@param eKeys Key - the processed Keycode
+*/
+eOSState cImageControl::ProcessKey(eKeys nKey)
+{
+ // Check for next image
+ SlideImage();
+
+ eOSState eOSRet = osContinue;
+
+ if(m_pImageMenu
+ || (m_ePlayMode != ePlayModeJump && (nKey == kRed)))
+ {
+ return ProcessKeyCommands(nKey);
+ }
+ else
+ {
+ switch(nKey)
+ {
+ //processed global keybindings
+ //None key, check for ErrorMsg from Worker
+ case kNone: if(player)player->ErrorMsg(); break;
+ case kOk: ToogleShowMode(); break;
+ // Toggle between Play/Pause
+ case kPlay:
+ case kPause: ToogleSlideShowActiv(); break;
+ // Stop Plugin
+ case kStop:
+ case kBlue: eOSRet = ProcessKeyStopped(); break;
+
+ default: //processed PlayMode depends keybindings
+ {
+ switch(m_ePlayMode)
+ {
+ case ePlayModeNormal: eOSRet = ProcessKeyPlayMode(nKey); break;
+ case ePlayModeZoom: eOSRet = ProcessKeyZoomMode(nKey); break;
+ case ePlayModeJump: eOSRet = ProcessKeyJumpMode(nKey); break;
+ }
+ }
+ }
+ }
+ // show current state at OSD
+ if(eOSRet == osContinue)
+ {
+ ShowOSD();
+ ShowStatusMsg();
+ }
+ return eOSRet;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+/** Handle a Key stroke on normal playmode
+@return eOSState
+@param eKeys Key - the processed Keycode
+*/
+eOSState cImageControl::ProcessKeyPlayMode(eKeys Key)
+{
+ switch (Key)
+ {
+ // Mode select
+ case kBack: return ProcessKeyStopped();
+ case k0: OriginalImage(false); break;
+ case k8: ProcessKeyBeginJump(); break;
+ case k5: ProcessKeyBeginZoom(); break;
+
+ // Rotate the image
+ case k1: LFlipImage(); break;
+ case k3: RFlipImage(); break;
+
+ // Change time how long image is see
+ case k4|k_Repeat:
+ case k4: DecSlideTime(); break;
+ case k6|k_Repeat:
+ case k6: IncSlideTime(); break;
+
+ // Navigate between images
+ case kLeft: PrevImage(1); break;
+ case kRight:NextImage(1); break;
+ case kDown: PrevImage(3); break;
+ case kUp: NextImage(3); break;
+ case k7: PrevImage(5); break;
+ case k9: NextImage(5); break;
+
+ default: break;
+ }
+
+ return osContinue;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+/** Handle a Key stroke on jumpmode
+@return eOSState
+@param eKeys Key - the processed Keycode
+*/
+eOSState cImageControl::ProcessKeyJumpMode(eKeys nKey)
+{
+ switch (nKey)
+ {
+ case kBack: // Restore Originalimage
+ case k0: OriginalImage(true); break;
+
+ case k1...k9: //Picture select
+ {
+ const int nPicture = nKey - k1;
+ NextImage(nPicture);
+ break;
+ }
+
+ // Navigate between images
+ case kLeft: ConvertJump(-9); break;
+ case kRight: ConvertJump(9); break;
+
+ default: break;
+ }
+
+ return osContinue;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+/** Handle a Key stroke on jumpmode
+@return eOSState
+@param eKeys Key - the processed Keycode
+*/
+eOSState cImageControl::ProcessKeyZoomMode(eKeys nKey)
+{
+ switch (nKey)
+ {
+ // Mode select
+ case k0: OriginalImage(true); break;
+ case kBack:
+ case k8: ZoomOut(); break;
+ case k5: ZoomIn(); break;
+
+ // Navigate inside image
+ case kLeft: MoveZoomWindow(eLeft); break;
+ case kRight:MoveZoomWindow(eRight); break;
+ case kUp: MoveZoomWindow(eUp); break;
+ case kDown: MoveZoomWindow(eDown); break;
+
+ default: break;
+ }
+
+ return osContinue;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+/** Zoom inside a image
+*/
+void cImageControl::ZoomIn()
+{
+ if(m_nZoomFactor < (m_nZoomMin + 5)) //Allow 5 Zoomlevel
+ {
+ ++m_nZoomFactor;
+ ConvertZoom();
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+/** Zoom outside a image
+*/
+void cImageControl::ZoomOut()
+{
+ if(m_nZoomFactor > m_nZoomMin)
+ {
+ --m_nZoomFactor;
+ ConvertZoom();
+ }
+ else
+ {
+ OriginalImage(true);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+/** Navigate inside a zoomed image
+@param eDirection nDirection - the wished navigation direction
+*/
+void cImageControl::MoveZoomWindow(eDirection nDirection)
+{
+ bool bReZoomImage = false;
+
+ switch(nDirection)
+ {
+ case eRight:
+ if(m_nZoomXMax > 0 && m_nMoveStepX < (m_nMaxStepX-1))
+ {
+ ++m_nMoveStepX;
+ bReZoomImage = true;
+ }
+ break;
+ case eLeft:
+ if(m_nZoomXMax > 0 && m_nMoveStepX > (m_nMaxStepX-1)*-1)
+ {
+ --m_nMoveStepX;
+ bReZoomImage = true;
+ }
+ break;
+ case eDown:
+ if(m_nZoomYMax > 0 && m_nMoveStepY < (m_nMaxStepY-1))
+ {
+ ++m_nMoveStepY;
+ bReZoomImage = true;
+ }
+ break;
+ case eUp:
+ if(m_nZoomYMax > 0 && m_nMoveStepY > (m_nMaxStepY-1)*-1)
+ {
+ --m_nMoveStepY;
+ bReZoomImage = true;
+ }
+ break;
+ }
+ if(bReZoomImage)
+ ConvertZoom();
+}
+
+//////////////////////////////////////////////////////////////////////////////
+/** Check if key for pluginstop was processed
+@return eOSState
+@param eKeys Key - the processed Keycode
+*/
+eOSState cImageControl::ProcessKeyStopped()
+{
+ if(player)
+ {
+ if(ePlayModeNormal != m_ePlayMode)
+ {
+ OriginalImage(true);
+ return osContinue;
+ }
+
+ if(IsConvertRunning())
+ return osContinue;
+ }
+
+ HideOSD();
+ Stop();
+ return osEnd;
+}
+
+
+//////////////////////////////////////////////////////////////////////////////
+/** Key was processed for Jumpmode
+*/
+void cImageControl::ProcessKeyBeginJump()
+{
+ m_bSlideShowBefore = m_bSlideShowActiv;
+
+ m_bSlideShowActiv = false;
+
+ ConvertJump(0);
+ m_ePlayMode = ePlayModeJump;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+/** Key was processed for Zoommode
+*/
+void cImageControl::ProcessKeyBeginZoom()
+{
+ m_bSlideShowBefore = m_bSlideShowActiv;
+
+ m_bSlideShowActiv = false;
+ PictureZoomInitial();
+ m_ePlayMode = ePlayModeZoom;
+}
+
+ //////////////////////////////////////////////////////////////////////////////
+/** Toogle between all supported OSD Modes
+*/
+void cImageControl::ToogleShowMode(void)
+{
+ switch(m_eOSDStatusVisable)
+ {
+ case eDisplayNothing: m_eOSDStatusVisable = eDisplayModeOnly; break;
+ case eDisplayModeOnly: m_eOSDStatusVisable = eDisplayProgress; break;
+ case eDisplayProgress: m_eOSDStatusVisable = eDisplayNothing; break;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+/** Toogle between Play and Stop of the current SlideShow
+*/
+void cImageControl::ToogleSlideShowActiv(void)
+{
+ m_nZoomFactor = 0;
+ m_bSlideShowActiv = !m_bSlideShowActiv;
+ m_ePlayMode = ePlayModeNormal;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+/** Handle a Key stroke on commandsmenu
+@return eOSState
+@param eKeys Key - the processed Keycode
+*/
+eOSState cImageControl::ProcessKeyCommands(eKeys nKey)
+{
+ if(m_pImageMenu)
+ {
+ eOSState eOSRet = m_pImageMenu->ProcessKey(nKey);
+ switch(eOSRet)
+ {
+ case osEnd:
+ case osBack:
+ if(m_pImageMenu->HasImageChanged())
+ OriginalImage(false);
+ delete m_pImageMenu;
+ m_pImageMenu = NULL;
+ return osContinue;
+ default:
+ return eOSRet;
+ }
+ }
+ else
+ {
+ if(!CheckAccess()) {
+ OSD_ErrorNumMsg(errno,tr("Operation failed"));
+ return osContinue;
+ }
+
+ cImageCommands* pCmd = new cImageCommands;
+ if(!pCmd)
+ return osContinue;
+
+ // Load additional Commands
+ pCmd->Load(AddDirectory(cPlugin::ConfigDirectory(), "imagecmds.conf"));
+
+ if(pCmd->Count() <= 0) {
+ delete pCmd;
+ return osContinue;
+ }
+
+ Hide();
+
+ const char* szFileName = FileName();
+ char* szTitle;
+ asprintf(&szTitle,"%s (%s)",tr("Commands"),basename(szFileName));
+ m_pImageMenu = new cImageMenuCommands(szTitle,pCmd,szFileName);
+ free(szTitle);
+
+ return osContinue;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+/** Check to get access for to viewed file
+@return bool - false access was denied
+*/
+bool cImageControl::CheckAccess() const
+{
+ if(!player) // Playback stopped ?
+ {
+ errno = EFAULT;
+ return false;
+ }
+
+ errno = 0; // unset error
+ const char *szFileName = player->FileName();
+ if(szFileName
+ && 0 == access(szFileName,F_OK))
+ {
+ return true;
+ }
+ if(!errno) // if unknown error
+ errno = ENOENT;
+ return false;
+}
+
+void cImageControl::OriginalImage(bool bCached)
+{
+ m_nZoomFactor = 0;
+ m_ePlayMode = ePlayModeNormal;
+ if(!CheckAccess()
+ || !player->Convert(bCached?"":"original"))
+ {
+ OSD_ErrorNumMsg(errno,tr("Operation failed"));
+ }
+}
+
+void cImageControl::RFlipImage(void)
+{
+ m_ePlayMode = ePlayModeNormal;
+ if(!CheckAccess()
+ || !player->Convert("right"))
+ {
+ OSD_ErrorNumMsg(errno,tr("Operation failed"));
+ }
+}
+
+void cImageControl::LFlipImage(void)
+{
+ m_ePlayMode = ePlayModeNormal;
+ if(!CheckAccess()
+ || !player->Convert("left"))
+ {
+ OSD_ErrorNumMsg(errno,tr("Operation failed"));
+ }
+}
+
+void cImageControl::PictureZoomInitial(void)
+{
+
+ char *szFileName;
+ FILE *f;
+ char buf[80];
+
+ cImage* pImage = theSlideShow.GetImage();
+ if(!pImage)
+ return;
+
+ asprintf(&szFileName, "%s.par", pImage->NamePNM());
+
+ if(!szFileName)
+ return;
+
+ strcpy(zoom_command, "original");
+
+ m_nRealImageWidth = 720;
+ m_nRealImageHeight = 576;
+
+ if((f = fopen(szFileName, "rt")))
+ {
+ dsyslog("imageplugin: open file %s", szFileName);
+ fgets(buf, sizeof(buf) - 1, f);
+ dsyslog("imageplugin: line=%s", buf);
+ sscanf(buf, "%d %d %s", &m_nRealImageWidth,&m_nRealImageHeight,zoom_command);
+ fclose(f);
+ }
+ else
+ {
+ esyslog("imageplugin: error by open file %s", szFileName);
+ }
+
+ free(szFileName);
+
+ if(m_nRealImageWidth <= 0
+ || m_nRealImageWidth > 720
+ || m_nRealImageHeight > 576 )
+ m_nZoomMin = 1;
+ else
+ {
+ m_nZoomMin = 800/m_nRealImageWidth;
+ }
+ if (m_nZoomMin < 0)
+ m_nZoomMin = 1;
+
+ // Start with minimum zoom
+ m_nZoomFactor = m_nZoomMin;
+ // Set Move to center
+ m_nMoveStepX = 0;
+ m_nMoveStepY = 0;
+
+ ConvertZoom();
+}
+
+void cImageControl::ConvertZoom()
+{
+ if(!player)
+ return;
+
+ m_ePlayMode = ePlayModeZoom;
+
+ // How may pixel are outside screen after zoom
+ m_nZoomXMax = ((m_nRealImageWidth * m_nZoomFactor) - player->UseWidth());
+ m_nZoomYMax = ((m_nRealImageHeight * m_nZoomFactor) - player->UseHeight());
+
+ // If image bigger than screen, how many step can i'm move in zoomed image
+ if(m_nZoomXMax > 0)
+ {
+ m_nMaxStepX = (m_nRealImageWidth * m_nZoomFactor) / player->UseWidth() * 2;
+ if(m_nMoveStepX >= m_nMaxStepX)
+ m_nMoveStepX = m_nMaxStepX;
+ if(m_nMoveStepX <= -m_nMaxStepX)
+ m_nMoveStepX = (m_nMaxStepX)*-1;
+ }
+ else
+ {
+ m_nMoveStepX = 0;
+ m_nMaxStepX = 0;
+ }
+
+ if(m_nZoomYMax > 0)
+ {
+ m_nMaxStepY = (m_nRealImageHeight * m_nZoomFactor) / player->UseHeight() * 2;
+ if(m_nMoveStepY >= m_nMaxStepY)
+ m_nMoveStepY = m_nMaxStepY-1;
+ if(m_nMoveStepY <= -m_nMaxStepY)
+ m_nMoveStepY = (m_nMaxStepY)*-1;
+ }
+ else
+ {
+ m_nMoveStepY = 0;
+ m_nMaxStepY = 0;
+ }
+
+ // Set Offset to center
+ int nZoomXoff = m_nZoomXMax/2;
+ int nZoomYoff = m_nZoomYMax/2;
+
+ // Add to offset the moved position
+ if(m_nMaxStepX > 0)
+ nZoomXoff += (m_nZoomXMax/m_nMaxStepX)*m_nMoveStepX;
+ if(m_nMaxStepY > 0)
+ nZoomYoff += (m_nZoomYMax/m_nMaxStepY)*m_nMoveStepY;
+
+ // execute
+ if(!CheckAccess()
+ || !player->ConvertZoom(zoom_command, m_nZoomFactor, nZoomXoff, nZoomYoff))
+ {
+ OSD_ErrorNumMsg(errno,tr("Operation failed"));
+ }
+}
+
+void cImageControl::ConvertJump(int nOffset)
+{
+ m_ePlayMode = ePlayModeJump;
+ if(!CheckAccess()
+ || !player->ConvertJump(nOffset))
+ {
+ OSD_ErrorNumMsg(errno,tr("Operation failed"));
+ }
+}
+
+bool cImageControl::IsConvertRunning() const
+{
+ if(player)
+ return player->IsConvertRunning();
+ return false;
+}
+
+
+void cImageControl::NextImage(int Step)
+{
+ if(!player)
+ return;
+ if(ePlayModeNormal != m_ePlayMode)
+ {
+ m_ePlayMode = ePlayModeNormal;
+ m_bSlideShowActiv = m_bSlideShowBefore;
+ }
+ if(player->NextImage(Step))
+ OriginalImage(true);
+ m_tStarted = time(NULL);
+}
+
+
+
+void cImageControl::PrevImage(int Step)
+{
+ if(!player)
+ return;
+ if(ePlayModeNormal != m_ePlayMode)
+ {
+ m_ePlayMode = ePlayModeNormal;
+ m_bSlideShowActiv = m_bSlideShowBefore;
+ }
+ if(player->PrevImage(Step))
+ OriginalImage(true);
+ m_tStarted = time(NULL);
+}
+
+
+void cImageControl::GotoImage(int Pict)
+{
+ if(!player)
+ return;
+ if(ePlayModeNormal != m_ePlayMode)
+ {
+ m_ePlayMode = ePlayModeNormal;
+ m_bSlideShowActiv = m_bSlideShowBefore;
+ }
+ if(player->GotoImage(Pict))
+ OriginalImage(true);
+ m_tStarted = time(NULL);
+}
+
+
+int cImageControl::ImageTotal(void) const
+{
+ if(!player)
+ return 0;
+ return theSlideShow.ImageTotal();
+}
+
+int cImageControl::ImageCurrent(void) const
+{
+ if(!player)
+ return 0;
+ return theSlideShow.ImageCurrent();
+}
+
+const char* cImageControl::FileName(void) const
+{
+ if(!player)
+ return 0;
+ return player->FileName();
+}
+
+
+
+void cImageControl::SlideImage()
+{
+ if(m_bSlideShowActiv && !IsConvertRunning())
+ {
+ if(ImageSetup.SSsec <=
+ (time(NULL) - m_tStarted))
+ {
+ NextImage(1);
+ }
+ }
+}
+
+void cImageControl::Stop(void)
+{
+ delete player;
+ player = NULL;
+}
+
+void cImageControl::IncSlideTime(void)
+{
+ if(ImageSetup.SSsec < cImageSetup::m_cSSMax) {
+ ImageSetup.SSsec++;
+ }
+}
+
+void cImageControl::DecSlideTime(void)
+{
+ if(ImageSetup.SSsec > cImageSetup::m_cSSMin) {
+ ImageSetup.SSsec--;
+ }
+}
diff --git a/control-image.h b/control-image.h
new file mode 100644
index 0000000..a09c02f
--- /dev/null
+++ b/control-image.h
@@ -0,0 +1,169 @@
+/*
+ * Image plugin to VDR (C++)
+ *
+ * (C) 2004 Andreas Brachold <vdr04 -at- deltab.de>
+ * based on (C) 2003 Kai Tobias Burwieck <kai -at- burwieck.net>
+ *
+ * This code is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This code is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+// --- cImageControl ---------------------------------------------------------
+
+#ifndef ___DVB_IMAGE_CONTROL_H
+#define ___DVB_IMAGE_CONTROL_H
+
+#include <vdr/player.h>
+#if VDRVERSNUM >= 10307
+#include <vdr/osdbase.h>
+#endif
+
+class cImagePlayer;
+class cImageMenuCommands;
+
+class cImageControl
+ : public cControl
+{
+ /** All supported Replaymodes */
+ enum ePlayMode { ePlayModeNormal, ePlayModeZoom, ePlayModeJump };
+ /** All supported OSD Modes */
+ enum eOSDStatusMode { eDisplayNothing, eDisplayModeOnly,
+ eDisplayProgress
+ };
+ /** All move directions to navigate inside zoomed image */
+ enum eDirection {eLeft = 1,eRight,eUp,eDown};
+
+ /** Our one and only Imageplayer, don't rename it see cControl */
+ cImagePlayer *player;
+ /** Interface to Menu with Usercommands*/
+ cImageMenuCommands *m_pImageMenu;
+ /** Current playing mode (Jump <-> Normal <-> Zoom) */
+ ePlayMode m_ePlayMode;
+ /** Remember the active Slideshow mode before Jump or Zoom used */
+ bool m_bSlideShowBefore;
+ /** What should on OSD visable (Nothing <-> ModeOnly <-> Progress) */
+ eOSDStatusMode m_eOSDStatusVisable;
+ /** Who was OSD Interface open (Nothing <-> ModeOnly <-> Progress) */
+ eOSDStatusMode m_eOSDStatusIsOpen;
+ /** View our picture in a slideshow */
+ bool m_bSlideShowActiv;
+ /** date at image was open*/
+ time_t m_tStarted;
+ /** Last Message for Statusmonitor */
+ static char* m_szLastShowStatusMsg;
+#if VDRVERSNUM >= 10307
+ /** Display replayprogress*/
+ cSkinDisplayReplay *m_pDisplayReplay;
+ /** Faked Marks for Images*/
+ cMarks m_Marks;
+#endif
+
+ /** zoom factor on zoom mode */
+ int m_nZoomFactor;
+ /** minimum zoom factor (1... */
+ int m_nZoomMin;
+ /** maximum value move steps on zoom on width */
+ int m_nMaxStepX;
+ /** maximum value move steps on zoom on height */
+ int m_nMaxStepY;
+ /** current value move steps on zoom on width, 0 is center*/
+ int m_nMoveStepX;
+ /** current value move steps on zoom on height, 0 is center*/
+ int m_nMoveStepY;
+ /** How may pixel are outside screen after zoom, on width */
+ int m_nZoomXMax;
+ /** How may pixel are outside screen after zoom, on height */
+ int m_nZoomYMax;
+ /** real image pixel width*/
+ int m_nRealImageWidth;
+ /** real image pixel height*/
+ int m_nRealImageHeight;
+
+ char zoom_command[20];
+
+private:
+ void ShowOSD(void);
+ void HideOSD(void);
+ void ShowMode(void);
+#if VDRVERSNUM < 10307
+ void DisplayAtBottom(const char *s);
+#endif
+ void ShowProgress(void);
+ /** Send Message if changed to any statusmonitor */
+ void ShowStatusMsg();
+
+ void PictureZoomInitial(void);
+
+ void ProcessKeyBeginJump();
+ void ProcessKeyBeginZoom();
+
+ /** Deliver the Status of running convert-script */
+ bool IsConvertRunning() const;
+ /** Deliver the current number of viewed Image */
+ int ImageCurrent(void) const;
+ /** Deliver the total number of viewed Image */
+ int ImageTotal(void) const;
+ /** Deliver the filename from the current number of viewed Image */
+ const char* FileName() const;
+
+ /** Check if key for pluginstop was processed*/
+ eOSState ProcessKeyStopped();
+ /** Toogle between all supported OSD Modes */
+ void ToogleShowMode(void);
+ /** Toogle between Play and Stop of the current SlideShow */
+ void ToogleSlideShowActiv(void);
+
+ /** Zoom inside a image*/
+ void ZoomIn();
+ /** Zoom outside a image*/
+ void ZoomOut();
+ /** Navigate inside a zoomed image */
+ void MoveZoomWindow(eDirection nDirection);
+
+ eOSState ProcessKeyPlayMode(eKeys nKey);
+ eOSState ProcessKeyJumpMode(eKeys nKey);
+ eOSState ProcessKeyZoomMode(eKeys nKey);
+ eOSState ProcessKeyCommands(eKeys nKey);
+
+ void NextImage(int Step);
+ void PrevImage(int Step);
+ void GotoImage(int Pict);
+ void RFlipImage(void);
+ void LFlipImage(void);
+ void OriginalImage(bool bCached);
+ void ConvertZoom();
+ void ConvertJump(int Step);
+
+ void IncSlideTime(void);
+ void DecSlideTime(void);
+
+ bool CheckAccess() const;
+private:
+ cImageControl(cSlideShow * pNewSlideShow);
+public:
+ virtual ~ cImageControl();
+
+ static void SetSlideShow(cSlideShow * pNewSlideShow);
+
+ virtual eOSState ProcessKey(eKeys Key);
+ virtual void Hide(void);
+ virtual void Show(void);
+
+protected:
+ void SlideImage();
+ void Stop(void);
+};
+
+#endif //___DVB_IMAGE_CONTROL_H
diff --git a/data-image.c b/data-image.c
new file mode 100644
index 0000000..f99ec76
--- /dev/null
+++ b/data-image.c
@@ -0,0 +1,266 @@
+/*
+ * Image plugin to VDR (C++)
+ *
+ * (C) 2003 Kai Tobias Burwieck <kai@burwieck.net>
+ *
+ * This code is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This code is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include "data-image.h"
+#include "data.h"
+#include "setup-image.h"
+
+cFileSources ImageSources;
+
+// --- cImage -------------------------------------------------------------
+
+cImage::cImage(const char *szName, cFileSource * pSource)
+: m_pSource(pSource)
+, m_szFileName(NULL)
+, m_szFileNamePNM(NULL)
+, m_szFileNameIndex(NULL)
+, m_szFileNameZoom(NULL)
+{
+ if(szName)
+ m_szFileName = m_pSource->BuildName(szName);
+ m_pSource->Block();
+}
+
+cImage::~cImage()
+{
+ Clear();
+ m_pSource->Unblock();
+}
+
+const char *cImage::NameIndex()
+{
+ if(!m_szFileNameIndex)
+ {
+ if(ImageSetup.m_bHousekeeping)
+ {
+ char sz[PATH_MAX];
+ strncpy(sz,ImageSetup.TempDir,sizeof(sz));
+ strncat(sz,"/IXXXXXX",sizeof(sz));
+ mktemp(sz);
+ strncat(sz,".pnm",sizeof(sz));
+ m_szFileNameIndex = strdup(sz);
+ }
+ else
+ {
+ asprintf(&m_szFileNameIndex, "%s%sI%s.pnm",
+ ImageSetup.TempDir,
+ *m_szFileName == '/'?"":"/",
+ m_szFileName);
+ }
+ }
+ return m_szFileNameIndex;
+}
+
+const char *cImage::NamePNM()
+{
+ if(!m_szFileNamePNM)
+ {
+ if(ImageSetup.m_bHousekeeping)
+ {
+ char sz[PATH_MAX];
+ strncpy(sz,ImageSetup.TempDir,sizeof(sz));
+ strncat(sz,"/VXXXXXX",sizeof(sz));
+ mktemp(sz);
+ strncat(sz,".pnm",sizeof(sz));
+ m_szFileNamePNM = strdup(sz);
+ }
+ else
+ {
+ asprintf(&m_szFileNamePNM, "%s%s%s.pnm",
+ ImageSetup.TempDir,
+ *m_szFileName == '/'?"":"/",
+ m_szFileName);
+ }
+ }
+ return m_szFileNamePNM;
+}
+
+const char *cImage::NameZoom()
+{
+ if(!m_szFileNameZoom)
+ {
+ if(ImageSetup.m_bHousekeeping)
+ {
+ char sz[PATH_MAX];
+ strncpy(sz,ImageSetup.TempDir,sizeof(sz));
+ strncat(sz,"/ZXXXXXX",sizeof(sz));
+ mktemp(sz);
+ strncat(sz,".pnm",sizeof(sz));
+ m_szFileNameZoom = strdup(sz);
+ }
+ else
+ {
+ asprintf(&m_szFileNameZoom, "%s%sZ%s.pnm",
+ ImageSetup.TempDir,
+ *m_szFileName == '/'?"":"/",
+ m_szFileName);
+ }
+ }
+ return m_szFileNameZoom;
+}
+
+bool UnlinkFile(const char *szFile)
+{
+ struct stat st;
+ if(0 == stat(szFile,&st) && S_ISREG(st.st_mode) && 0 != unlink(szFile))
+ {
+ char szErr[128];
+ int nErr = errno;
+ szErr[sizeof(szErr)-1] = '\0';
+ if(!nErr || 0 != strerror_r(nErr,szErr,sizeof(szErr)-1)) {
+ szErr[0] = '\0';
+ }
+ esyslog("imageplugin: Can't remove temporary file %s, because: %s.",szFile,szErr[0] != '\0'?szErr:"unknown");
+ return false;
+ }
+ return true;
+}
+
+void cImage::Unlink(const char *szName)
+{
+ if(ImageSetup.m_bHousekeeping)
+ {
+ char sz[PATH_MAX];
+ // remove /tmp/image/xxx.pnm
+ UnlinkFile(szName);
+ // remove /tmp/image/xxx.pnm.par
+ strncpy(sz,szName,sizeof(sz));
+ strncat(sz,".par",sizeof(sz));
+ UnlinkFile(sz);
+ // remove /tmp/image/xxx.pnm.tmp
+ strncpy(sz,szName,sizeof(sz));
+ strncat(sz,".tmp",sizeof(sz));
+ UnlinkFile(sz);
+ }
+}
+
+
+void cImage::Clear(void)
+{
+ if(m_szFileNameIndex)
+ {
+ Unlink(m_szFileNameIndex);
+ free(m_szFileNameIndex);
+ m_szFileNameIndex = NULL;
+ }
+
+ if(m_szFileNameZoom)
+ {
+ Unlink(m_szFileNameZoom);
+ free(m_szFileNameZoom);
+ m_szFileNameZoom = NULL;
+ }
+
+ if(m_szFileNamePNM)
+ {
+ Unlink(m_szFileNamePNM);
+ free(m_szFileNamePNM);
+ m_szFileNamePNM = NULL;
+ }
+
+ if(m_szFileName)
+ {
+ free(m_szFileName);
+ m_szFileName = NULL;
+ }
+}
+
+bool cImage::CompareBaseDir(const cFileSource * pSource) const
+{
+ return 0 == strcmp(m_pSource->BaseDir(),pSource->BaseDir());
+
+}
+// -- cSlideShow --------------------------------------------------------------
+
+cSlideShow::cSlideShow(cDirItem * Item)
+: m_szFirstImageName(NULL)
+, m_DirItem(Item->Source, Item->Subdir, Item->Name, Item->Type)
+{
+
+}
+
+cSlideShow::~cSlideShow()
+{
+ if(m_szFirstImageName)
+ free(m_szFirstImageName);
+}
+
+bool cSlideShow::Load(void)
+{
+ bool res = false;
+ Clear();
+ switch (m_DirItem.Type)
+ {
+ case itFile:
+ {
+ dsyslog("imageplugin: SlideShow: file %s\n", m_DirItem.Name);
+ const char *path = m_DirItem.Subdir;
+ res =
+ ScanDir(m_DirItem.Source, path, stFile, m_DirItem.Source->Include(),
+ 0, true);
+ if(res)
+ {
+ m_szFirstImageName = strdup(m_DirItem.Name);
+ }
+ else
+ {
+ DoItem(m_DirItem.Source, m_DirItem.Subdir, m_DirItem.Name);
+ res = true;
+ }
+ break;
+ }
+ case itDir:
+ {
+ dsyslog("imageplugin: SlideShow: dir name:%s\n", m_DirItem.Name);
+ char *path = m_DirItem.Path();
+ res =
+ ScanDir(m_DirItem.Source, path, stFile, m_DirItem.Source->Include(),
+ 0, true);
+ free(path);
+ break;
+ }
+ case itBase:
+ dsyslog("imageplugin: SlideShow: base\n");
+ res =
+ ScanDir(m_DirItem.Source, 0, stFile, m_DirItem.Source->Include(), 0,
+ true);
+ break;
+ default:
+ break;
+ }
+ return res;
+}
+
+void cSlideShow::DoItem(cFileSource * src, const char *subdir,
+ const char *name)
+{
+ char *path = (char *)name;
+ if(subdir)
+ path = AddPath(subdir, name);
+ Add(new cImage(path, src));
+ if(subdir)
+ free(path);
+}
diff --git a/data-image.h b/data-image.h
new file mode 100644
index 0000000..b21ba4b
--- /dev/null
+++ b/data-image.h
@@ -0,0 +1,87 @@
+/*
+ * Image plugin to VDR (C++)
+ *
+ * (C) 2003 Kai Tobias Burwieck <kai@burwieck.net>
+ *
+ * This code is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This code is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#ifndef ___DATA_IMAGE_H
+#define ___DATA_IMAGE_H
+
+#include "data.h"
+
+#include <vdr/tools.h>
+
+// ----------------------------------------------------------------
+
+extern cFileSources ImageSources;
+
+class cFileSource;
+
+// ----------------------------------------------------------------
+
+class cImage
+: public cListObject {
+
+ cFileSource *m_pSource;
+ char *m_szFileName;
+
+ char *m_szFileNamePNM;
+ char *m_szFileNameIndex;
+ char *m_szFileNameZoom;
+
+protected:
+ void Unlink(const char *szName);
+ void Clear(void);
+public:
+ cImage(const char *szName, cFileSource * pSource);
+ virtual ~cImage();
+ bool CompareBaseDir(const cFileSource * pSource) const;
+
+ inline const char *Name(void) const
+ {
+ return m_szFileName;
+ }
+ const char *NameZoom();
+ const char *NamePNM();
+ const char *NameIndex();
+
+};
+
+// ----------------------------------------------------------------
+
+
+class cSlideShow
+: public cScanDir
+, public cList < cImage > {
+
+ char* m_szFirstImageName;
+ cDirItem m_DirItem;
+
+protected:
+
+ virtual void DoItem(cFileSource * src, const char *subdir, const char *name);
+
+public:
+ cSlideShow(cDirItem * Item);
+ virtual ~cSlideShow();
+ virtual bool Load(void);
+
+ inline const char* FirstImage() const { return m_szFirstImageName; }
+};
+
+#endif //___DATA_IMAGE_H
diff --git a/data.c b/data.c
new file mode 100644
index 0000000..59605fb
--- /dev/null
+++ b/data.c
@@ -0,0 +1,508 @@
+/*
+ * Image plugin to VDR (C++)
+ *
+ * (C) 2003 Kai Tobias Burwieck <kai@burwieck.net>
+ *
+ * based on MP3/MPlayer plugin to VDR (C++)
+ * (C) 2001,2002 Stefan Huelswitt <huels@iname.com>
+ *
+ * This code is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This code is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <ctype.h>
+#include <dirent.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "data.h"
+#include "data-image.h"
+#include "list.h"
+
+#include <vdr/tools.h>
+
+// ----------------------------------------------------------------
+
+const char *g_szMountScript = "mount.sh";
+
+char *AddPath(const char *dir, const char *filename)
+{
+ int l = strlen(dir);
+ char *name = MALLOC(char, l + strlen(filename) + 2);
+ if(name)
+ {
+ strcpy(name, dir);
+ name[l] = '/';
+ strcpy(name + l + 1, filename);
+ } else {
+ esyslog("imageplugin: ERROR: no memory for filename");
+ }
+ return name;
+}
+
+class cFileExt : public cListObject
+{
+ char * m_szExt;
+protected:
+ void clean() { if(Ext()) free(m_szExt);m_szExt=0;}
+public:
+ cFileExt() { m_szExt = 0; }
+ virtual ~cFileExt() { clean(); }
+
+ cFileExt(cFileExt& x) //Copy-Ctor
+ {
+ if(x.Ext())
+ {
+ m_szExt = MALLOC(char, strlen(x.m_szExt)+1);
+ strcpy(m_szExt,x.m_szExt);
+ }
+ else m_szExt = 0;
+ }
+ const char* Ext() const { return m_szExt; }
+
+ const char* Set(const char* sz)
+ {
+ const char *f = sz;
+ for(;*sz != 0 && *sz == ' ';++sz); //Skip leading whitespace
+ for(f = sz;*f != 0 && *f != ' ';++f); //Find end of item \0 or ' '
+
+ clean(); //Prevebt double assign
+
+ if(f - sz > 0) // Store founded item
+ {
+ m_szExt = MALLOC(char, f - sz + 1);
+ strncpy(m_szExt,sz,f - sz);
+ *(m_szExt + (f - sz)) = '\0';
+ }
+ return f;
+ }
+};
+
+class cFileExtList : public cList<cFileExt>
+{
+public:
+ cFileExtList(const char* szExtList) {
+ const char* sz = szExtList;
+ while(*sz != 0)
+ {
+ cFileExt ext;
+ sz = ext.Set(sz);
+ if(ext.Ext())
+ Add(new cFileExt(ext)); // Add copy
+ };
+ }
+
+ virtual ~cFileExtList()
+ {
+
+ }
+};
+
+
+// -- cScanDir --------------------------------------------------------------
+
+bool cScanDir::ScanDir(cFileSource * src, const char *subdir, eScanType type,
+ const char *spec, const char *excl, bool recursiv)
+{
+ bool result = true;
+ char *cmd = 0, *dir = 0, *s = 0, *e = 0, tc;
+
+ switch (type)
+ {
+ default:
+ case stFile:
+ tc = 'f';
+ break;
+ case stDir:
+ tc = 'd';
+ break;
+ }
+ if(subdir)
+ asprintf(&dir, "%s/%s", src->BaseDir(), subdir);
+ else
+ asprintf(&dir, "%s", src->BaseDir());
+ // If is'nt set a filter use this a default
+ if(stFile == type && (0 == spec || 0>= strlen(spec)))
+ spec = "*.jpg *.jpeg *.jif *.jiff *.tif *.tiff *.gif *.bmp *.png *.pnm *.mps";
+ // Check if filter is set build complex include find ( -iname "*.jpg" -o -iname "*.jpeg" -o .. )
+ if(stFile == type && spec)
+ {
+ cFileExtList list(spec); //Splitt *.jpg *.jpeg
+
+ cFileExt *src = list.First();
+ while(src) //Loop throw all found item
+ {
+ char* sn;
+
+ if(!s) //open bracket
+ asprintf(&s, " \\( ");
+
+ asprintf(&sn, "%s-iname \"%s\" ",s,QuoteString(src->Ext()));
+ if(s) free(s);
+ s = sn;
+
+ src = list.Next(src);
+ if(src) //follow ext
+ {
+ char* sn;
+ asprintf(&sn, "%s-o ",s);
+ free(s);
+ s = sn;
+ }
+ }
+ if(s) // close bracket
+ {
+ char* sn;
+ asprintf(&sn, "%s\\)",s);
+ free(s);
+ s = sn;
+ }
+ }
+ // Check if filter is set build complex exclude find -not ( -iname "*.jpg" -o -iname "*.jpeg" -o .. )
+ if(stFile == type && excl) {
+ cFileExtList list(excl); //Splitt *.jpg *.jpeg
+
+ cFileExt *src = list.First();
+ while(src) //Loop throw all found item
+ {
+ char* en;
+
+ if(!e) //open bracket
+ asprintf(&e, "-not \\( ");
+
+ asprintf(&en, "%s-iname \"%s\" ",e,QuoteString(src->Ext()));
+ if(e) free(e);
+ e = en;
+
+ src = list.Next(src);
+ if(src) //follow ext
+ {
+ char* en;
+ asprintf(&en, "%s-o ",e);
+ free(e);
+ e = en;
+ }
+ }
+ if(e) // close bracket
+ {
+ char* en;
+ asprintf(&en, "%s\\)",e);
+ free(e);
+ e = en;
+ }
+ }
+#if 0
+ asprintf(&cmd, "find \"%s\" -follow -type %c %s %s %s 2>/dev/null | sort -df",
+ QuoteString(dir), tc, s?s:"", e?e:"", recursiv?"":"-maxdepth 1");
+#else
+ asprintf(&cmd, "find \"%s\" -follow -type %c %s %s %s 2>/dev/null | sort -df | grep -v \"/\\.\"",
+ QuoteString(dir), tc, s?s:"", e?e:"", recursiv?"":"-maxdepth 1");
+#endif
+ //fprintf(stderr,"%s\n",cmd);
+#if VDRVERSNUM >= 10318
+ cReadLine l;
+#endif
+ FILE *p = popen(cmd, "r");
+ if(p) {
+ int len = strlen(dir);
+ char *s;
+#if VDRVERSNUM >= 10318
+ while((s = l.Read(p)) != 0) {
+#else
+ while((s = readline(p)) != 0) {
+#endif
+ char *ss = strstr(s, dir);
+ if(ss) {
+ s = ss + len;
+ if(*s == '/')
+ s++;
+ }
+ if(*s)
+ DoItem(src, subdir, s);
+ }
+ pclose(p);
+ }
+ else
+ result = false;
+
+ free(cmd);
+ free(dir);
+ free(s);
+ free(e);
+ return result;
+}
+
+char *cScanDir::QuoteString(const char *str)
+{
+ static char *nstr = 0;
+
+ free(nstr);
+ nstr = MALLOC(char, strlen(str) * 2);
+ char *p = nstr;
+ while(*str)
+ {
+ switch (*str)
+ {
+ case '$': // dollar
+ case '\\': // backslash
+ case '\"': // double quote
+ case '`': // back tick
+ *p++ = '\\';
+ // fall through
+ default:
+ *p++ = *str++;
+ break;
+ }
+ }
+ *p = 0;
+ return nstr;
+}
+
+// -- cDirItem --------------------------------------------------------------
+
+cDirItem::cDirItem(cFileSource * src, const char *subdir, const char *name,
+ const eItemType type)
+{
+ Source = src;
+ Subdir = subdir ? strdup(subdir) : 0;
+ Name = name ? strdup(name) : 0;
+ Type = type;
+}
+
+cDirItem::~cDirItem()
+{
+ free(Name);
+ free(Subdir);
+}
+
+char *cDirItem::Path(void)
+{
+ char *path;
+ if(Subdir)
+ path = AddPath(Subdir, Name);
+ else
+ path = strdup(Name);
+ return path;
+}
+
+// -- cDirList --------------------------------------------------------------
+
+bool cDirList::Load(cFileSource * src, const char *subdir)
+{
+ bool res = false;
+ Clear();
+ if(subdir)
+ Add(new cDirItem(src, subdir, "..", itParent));
+ itype = itDir;
+ if(ScanDir(src, subdir, stDir, 0, 0, false)) {
+ itype = itFile;
+ if(ScanDir(src, subdir, stFile, src->Include(), 0, false))
+ res = true;
+ }
+ return res;
+}
+
+void cDirList::DoItem(cFileSource * src, const char *subdir, const char *name)
+{
+ Add(new cDirItem(src, subdir, name, itype));
+}
+
+
+
+// -- cFileSource --------------------------------------------------------------
+
+cFileSource::cFileSource(void)
+{
+ browsedir = browseparent = 0;
+ basedir = description = include = 0;
+ useCount = 0;
+ needsmount = false;
+}
+
+cFileSource::cFileSource(const char *Basedir, const char *Description,
+ const bool NeedsMount, const char *Include)
+{
+ browsedir = browseparent = 0;
+ basedir = description = include = 0;
+ useCount = 0;
+ Set(Basedir, Description, NeedsMount, Include);
+}
+
+cFileSource::~cFileSource()
+{
+ ClearRemember();
+ free(basedir);
+ free(description);
+ free(include);
+}
+
+void cFileSource::Set(const char *Basedir, const char *Description,
+ const bool NeedsMount, const char *Include)
+{
+ free(basedir);
+ basedir = strdup(Basedir);
+ free(description);
+ description = strdup(Description);
+ free(include);
+ include = Include ? strdup(Include) : 0;
+ needsmount = NeedsMount;
+}
+
+char *cFileSource::BuildName(const char *filename)
+{
+ return AddPath(basedir, filename);
+}
+
+void cFileSource::SetRemember(const char *dir, const char *parent)
+{
+ ClearRemember();
+ if(dir)
+ browsedir = strdup(dir);
+ if(parent)
+ browseparent = strdup(parent);
+}
+
+void cFileSource::ClearRemember(void)
+{
+ free(browsedir);
+ browsedir = 0;
+ free(browseparent);
+ browseparent = 0;
+}
+
+bool cFileSource::GetRemember(char *&dir, char *&parent)
+{
+ dir = parent = 0;
+ if(browsedir) {
+ if(browseparent)
+ parent = strdup(browseparent);
+ dir = strdup(browsedir);
+ return true;
+ }
+ return false;
+}
+
+bool cFileSource::Parse(char *s)
+{
+ char base[256], des[256], incl[256];
+ int needsmount, n;
+ if((n =
+ sscanf(s, "%255[^;];%255[^;];%d;%255[^;]", base, des, &needsmount,
+ incl)) >= 3)
+ {
+ char *base2 = skipspace(stripspace(base));
+ Set(base2, skipspace(stripspace(des)), needsmount != 0,
+ n > 3 ? skipspace(stripspace(incl)) : 0);
+
+ // do some checking of the basedir and issue a warning if apropriate
+ if(access(base2, R_OK)) {
+ esyslog("imageplugin: WARNING: source base %s not found/permission denied",base2);
+ }
+ else {
+ struct stat64 ds;
+ if(lstat64(base2, &ds)) {
+ esyslog("imageplugin: WARNING: can't stat source base %s",base2);
+ }
+ else {
+ if(S_ISLNK(ds.st_mode)) {
+ esyslog("imageplugin: WARNING: source base %s is a symbolic link",base2);
+ }
+ else if(!S_ISDIR(ds.st_mode)) {
+ esyslog("imageplugin: WARNING: source base %s is not a directory",base2);
+ }
+ }
+ }
+ return true;
+ }
+ return false;
+}
+
+bool cFileSource::Action(eAction act)
+{
+ static char *str[] = { "mount", "unmount", "eject", "status" };
+
+ char *cmd = 0;
+ asprintf(&cmd, "%s %s %s", g_szMountScript, str[act], basedir);
+ bool res = (system(cmd) == 0);
+ free(cmd);
+ return res;
+}
+
+bool cFileSource::Mount(void)
+{
+ bool res = false;
+ if(needsmount && (res = Action(acMount)))
+ ClearRemember();
+ return res;
+}
+
+bool cFileSource::Unmount(void)
+{
+ bool res = false;
+ if(needsmount) {
+
+ theSlideShow.Remove(this);
+
+ if(!useCount && (res = Action(acUnmount)))
+ ClearRemember();
+ }
+ return res;
+}
+
+bool cFileSource::Eject(void)
+{
+ bool res = false;
+ if(needsmount) {
+
+ theSlideShow.Remove(this);
+
+ if(!useCount && (res = Action(acEject)))
+ ClearRemember();
+ }
+ return res;
+}
+
+bool cFileSource::Status(void)
+{
+ if(needsmount)
+ return Action(acStatus);
+ return true;
+}
+
+// -- cFileSources --------------------------------------------------------------
+
+bool cFileSources::Load(const char *filename, bool dummy)
+{
+ if(cConfig < cFileSource >::Load(filename, true)) {
+ SetSource(First());
+ return true;
+ }
+ return false;
+}
+
+cFileSource *cFileSources::FindSource(const char *filename)
+{
+ cFileSource *src = First();
+ while(src) {
+ if(startswith(filename, src->BaseDir()))
+ return src;
+ src = Next(src);
+ }
+ return 0;
+}
diff --git a/data.h b/data.h
new file mode 100644
index 0000000..b6272bd
--- /dev/null
+++ b/data.h
@@ -0,0 +1,128 @@
+/*
+ * Image plugin to VDR (C++)
+ *
+ * (C) 2003 Kai Tobias Burwieck <kai@burwieck.net>
+*
+ * based on MP3/MPlayer plugin to VDR (C++)
+ * (C) 2001,2002 Stefan Huelswitt <huels@iname.com>
+ *
+ *
+ * This code is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This code is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#ifndef ___DATA_H
+#define ___DATA_H
+
+#include <vdr/tools.h>
+#include <vdr/config.h>
+
+extern const char *g_szMountScript;
+
+// ----------------------------------------------------------------
+
+class cFileSource;
+
+extern char *AddPath(const char *dir, const char *filename);
+
+// ----------------------------------------------------------------
+
+class cScanDir {
+ char *QuoteString(const char *str);
+protected:
+ enum eScanType { stFile, stDir };
+ virtual void DoItem(cFileSource *src, const char *subdir, const char *name)=0;
+public:
+ bool ScanDir(cFileSource *src, const char *subdir, eScanType type, const char *spec, const char *excl, bool recursiv);
+ };
+
+// ----------------------------------------------------------------
+
+enum eItemType { itDir, itParent, itFile, itBase };
+
+class cDirItem
+: public cListObject {
+public:
+ cDirItem(cFileSource *src, const char *subdir, const char *name, const eItemType type);
+ ~cDirItem();
+ char *Path(void);
+ //
+ cFileSource *Source;
+ char *Subdir;
+ char *Name;
+ eItemType Type;
+ };
+
+// ----------------------------------------------------------------
+
+class cDirList
+: public cScanDir
+, public cList<cDirItem> {
+ eItemType itype;
+protected:
+ virtual void DoItem(cFileSource *src, const char *subdir, const char *name);
+public:
+ bool Load(cFileSource *src, const char *subdir);
+ };
+
+// ----------------------------------------------------------------
+
+class cFileSource
+: public cListObject {
+ enum eAction { acMount, acUnmount, acEject, acStatus };
+ char *basedir;
+ char *description;
+ char *include;
+ bool needsmount;
+ int useCount;
+ // remember last browse position
+ char *browsedir, *browseparent;
+ //
+ void Set(const char *Basedir, const char *Description, const bool NeedsMount, const char *Include);
+ bool Action(eAction act);
+ void ClearRemember(void);
+public:
+ cFileSource(void);
+ cFileSource(const char *Basedir, const char *Description, const bool NeedsMount, const char *Include=0);
+ ~cFileSource();
+ bool Parse(char *s);
+ bool Mount(void);
+ bool Unmount(void);
+ bool Eject(void);
+ bool Status(void);
+ void Block(void) { ++useCount; }
+ void Unblock(void) { --useCount; }
+ char *BuildName(const char *filename);
+ void SetRemember(const char *dir, const char *parent);
+ bool GetRemember(char * &dir, char * &parent);
+ const char *BaseDir(void) const { return basedir; }
+ const char *Description(void) const { return description; }
+ const char *Include(void) const { return include; }
+ bool NeedsMount(void) const { return needsmount; }
+ };
+
+// ----------------------------------------------------------------
+
+class cFileSources
+: public cConfig<cFileSource> {
+ cFileSource *current;
+public:
+ virtual bool Load(const char *filename, bool dummy=false);
+ void SetSource(cFileSource *source) { current=source; }
+ cFileSource *GetSource(void) { return current; }
+ cFileSource *FindSource(const char *filename);
+ };
+
+#endif //___DATA_H
diff --git a/examples/imagecmds.conf b/examples/imagecmds.conf
new file mode 100644
index 0000000..779bd99
--- /dev/null
+++ b/examples/imagecmds.conf
@@ -0,0 +1,22 @@
+#
+# This is a example of imagecmds.conf with with usually using and sample entrys.
+#
+# This file should placed on VDR configuration folder with setup.conf
+# .../setup.conf
+# .../plugins/imagecmds.conf
+#
+# Syntax is: <name>[?];<command>
+#
+# <name> = descriptor displayed in VDR
+# [?] = confirmation request, before execute command (optionally)
+# <command>= command what execute with current watched file
+# %s - placeholder filename (optionally)
+#
+# See also at man-pages vdr(5), it the same syntax as reccmds.conf
+#
+Informations about picture : identify -verbose
+Size of picture : du -chs %s
+Dump Exif informations from JPEG-picture : jpegtopnm -dumpexif %s >/dev/null 2> $CONVERT_TEMPDIR/exif.tmp && cat $CONVERT_TEMPDIR/exif.tmp && rm -f $CONVERT_TEMPDIR/exif.tmp
+Rotate JPEG picture lossless by 90 degrees to the right : jpegtran -rotate 90 %s > $CONVERT_TEMPDIR/tmp.jpg && mv $CONVERT_TEMPDIR/tmp.jpg %s
+Rotate JPEG picture lossless by 90 degrees to the left : jpegtran -rotate 270 %s > $CONVERT_TEMPDIR/tmp.jpg && mv $CONVERT_TEMPDIR/tmp.jpg %s
+Delete picture ?: rm -f %s
diff --git a/examples/imagecmds.conf.DE b/examples/imagecmds.conf.DE
new file mode 100644
index 0000000..71d21bf
--- /dev/null
+++ b/examples/imagecmds.conf.DE
@@ -0,0 +1,16 @@
+#
+# Kommandos für Image plugins
+#
+# Format: see also man vdr(5)
+#
+## Menutext ?: Kommando %s
+#
+# ? - Bestätigung anfordern, (optional)
+# %s - Platzhalter für Dateinamen (optional)
+#
+Informationen über das Bild : identify -verbose
+Größe des Bildes : du -chs %s
+Exif Informationen des JPEG-Bildes ausgeben : jpegtopnm -dumpexif %s >/dev/null 2> $CONVERT_TEMPDIR/exif.tmp && cat $CONVERT_TEMPDIR/exif.tmp && rm -f $CONVERT_TEMPDIR/exif.tmp
+Rotatiere JPEG Bildes verlustlos um 90° nach Rechts : jpegtran -rotate 90 %s > $CONVERT_TEMPDIR/tmp.jpg && mv $CONVERT_TEMPDIR/tmp.jpg %s
+Rotatiere JPEG Bildes verlustlos um 90° nach Links : jpegtran -rotate 270 %s > $CONVERT_TEMPDIR/tmp.jpg && mv $CONVERT_TEMPDIR/tmp.jpg %s
+Lösche Bild ?: rm -f %s
diff --git a/examples/imagesources.conf b/examples/imagesources.conf
new file mode 100644
index 0000000..0b3c441
--- /dev/null
+++ b/examples/imagesources.conf
@@ -0,0 +1,21 @@
+#
+# This is a example of imagesources.conf with usually using and sample entrys.
+#
+# This file should placed on VDR configuration folder with setup.conf
+# .../setup.conf
+# .../plugins/imagesources.conf
+#
+# Syntax is: <path>;<name>;<mount>;<filter>
+#
+# <path> = Path, where to finde images or image directories
+# <name> = descriptor displayed in VDR
+# <mount> = 0 - if no mounting should be done
+# 1 - if <path> needs to be mounted first.
+# (Dont forget to setup fstab !!!)
+# <filter> = filters, what files should be displayed.
+# i.e. *.jpg *.jpeg *.png *.tif* *.bmp
+#
+/cdrom;CDROM;1;*.jpg *.jpeg *.png *.tif* *.bmp
+/video;VDR Recordings;0;*.pnm
+/mnt/images;Images on the network;0;*.jpg *.jpeg *.png *.tif* *.bmp
+/mnt/camera;Digital Camera;1;*.jpg
diff --git a/i18n.c b/i18n.c
new file mode 100644
index 0000000..54951ad
--- /dev/null
+++ b/i18n.c
@@ -0,0 +1,625 @@
+/*
+ * Image plugin to VDR (C++)
+ *
+ * (C) 2004 Andreas Brachold <vdr04-at-deltab.de>
+ * (C) 2003 Kai Tobias Burwieck <kai@burwieck.net>
+ *
+ * This code is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This code is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <vdr/config.h>
+#include "i18n.h"
+
+const tI18nPhrase Phrases[] = {
+ { "Image", /*English*/
+ "Bilder", /*Deutsch*/
+ "", // TODO /*Slovenski*/
+ "", // TODO /*Italiano*/
+ "", // TODO /*Nederlands*
+ "", // TODO /*Português*/
+ "", // TODO /*Français*/
+ "", // TODO /*Norsk*/
+ "Kuvat", /*suomi*/
+ "", // TODO /*Polski*/
+ "", // TODO /*Español*/
+ "", // TODO /*Ellinika*/
+ "", // TODO /*Svenska*/
+ "", // TODO /*Romaneste*/
+ "", // TODO /*Magyar*/
+ "", // TODO /*Català*/
+ },
+ { "A Image Viewer plugin", /*English*/
+ "Ein Bildbetrachter Plugin", /*Deutsch*/
+ "", // TODO /*Slovenski*/
+ "", // TODO /*Italiano*/
+ "", // TODO /*Nederlands*
+ "", // TODO /*Português*/
+ "", // TODO /*Français*/
+ "", // TODO /*Norsk*/
+ "Katso valokuvia ruudullasi", /*suomi*/
+ "", // TODO /*Polski*/
+ "", // TODO /*Español*/
+ "", // TODO /*Ellinika*/
+ "", // TODO /*Svenska*/
+ "", // TODO /*Romaneste*/
+ "", // TODO /*Magyar*/
+ "", // TODO /*Català*/
+ },
+ { "Image browser",
+ "Bilder Verzeichnisanzeige",
+ "Image navigator",
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "Navigateur Images",
+ "", // TODO
+ "Kuvat - selain",
+ "", // TODO
+ "", // TODO
+ "Image endiksi fakelon",
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ },
+ { "Image source",
+ "Bilder-Datenträger",
+ "Image izvor",
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "Source Images",
+ "", // TODO
+ "Kuvat - lähteet",
+ "", // TODO
+ "", // TODO
+ "Pigi Image",
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ },
+ { "Parent",
+ "Zurück",
+ "Nazaj",
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "Parent",
+ "", // TODO
+ "Takaisin",
+ "", // TODO
+ "Predecesor",
+ "Piso",
+ "Tillbaka",
+ "", // TODO
+ "", // TODO
+ "Anterior",
+ },
+ { "Select",
+ "Auswählen",
+ "Izberi",
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "Sélectionner",
+ "", // TODO
+ "Valitse",
+ "", // TODO
+ "Seleccionar",
+ "Epilogi",
+ "Välj",
+ "", // TODO
+ "", // TODO
+ "Escollir",
+ },
+ { "Error scanning directory!",
+ "Fehler beim Lesen des Verzeichnisses!",
+ "Napaka pri pregledovanju direktorija!",
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "Erreur de parcours du répertoire!",
+ "", // TODO
+ "Hakemiston selaus epäonnistui!",
+ "", // TODO
+ "¡Error al leer una carpeta!",
+ "Lathos stin sarosi tou fakelou!",
+ "Kunde inte läsa katalogen!",
+ "", // TODO
+ "", // TODO
+ "Error al llegir una carpeta!",
+ },
+ { "Source",
+ "Datenträger",
+ "Izvor",
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "Source",
+ "", // TODO
+ "Lähde",
+ "", // TODO
+ "Origen",
+ "Pigi",
+ "Källa",
+ "", // TODO
+ "", // TODO
+ "Orígen",
+ },
+ { "Mount",
+ "Einbinden",
+ "Priklopi",
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "Monter",
+ "", // TODO
+ "Kiinnitä",
+ "", // TODO
+ "Mount",
+ "Sindesi",
+ "Montera",
+ "", // TODO
+ "", // TODO
+ "Mount",
+ },
+ { "Unmount",
+ "Aushängen",
+ "Izklopi",
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "Démonter",
+ "", // TODO
+ "Irrota",
+ "", // TODO
+ "Unmount",
+ "Aposindesi",
+ "Avmontera",
+ "", // TODO
+ "", // TODO
+ "Unmount",
+ },
+ { "Selected source is not mounted!",
+ "Ausgewählter Datenträger ist nicht eingebunden!",
+ "Izbran izvor ni prikljucen!",
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "Source sélectionnée non montée!",
+ "", // TODO
+ "Valittua lähdettä ei ole kiinnitetty!",
+ "", // TODO
+ "¡El origen deseado no està montado!",
+ "Epilegmeni Pigi den ine sindemeni!",
+ "Den valda källan är inte monterad!",
+ "", // TODO
+ "", // TODO
+ "l'Origen sel.leccionat no està muntat!",
+ },
+ { "Mount succeeded",
+ "Einbinden erfolgreich",
+ "Priklop izveden",
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "Montage réussi",
+ "", // TODO
+ "Kiinnittäminen onnistui",
+ "", // TODO
+ "mount correcto",
+ "I sindesi petixe",
+ "Monteringen lyckades",
+ "", // TODO
+ "", // TODO
+ "mount correcte",
+ },
+ { "Mount failed!",
+ "Einbinden fehlgeschlagen!",
+ "Napaka pri priklopu!",
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "Echec du montage!",
+ "", // TODO
+ "Kiinnittäminen epäonnistui!",
+ "", // TODO
+ "¡No he podido montar!",
+ "I sindesi apetixe!",
+ "Monteringen misslyckades!",
+ "", // TODO
+ "", // TODO
+ "No he pogut muntar!",
+ },
+ { "Unmount succeeded",
+ "Aushängen erfolgreich",
+ "Izklop izveden",
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "Démontage réussi",
+ "", // TODO
+ "Irrottaminen onnistui",
+ "", // TODO
+ "Éxito al unmount",
+ "I aposindesi itan epitixisi",
+ "Avmonteringen lyckades",
+ "", // TODO
+ "", // TODO
+ "unmount amb èxit",
+ },
+ { "Unmount failed!",
+ "Aushängen fehlgeschlagen!",
+ "Napaka pri izklopu!",
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "Echec du démontage!",
+ "", // TODO
+ "Irrottaminen epäonnistui!",
+ "", // TODO
+ "¡No puedo desmontar!",
+ "I aposindesi den itan epitixis!",
+ "Avmonteringen misslyckades!",
+ "", // TODO
+ "", // TODO
+ "No puc desmontar!",
+ },
+ { "Eject failed!",
+ "Auswerfen fehlgeschlagen!",
+ "Napaka pri izmetu!",
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "Echec de l'éjection!",
+ "", // TODO
+ "Avaaminen epäonnistui!",
+ "", // TODO
+ "¡No puedo expulsar!",
+ "I apovoli apetixe!",
+ "Mata ut!",
+ "", // TODO
+ "", // TODO
+ "No puc expulsar!",
+ },
+ { "Select picture via key 1..9!",
+ "Bildauswahl über Taste 1..9!",
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "Valitse kuva näppäimillä 1..9!",
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ },
+ { "Building SlideShow...",
+ "Erzeuge Diavorführung...",
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "Laaditaan diaesitystä..",
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ },
+ { "Scanning directory...",
+ "Durchsuche Verzeichnis...",
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "Scanne le repertoire...",
+ "", // TODO
+ "Selataan hakemistoa...",
+ "", // TODO
+ "Leyendo las carpetas..",
+ "", // TODO
+ "Söker igenom katalog...",
+ "", // TODO
+ "", // TODO
+ "Revisant les carpetes..",
+ },
+ { "No Files!",
+ "Keine Dateien!",
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "Ei tiedostoja!",
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ },
+ { "Error building SlideShow!",
+ "Fehler beim Erzeugen der Diavorführung!",
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "Diaesityksen laatiminen epäonnistui!",
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ },
+ { "SlideShow ?",
+ "Diavorführung ?",
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "Käytä diaesitystä",
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ },
+ { "Slide duration (sec)",
+ "Anzeigedauer (Sek)",
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "Dian esitysaika (s)",
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ },
+ { "Directory with temporary files",
+ "Verzeichnis mit temporären Dateien",
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "Väliaikaistiedostot",
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ },
+ { "Show Filedate on OSD",
+ "Zeige Dateidatum im OSD",
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "Näytä päivämäärä näytöllä",
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ },
+ { "Repeat SlideShow",
+ "Wiederholung der Diavorführung",
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "Diaesityksen uudelleentoisto",
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ },
+ { "Show Numbers on index image",
+ "Zeige Zahlen im Indexbild",
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "Näytä numerot indeksikuvassa",
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ },
+#if VDRVERSNUM >= 10308
+ { "Live Audio from primary Device",
+ "Live Audio vom primären Gerät",
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "Live-ääni päävastaanottimelta",
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ },
+#endif
+ { "Convert...",
+ "Wandle...",
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "Konvertoi...",
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ },
+ { "Zoom",
+ "Vergrößere",
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "Suurenna",
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ },
+ { "Execute",
+ "Ausführen",
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "Suorita",
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ },
+ { "Operation failed", /*English*/
+ "Vorgang fehlgeschlagen", /*Deutsch*/
+ "", // TODO /*Slovenski*/
+ "", // TODO /*Italiano*/
+ "", // TODO /*Nederlands*
+ "", // TODO /*Português*/
+ "", // TODO /*Français*/
+ "", // TODO /*Norsk*/
+ "Toiminto epäonnistui", /*suomi*/
+ "", // TODO /*Polski*/
+ "", // TODO /*Español*/
+ "", // TODO /*Ellinika*/
+ "", // TODO /*Svenska*/
+ "", // TODO /*Romaneste*/
+ "", // TODO /*Magyar*/
+ "", // TODO /*Català*/
+ },
+ { "Image couldn't load", /*English*/
+ "Konnte Bild nicht lesen", /*Deutsch*/
+ "", // TODO /*Slovenski*/
+ "", // TODO /*Italiano*/
+ "", // TODO /*Nederlands*
+ "", // TODO /*Português*/
+ "", // TODO /*Français*/
+ "", // TODO /*Norsk*/
+ "Kuvan lukeminen epäonnistui", /*suomi*/
+ "", // TODO /*Polski*/
+ "", // TODO /*Español*/
+ "", // TODO /*Ellinika*/
+ "", // TODO /*Svenska*/
+ "", // TODO /*Romaneste*/
+ "", // TODO /*Magyar*/
+ "", // TODO /*Català*/
+ },
+ { "Script execution failed", /*English*/
+ "Scriptausführung fehlgeschlagen", /*Deutsch*/
+ "", // TODO /*Slovenski*/
+ "", // TODO /*Italiano*/
+ "", // TODO /*Nederlands*
+ "", // TODO /*Português*/
+ "", // TODO /*Français*/
+ "", // TODO /*Norsk*/
+ "Skriptin suoritus epäonnistui", /*suomi*/
+ "", // TODO /*Polski*/
+ "", // TODO /*Español*/
+ "", // TODO /*Ellinika*/
+ "", // TODO /*Svenska*/
+ "", // TODO /*Romaneste*/
+ "", // TODO /*Magyar*/
+ "", // TODO /*Català*/
+ },
+ { "Remove temporary files", /*English*/
+ "Lösche temporäre Dateien", /*Deutsch*/
+ "", // TODO /*Slovenski*/
+ "", // TODO /*Italiano*/
+ "", // TODO /*Nederlands*
+ "", // TODO /*Português*/
+ "", // TODO /*Français*/
+ "", // TODO /*Norsk*/
+ "Tyhjennä väliaikaistiedostot", /*suomi*/
+ "", // TODO /*Polski*/
+ "", // TODO /*Español*/
+ "", // TODO /*Ellinika*/
+ "", // TODO /*Svenska*/
+ "", // TODO /*Romaneste*/
+ "", // TODO /*Magyar*/
+ "", // TODO /*Català*/
+ },
+ { NULL }
+};
diff --git a/i18n.h b/i18n.h
new file mode 100644
index 0000000..936d2cf
--- /dev/null
+++ b/i18n.h
@@ -0,0 +1,30 @@
+/*
+ * Image plugin to VDR (C++)
+ *
+ * (C) 2004 Andreas Brachold <vdr04-at-deltab.de>
+ * (C) 2003 Kai Tobias Burwieck <kai@burwieck.net>
+ *
+ * This code is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This code is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#ifndef ___I18N_H
+#define ___I18N_H
+
+#include <vdr/i18n.h>
+
+extern const tI18nPhrase Phrases[];
+
+#endif //___I18N_H
diff --git a/image.c b/image.c
new file mode 100644
index 0000000..fae80ca
--- /dev/null
+++ b/image.c
@@ -0,0 +1,129 @@
+/*
+ * Image plugin to VDR (C++)
+ *
+ * (C) 2003 Kai Tobias Burwieck <kai@burwieck.net>
+ * (C) 2004 "Interpohl" <interpohl@vdr-portal.de>
+ * (C) 2004 A. Brachold <vdr04-at-deltab.de>
+ * (C) 2004 O. Kreuzinger <Onno@Kreuzinger.biz>
+ * (C) 2004 A. Holzhammer for the massive script updates
+ *
+ * based on mp3/mplayer plguin by Stefan Hülswitt <huels@iname.com>
+ *
+ * This code is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This code is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <getopt.h>
+#include <vdr/plugin.h>
+
+
+#include "setup-image.h"
+#include "data-image.h"
+#include "menu-image.h"
+#include "i18n.h"
+#include "commands.h"
+#include "liboutput/encode.h"
+
+static const char *VERSION = "0.2.3";
+static const char *DESCRIPTION = "A Image Viewer plugin";
+static const char *MAINMENUENTRY = "Image";
+
+class cPluginImage : public cPlugin {
+public:
+ virtual ~cPluginImage();
+ virtual const char *Version(void) { return VERSION; }
+ virtual const char *Description(void) { return tr(DESCRIPTION); }
+ virtual const char *CommandLineHelp(void);
+ virtual bool ProcessArgs(int argc, char *argv[]);
+ virtual bool Start(void);
+ virtual const char *MainMenuEntry(void) { return tr(MAINMENUENTRY); }
+ virtual cOsdMenu *MainMenuAction(void);
+ virtual cMenuSetupPage *SetupMenu(void);
+ virtual bool SetupParse(const char *Name, const char *Value);
+ };
+
+bool cPluginImage::SetupParse(const char *szName, const char *szValue)
+{
+ return ImageSetup.SetupParse(szName,szValue);
+}
+
+const char *cPluginImage::CommandLineHelp(void)
+{
+ static char *help_str=0;
+
+ free(help_str); // for easier orientation, this is column 80|
+ asprintf(&help_str," -m CMD, --mount=CMD use CMD to mount/unmount/eject image sources\n"
+ " (default: %s)\n"
+ " -C CMD, --convert=CMD use CMD when converting Images\n"
+ " (default: %s)\n",
+ g_szMountScript,
+ g_szConvertScript
+ );
+ return help_str;
+}
+
+bool cPluginImage::ProcessArgs(int argc, char *argv[])
+{
+ static struct option long_options[] = {
+ { "mount", required_argument, NULL, 'm' },
+ { "convert", required_argument, NULL, 'C' },
+ { NULL }
+ };
+
+ int c, option_index = 0;
+ while((c=getopt_long(argc,argv,"m:C:",long_options,&option_index))!=-1) {
+ switch (c) {
+ case 'm': g_szMountScript=optarg;break;
+ case 'C': g_szConvertScript=optarg;break;
+ default: return false;
+ }
+ }
+ return true;
+}
+
+bool cPluginImage::Start(void)
+{
+ if(!cEncode::Register()) {
+ return false;
+ }
+
+ ImageSources.Load(AddDirectory(ConfigDirectory(), "imagesources.conf"));
+ if(ImageSources.Count()<1) {
+ esyslog("imageplugin: you must have defined at least one source in imagesources.conf");
+ return false;
+ }
+
+ RegisterI18n(Phrases);
+ return true;
+}
+
+cPluginImage::~cPluginImage()
+{
+ cEncode::UnRegister();
+}
+
+
+cOsdMenu *cPluginImage::MainMenuAction(void)
+{
+ return new cMenuImageBrowse;
+}
+
+cMenuSetupPage *cPluginImage::SetupMenu(void)
+{
+ return new cMenuSetupImage;
+}
+
+
+VDRPLUGINCREATOR(cPluginImage); // Don't touch this!
diff --git a/image.h b/image.h
new file mode 100644
index 0000000..84e0c5c
--- /dev/null
+++ b/image.h
@@ -0,0 +1,67 @@
+/*
+ * Image plugin to VDR (C++)
+ *
+ * (C) 2004 Andreas Brachold <vdr04-at-deltab.de>
+ *
+ * This code is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This code is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#ifndef ___IMAGE_H
+#define ___IMAGE_H
+
+#include <vdr/config.h>
+#if VDRVERSNUM < 10307
+#include <vdr/osd.h>
+#else
+#include <vdr/osdbase.h>
+#include <vdr/skins.h>
+#endif
+#include <string.h>
+
+inline void OSD_InfoMsg(const char* sz)
+{
+#if VDRVERSNUM < 10307
+ Interface->Info(sz);
+ Interface->Flush();
+#else
+ Skins.Message(mtInfo,sz);
+ Skins.Flush();
+#endif
+}
+
+inline void OSD_ErrorMsg(const char* sz)
+{
+#if VDRVERSNUM < 10307
+ Interface->Error(sz);
+ Interface->Flush();
+#else
+ Skins.Message(mtError,sz);
+ Skins.Flush();
+#endif
+}
+
+inline void OSD_ErrorNumMsg(int err, const char* szDef)
+{
+ char szErr[128];
+ int nErr = err;
+ szErr[sizeof(szErr)-1] = '\0';
+ if(0 != strerror_r(nErr,szErr,sizeof(szErr)-1)) {
+ szErr[0] = '\0';
+ }
+ OSD_ErrorMsg(szErr[0] != '\0'?szErr:szDef);
+}
+
+#endif //___IMAGE_H
diff --git a/libimage/Makefile b/libimage/Makefile
new file mode 100644
index 0000000..e46bb0c
--- /dev/null
+++ b/libimage/Makefile
@@ -0,0 +1,51 @@
+#
+# Makefile for a Video Disk Recorder plugin
+#
+# $Id$
+
+VDRDIR = ../../../..
+
+### The C++ compiler and options:
+
+CXX ?= g++
+CXXFLAGS ?= -O0 -g -Wall -Woverloaded-virtual
+
+-include $(VDRDIR)/Make.config
+
+### The directory environment:
+
+
+INCLUDES += -I$(VDRDIR)/include
+
+DEFINES += -D_GNU_SOURCE
+
+LIBS +=
+
+### The object files (add further files here):
+
+OBJS = pnm.o xpm.o
+
+### Implicit rules:
+
+%.o: %.c
+ $(CXX) $(CXXFLAGS) -c $(DEFINES) $(INCLUDES) $<
+
+# Dependencies:
+
+MAKEDEP = $(CXX) -MM -MG
+DEPFILE = .dependencies
+$(DEPFILE): Makefile
+ @$(MAKEDEP) $(DEFINES) $(INCLUDES) $(OBJS:%.o=%.c) > $@
+
+-include $(DEPFILE)
+
+### Targets:
+
+all: libimage.a
+
+libimage.a : $(OBJS)
+ $(AR) $(ARFLAGS) $@ $(OBJS)
+# $(CXX) $(CXXFLAGS) -shared $(OBJS) $(LIBS) -o $@
+
+clean:
+ @-rm -f $(OBJS) $(DEPFILE) *.a *.so *.tgz core* *~
diff --git a/libimage/error.svg b/libimage/error.svg
new file mode 100644
index 0000000..b633739
--- /dev/null
+++ b/libimage/error.svg
@@ -0,0 +1,2167 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
+"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
+<!-- Created with Sodipodi ("http://www.sodipodi.com/") -->
+<svg
+ width="704pt"
+ height="576pt"
+ viewBox="0 0 256 256"
+ style="overflow:visible;enable-background:new 0 0 256 256"
+ xml:space="preserve"
+ id="svg3469"
+ sodipodi:version="0.33"
+ sodipodi:docname="Image-Error.svg"
+ sodipodi:docbase="."
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:xml="http://www.w3.org/XML/1998/namespace"
+ xmlns:xlink="http://www.w3.org/1999/xlink">
+ <defs
+ id="defs3579">
+ <linearGradient
+ id="XMLID_1_"
+ gradientUnits="userSpaceOnUse"
+ x1="128.9995"
+ y1="11"
+ x2="128.9995"
+ y2="245.0005">
+ <stop
+ offset="0"
+ style="stop-color:#494949"
+ id="stop3476" />
+
+
+
+
+ <stop
+ offset="1"
+ style="stop-color:#000000"
+ id="stop3477" />
+
+
+
+
+ </linearGradient>
+
+
+
+
+ <linearGradient
+ id="XMLID_2_"
+ gradientUnits="userSpaceOnUse"
+ x1="29.0532"
+ y1="29.0532"
+ x2="226.9471"
+ y2="226.9471">
+ <stop
+ offset="0"
+ style="stop-color:#FFFFFF"
+ id="stop3484" />
+
+
+
+
+ <stop
+ offset="1"
+ style="stop-color:#DADADA"
+ id="stop3485" />
+
+
+
+
+ </linearGradient>
+
+
+
+
+ </defs>
+
+
+
+ <sodipodi:namedview
+ id="base">
+ <sodipodi:guide
+ orientation="horizontal"
+ position="287.129913"
+ id="guide2108" />
+
+ <sodipodi:guide
+ orientation="vertical"
+ position="29.7764359"
+ id="guide2109" />
+
+ <sodipodi:guide
+ orientation="vertical"
+ position="676.350464"
+ id="guide2110" />
+
+ <sodipodi:guide
+ orientation="horizontal"
+ position="550.864075"
+ id="guide2111" />
+
+ <sodipodi:guide
+ orientation="horizontal"
+ position="31.9033241"
+ id="guide2112" />
+
+</sodipodi:namedview>
+
+
+
+ <path
+ style="font-size:12;fill:none;"
+ d="M 222.4303 159.0556 L 118.0510 159.0556 L 118.0510 34.51719 L 222.4303 34.51719 L 222.4303 159.0556 z "
+ id="path3490" />
+
+
+
+ <g
+ id="g3491"
+ style="font-size:12;stroke:#000000;"
+ transform="matrix(0.583057,0,0,0.695663,106.1162,4.982203)">
+ <path
+ style="opacity:0.2;stroke:none;"
+ d="M72.626,58.727c-3.336,0-6.049,2.713-6.049,6.048v132.323c0,3.335,2.713,6.048,6.049,6.048h115.445 c3.335,0,6.049-2.713,6.049-6.048V64.774c0-3.335-2.714-6.048-6.049-6.048H72.626z"
+ id="path3492" />
+
+
+
+
+ <path
+ style="opacity:0.2;stroke:none;"
+ d="M71.451,56.378c-3.335,0-6.048,2.713-6.048,6.049v132.322c0,3.335,2.713,6.048,6.048,6.048h115.445 c3.335,0,6.049-2.713,6.049-6.048V62.427c0-3.336-2.714-6.049-6.049-6.049H71.451z"
+ id="path3493" />
+
+
+
+
+ <path
+ style="opacity:0.2;stroke:none;"
+ d="M69.103,55.203c-3.335,0-6.049,2.713-6.049,6.048v132.323c0,3.335,2.714,6.048,6.049,6.048h115.445 c3.335,0,6.049-2.713,6.049-6.048V61.251c0-3.335-2.714-6.048-6.049-6.048H69.103z"
+ id="path3494" />
+
+
+
+
+ <linearGradient
+ id="XMLID_3_"
+ gradientUnits="userSpaceOnUse"
+ x1="61.8799"
+ y1="125.064"
+ x2="189.4219"
+ y2="125.064">
+ <stop
+ offset="0"
+ style="stop-color:#616161"
+ id="stop3496" />
+
+
+
+
+ <stop
+ offset="1"
+ style="stop-color:#464646"
+ id="stop3497" />
+
+
+
+
+ </linearGradient>
+
+
+
+
+ <path
+ style="fill:url(#XMLID_3_);stroke:none;"
+ d="M67.928,52.854c-3.335,0-6.048,2.713-6.048,6.049v132.322c0,3.335,2.713,6.048,6.048,6.048h115.444 c3.336,0,6.05-2.713,6.05-6.048V58.903c0-3.336-2.714-6.049-6.05-6.049H67.928z"
+ id="path3501" />
+
+
+
+
+ <path
+ style="fill:#FFFFFF;stroke:none;"
+ d="M184.725,191.226c0,0.747-0.606,1.351-1.353,1.351H67.928c-0.745,0-1.351-0.604-1.351-1.351V58.903 c0-0.746,0.605-1.352,1.351-1.352h115.444c0.746,0,1.353,0.605,1.353,1.352V191.226z"
+ id="path3502" />
+
+
+
+
+ <linearGradient
+ id="XMLID_4_"
+ gradientUnits="userSpaceOnUse"
+ x1="233.4971"
+ y1="232.6162"
+ x2="51.0675"
+ y2="50.1867">
+ <stop
+ offset="0"
+ style="stop-color:#CECEDB"
+ id="stop3504" />
+
+
+
+
+ <stop
+ offset="0.691"
+ style="stop-color:#E9E9EF"
+ id="stop3505" />
+
+
+
+
+ <stop
+ offset="1"
+ style="stop-color:#FFFFFF"
+ id="stop3506" />
+
+
+
+
+ </linearGradient>
+
+
+
+
+ <path
+ style="fill:url(#XMLID_4_);stroke:none;"
+ d="M69.954,60.635c0,4.134,0,123.846,0,127.978c4.104,0,106.998,0,111.101,0c0-4.132,0-123.844,0-127.978 C176.952,60.635,74.058,60.635,69.954,60.635z"
+ id="path3512" />
+
+
+
+
+ <linearGradient
+ id="XMLID_5_"
+ gradientUnits="userSpaceOnUse"
+ x1="-654.9922"
+ y1="23.1128"
+ x2="-520.0903"
+ y2="23.1128"
+ gradientTransform="matrix(0 1 -1 0 148.7637 686.498)">
+ <stop
+ offset="0"
+ style="stop-color:#BDBDCF"
+ id="stop3514" />
+
+
+
+
+ <stop
+ offset="0.1923"
+ style="stop-color:#CCCCDA"
+ id="stop3515" />
+
+
+
+
+ <stop
+ offset="0.7201"
+ style="stop-color:#F1F1F5"
+ id="stop3516" />
+
+
+
+
+ <stop
+ offset="1"
+ style="stop-color:#FFFFFF"
+ id="stop3517" />
+
+
+
+
+ </linearGradient>
+
+
+
+
+ <path
+ style="fill:url(#XMLID_5_);stroke:none;"
+ d="M176.563,67.165c-3.289,0-98.536,0-101.823,0c0,3.266,0,85.131,0,88.396c3.287,0,98.534,0,101.823,0 C176.563,152.296,176.563,70.431,176.563,67.165z"
+ id="path3521" />
+
+
+
+
+ <path
+ style="stroke:none;"
+ d="M78.46,70.886c0,6.529,0,74.425,0,80.955c6.646,0,87.734,0,94.381,0c0-6.53,0-74.426,0-80.955 C166.194,70.886,85.106,70.886,78.46,70.886z"
+ id="path3522" />
+
+
+
+
+ <radialGradient
+ id="XMLID_6_"
+ cx="109.7783"
+ cy="116.7822"
+ r="66.1491"
+ fx="109.7783"
+ fy="116.7822"
+ gradientUnits="userSpaceOnUse">
+ <stop
+ offset="0"
+ style="stop-color:#B1E1FF"
+ id="stop3524" />
+
+
+
+
+ <stop
+ offset="0.1441"
+ style="stop-color:#AEDFFF"
+ id="stop3525" />
+
+
+
+
+ <stop
+ offset="0.2661"
+ style="stop-color:#A3D9FF"
+ id="stop3526" />
+
+
+
+
+ <stop
+ offset="0.38"
+ style="stop-color:#92CFFF"
+ id="stop3527" />
+
+
+
+
+ <stop
+ offset="0.489"
+ style="stop-color:#79C1FF"
+ id="stop3528" />
+
+
+
+
+ <stop
+ offset="0.5937"
+ style="stop-color:#5AAEFF"
+ id="stop3529" />
+
+
+
+
+ <stop
+ offset="0.6011"
+ style="stop-color:#57ADFF"
+ id="stop3530" />
+
+
+
+
+ <stop
+ offset="1"
+ style="stop-color:#0035ED"
+ id="stop3531" />
+
+
+
+
+ </radialGradient>
+
+
+
+
+ <path
+ style="fill:url(#XMLID_6_);stroke:none;"
+ d="M80.94,73.367c0,4.515,0,71.477,0,75.993c4.577,0,84.844,0,89.42,0c0-4.517,0-71.479,0-75.993 C165.784,73.367,85.518,73.367,80.94,73.367z"
+ id="path3537" />
+
+
+
+
+ <linearGradient
+ id="XMLID_7_"
+ gradientUnits="userSpaceOnUse"
+ x1="100.4385"
+ y1="122.5811"
+ x2="118.1257"
+ y2="104.8938">
+ <stop
+ offset="0"
+ style="stop-color:#FFFFFF"
+ id="stop3539" />
+
+
+
+
+ <stop
+ offset="1"
+ style="stop-color:#E9E9EF"
+ id="stop3540" />
+
+
+
+
+ </linearGradient>
+
+
+
+
+ <path
+ style="fill:url(#XMLID_7_);stroke:none;"
+ d="M96.774,113.736c0,6.907,5.6,12.508,12.507,12.508s12.506-5.601,12.506-12.508 s-5.599-12.507-12.506-12.507S96.774,106.829,96.774,113.736z"
+ id="path3544" />
+
+
+
+
+ <path
+ style="stroke:none;"
+ d="M78.46,137.866c0,1.127,0,12.845,0,13.975c6.646,0,87.734,0,94.381,0c0-1.13,0-12.848,0-13.975 C166.194,137.866,85.106,137.866,78.46,137.866z"
+ id="path3545" />
+
+
+
+
+ <path
+ style="stroke:none;"
+ d="M100.665,117.415h-8.486v25.92h8.486V117.415z"
+ id="path3546" />
+
+
+
+
+ <path
+ style="stroke:none;"
+ d="M108.007,127.737h-8.488v15.828h8.488V127.737z"
+ id="path3547" />
+
+
+
+
+ <path
+ style="stroke:none;"
+ d="M118.099,144.023h-11.01v-10.78h11.01V144.023z"
+ id="path3548" />
+
+
+
+
+ <path
+ style="stroke:none;"
+ d="M123.374,119.938h-6.65v19.499h6.65V119.938z"
+ id="path3549" />
+
+
+
+
+ <path
+ style="stroke:none;"
+ d="M139.892,144.023h-17.205v-15.599h17.205V144.023z"
+ id="path3550" />
+
+
+
+
+ <path
+ style="stroke:none;"
+ d="M145.396,108.927h-7.34v32.116h7.34V108.927z"
+ id="path3551" />
+
+
+
+
+ <path
+ style="stroke:none;"
+ d="M80.708,144.023h11.699v-19.817H80.708V144.023z"
+ id="path3552" />
+
+
+
+
+ <path
+ style="stroke:none;"
+ d="M154.571,143.104h-11.01v-23.396h11.01V143.104z"
+ id="path3553" />
+
+
+
+
+ <path
+ style="stroke:none;"
+ d="M170.683,145.87h-12.664v-17.662h12.664V145.87z"
+ id="path3554" />
+
+
+
+
+ <path
+ style="stroke:none;"
+ d="M169.024,142.416h-7.57v-25.003h7.57V142.416z"
+ id="path3555" />
+
+
+
+
+ <path
+ style="stroke:none;"
+ d="M130.486,138.059h-7.569v-25.005h7.569V138.059z"
+ id="path3556" />
+
+
+
+
+ <path
+ style="stroke:none;"
+ d="M80.938,144.482h88.086v-7.8H80.938V144.482z"
+ id="path3557" />
+
+
+
+
+ <path
+ style="fill:#FFF030;stroke:none;"
+ d="M94.063,121.743h2.016v-2.016h-2.016V121.743z"
+ id="path3558" />
+
+
+
+
+ <path
+ style="fill:#FFF030;stroke:none;"
+ d="M94.063,126.23h2.016v-2.016h-2.016V126.23z"
+ id="path3559" />
+
+
+
+
+ <path
+ style="fill:#FFF030;stroke:none;"
+ d="M94.063,135.205h2.016v-2.016h-2.016V135.205z"
+ id="path3560" />
+
+
+
+
+ <path
+ style="fill:#FFF030;stroke:none;"
+ d="M125.874,122.173h2.015v-2.016h-2.015V122.173z"
+ id="path3561" />
+
+
+
+
+ <path
+ style="fill:#FFF030;stroke:none;"
+ d="M125.874,131.147h2.015v-2.016h-2.015V131.147z"
+ id="path3562" />
+
+
+
+
+ <path
+ style="fill:#FFF030;stroke:none;"
+ d="M119.207,124.662h2.016v-2.015h-2.016V124.662z"
+ id="path3563" />
+
+
+
+
+ <path
+ style="fill:#FFF030;stroke:none;"
+ d="M119.207,129.149h2.016v-2.016h-2.016V129.149z"
+ id="path3564" />
+
+
+
+
+ <path
+ style="fill:#FFF030;stroke:none;"
+ d="M119.207,133.638h2.016v-2.016h-2.016V133.638z"
+ id="path3565" />
+
+
+
+
+ <path
+ style="fill:#FFF030;stroke:none;"
+ d="M102.489,132.554h2.015v-2.018h-2.015V132.554z"
+ id="path3566" />
+
+
+
+
+ <path
+ style="fill:#FFF030;stroke:none;"
+ d="M102.489,137.038h2.015v-2.015h-2.015V137.038z"
+ id="path3567" />
+
+
+
+
+ <path
+ style="fill:#FFF030;stroke:none;"
+ d="M82.507,128.463h2.015v-2.016h-2.015V128.463z"
+ id="path3568" />
+
+
+
+
+ <path
+ style="fill:#FFF030;stroke:none;"
+ d="M82.507,132.95h2.015v-2.017h-2.015V132.95z"
+ id="path3569" />
+
+
+
+
+ <path
+ style="fill:#FFF030;stroke:none;"
+ d="M149.356,128.747h2.015v-2.016h-2.015V128.747z"
+ id="path3570" />
+
+
+
+
+ <path
+ style="fill:#FFF030;stroke:none;"
+ d="M140.674,114.493h2.015v-2.017h-2.015V114.493z"
+ id="path3571" />
+
+
+
+
+ <path
+ style="fill:#FFF030;stroke:none;"
+ d="M140.674,118.979h2.015v-2.016h-2.015V118.979z"
+ id="path3572" />
+
+
+
+
+ <path
+ style="fill:#FFF030;stroke:none;"
+ d="M140.674,123.468h2.015v-2.016h-2.015V123.468z"
+ id="path3573" />
+
+
+
+
+ <path
+ style="fill:#FFF030;stroke:none;"
+ d="M140.674,127.954h2.015v-2.016h-2.015V127.954z"
+ id="path3574" />
+
+
+
+
+ <path
+ style="fill:#FFF030;stroke:none;"
+ d="M164.085,122.089h2.016v-2.016h-2.016V122.089z"
+ id="path3575" />
+
+
+
+
+ <path
+ style="fill:#FFF030;stroke:none;"
+ d="M164.085,131.065h2.016v-2.016h-2.016V131.065z"
+ id="path3576" />
+
+
+
+
+ <path
+ style="fill:#FFF030;stroke:none;"
+ d="M129.602,134.712v2.016h2.016v-2.016H129.602z"
+ id="path3577" />
+
+
+
+
+ </g>
+
+
+
+ <path
+ style="font-size:12;fill:none;"
+ d="M 226.0942 146.8087 L 135.6992 209.0778 L 83.50963 101.2246 L 173.9046 38.95533 L 226.0942 146.8087 z "
+ id="path1323" />
+
+
+
+ <g
+ id="g1324"
+ style="font-size:12;stroke:#000000;"
+ transform="matrix(0.504941,-0.347832,0.291527,0.602462,63.36050,88.66474)">
+ <path
+ style="opacity:0.2;stroke:none;"
+ d="M72.626,58.727c-3.336,0-6.049,2.713-6.049,6.048v132.323c0,3.335,2.713,6.048,6.049,6.048h115.445 c3.335,0,6.049-2.713,6.049-6.048V64.774c0-3.335-2.714-6.048-6.049-6.048H72.626z"
+ id="path1325" />
+
+
+
+
+ <path
+ style="opacity:0.2;stroke:none;"
+ d="M71.451,56.378c-3.335,0-6.048,2.713-6.048,6.049v132.322c0,3.335,2.713,6.048,6.048,6.048h115.445 c3.335,0,6.049-2.713,6.049-6.048V62.427c0-3.336-2.714-6.049-6.049-6.049H71.451z"
+ id="path1326" />
+
+
+
+
+ <path
+ style="opacity:0.2;stroke:none;"
+ d="M69.103,55.203c-3.335,0-6.049,2.713-6.049,6.048v132.323c0,3.335,2.714,6.048,6.049,6.048h115.445 c3.335,0,6.049-2.713,6.049-6.048V61.251c0-3.335-2.714-6.048-6.049-6.048H69.103z"
+ id="path1327" />
+
+
+
+
+ <linearGradient
+ id="linearGradient1328"
+ gradientUnits="userSpaceOnUse"
+ x1="61.8799"
+ y1="125.064"
+ x2="189.4219"
+ y2="125.064">
+ <stop
+ offset="0"
+ style="stop-color:#616161"
+ id="stop1329" />
+
+
+
+
+ <stop
+ offset="1"
+ style="stop-color:#464646"
+ id="stop1330" />
+
+
+
+
+ </linearGradient>
+
+
+
+
+ <path
+ style="fill:url(#XMLID_3_);stroke:none;"
+ d="M67.928,52.854c-3.335,0-6.048,2.713-6.048,6.049v132.322c0,3.335,2.713,6.048,6.048,6.048h115.444 c3.336,0,6.05-2.713,6.05-6.048V58.903c0-3.336-2.714-6.049-6.05-6.049H67.928z"
+ id="path1331" />
+
+
+
+
+ <path
+ style="fill:#FFFFFF;stroke:none;"
+ d="M184.725,191.226c0,0.747-0.606,1.351-1.353,1.351H67.928c-0.745,0-1.351-0.604-1.351-1.351V58.903 c0-0.746,0.605-1.352,1.351-1.352h115.444c0.746,0,1.353,0.605,1.353,1.352V191.226z"
+ id="path1332" />
+
+
+
+
+ <linearGradient
+ id="linearGradient1333"
+ gradientUnits="userSpaceOnUse"
+ x1="233.4971"
+ y1="232.6162"
+ x2="51.0675"
+ y2="50.1867">
+ <stop
+ offset="0"
+ style="stop-color:#CECEDB"
+ id="stop1334" />
+
+
+
+
+ <stop
+ offset="0.691"
+ style="stop-color:#E9E9EF"
+ id="stop1335" />
+
+
+
+
+ <stop
+ offset="1"
+ style="stop-color:#FFFFFF"
+ id="stop1336" />
+
+
+
+
+ </linearGradient>
+
+
+
+
+ <path
+ style="fill:url(#XMLID_4_);stroke:none;"
+ d="M69.954,60.635c0,4.134,0,123.846,0,127.978c4.104,0,106.998,0,111.101,0c0-4.132,0-123.844,0-127.978 C176.952,60.635,74.058,60.635,69.954,60.635z"
+ id="path1337" />
+
+
+
+
+ <linearGradient
+ id="linearGradient1338"
+ gradientUnits="userSpaceOnUse"
+ x1="-654.9922"
+ y1="23.1128"
+ x2="-520.0903"
+ y2="23.1128"
+ gradientTransform="matrix(0 1 -1 0 148.7637 686.498)">
+ <stop
+ offset="0"
+ style="stop-color:#BDBDCF"
+ id="stop1339" />
+
+
+
+
+ <stop
+ offset="0.1923"
+ style="stop-color:#CCCCDA"
+ id="stop1340" />
+
+
+
+
+ <stop
+ offset="0.7201"
+ style="stop-color:#F1F1F5"
+ id="stop1341" />
+
+
+
+
+ <stop
+ offset="1"
+ style="stop-color:#FFFFFF"
+ id="stop1342" />
+
+
+
+
+ </linearGradient>
+
+
+
+
+ <path
+ style="fill:url(#XMLID_5_);stroke:none;"
+ d="M176.563,67.165c-3.289,0-98.536,0-101.823,0c0,3.266,0,85.131,0,88.396c3.287,0,98.534,0,101.823,0 C176.563,152.296,176.563,70.431,176.563,67.165z"
+ id="path1343" />
+
+
+
+
+ <path
+ style="stroke:none;"
+ d="M78.46,70.886c0,6.529,0,74.425,0,80.955c6.646,0,87.734,0,94.381,0c0-6.53,0-74.426,0-80.955 C166.194,70.886,85.106,70.886,78.46,70.886z"
+ id="path1344" />
+
+
+
+
+ <radialGradient
+ id="radialGradient1345"
+ cx="109.7783"
+ cy="116.7822"
+ r="66.1491"
+ fx="109.7783"
+ fy="116.7822"
+ gradientUnits="userSpaceOnUse">
+ <stop
+ offset="0"
+ style="stop-color:#B1E1FF"
+ id="stop1346" />
+
+
+
+
+ <stop
+ offset="0.1441"
+ style="stop-color:#AEDFFF"
+ id="stop1347" />
+
+
+
+
+ <stop
+ offset="0.2661"
+ style="stop-color:#A3D9FF"
+ id="stop1348" />
+
+
+
+
+ <stop
+ offset="0.38"
+ style="stop-color:#92CFFF"
+ id="stop1349" />
+
+
+
+
+ <stop
+ offset="0.489"
+ style="stop-color:#79C1FF"
+ id="stop1350" />
+
+
+
+
+ <stop
+ offset="0.5937"
+ style="stop-color:#5AAEFF"
+ id="stop1351" />
+
+
+
+
+ <stop
+ offset="0.6011"
+ style="stop-color:#57ADFF"
+ id="stop1352" />
+
+
+
+
+ <stop
+ offset="1"
+ style="stop-color:#0035ED"
+ id="stop1353" />
+
+
+
+
+ </radialGradient>
+
+
+
+
+ <path
+ style="fill:url(#XMLID_6_);stroke:none;"
+ d="M80.94,73.367c0,4.515,0,71.477,0,75.993c4.577,0,84.844,0,89.42,0c0-4.517,0-71.479,0-75.993 C165.784,73.367,85.518,73.367,80.94,73.367z"
+ id="path1354" />
+
+
+
+
+ <linearGradient
+ id="linearGradient1355"
+ gradientUnits="userSpaceOnUse"
+ x1="100.4385"
+ y1="122.5811"
+ x2="118.1257"
+ y2="104.8938">
+ <stop
+ offset="0"
+ style="stop-color:#FFFFFF"
+ id="stop1356" />
+
+
+
+
+ <stop
+ offset="1"
+ style="stop-color:#E9E9EF"
+ id="stop1357" />
+
+
+
+
+ </linearGradient>
+
+
+
+
+ <path
+ style="fill:url(#XMLID_7_);stroke:none;"
+ d="M96.774,113.736c0,6.907,5.6,12.508,12.507,12.508s12.506-5.601,12.506-12.508 s-5.599-12.507-12.506-12.507S96.774,106.829,96.774,113.736z"
+ id="path1358" />
+
+
+
+
+ <path
+ style="stroke:none;"
+ d="M78.46,137.866c0,1.127,0,12.845,0,13.975c6.646,0,87.734,0,94.381,0c0-1.13,0-12.848,0-13.975 C166.194,137.866,85.106,137.866,78.46,137.866z"
+ id="path1359" />
+
+
+
+
+ <path
+ style="stroke:none;"
+ d="M100.665,117.415h-8.486v25.92h8.486V117.415z"
+ id="path1360" />
+
+
+
+
+ <path
+ style="stroke:none;"
+ d="M108.007,127.737h-8.488v15.828h8.488V127.737z"
+ id="path1361" />
+
+
+
+
+ <path
+ style="stroke:none;"
+ d="M118.099,144.023h-11.01v-10.78h11.01V144.023z"
+ id="path1362" />
+
+
+
+
+ <path
+ style="stroke:none;"
+ d="M123.374,119.938h-6.65v19.499h6.65V119.938z"
+ id="path1363" />
+
+
+
+
+ <path
+ style="stroke:none;"
+ d="M139.892,144.023h-17.205v-15.599h17.205V144.023z"
+ id="path1364" />
+
+
+
+
+ <path
+ style="stroke:none;"
+ d="M145.396,108.927h-7.34v32.116h7.34V108.927z"
+ id="path1365" />
+
+
+
+
+ <path
+ style="stroke:none;"
+ d="M80.708,144.023h11.699v-19.817H80.708V144.023z"
+ id="path1366" />
+
+
+
+
+ <path
+ style="stroke:none;"
+ d="M154.571,143.104h-11.01v-23.396h11.01V143.104z"
+ id="path1367" />
+
+
+
+
+ <path
+ style="stroke:none;"
+ d="M170.683,145.87h-12.664v-17.662h12.664V145.87z"
+ id="path1368" />
+
+
+
+
+ <path
+ style="stroke:none;"
+ d="M169.024,142.416h-7.57v-25.003h7.57V142.416z"
+ id="path1369" />
+
+
+
+
+ <path
+ style="stroke:none;"
+ d="M130.486,138.059h-7.569v-25.005h7.569V138.059z"
+ id="path1370" />
+
+
+
+
+ <path
+ style="stroke:none;"
+ d="M80.938,144.482h88.086v-7.8H80.938V144.482z"
+ id="path1371" />
+
+
+
+
+ <path
+ style="fill:#FFF030;stroke:none;"
+ d="M94.063,121.743h2.016v-2.016h-2.016V121.743z"
+ id="path1372" />
+
+
+
+
+ <path
+ style="fill:#FFF030;stroke:none;"
+ d="M94.063,126.23h2.016v-2.016h-2.016V126.23z"
+ id="path1373" />
+
+
+
+
+ <path
+ style="fill:#FFF030;stroke:none;"
+ d="M94.063,135.205h2.016v-2.016h-2.016V135.205z"
+ id="path1374" />
+
+
+
+
+ <path
+ style="fill:#FFF030;stroke:none;"
+ d="M125.874,122.173h2.015v-2.016h-2.015V122.173z"
+ id="path1375" />
+
+
+
+
+ <path
+ style="fill:#FFF030;stroke:none;"
+ d="M125.874,131.147h2.015v-2.016h-2.015V131.147z"
+ id="path1376" />
+
+
+
+
+ <path
+ style="fill:#FFF030;stroke:none;"
+ d="M119.207,124.662h2.016v-2.015h-2.016V124.662z"
+ id="path1377" />
+
+
+
+
+ <path
+ style="fill:#FFF030;stroke:none;"
+ d="M119.207,129.149h2.016v-2.016h-2.016V129.149z"
+ id="path1378" />
+
+
+
+
+ <path
+ style="fill:#FFF030;stroke:none;"
+ d="M119.207,133.638h2.016v-2.016h-2.016V133.638z"
+ id="path1379" />
+
+
+
+
+ <path
+ style="fill:#FFF030;stroke:none;"
+ d="M102.489,132.554h2.015v-2.018h-2.015V132.554z"
+ id="path1380" />
+
+
+
+
+ <path
+ style="fill:#FFF030;stroke:none;"
+ d="M102.489,137.038h2.015v-2.015h-2.015V137.038z"
+ id="path1381" />
+
+
+
+
+ <path
+ style="fill:#FFF030;stroke:none;"
+ d="M82.507,128.463h2.015v-2.016h-2.015V128.463z"
+ id="path1382" />
+
+
+
+
+ <path
+ style="fill:#FFF030;stroke:none;"
+ d="M82.507,132.95h2.015v-2.017h-2.015V132.95z"
+ id="path1383" />
+
+
+
+
+ <path
+ style="fill:#FFF030;stroke:none;"
+ d="M149.356,128.747h2.015v-2.016h-2.015V128.747z"
+ id="path1384" />
+
+
+
+
+ <path
+ style="fill:#FFF030;stroke:none;"
+ d="M140.674,114.493h2.015v-2.017h-2.015V114.493z"
+ id="path1385" />
+
+
+
+
+ <path
+ style="fill:#FFF030;stroke:none;"
+ d="M140.674,118.979h2.015v-2.016h-2.015V118.979z"
+ id="path1386" />
+
+
+
+
+ <path
+ style="fill:#FFF030;stroke:none;"
+ d="M140.674,123.468h2.015v-2.016h-2.015V123.468z"
+ id="path1387" />
+
+
+
+
+ <path
+ style="fill:#FFF030;stroke:none;"
+ d="M140.674,127.954h2.015v-2.016h-2.015V127.954z"
+ id="path1388" />
+
+
+
+
+ <path
+ style="fill:#FFF030;stroke:none;"
+ d="M164.085,122.089h2.016v-2.016h-2.016V122.089z"
+ id="path1389" />
+
+
+
+
+ <path
+ style="fill:#FFF030;stroke:none;"
+ d="M164.085,131.065h2.016v-2.016h-2.016V131.065z"
+ id="path1390" />
+
+
+
+
+ <path
+ style="fill:#FFF030;stroke:none;"
+ d="M129.602,134.712v2.016h2.016v-2.016H129.602z"
+ id="path1391" />
+
+
+
+
+ </g>
+
+
+
+ <path
+ style="font-size:12;fill:none;"
+ d="M 223.0736 130.8574 L 170.8839 238.7107 L 80.48895 176.4417 L 132.6785 68.58834 L 223.0736 130.8574 z "
+ id="path1392" />
+
+
+
+ <g
+ id="g1393"
+ style="font-size:12;stroke:#000000;"
+ transform="matrix(0.291527,-0.602462,0.504941,0.347832,60.33992,183.4832)">
+ <path
+ style="opacity:0.2;stroke:none;"
+ d="M72.626,58.727c-3.336,0-6.049,2.713-6.049,6.048v132.323c0,3.335,2.713,6.048,6.049,6.048h115.445 c3.335,0,6.049-2.713,6.049-6.048V64.774c0-3.335-2.714-6.048-6.049-6.048H72.626z"
+ id="path1394" />
+
+
+
+
+ <path
+ style="opacity:0.2;stroke:none;"
+ d="M71.451,56.378c-3.335,0-6.048,2.713-6.048,6.049v132.322c0,3.335,2.713,6.048,6.048,6.048h115.445 c3.335,0,6.049-2.713,6.049-6.048V62.427c0-3.336-2.714-6.049-6.049-6.049H71.451z"
+ id="path1395" />
+
+
+
+
+ <path
+ style="opacity:0.2;stroke:none;"
+ d="M69.103,55.203c-3.335,0-6.049,2.713-6.049,6.048v132.323c0,3.335,2.714,6.048,6.049,6.048h115.445 c3.335,0,6.049-2.713,6.049-6.048V61.251c0-3.335-2.714-6.048-6.049-6.048H69.103z"
+ id="path1396" />
+
+
+
+
+ <linearGradient
+ id="linearGradient1397"
+ gradientUnits="userSpaceOnUse"
+ x1="61.8799"
+ y1="125.064"
+ x2="189.4219"
+ y2="125.064">
+ <stop
+ offset="0"
+ style="stop-color:#616161"
+ id="stop1398" />
+
+
+
+
+ <stop
+ offset="1"
+ style="stop-color:#464646"
+ id="stop1399" />
+
+
+
+
+ </linearGradient>
+
+
+
+
+ <path
+ style="fill:url(#XMLID_3_);stroke:none;"
+ d="M67.928,52.854c-3.335,0-6.048,2.713-6.048,6.049v132.322c0,3.335,2.713,6.048,6.048,6.048h115.444 c3.336,0,6.05-2.713,6.05-6.048V58.903c0-3.336-2.714-6.049-6.05-6.049H67.928z"
+ id="path1400" />
+
+
+
+
+ <path
+ style="fill:#FFFFFF;stroke:none;"
+ d="M184.725,191.226c0,0.747-0.606,1.351-1.353,1.351H67.928c-0.745,0-1.351-0.604-1.351-1.351V58.903 c0-0.746,0.605-1.352,1.351-1.352h115.444c0.746,0,1.353,0.605,1.353,1.352V191.226z"
+ id="path1401" />
+
+
+
+
+ <linearGradient
+ id="linearGradient1402"
+ gradientUnits="userSpaceOnUse"
+ x1="233.4971"
+ y1="232.6162"
+ x2="51.0675"
+ y2="50.1867">
+ <stop
+ offset="0"
+ style="stop-color:#CECEDB"
+ id="stop1403" />
+
+
+
+
+ <stop
+ offset="0.691"
+ style="stop-color:#E9E9EF"
+ id="stop1404" />
+
+
+
+
+ <stop
+ offset="1"
+ style="stop-color:#FFFFFF"
+ id="stop1405" />
+
+
+
+
+ </linearGradient>
+
+
+
+
+ <path
+ style="fill:url(#XMLID_4_);stroke:none;"
+ d="M69.954,60.635c0,4.134,0,123.846,0,127.978c4.104,0,106.998,0,111.101,0c0-4.132,0-123.844,0-127.978 C176.952,60.635,74.058,60.635,69.954,60.635z"
+ id="path1406" />
+
+
+
+
+ <linearGradient
+ id="linearGradient1407"
+ gradientUnits="userSpaceOnUse"
+ x1="-654.9922"
+ y1="23.1128"
+ x2="-520.0903"
+ y2="23.1128"
+ gradientTransform="matrix(0 1 -1 0 148.7637 686.498)">
+ <stop
+ offset="0"
+ style="stop-color:#BDBDCF"
+ id="stop1408" />
+
+
+
+
+ <stop
+ offset="0.1923"
+ style="stop-color:#CCCCDA"
+ id="stop1409" />
+
+
+
+
+ <stop
+ offset="0.7201"
+ style="stop-color:#F1F1F5"
+ id="stop1410" />
+
+
+
+
+ <stop
+ offset="1"
+ style="stop-color:#FFFFFF"
+ id="stop1411" />
+
+
+
+
+ </linearGradient>
+
+
+
+
+ <path
+ style="fill:url(#XMLID_5_);stroke:none;"
+ d="M176.563,67.165c-3.289,0-98.536,0-101.823,0c0,3.266,0,85.131,0,88.396c3.287,0,98.534,0,101.823,0 C176.563,152.296,176.563,70.431,176.563,67.165z"
+ id="path1412" />
+
+
+
+
+ <path
+ style="stroke:none;"
+ d="M78.46,70.886c0,6.529,0,74.425,0,80.955c6.646,0,87.734,0,94.381,0c0-6.53,0-74.426,0-80.955 C166.194,70.886,85.106,70.886,78.46,70.886z"
+ id="path1413" />
+
+
+
+
+ <radialGradient
+ id="radialGradient1414"
+ cx="109.7783"
+ cy="116.7822"
+ r="66.1491"
+ fx="109.7783"
+ fy="116.7822"
+ gradientUnits="userSpaceOnUse">
+ <stop
+ offset="0"
+ style="stop-color:#B1E1FF"
+ id="stop1415" />
+
+
+
+
+ <stop
+ offset="0.1441"
+ style="stop-color:#AEDFFF"
+ id="stop1416" />
+
+
+
+
+ <stop
+ offset="0.2661"
+ style="stop-color:#A3D9FF"
+ id="stop1417" />
+
+
+
+
+ <stop
+ offset="0.38"
+ style="stop-color:#92CFFF"
+ id="stop1418" />
+
+
+
+
+ <stop
+ offset="0.489"
+ style="stop-color:#79C1FF"
+ id="stop1419" />
+
+
+
+
+ <stop
+ offset="0.5937"
+ style="stop-color:#5AAEFF"
+ id="stop1420" />
+
+
+
+
+ <stop
+ offset="0.6011"
+ style="stop-color:#57ADFF"
+ id="stop1421" />
+
+
+
+
+ <stop
+ offset="1"
+ style="stop-color:#0035ED"
+ id="stop1422" />
+
+
+
+
+ </radialGradient>
+
+
+
+
+ <path
+ style="fill:url(#XMLID_6_);stroke:none;"
+ d="M80.94,73.367c0,4.515,0,71.477,0,75.993c4.577,0,84.844,0,89.42,0c0-4.517,0-71.479,0-75.993 C165.784,73.367,85.518,73.367,80.94,73.367z"
+ id="path1423" />
+
+
+
+
+ <linearGradient
+ id="linearGradient1424"
+ gradientUnits="userSpaceOnUse"
+ x1="100.4385"
+ y1="122.5811"
+ x2="118.1257"
+ y2="104.8938">
+ <stop
+ offset="0"
+ style="stop-color:#FFFFFF"
+ id="stop1425" />
+
+
+
+
+ <stop
+ offset="1"
+ style="stop-color:#E9E9EF"
+ id="stop1426" />
+
+
+
+
+ </linearGradient>
+
+
+
+
+ <path
+ style="fill:url(#XMLID_7_);stroke:none;"
+ d="M96.774,113.736c0,6.907,5.6,12.508,12.507,12.508s12.506-5.601,12.506-12.508 s-5.599-12.507-12.506-12.507S96.774,106.829,96.774,113.736z"
+ id="path1427" />
+
+
+
+
+ <path
+ style="stroke:none;"
+ d="M78.46,137.866c0,1.127,0,12.845,0,13.975c6.646,0,87.734,0,94.381,0c0-1.13,0-12.848,0-13.975 C166.194,137.866,85.106,137.866,78.46,137.866z"
+ id="path1428" />
+
+
+
+
+ <path
+ style="stroke:none;"
+ d="M100.665,117.415h-8.486v25.92h8.486V117.415z"
+ id="path1429" />
+
+
+
+
+ <path
+ style="stroke:none;"
+ d="M108.007,127.737h-8.488v15.828h8.488V127.737z"
+ id="path1430" />
+
+
+
+
+ <path
+ style="stroke:none;"
+ d="M118.099,144.023h-11.01v-10.78h11.01V144.023z"
+ id="path1431" />
+
+
+
+
+ <path
+ style="stroke:none;"
+ d="M123.374,119.938h-6.65v19.499h6.65V119.938z"
+ id="path1432" />
+
+
+
+
+ <path
+ style="stroke:none;"
+ d="M139.892,144.023h-17.205v-15.599h17.205V144.023z"
+ id="path1433" />
+
+
+
+
+ <path
+ style="stroke:none;"
+ d="M145.396,108.927h-7.34v32.116h7.34V108.927z"
+ id="path1434" />
+
+
+
+
+ <path
+ style="stroke:none;"
+ d="M80.708,144.023h11.699v-19.817H80.708V144.023z"
+ id="path1435" />
+
+
+
+
+ <path
+ style="stroke:none;"
+ d="M154.571,143.104h-11.01v-23.396h11.01V143.104z"
+ id="path1436" />
+
+
+
+
+ <path
+ style="stroke:none;"
+ d="M170.683,145.87h-12.664v-17.662h12.664V145.87z"
+ id="path1437" />
+
+
+
+
+ <path
+ style="stroke:none;"
+ d="M169.024,142.416h-7.57v-25.003h7.57V142.416z"
+ id="path1438" />
+
+
+
+
+ <path
+ style="stroke:none;"
+ d="M130.486,138.059h-7.569v-25.005h7.569V138.059z"
+ id="path1439" />
+
+
+
+
+ <path
+ style="stroke:none;"
+ d="M80.938,144.482h88.086v-7.8H80.938V144.482z"
+ id="path1440" />
+
+
+
+
+ <path
+ style="fill:#FFF030;stroke:none;"
+ d="M94.063,121.743h2.016v-2.016h-2.016V121.743z"
+ id="path1441" />
+
+
+
+
+ <path
+ style="fill:#FFF030;stroke:none;"
+ d="M94.063,126.23h2.016v-2.016h-2.016V126.23z"
+ id="path1442" />
+
+
+
+
+ <path
+ style="fill:#FFF030;stroke:none;"
+ d="M94.063,135.205h2.016v-2.016h-2.016V135.205z"
+ id="path1443" />
+
+
+
+
+ <path
+ style="fill:#FFF030;stroke:none;"
+ d="M125.874,122.173h2.015v-2.016h-2.015V122.173z"
+ id="path1444" />
+
+
+
+
+ <path
+ style="fill:#FFF030;stroke:none;"
+ d="M125.874,131.147h2.015v-2.016h-2.015V131.147z"
+ id="path1445" />
+
+
+
+
+ <path
+ style="fill:#FFF030;stroke:none;"
+ d="M119.207,124.662h2.016v-2.015h-2.016V124.662z"
+ id="path1446" />
+
+
+
+
+ <path
+ style="fill:#FFF030;stroke:none;"
+ d="M119.207,129.149h2.016v-2.016h-2.016V129.149z"
+ id="path1447" />
+
+
+
+
+ <path
+ style="fill:#FFF030;stroke:none;"
+ d="M119.207,133.638h2.016v-2.016h-2.016V133.638z"
+ id="path1448" />
+
+
+
+
+ <path
+ style="fill:#FFF030;stroke:none;"
+ d="M102.489,132.554h2.015v-2.018h-2.015V132.554z"
+ id="path1449" />
+
+
+
+
+ <path
+ style="fill:#FFF030;stroke:none;"
+ d="M102.489,137.038h2.015v-2.015h-2.015V137.038z"
+ id="path1450" />
+
+
+
+
+ <path
+ style="fill:#FFF030;stroke:none;"
+ d="M82.507,128.463h2.015v-2.016h-2.015V128.463z"
+ id="path1451" />
+
+
+
+
+ <path
+ style="fill:#FFF030;stroke:none;"
+ d="M82.507,132.95h2.015v-2.017h-2.015V132.95z"
+ id="path1452" />
+
+
+
+
+ <path
+ style="fill:#FFF030;stroke:none;"
+ d="M149.356,128.747h2.015v-2.016h-2.015V128.747z"
+ id="path1453" />
+
+
+
+
+ <path
+ style="fill:#FFF030;stroke:none;"
+ d="M140.674,114.493h2.015v-2.017h-2.015V114.493z"
+ id="path1454" />
+
+
+
+
+ <path
+ style="fill:#FFF030;stroke:none;"
+ d="M140.674,118.979h2.015v-2.016h-2.015V118.979z"
+ id="path1455" />
+
+
+
+
+ <path
+ style="fill:#FFF030;stroke:none;"
+ d="M140.674,123.468h2.015v-2.016h-2.015V123.468z"
+ id="path1456" />
+
+
+
+
+ <path
+ style="fill:#FFF030;stroke:none;"
+ d="M140.674,127.954h2.015v-2.016h-2.015V127.954z"
+ id="path1457" />
+
+
+
+
+ <path
+ style="fill:#FFF030;stroke:none;"
+ d="M164.085,122.089h2.016v-2.016h-2.016V122.089z"
+ id="path1458" />
+
+
+
+
+ <path
+ style="fill:#FFF030;stroke:none;"
+ d="M164.085,131.065h2.016v-2.016h-2.016V131.065z"
+ id="path1459" />
+
+
+
+
+ <path
+ style="fill:#FFF030;stroke:none;"
+ d="M129.602,134.712v2.016h2.016v-2.016H129.602z"
+ id="path1460" />
+
+
+
+
+ </g>
+
+
+
+<g
+ id="g2009"
+ transform="matrix(2.357251,0,0,2.832578,34.95087,61.80306)">
+ <defs
+ id="defs232" />
+
+
+ <sodipodi:namedview
+ id="namedview2013" />
+
+
+ <g
+ id="Layer_1"
+ stroke="#000000">
+ <path
+ id="path170"
+ fill="none"
+ stroke="none"
+ d="M3.621,36.109l11.775-25.814c0.321-0.971,3.194-2.688,4.462-2.461l22.103,1.858 c-0.26-0.156-0.566-0.274-0.91-0.335L19.46,7.218c-1.269-0.226-4.141,1.491-4.462,2.461L3.224,35.494 c-0.183,0.553-0.011,1.106,0.406,1.521C3.528,36.729,3.519,36.42,3.621,36.109z" />
+
+
+ <g
+ id="Layer_2">
+ <path
+ id="path241"
+ fill="none"
+ stroke="none"
+ d="M48,48H0V0h48V48z" />
+
+
+ </g>
+
+
+ <g
+ id="g171" />
+
+
+ <linearGradient
+ id="path205_1_"
+ gradientUnits="userSpaceOnUse"
+ x1="-235.5874"
+ y1="318.7158"
+ x2="-234.9869"
+ y2="318.1155"
+ gradientTransform="matrix(0.3069 0 0 -0.3101 81.6819 119.6117)">
+ <stop
+ offset="0"
+ style="stop-color:#FFFFFF"
+ id="stop160" />
+
+
+ <stop
+ offset="0.3726"
+ style="stop-color:#FDFDFD"
+ id="stop161" />
+
+
+ <stop
+ offset="0.5069"
+ style="stop-color:#F6F6F6"
+ id="stop162" />
+
+
+ <stop
+ offset="0.6026"
+ style="stop-color:#EBEBEB"
+ id="stop163" />
+
+
+ <stop
+ offset="0.68"
+ style="stop-color:#DADADA"
+ id="stop164" />
+
+
+ <stop
+ offset="0.7463"
+ style="stop-color:#C4C4C4"
+ id="stop165" />
+
+
+ <stop
+ offset="0.805"
+ style="stop-color:#A8A8A8"
+ id="stop166" />
+
+
+ <stop
+ offset="0.8581"
+ style="stop-color:#888888"
+ id="stop167" />
+
+
+ <stop
+ offset="0.9069"
+ style="stop-color:#626262"
+ id="stop168" />
+
+
+ <stop
+ offset="0.9523"
+ style="stop-color:#373737"
+ id="stop169" />
+
+
+ <stop
+ offset="0.9926"
+ style="stop-color:#090909"
+ id="stop170" />
+
+
+ <stop
+ offset="1"
+ style="stop-color:#000000"
+ id="stop171" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ </linearGradient>
+
+
+ <path
+ id="path205"
+ fill="url(#path205_1_)"
+ stroke="none"
+ d="M10.011,20.379c-0.109-0.062-0.233-0.1-0.367-0.1 c-0.421,0-0.763,0.346-0.763,0.771c0,0.115,0.027,0.223,0.072,0.321C9.106,20.878,9.509,20.498,10.011,20.379z" />
+
+
+ <path
+ id="path206"
+ opacity="0.36"
+ fill="#999999"
+ stroke="#999999"
+ stroke-width="1.5343"
+ stroke-miterlimit="1.2275"
+ d=" M6.653,25.182c0,11.198,8.983,20.275,20.064,20.275c11.082,0,20.064-9.076,20.064-20.275c0-11.198-8.982-20.275-20.064-20.275 C15.636,4.906,6.653,13.984,6.653,25.182z" />
+
+
+ <path
+ fill="#990000"
+ stroke="none"
+ d="M2.046,22.701c0,11.731,9.449,21.275,21.064,21.275c11.615,0,21.065-9.544,21.065-21.275 S34.726,1.425,23.11,1.425C11.496,1.426,2.046,10.97,2.046,22.701z"
+ id="path197" />
+
+
+ <linearGradient
+ id="path213_1_"
+ gradientUnits="userSpaceOnUse"
+ x1="-232.3296"
+ y1="353.9814"
+ x2="-137.7065"
+ y2="259.358"
+ gradientTransform="matrix(0.3069 0 0 -0.3101 81.6819 119.6117)">
+ <stop
+ offset="0"
+ style="stop-color:#FB7462"
+ id="stop199" />
+
+
+ <stop
+ offset="1"
+ style="stop-color:#FC2829"
+ id="stop200" />
+
+
+
+
+
+ </linearGradient>
+
+
+ <path
+ id="path213"
+ fill="url(#path213_1_)"
+ stroke="none"
+ d="M3.046,22.701c0,11.198,8.983,20.275,20.064,20.275 c11.082,0,20.065-9.077,20.065-20.275S34.192,2.425,23.11,2.425C12.03,2.426,3.046,11.503,3.046,22.701z" />
+
+
+ <radialGradient
+ id="path239_1_"
+ cx="-141.856"
+ cy="240.4365"
+ r="94.1356"
+ fx="-141.856"
+ fy="240.4365"
+ gradientTransform="matrix(0.3069 0 0 -0.3101 81.6819 119.6117)"
+ gradientUnits="userSpaceOnUse">
+ <stop
+ offset="0"
+ style="stop-color:#000000"
+ id="stop206" />
+
+
+ <stop
+ offset="1"
+ style="stop-color:#FF3333"
+ id="stop207" />
+
+
+
+
+
+ </radialGradient>
+
+
+ <path
+ id="path239"
+ opacity="0.4"
+ fill="url(#path239_1_)"
+ stroke="none"
+ d="M41.881,18.448c-2.748-1.396-5.852-2.184-9.139-2.184 c-3.571,0-6.928,0.929-9.846,2.561h15.301v0.446h0.387v8.345H14.529c-1.383,2.778-2.163,5.917-2.163,9.239 c0,1.053,0.079,2.086,0.229,3.097c2.839,1.556,6.093,2.439,9.549,2.439c11.082,0,20.065-9.078,20.065-20.276 C42.21,20.864,42.097,19.638,41.881,18.448z" />
+
+
+ <g
+ id="g214">
+ <path
+ id="path215"
+ fill="#B11C1D"
+ stroke="none"
+ d="M39.549,28.201H6.989v-8.344h32.56V28.201z" />
+
+
+ <g
+ id="g216">
+ <path
+ id="path217"
+ fill="#FB9A86"
+ stroke="none"
+ d="M39.162,27.812H6.672v-8.4h32.49V27.812z" />
+
+
+ <path
+ id="path218"
+ fill="#FFFFFF"
+ stroke="none"
+ d="M39.162,27.812H7.059v-7.913h32.103V27.812z" />
+
+
+ <linearGradient
+ id="path225_1_"
+ gradientUnits="userSpaceOnUse"
+ x1="-190.8691"
+ y1="300.7178"
+ x2="-190.8691"
+ y2="330.2731"
+ gradientTransform="matrix(0.3069 0 0 -0.3101 81.6819 119.6117)">
+ <stop
+ offset="0"
+ style="stop-color:#FFFFFF"
+ id="stop218" />
+
+
+ <stop
+ offset="1"
+ style="stop-color:#CCCCCC"
+ id="stop219" />
+
+
+
+
+
+ </linearGradient>
+
+
+ <path
+ id="path225"
+ fill="url(#path225_1_)"
+ stroke="none"
+ d="M7.385,22.834c0-1.293,1.038-2.342,2.318-2.342H38.28 c0.313,0,0.61,0.063,0.882,0.177v-0.771H7.059v7.913h0.326V22.834z" />
+
+
+ </g>
+
+
+ </g>
+
+
+ <linearGradient
+ id="path232_1_"
+ gradientUnits="userSpaceOnUse"
+ x1="14.1377"
+ y1="12.6128"
+ x2="4.2627"
+ y2="2.7378">
+ <stop
+ offset="0"
+ style="stop-color:#FB7462"
+ id="stop225" />
+
+
+ <stop
+ offset="1"
+ style="stop-color:#FFFFFF"
+ id="stop226" />
+
+
+
+
+
+ </linearGradient>
+
+
+ <path
+ id="path232"
+ fill="url(#path232_1_)"
+ stroke="none"
+ d="M8.45,18.411c4.288-7.016,11.965-11.692,20.725-11.692 c2.281,0,4.488,0.318,6.582,0.911c-3.458-3.244-8.089-5.229-13.18-5.229C11.875,2.401,3.2,11.168,3.2,21.983 c0,2.837,0.598,5.53,1.671,7.965c0.094-1.738,0.369-3.429,0.802-5.052v-6.485H8.45z" />
+
+
+ </g>
+
+
+</g>
+
+</svg>
diff --git a/libimage/error.xpm b/libimage/error.xpm
new file mode 100644
index 0000000..f704a56
--- /dev/null
+++ b/libimage/error.xpm
@@ -0,0 +1,454 @@
+/* XPM */
+static char * error_xpm[] = {
+"491 386 65 1",
+" c None",
+". c #5E605D",
+"+ c #525350",
+"@ c #454744",
+"# c #717271",
+"$ c #45586D",
+"% c #8F908F",
+"& c #9C9DA7",
+"* c #CFD0DA",
+"= c #C6C6C6",
+"- c #AAAAAA",
+"; c #FBABA0",
+"> c #2F2F2C",
+", c #F5F2F6",
+"' c #FDFFFC",
+") c #985053",
+"! c #E9E7EB",
+"~ c #DDDEE8",
+"{ c #B1DFFE",
+"] c #D8D9D8",
+"^ c #0F1929",
+"/ c #000100",
+"( c #1A1B17",
+"_ c #0C2154",
+": c #266BF5",
+"< c #2F79F6",
+"[ c #3A8CFA",
+"} c #4B78D3",
+"| c #4BA1FD",
+"1 c #3D6894",
+"2 c #2E619E",
+"3 c #5A90D1",
+"4 c #135AF2",
+"5 c #084BF1",
+"6 c #003AEF",
+"7 c #62B3FF",
+"8 c #6490BD",
+"9 c #84B1DB",
+"0 c #9B0400",
+"a c #C12625",
+"b c #96292D",
+"c c #C44842",
+"d c #FC5F52",
+"e c #DC7C7A",
+"f c #FC9687",
+"g c #FB8474",
+"h c #FC705E",
+"i c #80C6FF",
+"j c #D5302D",
+"k c #215EB9",
+"l c #EF9C92",
+"m c #8FADC7",
+"n c #0A2B88",
+"o c #EA3C38",
+"p c #FC544A",
+"q c #97BEDB",
+"r c #9AD3FF",
+"s c #5B5712",
+"t c #736B15",
+"u c #49460E",
+"v c #FC4C45",
+"w c #F4E52D",
+"x c #A49A1B",
+"y c #F93D39",
+"z c #FD2B2C",
+" .......................................+..+..+.+.+.+.++.++.++.+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++@+++@++@++@++@+@+@+@+@@+@@+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ",
+" ..................+...+...+.+.+.++.+++.+.+.+..+.+.+.++.+.++.++.++..+.+.++.+.++.++++++++++++++++++++++++++++++++++++++@++@++@+@+@+@+@+++@++@++@+@+@+@+@+@+@+@@+@@+++@++@++@+@+@+@+@@+@@@+@@@@@@@@@@@ ",
+" ......................+...+........+...+...+.++...+.++.+.++.++.++.++++.++.++++.+++.++.+.++++++++++++++++++++++++@++@+++++++@++++++++++@+@+@++@+@+++@+@+@+@+@++@+@+@@@+@@@@@@@@@@@@@@@@@+@@@@@@@@@@@@@@ ",
+" ....#$..$..$..$+.$..$+.$..$..$+.$+.$..$..$+.$..$++$.+$.+$+.$+.$+.$++$.+$++$.+$++$++$++$++$++$++$++$++$++$++$++$++$++$++$+@$++$+@$+@$@@$+@$+@$+@$+@$+@$+@$@+@$@@$@@$@@$@@$+@$+@$+@$+@$@@$@@+@$+@$@@$@@@@@ ",
+" ......#+#............#+.............+.+.+...+.+...+.+.+..+.++.++.+.+++.+.++++.+++.+++++++++++++++++++++++++++++@++@++@++++@++@++@+++++@++@++@+@@@+@+@@@@@@$@@@@+@@+@+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ",
+" .#$.......+#+#+#+#+....+#+.+..+.+.+.+.......+....+..+.+.++.+.++.++.++.+++++++++.++++++++++++++++++++++++++++@++++++++++++@+++++@++@+@+++@+++@+++++@+@++@++@+@++@+@+@@@@@+@@+@+@@@@@@@+@@@@$@@@@@@@@@@@@@@@@ ",
+" ....#+#+#+#+.$.+.$..+$#+.$#+#$..$.#$.+$.+$+#$++$.+$.+$+.$+.$+.$+.$+.$+.$.+$.+$++$+.$++$.+$++$++$++$++$++$++$++$++$++$++$++$@+$++$@+$+@$++@+$@+$@@$@++$@+@@+@+@@$@@$@+$@@$@@$@@$@+$@@$@@@$@@@@@$@@@$@@@$@@@@ ",
+" +#+#+.$+%&************=***=*=*=**==*=**=***=*=**=**=**=**=**=**=**=*==*==**=**=**=*==*==**=**=*==**==*============================*===*-**==*===*==**==*****=**=====*==**==*;**=-=====**==-**===*==%$@@@@@@@> ",
+" ....#.##,,,,,'',,,,,,',,,,,,,,,,,,,,',,',,,,,',,',,',,'',,,'',','',',,'',''','',',,''''','''''''',',,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,',,,,,,,,,,,,,,,,,,,,,,,-@@@@@@@> ",
+" .#$+.++&''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''',''''''',,,''''''''''''',,,,,,''''',,,,,',,,+>$@@@@> ",
+" ...#.#+-'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''',@@@@@$@> ",
+" ..$)...-'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''',@@@@@@@> ",
+" ...$#+.-''''''',',,,,,,'',',',',',,',,',,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,!,,,,,,,!,,!,,!,!,!,!,!,!,!!,!!,!!,!!,!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!''''''@@@@@@@> ",
+" #......-''''',,','''''',,',',',,,'','',,',',''',,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,!,,!,,!,!,!,!,!!!!!!!!!!,!!!!!!!!!!,!!!!,!!!,!!!!!,!!!,~,!!!,~,!,!!,!!,!!,!!,!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!,'''',$>$@@@@> ",
+" ....#+.&'''''',',,,,,',',,,',',,,,,,,,,,,,,,,,,''''''''''''''''!''!''!'!'!'!'!,!,!'!'!'!,!'!'!'!,,,'!','!,!,,!,!,!,!,!!,,!!,!,!!,!,~!,!,!!!~,~~,!~~~,~~,~~,~~,~~,~,~,~,~,~,~,~,~!~~~~~~~~~~,~!!''''',+@@@@$@> ",
+" +#$....-''''''',''''',',',',',,',','''''',''',,!,!!,!!,!!!!,!!,,!',,!'!'!,!'!'!'!'!,!'!,!,!,!,!,!!!!!!!!,!,!,!,!!,!,!,~,~!,!!,~,~,~,~!,~,~,,~,~,~,,,~,~,~,~,~,~,~~,~~,~~!~!~!~!~!,,,,,,,,~!~!~!,''',,@@@@@@@> ",
+" ...#+#+-'''',,',,,,',,,,',,,',',','!!,!,,'!,!',,',,,',,',',,',,,,!,,,,!,!'!,,,!'!!,,!,,'!,!'!!'~',,'!,!,!!'~'~'~'~!'~,!,,~'~'~,,,~,,,~,!~,~,,~,~,~~,~,~,~,~,~,~,~,~,~,~,~,~,~,~,~~~~~~~~~,~,~!!''''',@@@@@@@> ",
+" ...$...-'''''',,''','',',',',,',,!',',',,,'!','~',,,,,,,,,,,!'!'!'!'!',',,,'!!'!,!'!'!!!'!!'~'~,!!!!!'~'~'~,!~'~!,!~'~,~'~,~,~,~,~,~!,~,~,~,~,~,~,~,~,~,~,~,~~,~,~,~,~,~,~,~,~!~!!!!!!,,~,~!~!~''''',>$@@@@@> ",
+" .#+#...&''''''',,',,,',,'',,,','','!,,,,,',,,,,,,',,',,'~',,,,,,',~'!~,~'!,!'!,'!,!~'~'!!'!~,,~'~''!'~~'~,~'~'~'!~'~,~'~,~'~'~,~,,~'~,~,,~,~,~,~,~,~,~,~,~,~,!~!~,~!~,~,~,~!~,~,~~,~~~~~!~,~,~!''''''@@@@@$@> ",
+" .......-''''',,'','',',',,,',',,,,,',''!',!'!',',,,'~,,',',,'~'~,',,'','!'~'~'~'!','~,!'~'~','~'!~'~,,,~'!,~'~,~'~'~,~'~'~,~,~,~,~,~,~,~,~,~,,~,~,~,~,~,~,~,~,~,~!~,~,~!~,~,~!~!,~~,!,!,~!~!!~!,'''',@@@@@@@> ",
+" ...$#+.-'''''',',,,,',',','!,,',,',,~',,'~',,,~','~'',,,~'~',,,',~'~,~'~','~',~'~,~''~'~'~'~~'~'~,~'~'~,~'~,~'~'~,~'~,~'~'~'~,,,~'~,~,~,~,~,~~,~,~,~,~,~,~!~,~,~,~,~!~,~,~!~,~,~~,!~~~~~,~!~!!!''''',@@$>@@@> ",
+" +#.....-''''',','',',',,',,','!',,,''!',''~',''~,,'~',',','~'~',,,,,',,,,~,'~''~'!'~'~,'~'~'~'!,'~'~,~'~',~'~'~~'~,~'~'~,~~,~,~,~,~,~,~,~,,~,~,~,~,~,~,~,~,~!~,~,~,~,~,~!~,~!~!,~!~,!,,~!~,~~~!,''',,@@@$@@+> ",
+" ...#+..&''''',,,,',,!~~~~~~~~~~~~~~~~~~*~~~~~~~{~~~~~{~~~{~~~~~{~~~{~~~~{]~~~~~~~~~~~~~~~~~~~~~*~~~~~~{~~{~~*~~{~~~~~~~~~~~~~{~~~~~{~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*~~~~**~~~!~,!!!''''',@@@@@@@> ",
+" #+.+#..-'''''''',','~*************{*****{***{****{*****************************{********{*************************{********************{****{*************{*************************~***~,~~~!~''''',@@@@@@@> ",
+" .$#+#+.-''''',,',','~~*{***{*{******{**********************{************~*{******{**{*****{***{**{*********{********{**{*{**~***{**~*****{*********{**************{************~*****{*~~!~,~!~''''',@@@@@$@> ",
+" .)$....-'''''',,',,'~*************************~***************~**~***********************************~******~**~**************~****************{*************{*******{**{***~***********~,~!!!~''''',@$@@@@@> ",
+" ...#...&''''',',',',~~***~****~****~*~*~*~**~***~*~*{*~*~*~*~*****~*{*~****~******~**~**~**~*~*~**~***~*~****~***~**~*~*~*~****~*~**~*****~**~**~**~*{*~*~*~**~*~**~**~**~*{**{**{*~**~*~!!~~,~''''',@>@@@@@> ",
+" #...$..-'''''',',,,'~*~****~****~*************~****~**********~*{***~***~***~*~*~********~******~***~*****~****~************~*****~**~*~***~********~************~**********~***~***~**~~~!!!~~''''',@$@@@@@> ",
+" ...#+#+-''''',,,',',,**~**&^>^>^>^>^>^>^>^>^>^>^>^^>^>^>^>^>^>^>>^>^>^>^>^>^^>^^>^>^>^>^^>^>^>^^>^>^>^^>^>^>^^>^>^>^>^>^>^>^>^>^>^>^^>^>^>^>^>^>^^>^^>^>^>^>^>^^>^>^>^>^>^>^^>^>^>~**~**~,~~!!~''''',@@@@@$@> ",
+" #+.....-''','',,,,,,~~****&////////////^/////////////////////////////////////////////////////////////(/////(///////////////////////////////////(////////^////(///////////////////^****~~~,!!~,~''''',@@@@@@@> ",
+" ..#+#+.-,'''',',',',,*{***&///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////~*****~~~,~~~''''',@@@@@@@> ",
+" ...$...-''''',,,,',,~~****#//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////^**{~*~,~!~!!!'''',,$>$@@@@> ",
+" ..#.#+.-,''''',',,,,~~*~*~&///_:<:<<<<<<<<<[<[[[[[[[[[[[[[[[[}|[}[[[|[|[|}|[||}|[|}11232[|[|[}|[[[[[[[[[[[<[[<[<[<<<<<<<<:<::::::::4:44444454545555556556666666666666666666666////~****~~!~,~~~''''''@@@@@$@> ",
+" #+.+...-'''''',,',',~*****%///_<<<<<[<[[[[[[[[[[[[[[[|[|}||||[||||||||||||||||||1++++@+++1[|||[|||[|[}[[[[[}[[[[}<[[[<<<<<<<<<<:::::::::44444444455555565566666666666666666666///^**~*~*,~~~!~!,''',,@@@@@@@> ",
+" ..#$#..-,'''',',,,,,,~****&///_<<<<<[<[<[[[[[[[[[[|}[|[|[|[|||||||||||||||||||3@@@@@@@@@@+$2|}|}|[||[|[|[|[[[[[[[[<[[<[<<<<<<<:<::::::4:4:444445455555556666666666666666666666////~***~*~~,!~!!''''',@@@@@@@> ",
+" #+.....-''''',,'',,,~**~**%//(_<<[[<[<[[[[[[[[}[|[|[|[||||||[|||||||||||||||3$@@$@@@@@@@@@@@13}3}|[||[||[[|[[[[[[[[[[[<[[[<<<<<<<<:::::::4:44444445555565556666666666666666666///^*~*~*~!!~!!~!,''',,@@@@@@@> ",
+" ..#+.$.-''''',,,',,,,~****&///_[<[<[[[[[[[[[[|[|[|||||[||[|||||||||||||7||3$@@@@@@@@@@@@$@@@@22323}|||[||[|[|[[[[[[<[[[<<<<<<<<:<::<:::::4:44444454555556566666666666666666666////*~***~~~~!~!!''''',@@$>$@+> ",
+" $.+#...-''''',',,',,~**~**%//(_<[<[<[[[[[}[[|[[|[|[|[|||||||||||7||77|7|71+@+@@@@@@$@@@@@@@@@@122222}|||||[||[|[[[[[[[[[[[[[<<<<<<<:::::::4::444444445555555666666666666666666///^**~~~*!!~!!~!,''',,@@@@@@@> ",
+" .#+#+..&''''',,,,,,,,~*~**#///_[[[[[[[[[[|[|[|||[||||||||7|7|7||7|7|7|78@@@@@@@@@@@@@@@@@@@@@@$211213}|[||[||[|[|[}[[[[[[<<[<[<<<<<<:<:::::4:444445454555566666666666666666666////~***~~~~,~~!!''''',@@@@@@$> ",
+" #+#+#..-''''',',',,,~*~*~~%//(_[<[[[[[[[[[|[|[||||||||7|||7||7|777|778$@@+@$@@@$@@@@+$@@@@$>$@>12122223||||||[||[|[|[[[[[[[[[<[<<<<<<<:::::::::4444544555555566666666666666666///^*~*~*~!~~,~~!,''',,@@@@@@@> ",
+" ...+#+.-''''',,,,,,,,~****&///_[[[[[}|[||||||||||||||||77|77777|7773$@@@@@@@@@@@@@$*,,&+@@@@@@@$11112233|||||||[|[|[[}[[[[[<[<[<<<<<<<<:::::444:444445455555656666666666666666////~*~**~~~~!~!!''''',@@@@@@@> ",
+" +#$....-''''',','',,~*~~**%//(_[[[[[[[[|[[|[|||||||7|7|7|7|||777771@+@@+@@$@@+>$+-,',,,&@@@@@@>>1213112}3|||||||||[|[|[[[[[[[[<[[[<<<<<<<:::::::4:4444445555556666666666666666///^*~*~~~!!!!~~!,'''',>$@@@@@> ",
+" ...#+#+&''''',,,,',,,~*~*~%///_|[[|[||[||||||||||7|7|7|777777|738@@@@@@@@@@@@$)%,,'''',,+@@@@@@@$111211333|||||[||||[|[|[[[[[[[<<<<<<<<:<::::::4:44444545455655666666666666666////~~***~~~~~!!!''''''@@@@@$@> ",
+" #..$...-''''',',,,,,~*~~**%//(_[|[[|[|[||[||||7|7|7|7777|777778$@@@$+@$@+@@@@#!,'''''',,-$>$@@@@>$11321233||||||||[|||[|[|[[[[[[[[<[<<<<<<<:::::::4444444555555666666666666666///^**~~~~~,,~~~!,'''',@@@@@@@> ",
+" ..#)$#+-''''',',,,,,,~*~*~%///$[[|[||||||||||||77|77|77777778$@@@@@@@@@@@@@#*,'''''''''',#@@@@@@@$1111213337|7||||||[|[|[[[}[[[[<[[<<<<<<<:<::::4:4:44454455555566666666666666////~~**~~~~~!!~!''''',@@@@@@@> ",
+" .+.....-''''',,',,,,~~~*~*%///_[|[|[|[||||7|77|7|77777777731+@+@+@+@@@@$@+*,'''''''''''''*@$>$@@>>$11821233|7|7|||||||||||[|[[[[[<[[[[<<<<<<::::::4:44444445555556666666666666///^~*~~*~!!~!~!~'''',,$>$@@@@> ",
+" .......-''''',,,',,,~~~*~*&///$[[|||||||7|||77|77|77777771++@@@@@@$@@@@)&,,''''''',,''''',%@@@@@@@>111111333777|7||||||[|[|[|[[[[[[<[<<<<<<:<::::::4:4444545555556666666666666////~*~~*~~~!~,~~''''',@@@@@$@> ",
+" ..#$...-,'''''~,,,,,~~~~**%///_|||[|||||||77|77777777778++@@++@$+>)@$@%~,,'''''',~!!,'''',!+@@@@@@>$11311133||7|77||||||||[|[|[[[[[[[[[[<<<<<<<:::::4:444445455555666666666666///^~~*~~~~,~~~!~''''',@@@@@@@> ",
+" .......-''''',',,,,,,~*~~*%///$[||||||7|77|7|77|777778$@@@@@@@@@@$>@#*,''''''',!!~!!,''''',-@@$@@@@>$111111337777|7||||||||[|[|[[}[[<[<<<<<<<:<:::::4:444444555555566666666666////*~~*~~~~,~,~~''''''@@@@@@@> ",
+" #.+#...-''''''~',,,,~~~*~*%///_|||||7||77|7777777773$@@@++@$+@+@@+.*,'''''''',!!!!!~~,'''',,.@>$@@@$>111311333|7|7777|||||||||||[[[[[[[[[[<<<<:<::::::4:4444445555566666666666///^~*~~*~,~~~~!~'''',,@@@@@@@> ",
+" ...$...&''''',',',,,,~~~~~%///$[|||||7|77777777777$.++@@@@@@@@$@+-,''''''',,~!!~!~!!!!''''''*@@@@@@@@$11118133777|7|7|7|||||[|[[|[[[[[[<<[<<<<<<:<:::4:4:445454555556666666666////~~~~~*~~,~~,~''''',@@$>$@@> ",
+" +#.#...-''''''~',,,,~~*~**%///_||||7|7777|7|77778++@+@++$@+@@@+&,,''''''',!~!~,~,~!!!~,''''',%@@@@@@@>111111833777777|7|||||||||[|[[[[[[<}<[<<<<<::::::44444454555555666666666////~~*~~~,~~!~~!,''',,@@@@@@+> ",
+" .+...$.-''''',,,,,,,,~~~~~%///$|7|7|7|7|7777778+@@+$@@@@@+@$@#!,''''''',!!~!!!~!~,~~!~!''''''!+@$>@@$@$1181113377|7|7|7|7|||||[||[|[}[[[[[<<<<<<:<<::::::4:4445455556566666666////~~~~~~~~,~,~~''''',@@@@@@@> ",
+" #+#....-'''''',,',,,~~~~**%///_|||77|77777779$@++@@@@$+@+@@#*,''''''',,!~!,~~,~,~!!~,~!!''''',&@@@@@@>@$111811337777777|7||||||||[||[[[[<[[[[<<<<:<::4:::444445455555566666666///^~~*~~~,~~!~!!'''',,@@@@@@@> ",
+" 0000000000000000 .......&''''',,'~',,,~~~~~&///$77|77|777777$++@@$@+@@@@+@)-,'''''''',~!!!~~,!~!~,~!!~!!~,''''',.@@@@@@@$$11818833777777777|7||[|[|[|[|[[[[[<<<<<<<<:<:::4:4:444545555656666666////*~~~*~~~!!~~~''''',@@@@$@@> ",
+" 0000000000000000000000000000000000000 #$#+#..-''''',,',,,,~*~~~*%///$|7|77777778++@@++@+@++@@$&,'''''''',!~!,~!,~~,~,~,~~!~!!!!''''',*$@@@@@@@$11111138777777|7|7||7||||[|[|[[[[[[[[}<<<<<::::4::4444445555556666666////~~~~~~,~~!!!!,''',,@@$>@@@> ",
+" 00000000000000000000000000000000000000000000000000 .+#+...-''''''~',,,,,~~*~~%///$||77|7778@@@+$@@+@+@+@$%!'''''''',~,~!!~,~~,!~!~!~,!~,~~!~,''''''#@@@$@@@@11181883377777777|7|||||||[|[|[[[[[<<<<<<<:<:::::4:444445455556566666////~~~~~~~!!~~!~''''',@@@@@@$> ",
+" 000000000000000000000000000000000000000000000000000000000000 ..+#...-''''',,,,,,,,~~~~*%///_7777778$@+++@+@$+@@$@#!,''''''',,!!~,~,~!,~~,~,~,~~,~!!!!!!''''',!$@>@@@@@$$181118877777777|77|7|||||[|[[[[[[[[<}<<<<<<:::::4:44454555556666666////~~~*~~~~!!~!~'''',,@@@@@@@> ",
+" 00000000000000000000000000000000000000000000000000000000000000000000 .#+$...&'''''',,,,,,~~~*~~%///$|7|73$+++@+@+@@@@+@)*,,'''''''!~!!!!~!~,~~,!~!~,~,~~!~~~~!~,''''''%@@@@$>@@111818838777777777|7|||||||[|[|[[[[[<<<<<<<:<::::4:44444545555566666////~~~~~~!!~!!~~''''',@@@@@@@> ",
+" 0000000000000000000000000000000000000000000000000000000000000000000000000000 ...#...-'''''~'',,,,,~~~~*%///$7778++++@+@$@++@$+&,,''''''',!!,~,~,~,~!,~~,~,~!~**~!!,!!!~!,''''''$@@@@@@@@$1811183377777|77|777||||||[|}[[[[[[[}<<<<:<:::::4:4444455555565666////~~~~~~~!~~!~!,''',,@$@@@@+> ",
+" 000000000000000000000000000a0aabaccccccccdcacaaa0000000000000000000000000000000000 ..+#+#+-''''',,,',,,~~~~~~%///_78@@+@$@++@+@+@+&,'''''''',~!!~!!~,~,~,~~,!~!~,~*~**~~~~~,~!!''''''=@@@@@@@@$118188837777777777||7|||||[|[|[[[[<[<<<<<<:<:::4:44445454555566666////~~~~~~!~,~!~!''''',@@@@@@@> ",
+" 0000000000000000000000acccefegeefgggggggggggghghghghhghchdcaa000000000000000000000000000 #+#+...-'''''',,,,,,,~~~~*%///$$@++@++@$@++++#!,''''''',!!!,~,~,~!,~~!,~!~,~~******~~!!!~!!~,''''',#@@$@@@@@$$18118897i77777|77|7|||||||[|[[[[[[[<}<<<:<:::::::444445555555566////~~~~~~~!~~~!~,'''',>$>$@@@> ",
+" 000000000000000000abcceefegffggfggggggggghghghhghghhhhhhghhghhhhghhcjaa00000000000000000000000 ..$#+#+-,'''',,,,,,,~~~~~~%//@@+@++@++@+@+@#*,,'''''',,!,~~~!~,~,~~,~!~,~!~~**~*****~~~~!~!!~''''''~@@@@@@@$$1181813877i7777777|7|||||[||[|[[[[<[[<<<<<<:::::444:5445455656666////~~~~~~!~~!~~!''''''@@@@@@$> ",
+" 000000000000000000ceefffffffffgfgfgfgggggggggggghghghhghghhhhhhhhghhhhhghhhdcaa00000000000000000000 #......-'''''~',,,,,,~~~~~%>@+@++@$++@++@#&,,''''''',!!~!,,~,~,~!,~,~,~~~~**{****{*~~~,~!!!~!,''''',&@>$@@@@@$$11818887777777777777|||||[|[|[[[[<[<}<<<:k:::::4:44444555555556////~~~~~~~~,~~!~''''',+@@@@@@> ",
+" 0000000000000000ceefffffffffgfffgfgfgggggggggghghghghghhhhhhghhhghhhhhghhhhhghhhhdcaa0000000000000000000 ...$#+.-,'''',~',,,,~~~~*&++++@++@+@+$@+&,,''''''',~!!~,~~,~,~~,~~,~,~,~*~*****~*****~~!~~!~~!,''''',+@@@@@@@$$1818138i7i777777|7|7|7||||[|[[|[[[[<<<<<<::<:::4:44445455556666////~~~~~~~!~~,~~''''',@@@@@@@> ",
+" 000000000000000ceelfflfffffffffffgfgfgfggfgggggghgghghghhghghhhhghhghhhhhhhhghhhhghhhhhhdcaa00000000000000000 #$)....-'''''''~,,,,,~~&.@++@++$@+++@+%,,''''''',!!,~,~,,~!~!,~!,~,!~~*******~***~*~**~!!!!~!~!''''',=@@@@@@@@$$18188387i7i777777|7||||||[||[[[[[[[<}<<<:<:::::4:4445445555556////~~~~~~~,~~~!~'''',,@$@@@@@> ",
+" 00000000000000celfffflfffffffefffffgfgfgfggggggghggghgghghhhhhhhhhhhhhhghghhghhhhhhhhghhhhhhhddca00000000000000000 ....#+.-,'''',,'','!,*#++$@++$@+@+@+#*,,'''''',,!!~,!,~,~,~,~,~,~!~~~**~***~***8&~**{~*~~~,~!!~,''''',#@$>@@@@$$$81188837777777777|77|||||[|[|[[[[<[<<<<<<:::::4:4444555555656///^~~~~~~!~~,~~~''''',@>@@@$@> ",
+" 0000000000000bellflll;f;fllffffffgfgffgfggggggggggghghghghhhhghghhghhhhhhhhhhhhhhhghghhhhhghhhhhhdddaa0000000000000000 ...$...-'''''~'~,~!~#+@+@++@+@+++$+*,'''''''',!~,~,~~~,~~,~,~!~,~!~{***{*~**~&>/^*****~~,!~~!~!!''''''*@@@$@@@@$1$8118887i7i777777|7|7|||||[|[[[[[[[[[<<<<<:::::4:444445555556////~~~~~~~~~~~!~''''',@$@@@@@> ",
+" 000000000000bcl;f;f;fff;fffffffffffefgfgggfgfggggghghghghhghghhhhhhhhhghhghhghhghghhhhhhhghhhhghhhghhhhdda0000000000000000 ...#...-,'''','',~&++$++++++++@++&,,''''''',!!!,~,~,,!,~,~,~,~,~~**********m$////&**~***~!~,~~!~,''''',%@@@@@@@@$1$881888i7777777777|7|||||[|[|[[[[<<<<<<<:<:::4:4444445555656////,~!~~~,~~,~!~''''',@@@@@@@> ",
+" 00000000000bcf;;;;;f;;;fflfff;effffgfgeggefggggggghgghgghgghhhghhhhghghhghhhhhghhhhhhhhhhhhhhhhhhghhhhghhghddda000000000000000 #......-''''',,~-.++@++@$+@$@++&,'''''''',,~,~,~,~,~,~,~,~,~,~~****~**~***.//////^***~*~~!~~!!~!]'''''',+@@@@@@@.1$1818387i7i77777|77|7|||||[|[[[[[[[<}<<<:<::::::444445555565////~~~,~~~~!~~~!,'''',@@@@@@@> ",
+" 00000000000bg;;;;f;f;f;ff;ffffffffffffgfgfgggggggggggggghhghhghhhhgchhhhhhhhghchhhhghghghghghhghhhhhhghhhhhhhhhdddca00000000000000 ..$#+..-,'''',=#++@+++@+@++++#~,,'''''',!!!!~,~,~~,~,~,~,~!~~~*{********1^////////$~****~~!~!~!~~,''''',-@$@@@@@@$1$8188887i77i77777|7|7|||||[|[[[[[[<<<<<<:<:::4:444444555556///(~~~~~~,~!~!~!''''',@@@@$@@> ",
+" 00000000000ce;;;f;;;;f;ff;ff;flfffffffgfgfgfgegggggggghghgghghhhhhhhghhghhghhhghhhghhchhhhhhhhhhhghghhhhhhhhhhhghghdddja00000000000000 ..+#$..-'''']#$@+++$++++++@#*,,''''''',!,~,,~,~,~,~,~~'~~,~~****~**~**&>//////^////m*~*~**!~!!!!~~,''''',.@@@@$@@.1$81818897i7777777777||||||[||[[[[[[<}<<<<:::::4:44545455555////!~~~~~~~!~!~!,''',,$@@@@@@> ",
+" 0000000000bl;;;;;;f;ff;f;f;ffffffffeffgffgfgfgggfgggghgghghghghhghghhhhghhhcghhhhghhhghhhghhgcghhhhhhhghghghghghhhhhhhhddda0000000000000 .#+.#+.-,'!%+++++$@+@+$@++&,,'''''',,!!!~,~,~,~,~,~,~,~~,~*{********m^//////^56^///@*~**~~~~!~!~!!!''''''*@@@@@@@@$$18181387i7i77777|7|7||||||[[[[[[[<[<<<<<:<::::44:444555565////~~,~,~~~!~!~!''''',@@@@@$@> ",
+" 0000000000cl;;;;;f;;;;;;f;fflf;fffffgffegfgfgggggggggggghghghghhghhhhhghhhhghhhhghhhhhhhghhhhhhhhghghhghhhhhhhhhhhhhhghghhhddda0000000000000 #+#....-'-.++++@++++++++&,'''''''''!,~,,~,~,~,~,~,~,~,~~****~**~***#//////^n6665////&**~**~~!~!!!~~,''''',&@@@@@@@+$$1818883i77i777777|77||||||||[[[[[<}<<<<<::::::44444445556///(~~~~~!~!!~!~~,''',,@@@@@@@> ",
+" 0000000000cl;;;;;;;;;fff;f;f;fffffffffffgffgfgfgfggggggghggghghhghhhhghhhhhhhghhgdhhghhghhhhghhghhhhhhhhchhhghhhghgchghhhhhhhhhddda0000000000000 ..+#+#+%.++++++++++@+@&!,''''''',!!!!!,~,~,~,~,~,~,~,~***********$^//////n666666_///^**~~*~~~!~!~!~~''''',!@@@@@@@@$$$8181887i777777777||||||}|[[[[[[[<[<<<<:<:::4:44444545555////,~!~!~~!~!~~!''''',@@@@@@@> ",
+" 00000000bc;;;;;;;;;;f;;;f;fff;ffffffefffffgfgfggggggggggghghghghhhgchhhhghhghhhhhhghdghhdghhhhhhhhghhhghhghhhcghhhhhhhhhhhhghhghhdddda000000000000 #...$++++++@+$+$@+++#*,''''''',,,~,~,~,~,~,~,~,~,~,~***{~***~**&^//////_666666666////$~**~*~~!~!~!!!!''''',&@$@@@@@+$1$8188887i7i777|77777||||||||[[[[[<}<<<<<::::::4444455555///(~~~~,~!~!~!~~''''',>$@@@@@> ",
+" 000000000bf;;;;;;;;f;;;f;f;f;f;ff;fffffffgggfgfggggggggghghgghghghhhhhghhhhhhhhhghhhhhhhhghhhghghhghhhghhhhhghghhhhghghghghhhhhhhhghhdddda000000000000 ..$..++$++$++@++++$*,,,'''''',!!~,,,~,~,,,~,~,~,~~~*~*****~**&^//////_56666666666n////**~*~*~~!!~!~~!,''''',.@@@@$@@+1$818188i77i777777|7|||||[|[[[[[[<[<<<<<:<:::4:4444445555////~,~!~~!~~~~!~''''''@@@@@$@> ",
+" 0000000000e;;;;;;;;;;;f;f;f;ff;fffffefffgffffgfggfgfgggggghgghghgdghhghhghchghhghhhghghhghhhghhhhhhhhghhhhhghhhhhghhhhhhhhhhhghghghhhhhghdddoa00000000000 $)+++++++++++++++-,,''''''',!!,,,~,~,,~,~~'~,~,~~**{****~*~&$//////^n6666666666666^///$~*~*~~~~!!~~!~!''''''*@@@@@@@@$1$818883977i7777777|7||||||[|[[[[[<}<<<:<::::4:444454555///(!~~!~!~,~~!~~,'''',@@@@@@@> ",
+" 000000000c;;;;;;;;;;;f;;;;f;ff;fflffffffffegfggfgeggggggghgghghghhhhhhhhhhhhgdhgdhgdhhhhgdhgchhghcghhhhhghghhhhhhhhhghhhghhhhhhhhhhchghhhhhhdddj000000000000 .++++++++++++++&!,''''''',!,!!~,~,~'~~,~'~,~,~~~~****~****$^//////n666666666666666n////&*~*~~~~!~~,~~!,''''',#$@@@@@@@$18118887i777777|777|||||[|[[[[[[[<<<<<<:<::::4444455555////~~,~,~~~~!~!~''''',@@$>@@@> ",
+" 00000000bl;;;;;;;;;;;;f;f;f;f;f;fffffffeffgfgfgfggggggggggghghghghghghghhghhhghdghhhghghhghhhhhhhhhhhghhchhhhghhghhhhghhhhhhgcghhghhghhhhhghhgdddda00000000000 +++$+++$++$+++#~,'''''''',!,~,,,~,,,~'~,~,~,~~*{*********&///////n666666666666666666_///^~*~~*~~~,~~~!~~''''''!@@@@@@@@$$$8181883i7i77777|7|7|||||||[[[[[[[<<<<<::::::444444555///(,~~~~~!~~!~~~'''',,@@@$@@+> ",
+" 000000000e;;;;;;;;;;;;f;;f;f;ff;fef;fffffffgfgfegggfgggggghghggghhghhhhhhhhhhghhhghhhgdchhhhhghghhghhghhhghhhhhhhghhghhhhhghghhhhhhhhhhhghhhhhhhhhdddda00000000000 +++++++$++@+@+#*,,'''''',,!!,!'~,~,~~,~,~'~,~~~~****~*~**8^//////_666666666666666666666////#~*~*~~~!~,~,~~,''''',-@@@$@@@@$$8181883i777777777||||||[[[|[[[<[<[<<<:<:::4:444454555////~!~,!!~~~~!!~''''',@@@@@@@> ",
+" 00000000e;;;;!;;;;;;;;;;;;f;f;ff;fffffffffffgfgfggfggggggggggghhghghhghhghhghhhhhgdghhhhghghhhhhhhhghhhhhhhghghghhhhhhchghhhhhghhghhghghhhghhghhghghdppdj00000000000 +++++++++++++++-,,''''''',!,~,~,~'~'~'~'~,~~,~~*********~&@//////_n6666666666666666666666n////*~~~*~~~~!~!~!~,''''',$@@@@@@@$$$1818887i7i7777777|7||||[|[[[[[<[<<<<<::::4::44445455///(~,~~~,~!~~~~~,''',,@@@@@@@> ",
+" 00000000e;;;;;;;;;;;;;f;f;f;f;f;fffffeffffgfgfgfgfgggggggghghghghhgdgdhhhhhgdhghghhchhhghhhhhghhghhhhhghhhhhhhhhhhghghghhhhghhhhhghhhhhhhhhhhghhhhhhhhhddpda0000000000 ++++$+++++++++&,,''''''',!,!,,,~'~,~,{,~'~'~,~~*{**~*~*~q$///////n6666666666666666666666666^///$*~~*~~~~~~!!~!!''''''=@@@@@@@@$$81818887777777|77|||||||[|[[[[[<<}<:<<::::4444445455////~~,~!~~~~,~!~''''',@@@@@$@> ",
+" 00000000b;;;;;;;;;;;;;;;;;f;;f;fffff;fffeffgfefggfgggggggghgghghghghhhghghghdghhhhhhghhhghhhghhhhhhhghhhhhghhghhhgchhhhhhhghhhhhhhhhhghhhhghghhhhhhhhhhghhddpdj00000000000 ++++++++$++++@&!,'''''''',!,~,~,~'~,!,,,,,~,~~{**********#///////n566666666666666666666666666n////&*~~~~~,~~!~!!~,''''',#@@$@@@@+$$81818397i777777777|||||[|[[[[}[<<<<<:<::::::4444555///(~!~!~,!~~~!~~'''',,@$@@@@@> ",
+" 00000000e;;;*;;;;;;;;;;f;f;;ff;flf;fffffffffgfgfgfggfggggggggghghghghhhchhhhhghhhghhhhghhhhhhhhghhghhhhhgchghhhhghhhhhhhghhhhghghghhhhhhghhhhhhgchghhhghhhhhhdddda0000000000 ++++$+++++++$+#*,,'''''',,!!,!'~'~,~'~'~!~~'~~~****~*~**~&^//////_556556666666666666666666666666_///>*~~~~~~~,~!~!~!''''''!@@@@@@$@$$1818188777i77|7||7||||[|[[[[[<[<[<<<:<:::4444444455////,~,~!~~~,~~!~''''',@>@@@@@> ",
+" 0000000ae;;;;;;;;;;;;;;;;f;f;f;ff;effffffffgffgfggfgggggggghghghghghhhghghhghhcghhhhghhhhhghghcghhhhhhghhhhhhhghhhhhghghhhhghchhhhhhghghhcghghhhhghhghhhhhhghhhdpppa0000000000 +++++++++++++++=,'''''''',!,~'!~'~,~'~'~,~,'~~~***~*****~&^//////_n55556555566666666666666666666665////#~*~*~~~~~!!~!~!''''',&@@@@@@@@$$1818883i7777777|7||||[|||[[[[[<[<<<<:::::::4454545////!~!~!,~~~!~~~,'''',@$@@@@@> ",
+" 0000000b;;;*;;;;;;;;;;;;f;;f;f;f;fff;fffefffgfgfgfggegggggggghgghhhghhgdhhhhhhghhhhgdhhghhhhhhhhhhhgchghhghghhhhhghhgdhhhghhhhghhghhchhhhghhhhhhghhhhhghhghhhhhhhddpdj0000000000 ++++++$+++$++++&,,''''''',,!,,!~'~,~'{'~!!,~,~~~**~****~*m$//////^n5555555565565666666666666666666666n////*~~~~~~!!~!~!!~,''''',+@$@@@@@$$$81188377777777|7|||||[[[[[[[<[<<<<<:<::4:44444545///(~,~,~~~!~~!~!''''',@@@@@$@> ",
+" 00000000e;;;;;;;;;;;;;;f;;;f;;f;fff;ffffffffffgfgegfggggggggghggghghghhhhhghhghhhhhhhhghdghhghhghhghdghhhhhhhhhghhhhhdghghhhghhhhhhdghhghhhhghhghhhhghhhhhhhhghhhhghhpdpda000000000 +++$.+++++++++&,'''''''',,!,~'~,,~'!,!,~'~,,~~***{***~***#///////n5555555555555555566666666666666666666^///$*~~~~~~!~!!~!~!''''''-@@@@@@@@$$$$$$1$1187777|7|7|||||||[[[[<[[<<<<::::::4:445455////!~!~,!~~!~~~~''''',@@@@@@@> ",
+" 0000000be;;!;;;;;;;;;;;;;;f;f;f;ff;ffffffffgfgfegfgfgggggggghgghhghghhhghhhhhhhhhghhghhhhhhhgdhhhhhhghhhghhhghhhhhhchhhddddddddddddddddddddddddddhhhhhhgcghhgchghhhhhhhpdppa000000000 +++.++++.+++++#~,,'''''',,!,!,!,!!'~~'!!,!,~'{~~********r&^//////_444444554555555555655666666666666666666n////&~~~~~~~!~!!~!~,''''''#@@@$@@@>//////////777777|7||||[|[|[[[[<<[<<<<::::4:4444455///(~,~!~~~!~!~~~''''',@@@@@@@> ",
+" 0000000a;;;;;;;;;;;;;;;;;;f;;f;ff;effffffeffffgfggfgggfgggggghgghghghghhhhghghghgdghhdghghghhhghghghhhghhhhhdddcddddddddddddddddddddddddddddddddddddddddddhhhhhhhghhghhghdppdo0000000000 ++.++++++$++$+#*,'''''''',!',~,~'~'~~'~,~,~,!!**{*~***~*~&(//////_444454544544545555555555556666666666666666_///>~*~~~~~!!~!~!~!''''',~@@@@@@@@(/////////77777|7|7|||[|[[[[[[[<<<<<:<::::4:444545///(~!~,,,~~~!~!~,''',,$>$@@@@> ",
+" 0000000b;;;;;;;!;;;;;;;;;f;f;f;f;fff;ffffffgfgfgfgfggfggggggghghghghhchhghhhhhhhhhhhghgdhhhhhhgdhhhhhhhdhdddhdddhddddddhdhddddddddddddddddddddddddpdddpddddpddddhdhhhhhhhhhdpdpda000000000 +++++$++++++++#&,,''''''',,,!!,'~'~,~'~'~'~,,~~*******~**m$//////^n44444444444544454555555555655666666666666666////&*~~~~~~!~!~!~~,''''',%@@@@@@@@////////(7777777|||||||[|[[[<[[<<<<<:::::44444454////,~!~~~~!~~~~!''''''@@@@@$@> ",
+" 0000000c;!;;!;;;;;;;;;;f;;f;;;f;ff;fffffffffegffgfgfgggggggghggghghghhghhdghhcghhhghhhhhghhhcghhhghhdhddddhdhdhdhdddhdhddddddddddddddddddddddddppdddpdddddpddpdppdddddhdhhhghdpppoa000000000 ++.+++.++$.+++&,'''''''''!,!!'~'~,~'~,~,~'~,~~**~****~***+///////n4:4:444444444445445445455555555556666666666666n////**~~~~~~,~~~!~~,''''',$@@@@$@@>////////377777|77|||[|[|[[[[<[<<<<<::::4::444455////,~!,,,~~~!~~~'''',,@@@@@@@> ",
+" 0000000b;;;;;;;;;;;;;;;;;;f;f;fff;fff;fefffffgfgfggfggggggghgghghghhghhhghhhhghhhhhhchghhcghhhhhhdddhdhdhddddddddddddddddhddddddddddddddddddddddddpddodddppppdpdpdpdoddpdddddhhdppppa000000000 ++.+$+.++.++++&!,''''''',,,!,,!,!~'~,~','~,~'~{~*********1///////_:::4:4::4:444444444444545455555556565666666666666////#*~~~~~~~!~!~!!!''''''-@@@@@@@@////////3777|77|||||||[[}[[[[<[<<<:<::::44444455///(~,~~~,~~~!~~~''''',@@@@@@@> ",
+" 0000000g!;;;;;;;;;;;;;;;;;f;;f;f;fff;ffffffgfffgfgfgggggggggghggghghhhghhhhghhhghhhghhhhhhhhhhhdhdhcdddddhdhdhdhhdhdhdhddddddhdddddhdddddddddddodddddddpdpddddppdpdpdpdodppdpdddddppppa000000000 ++.++++$++++++#*,,''''''',,!!'!!'!''~'~'!~,~,~******~*~**&>//////_::4:::4::4:4:4:4:4444444444454455555555556666666666n////*~~~~~~!~~!~!~~,'''',,#@@@@@@@@stu///(337777|77||||||[|[<[[[<<<<<<::::::444445///(~,,,~,~~~~!~~,''',,@@@@@@@> ",
+" 0000000e;;;!;;;;;;;;;;;;;f;f;f;f;f;effffffeffggfgfgggfgggggghgghghhghghhhhghhhhhhhghhhghghhhhdhdddddhdhhddddhcddddddddddhddddddddhddddddddddddddddoddpddpddpppdpdpdppdpdpdpppdopdpppppppa000000000 ++.++$+.+++$+.#*,,''''''',,,'!~,~'~!~,~'~'~'~!~~*~*******m^//////^k::::::::::4:::4:44:4444444454454545555555655666666666^///>~~~~~~~~~~~!~!~''''''*@@@@$@@@sts////8377777|||||[|[[|[[[<[<<<<:::::444444545////!~~,~,~~!~~~!''''',@@$>$@+> ",
+" 000000be;;;;;;;!;;;;;;;;;;f;f;f;fffff;ffffffgeffgegfgggggggggggghghghhhhhhhdghhghhhhghhhdhhddddhdhhdhdhdddhdddddddddhddddddddddddddddddddddddddddddddpddddppdpddpdodpdpdpppdodpppppppdppppj000000000 ++.+++.+.++.+.+&,,''''''',,!!!!','~!','~'~,~'~~*{*****~***+//////^n:::::::::::::::::::4:::4:4444444445445455556555656666665////&~~~~~~~,~~~!~!,''''',&@@@@@@@+tu////383777|77||||||[[[[[[<[<<<<<::::::444445////,,~,~!~~~!~~~'''',,@@@@@@@> ",
+" 0000000e;;!;;;;;;;;;;;;;f;f;f;f;f;f;ffffffffffgfgggfggggggghghghghhghhghghghgdhgdghhhhhhhddhhdhddddhddddhddhddhdhdhdddhdhdhdddddcdddddddddddddddddddddddppddpdpdpddpdppdodppdpppppppdppoppdpj000000000 +.++$.++++++++&,,,'''''',,,,'!,,~~',~'~,~'~'~~~******~*r*$^//////_<<:<<<:::::::::::::::::44:4:4:4444444544554555555556566666_///(*~~~~~~~~,~!~~!''''',!@@@@@@@@@(////1337|77||||||[|[|[[[[<[<<<<:<:::44444454////,~,~,,~~~~!~~''''',@@@@@@$> ",
+" 000000b;;;;;;!;;;;;;;;;f;;;;;;f;fffffffeffefgffgffgfgggggggggghghggcghhhhhchhhghhhhghdhdddhhdhdhdhhdhdhhdhddhdddhddddddddddddhddddddddddddjdddoddoddddddpddpppddpdppdodpddpdpdppdpvpppppppppddo000000000 .+.++.+++.+.$.#!,'''''''',',!!!'!''~,!'~'~'~,~~******~***&(//////_<<:<<:<:<<<<<::<::::::::::::::4:4:444444444545455555565656665////#~~!~~~~~~!~~!~!''''',&@@@@@@@@(////818377777||||[|[[[[[[<[<<<<<::::::444445///(~,~,~!~!~~~~~,''',,@@@@@@@> ",
+" 0000000e;;;;;;;;;;;;;;;;;f;fff;ff;f;fffffffgffgfggfgggggggggghggghgdghhhghhghhhhghchdhdhhhddddhdhddcddddddhdddhdhddhdhdddhdddddddddddddddddddddddpdddjdddoddddppdpdpdpdpppdodoppppppdoppppppppdpp00000000b ++.+$.++.$+.++#*,,'''''',,!,!'!,!,~,~'~'~'~,,~{**{**~****&^//////_k<<<<<<<<<<<:<<<<:<:<:<::::::::4:::4:4:444444454555555555656666n////*~~~~~,~~!~~!~!,''''',#@@$@@@@>////138337|7||||||[|[[[[[[<<<<:<::::4:444545////,~'~'~~!~~!~~''''',@@@@@@@> ",
+" 0000000e;!;!;;;;;;;;;;;;;;f;;f;f;fef;fffffffffgfgfgggegggggghgghghghhghhhhhhhhghhhdhdhdhddhhhdhdhdhdhdhdhdddddhddddddddhdddddddddddhddddddddddpdpdddppddpdddppdpdpdodpdppppddpdpppppdppppppppppppdj00000000b .+.++.+$.+++++#-,''''''''',,'!,,,'~',,'~'~'~,!~**~******~&$//////^k<<<<<<<<<<<<<<<:<<<<<<:<:<:::::::::::4:4:444444444445555555565666^///@~~~~~~~~~!~~~~!'''',,*@@@@@@@@(///111337|77|||||[|[[[[<[[<<<<<::::4:444445////,,~,!,~~!~~!~''''',>$@@@@@> ",
+" 0000000e;;;;;;;;;;;;;;;;f;f;;f;f;fffffffffegfgfgfgfggggggggghgghghgdghhhhghghhhhdhhdhhhdcdhddhdddhdhdddhddhdhddcdddhddddcddhddddhdddddddddddddddddpdpddddpdpdppdppddppdpdpdopdppppppppppppppppoppoppo00000000b #.++.+++.++.+.$&,,''''''',,,,!,!,!~'~'~~'~'!'~~{******~*~q$///////2<<[<[<[<<<<<<<<<<<<<<<:<<<:<<:<:::::::4::4:4:4444445455555555556556n////&~!~!~~!~!!!!~~,'''',,%@@@@@@@@///1313337||7||[||[|[[[[<<<<<<:<:::4:444545////,~,,~,~~~~~!~,'''''@@@@@$@> ",
+" 000000e;;;;;;!;;;;;;;;;;;;;f;f;ff;f;ffffffffegfgfgggfgggggggghghghghghhhhhhhghchhdhhdhdhhdhdhdhdhdhddhdhddcddhdddddddhdddddddddddddddddddddddpddoddddddpddppddpdpdppdppdodddpppppppppppppppoppdpppppdj00000000b%####.$+.+.+$++%!,'''''''',,'!,!''~','~','~'~!~~**~***~***&(//////_<[<[<[<[<<[[<[<[<[<[<<<<<<<<<<<:<:<::::::::4::4:4444444444455555556666_///>~~~~~~!~!~~~~~!'''',,~+@@$@@@@>//11113377|||||[|[[[[[[[[<<k///////////^54///^,~,~,~!~!~~~~''''',@@@@@@@> ",
+" 000000c;;!;;;;;;;;;;;;;f;f;f;fff;ffffffffffffgfggfggggggggghghgghghhhhhghhhhhddhdhhdhdhddhdhdhddhdcdhddddddhddhdhdhdddddhddddddhdddddddddddddddddddpdpdpppdpdpdpdopdppdpdppppppppppdppppppppppppopvppppa00000000)####..+.++).#*,,'''''',,,,!,!'~,~'~'~'~'~,,~~******~***&^//////_}[[[<[<[<[[[<[<[<[<[<<<<[<<<<<<<<<<<:<<:<::::::4::4:444444454555555556565////#~~~,~~~!~!!!~~!'''',,&@@@@@@@$//1811333|7|||||[|}[[[<<[<<k///////////^55////,~,,,~!~~~!~~,'''',@@@@@@@> ",
+" 000000c*;;;;;;;;;;;;;;;f;;f;f;f;ff;ffffffefgffgfgfggfgggggggggghghghghhhghghhdhhhddhdhhdhdhddchddhddhddhdhdhddddddddddhdddddddddddddddddjddddddpddddpdddddpddodpdpdpdpdppdpdpppppppppppppppppppvpppvpppppa00000000+#####$+.$#-,'''''''',',,,,',,','!'~'!,~'~~**~***~**~&>//////^k[[[<[[[[[[[<[[[[<[[[<[[[<[<[<<<<<<<<<<<<:<:<:::::::::4:4444445445555555655n////*~~!~~!~!~~~~~~,'''',,+@@@@@@@@/111113337||||[|[|[[[[<[<<k///////////^45///(,,~,~,~~!~~~~'''',,$>$@@@@> ",
+" 000000b;;;;;;!;;;;;;;;;;;;f;f;f;ffffffffefgffgfgfggggggggghghghhghhhhhhhhhcdhhdhdhhhdhdhdhdhdhdhdhdhdhddhddddhdhddhdhddddhddddcddddhdddddddddddodddpdddodpdppdpdpddpppdodpdopppppppdoppoppopppppppvpppppvppj00000000######+.&,''''''''',!,!'!!!!'~!,~'~'!'~{~*{***~***q$//////^2[[[[[[[[[[[[[[[[[[[<[[[<[[<[<[<}<<}<<<<<<<<:<<:<:::::4::4:4444444445555555656^///+~~~~,~!~!~!~!~!''''',*@@@$>@@@^^$$$^1_11122122[[[[[[<[<<k///////////^54////~'~,,,~~!~~~~,'''',@@@@@$@> ",
+" 000000c;;;!;;;;;;;;;;;f;f;;f;f;f;f;fffffffffgfgfggefggggggggghgghghghhghgdhdhdhhdhdhdhdhdhhdhdhddhdhdddddhddhddhddddddddhddddddddddddddddddddddddddddodddpdpddddodpppdppdpppdpddppppppdppdppppppoppppvopvvvpoa0000000b#####%!'''''''',,!',,,!''~'~',',,,!,~~*********&.///////_}[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[<[<[<<}<<<<<<<<:<<:::::::4::4:4444544455555556n////&,~~~~!~!~!~~!~,'''',,#@@@@$@@@//////////////$[|[[[[<[<<k////www(///^55///^'~,!!!~~~~~~~''''',@@@@@@@> ",
+" 000000bl;;;;;;;;;;;;;;;;;f;f;f;ffffffffffffgfgfgfgggggggggghghghghhghhhhhhdgdhdhdhdhhdhhdhddhdhdhdhdhdhdhdhddhddddhdhdcddddddddddddddddddddddjddddpdddddpdpdpdoddppddpddppdpdppodpppppvppvpppppppppopppppvppvpoa0000000b$#%=*],'''''',,,,,!',,,,,,'~,~'~'~~****~***m&$^//////^k}}}[[[[[[[[[[[[[[[[[[[[[[[[[[[[<[[[[<[[<<<}<<<<<<<<:<:<::::::4:4:4444454455555556_///>~~~,~!~!~~!~~~~''''',~@@@@@@@@>/////////////^[[[[[<[<<<k////www(///^55////!!,~'!~~~~~~~,'''''@@@@@@@> ",
+" 000000l;;;;;;;;;;;;;;;f;;f;f;f;ff;fffffefgfffgfgfgfggggggggggghghghhghhhdhdhdhdhhdhdhcdhdhdhdddhddhddddhddddhdddcddddhdddhdddhdddddddddpddddddpdddodddpddpdpdpdppddppoppdodpppppppppppppppppppopvpppvvvpvvpvvvpp00000000)-*]]]]'''',!'',,'~'!,!'~,,,'~'~~****&#$+$++>//////^2222k}}}[|[|[|[|[|[|[|[|[[[[[[[[[[[[[[[<[[[[<[<<}<<<<<<<<:<:::::::4::4444454445555555////%~~~!~!~!~!~!~~!'''',,&@@@@@@@@////////////(_|[[[[[<[<<k///(www(///^45////,,,,~,~~!~~~~'''',,@@@@@@@> ",
+" 000000c;;!;;;;;;;;;;;;;;;f;f;f;ffffffffffffgfgfggggggggggghghghgdghhhhhchdgdhhhdhdhdhdhdhdhdhdhddhddhdddddhdddhdhdddddddddddddddddddddjdddddddddpdddddpdpdpddpdddpppddddpdpppppppppppppppdodppppppvpvpvppvppvvvovo00000000=!]!]]],',,,!,!,'!'~'!''!'~'~~**{*&@@@@@@@$@>(///_2k222223}}}|[[|[|[|[|[|[|[|[|[}[[}[[[[[[[[[<[[<[<<<}<<<<<:<:<:::::::4:4:44444545555555n////~!~~!~~~~~!~!~~,'''',,+@@@@@@@>////////////$[[[[[<[<<<k////sss////^55////,~,~'~~~~~~!~''''',@@$>$@@> ",
+" 000000c;;;;;;!;;;;;;;;;f;f;f;ff;f;ffffffffgefgfgfgfgggggghggghghghghghdhhhhhdchdhhhdhhdhdhcddhdhddgddhddhhddhdhdddhdddhddddddddhdddddddddddpdpddpdpdddddpddpppdpppdodpppppdpppdpppppppppppppppppopppvppppvvpvvppvppj0000000c=]]]]*]~',,,','~,,,'~'~'~,~~****+@@@@@@@@@+@+>^22222222222}3}||||[|[|[|[|[||[|[|[|[|[[[[[[[[[[[[[[[<<<}<:}<<:<<<::::::::4:44444544555555////.~,~~!!,~!~~!~!!'''',,*@$@@@@@@(///////////_|[[[[<[<<<k///////////^45///(~',,,,~~~~~~~,''',,@@@@@@+> ",
+" 000000bl;;;;;;;;;;;;;;f;;f;f;ff;feffffefffgfgfggfgggggggggghghghgdghhdhdhddhhhdhdhdhhdddhhddhdhdcdddcdhdddddddddddddhdddhddddddddddddddddddddddpddddppdoddpdddpdpdpdpdddodppdopppppppppppopppppppppoppopvvpvppvvvvvvvj0000000)]]]]=]=~'!'~,,''~'~'~',~{*****.$@@@$@@@@@@@@$@$_2_222222222}3}||||||||||[||[|[|[|[|[|[|[}[[[[[[[<[[[<<[<<<<<<:<:<::::4::4:4444445455555n////&,~,~~~~!~!~~~~,'''',,#@@@$@@@>///////////^|[[[[[<[<<k///////////^55////,~~,~'~~~~~~~''''',@@@@@@@> ",
+" 000000e!;;!;;;;;;;;;;;f;f;f;f;ff;ffffffgefffgfgeggggggggghgghgghghhhhdhhdgdhddgdhdhdhhdhdhddgdddhhdddhddhdhdcdhdddddddddddddhddddddddddddddpdpdddpdddddpddppdodpdpdpdoppddpppdppppppppppdpppppoppvpvvpppvpvvovvpppvvooa0000000l==]]*]]~,,'~'~,,,,,,!*~**{*~#@@@@@@@@@@@@@@@@@@$222222222222}3}|||||[||||||||||[|[|[|[|[[}[[[[[[[<[[<[<<}<<<<<<<::::::4::44444445455565_///>!~!~,~~~~~!~!~~''''',*@@>@@$@@>/////////(_[[[[[<[<<<k///////////^45////''~'~,~~!~~~~'''',,@@@@@@@> ",
+" 000000c;;;;;;;;;;;;;;f;;;f;f;ffffffffffffgfgfgfggfggggggggggdghgdghcdhhdgdhdhhddhhdhddhddhdhdddhdddhdhdddhdddddddhddhddhddddddddddddddpddddddddjddddjdddpdpdpdddodpdppddpppdppppppppppodoppppopppvpppvpvpvppvppvvvvvvvvo0000000b-==]*=*]~'~',''~'~'~*******&$@@@@@@@@@@@@@@@@@@@>$221222221223}3}|||||||||||||||||[|[|[|[|[[[}[[[[[[[<[<<<<}:<:<<:<:::::4::4444445454554////&~!~!~,!~!~!~!~,''''',&@@@@@@@@//////////^|[[[<[<<<<k///////////_55////~'~',,~~~~!!~''''',@@@@$@@> ",
+" 000000c;;;;;;;;;;;;;;;f;f;f;f;ff;ffefffffffgfgfgggggggggghghggghhgdhhdhdhdhdhdhdhdhdhhhdhhhddhddgdhddhdddhddhdhddddddddddddhdddddddddddddddddpddddpdddpddpppdpdppdpppddodppopppppppppppdppppppppvvvvvppvvvpvpvvvvvvvvpvvvj0000000b=*=]]]*]!,,,~,',~*{**~**~*#@@@@@@@@$>$@@@@@@@@@>@^$111212321223333|||||||||||||||||||[||[|[|[[[[[[[[[[[[[<<<<<<<:<::::::4::444444455555_////~!~,~~!~!~~~~~~,'''',,+@@@@@@@>////////(_[[[[[<[<<<k///////////^44///^,,,,~,~~~~~~~,''',,@@$>@@@> ",
+" 000000bl;;;;;;;;;;;;;f;;f;f;fff;fffffffegffgfgfgfgggggggggghghhgdhdhhhhhhhhhhdhhhdhhdcddhdddhdhddddhddhdddhddhddhdhdhdhdddhddddhdddddddddddjddddddddpddpdpddpdodppdpdpppdpddpdpppppppppopppppoppppppppppppvpopvvvvpvvvvpvvvj0000000)===]=*]*!'!'~~*********~&@@@$@@$>)*&+@@@@@@@@@$@@@$121121213223}33||7|||||||||||||[|||[|[|[|[|[[[[[[<[<[<<}<<<<<<<:<::::4:4:44444455555////.~!~,!~~~!~!~~~!''''',-@@@@@@@@/////////$[[[[[<[<<<k///////////_55////'~'~',~~~~~~~''''',@@@@@@$> ",
+" 000000l;;!;;;;;;;;;;;;;f;f;f;ff;ffffffffffgfgfgggggggggghgghghghghdhdhdhdhddhhdhdhdhhhdhdhddhddhdhddhdddhcddhdddddddddddddddddddddddddddddddddpdpdpddpddpdppdddpdppdodpdppppppppppppppdpppppppvvoppppvvovvpvpvppopvvvpvvvvooa0000000-==*]]=]~~'~~~*****~***&@@+>@@@@#,,,,&$>$@@@@@@@@>>>$1111211211233337|7|||||||||||||||||||[|[|}[[[[[[[[[[[<}<<<<<:<:<:::::4:44444445455n////*,~~,!,~!~!~!!~,'''',,#>$@@@@@@(uu(////^[[[[<[<<<<k///////////^45////!'!,!,~~!~~!~,''',,@@@@@@@> ",
+" 000000c;;;;;;;;;;;;;f;f;f;f;fff;fffffffffgfgfeggfggggggghgghghgdddhhhhhdhhdgdhdhdhdhddhdhhdhddhcddhddhdhdddddddhdhddhdhddddddddddddddjhpddddpdpdddddpdpdpdpddpppdpdppdpdodpppppppppppopppppppppppvpoppppppvpvvpopvvvvvvvvpvvvo0000000b===*]=]=*~**{***~***&^^@@@$@@@+~'',,,,%>@@@@@@@@@@@>>11112112121233337|77|7||7|||||||||[||[|[|[|[}[[[[[<[<<[<<<<<<<:<:::::4:44444454555^///>~,~!~~~~!~~~~~!''''''~@@@@@$@@utxs////^[[[[[<<<<<n///////////^55///(~,~'~,~~!]~!~''''',@@@@@@@> ",
+" 000000;;;;;;;;;;;;;f;;f;ff;f;ffffefffgfgfgfggfgggggggggghgghghhhhhdhdhhhhdcdhdhdhhdhdhdhddhdhdhdhddhddddhdhddhdddddddddddddddddddddddddphoddddddjddppdddodppppdppppdppdppppppppppdodpdpppppppvppppvpopvvvpvvvpvvvvvvvvvvvvvvvoo0000000%-===]=*-****~*****$//+@@@@@@@%,''',,,,*#$@@@@@@@@$@>@^$111131132123337|777|7|77||||||||||||||[||[[[[[[[[[<[<}<<<<<:<::::::4:44444454554////&~,~,,~!~~~~~!~,'''',,&@@@@@@@@ttu///(^}[[[<}}k<kk///////////^44////,'',,,~~~~~~~,''',,@$@@@@+> ",
+" 000000e;;;;;;;;;;;;f;;f;;f;fffffffffffefffgfgfggggggggghggdggdhhdhhhdgddhdgddhhdhdchdhdhdhddhdddhddhddhddddhdddddcdhddddhdddddddddddddddddddjddpdddpddppddpddpppdpdppdodpppppppppppppppppodpoppvvppppvpppvvvpovvppvvpvvvvvvvvvvva000000a-==**=*-*m***~**$///>+@@@@@$+,'''''''',,=+@@$@@@@@@@@@@$$111121131233337|777|7|77|||||||||[|[|[[|[|[[[[[[[[<<<}<<<<:<:::::::4:444454455_///(~~!~~,~~!~!~~~~''''',,@@@@@@@@usu////^}}<[<^//////////////////////(~~,!'!~~~~~~~''''',@@@@@@@> ",
+" 000000b;;;;;;;;;;;;;;;f;ff;f;f;fffffffgfgfgfgggfgggggggghggdghdhhdhdhddchhddhhdhdhdhdhdhddhddhdhddhdddhddhddddddhdhddddddddhddddddddddddddddddpdddddpdpdpdodpdpdpdodpdpdpppppppppppppopppdpopppvpppppppvvppopppvvvvpovvvvvvvvvvvyo0000000)=----m*-*****&////($@@$@@@@-'''''''''',,,&+>$@@@@@@$@>$>$111131111113333|7|77|7777|7|||||||||||[|[[[[[[[[[[[<<<<<<<<<::::::444444445555////#!,,~,~!~~~~~~!~'''',,&@@@@@$+@//////^}}[[<^/////////wxw(//////////','!,,~~~~~~~,'''',>$>$@@@> ",
+" 000000l;;!;;;;;;;;ff;f;f;f;fffffffffffffgfgggfggggggghghgghgdhhhdgdgdgdhddgdhdhhhhdhdhdhdhdhddgddhddhddddhdhdhddddddhdddddddcddddddddddddpddddddpdpdddpdpdpdpdpdppdpdpppdpppppppppppdppppppvppppopvvvvppvvpvvvvvvpvpvpvvvvovvvvvvvj0000000&--*=*-*&**&^/////@@@@@@@+#,''''''''''''',,%@@@@@@@@@@@@>>$1111313112133877777|7|77|7||||||||[||[||[|[[[[[[[[<[<<<<<<:<::::::4444444455n////*~~,~,~!~~!!~~~,''''',#@@@@@@@>/////^k}}[<^/////////www(//////////,,,~''~!~~~~~''''''@@@@@@$> ",
+" 000000c;;;;;;;;;;f;;;;f;f;ff;ffefffffegfgfgfgegggggggggghghgdhhdhddhddhdghdhdhhdhdhdhdhdcdhdhdddhddhdddhdhdddddddddddddddddddddddhjddddddddpddpdddddppddpdpdppdppdpdpppdpppppppppppppvdoppppppopppvvpppopvvvppvpvvypvvvvvvvvovvvvvvoa000000b-&&-&*&*&$(/////^@@@@@@@@*'''''!,'''''''',,~#@@@@@@@@@@$@>>$11111318111333377777|77777|7|||||||[|[|[[[[[[[[[[<[<<<<<:<:::::::4:44445445^///@,~,~,~!~!~~~!!!'''',,*@@@@@@+@(////^2}k[<^////////(www(/////////(,,'!,!~~~~~~~,'''',+@@@@@@> ",
+" 00000b;;;;;;;;;;;;;ff;ff;ff;ffffffeffgffggfeggggggggggdgghdhhhdgdhgdhhhdddhdhddhhdhdhdhdhdddhddhddhdhddddddddchdhdddddhdddddddddpddddddjddddpddpdoddpdpppdpdodpdpdodpdopppppppppppoppppdppppopppvppvpopvpvppvypvvpvvpypvyvvvvvvvvyvyo0000000)*&*-*&#>@>////2++@@$@@$%,''',,!~!,'''''''',,*$@@@@@@@@@@@$>@$11111131318333777777|7|77||7|||||||||[|[[[[[<[[<<[<<<<<:<::::::4444444545n////&!,~,,~~~~~~~~~,'''',,#@@@@@@@@////^kkk}<^/////////tst///////////,~'~',~~~~~~~''''',@@@@@@@> ",
+" 000000e;;;;;;;;;;;f;;f;;f;fffffffffgfffgffgggggggggghgggggdgdhhdcddhhdhhdgdhdgdhdhdhdhdddgdhddhddhdddddhdchdhdddddddhddddddddddddddddddddpdjdddddddpddppdpdpddpdodpdppdpppppppdppppdpppppopvppppvpvppvpvpopyppvpvvpvvvvvvvvvvvvvvyvvvva0000a0a&--&&@@>>@>/2[$@@@@@@@+,'''''!~!!!,,'''''''',,-@$@@@@@@@@@@@>$$111111111133337|7777|777|7|7|||||[|||[|[[[[[[[[[<[<<<<:<::::::::44444555_///(!~,~!,!~~!~~~~!,'''',,$>$@@@$@>///^22kk<^///////////////////////'',,,!~~~~~~~,''',,@$@@@@@> ",
+" 000000c;;;;;;;;;;f;;f;f;ffff;fffffffffgfgggfgfggggggggghhhdgdhdgdhhhdhhdhddhdhddhcdhdhdhdddddhddhdddhdhddhddddhdhddhdddddddhddddddddddddddddddddjdpdpdppddpdpppdpdpdpdodppppppppodppppodppvpvppvpvpvvpvppvvpppypovvvvvpvvvvvvvvvvvvvvvvy0000000)-&#+@>@>>$3}3@@@$>)@@-,'''',~,~~~!!,'''''''''',%@@$>@@@@@@@@@>$$11181811183333777777|77|7||||||||[|[|[|[[[[[<[<<[<<<<:<::::4:4444444554////#,~,~,!!~~~!!~~!''''',-@@@@@@@@^//^kkkkk^///////////////////////,,,,'!~~~~~~~''''',@>@@@$@> ",
+" 000000l;;;;;;;;f;;f;f;f;f;fffffffffgffgfgfggggggggghghggghdhdhdhhdhhchdhdgdhhdhhddhdhddhdhdhcdhddhdhddhdddhddddddddddddddddddddddddddddpdddpdpddpdpdpdpdodpdpdppdpdodpdpppppdppppppopdpvvpppvpvpvdoppvppvvpvopvvvvvvvovvvvvvvvvyvyvyvovvj0000000)+++$@>$833|$+@@@$@@#''''',!~!!!!!~!~,''''''''',~#@$@@@@@@@@@@@@$11118181118833377777777|777|||||||||[[|[[[}[[[[<[<<<<:<::::::4:4445445n////*~,~!!!~!!~~~~~,''',,,+@@@@@@@>//^22kkk^///////////////////////'~'!,,~~~~~~~''''',@$@@@@@> ",
+" 000000c;;;;;;;;;;f;;f;ff;fffffffeffffgfgfggfgggggggggghgdddgdhhdgdhhdhdhhdhdhdhhdhdhddgdddgdddddhddddddcdddddhddddhddddddhdddddpddddddjddppdddddddpddppdddppdpdpdodpppppppppppppppppppppppoppvvppvppvpvvvvpyppypvpypvpvvvovvyvyvvvvvvvyvvva000000b...+$133388@@@@@@@@*,''''!~,~!~!~~!~~!,'''''''''']#@@$@@$>$@@@@@@$11111811118333777|7|77|7|7||||||[||[|[[[[[[<[<[<<<<<<::::::4:44444445^///+,,,,~,~~~~~~~~!''''',*@@@@@@@@(/^k2kkk^///////////////////////',,,'!~~~~~~~''''',@@@@@@@> ",
+" 000000l;;;;;;;;;;;f;f;f;ff;fffffffegffgefgfggggggghghggdghhdgdhdhdhdgdhdhdhdhdhddgdhdddhddddhdhddhdhdhddhdhdddddhddddddddddddddddddddddddddpdppdpdpdppdppddoppdpddpdppppppppdoppppppppppppdvppopvvpvppvppyppyppvvppyvvvvvvvvvvvvvyvyvvyvyoo000000b)++&893393.)$@+@@@%'''''!,~!~!!!!!!!!~!!,''''''''',-+@@@@@@@@@@@@@@$111813181183337777777|7|77||||||||}||[[[}[[[[<[<<:<<<:::::44:445455n////&~,~,!,~~~!!~~~,'''',,#>$@@@@@@/^2n2kk^//////////////////////^,,',,,~~~~~~~,'''',@@@@@@@> ",
+" 000000b;;;;;;;;f;f;f;f;ffffffffffffgfggfggggggggggggghgdhdhdhdhhhdgdddgdhdgddhdhdhddhdhdhdgdddhddhdddddhdddddhdhddddddddddddddddddjdddpdddpdddddpdddpdpddppddppdodppppdpppppppdppopppppopvvvpppppvpvvvvopvppvpvypypvpyvvpvvvvvvvvvvyvvvvyvvoa000000b$8888933&..+@@@$@!,''''~!,~,~~~!!~!!!!!~,,'''','''',&+@@@@@$>$@@@@@$$111181811883337777777|7|7|||||[|[[[|[[[[[<[<<<<<<:<::::::4:4444455_///>~,~,!,!!~~~~~~~''''''!+@>$@@@@>^22nkk^///////////////////////'~'~',~~~~~~~''''',@@@@$@@> ",
+" 00000a;;;;;;;;f;;f;fff;f;ffeffeffffffgfgfggfggggggghghghhhhdhhdhhddgddhdhddcdgdhdhdhddhdddhdhddhddddhdddddhdddddddddddcddddddddddddddddppdddpdpddppdppdpppdpdpdpdppdppppppdppppppdpdopppvpvdopvpvpvpvppvppypypvpppvvvpovvovypyvvyvvvvyvvyvyvo0000000888&8988$.##+@@@-''''',~~!~!,,~~!!!~!~!!~!,''''''''''~&@@@@@@@>$@@@@@$$$1181811188333777777|77|7|||||||[|[[[[[[[[[<[<<<:<<:::::444444454////%~,!,!,~~~~~~~~!'''',,&@@@@@@@>^_2k_k^//////////////////////(,',',,~~~~~~~,''',,$@@@@@@> ",
+" 000000b;;;;;;;f;;f;f;f;ffffffffffgfggfggggggggggggdgghdddhdgdhdhhdgdhdhdhhhhdhdddhdhdhddhdddhddhddhcdddhdddddhdddddhdddddhddddddddddddddddpdjhpdppddpdjddppdododpdpdodppppodppoppododppppvpvpppvpvvppovvpyppvpypypypypvpvvvvvvyvvvvyvvyvyvyvvva000000)8888m8##..$#+@.,'''',!!,~,~~~,!~~~!!!!~!~~!,''''''''',~#@@@@@@@@@@@@@$$1$118181118333377777|7|||||||||[|[|[[[[[<[<[<<<<:::::::::4444445_////~!~,~,!~~~~~~~~,''''',+@@@@$@@>k_kkk////////////////////////,,~',,~~~~]~~''''',@@@@@$@> ",
+" 000000l;;;;;f;;f;f;f;ff;fffffffffgffgfgfgfggggggggghgghghhdhdgdhdhdhhdhhddhhdhdhdhdddgddchddhdddhddhdhddhdhddddddddddddddddddjdddddddpdjddddpddpddppdddpppdpdddppppppppppdppppdpdpppvppvpoppvppvpvvppppvpvvpypppvvpvvvvvvvvpyvvvyvvyvvvyvvyvyyo000000a88m88&#.#)#..@*''''',~~!~!!,~~!,,~~~!!!~!!~~!,'''''','',=$@@@@>$@@@@@@@@$11111818118333777777777|||||||||[|[[[[[[<[<<<<<<<::::4:44444555^///+,!,,!,!!~~~~~!!'''',,*@@@@@@@@(////////////////////////////'',~',~~~~~~~,''',,@@@@@@@> ",
+" 000000a;;;;;;;;f;f;ff;ffffffffffgefgfgegfgggggggghghgddddhdhdhdhhhhdhdgddhhdhhhdcdhdhdddhddhdddhddddddddhddddhddhddddddddddddddddddddpddddpdpdppdpdppdpddpdpdpppdpdpdpppppppppppvpppppodvdpvppopvpvpovvpopvvppvypypypvvyvvvvvvvvvvvvvvyvvyvyvyvva000000)3m8&###$#.#.#,''''!~,!~,~~~,,~~~!,~,~!!!~!!!!!,''''''''''-+@$@@>@@@@@@@@$$1118118181833377777|77||||[|[|[|[}[[[[[[<[<<<:<::::4:44444n44n////-,~,!,!~~~~~~~~,'''',,#@@@@@@@@////////////////////////////,,',,,~~~~~~~''''',@@@@@@@> ",
+" 000000e;;;;;f;;f;;f;f;ff;ffffeffgffgfgggggggggggggggdgdgdhhhhhdgdchdhddhhdhddhddgdddhdhdddhdhddhdhdhdhddddhdddddddddddhddddddddddddpdddpddddpddpdppdpddoddodpppdodoppppppppopppppppppppvpvpopppvpvpvppypvvvvopvpvpvvvvvpyvvyvyvvvyvvyvyyvvvyvvyoo000000a3888%####+##=''''',~~,~!!,~!~~***~!~!!~!!~!~~~!,''''''''',,%@@$@@$>$@@@@@@$$11118118118333|77|7|77|||||||[|[[[[[[<[<<<<<:<:::::::4:_/_55_///>'~,~,,,~~~~~~~]'''',,*$>$@@@@@(///////////////////////////,'~'',~~]~~~~,'''',>$@@@@@> ",
+" 000000b;;;;f;;f;f;ff;ffffffffffgffgfgfgfgggggggdgdgddhdhdhhdhdhddhdhhdhhdhdhhdhddddhhddhdhddddhddddhddddhddddhddddhdddddddddpddddddjhpdpddddpdppddpdpppddpddppdpdpdpdpppdppdppppppvppoppvpppppvpvpvvppvvpvvpvvvpyvvypvovvvvvvvyvvvvyvvyvvvyvyvyvyoa000000)3&######.#&*!'''!~,!~,~~,~!!~****~~~~,~!~!!!~!~!,''''''''''!#@@@@@@@@@@@@@@$$11181181183333777|7|7|||||[|[|[[[[[[[[<<<<<<::::4::_////n54////%,,~,!,~!~~!~~~,''''',%@@@@@@$@///////////////////////////,',!,'~~~~~~~''''''@@@@@$@> ",
+" 000000g;;;;;f;;f;f;fff;fffffffffgfgfgggggggggggggggdgdgdgdhchdgdhhdhchdhdhhdhdhhdgdddhdhdhdhdhdddddddhdddddddddddddddddddddddddddpdpdddpdpdddpdpdppdpdpppdpppdpppppppppppppppppppppppppvpvpvpvpopppvvvvvvvvvvpypvpvvvvvvvvvvyvvvyvvvyvyvvyvvvyvvyyo00000003###%###.#]]]'',,~~,~!,~,~~~***~**~~~!~!!~!~!~!~!~,''''''''''*#@@@>$@@@@@@@@$$$1181181118333777|77||||||[|[|[[[[[<[<[<<<:<<:::n///////45_////~'~,,!,~~~~!~~~,'''',,+>$@@@@@>//////////////////////////~,,',,~~~~~~~,'''',@@@@@@@> ",
+" 000000a;;;f;f;f;f;ff;ffffeffefgfgfggfgfgggggggghggdgdhdhddgdhdhdgddgdddhhdddhdhdddddhdhddhddddhdhdddhddddhddddddddcdddhddddpddddddddddpddddpdpdpddoddppddpppdpdodpdppppppppoppopppppoppvppppppvvpvovpvpvvvvvvvpvvvypvvvyvvyvvvvvvvvvyvvvyvyvvyvyvyyya000000)########-!]]]'!~,!~!~~~,~,~*******~*~~!~!!~!~!~!!~!,'''''''',,=+@@@@@@@@@@@@@$$1118118111833377|77|||||||[|[}[[[[[<[<<<<:<:k/////////n45////.~'~'!,~~~~~~~~!''''',=@@@@@@@@//////////////////////////''~','~~~~~~~''''',@@$>@@@> ",
+" 000000e;;;;f;;f;ff;f;fffffffffffgfgfgggggggggggghgdhhhhhhddgdgddgdddhhddgdgdddhhdhdhddcddhdhddcdhdcddhddddddddddddddddddddddddphjddppddpdddpdpdppddpddodppppppdpppppppppppdppdpppppppvppopypvppvpppovpvpvpvvvvpypvvvvyvvvvvvvyvyvvyvyvvyvvyvyvvyvvyyo0000000.#%#%##%]]]]]~,~~,~!,~,~~~**{~*~****~~~~!~!~!!~!~!~!!''''''''',,&@@$>$@@@@@@@@@$$1111318111333377|7|||||||[|[[[[[[[<[<<<<k^///////////45n////=!,~,,,~~~!~]!~,'''',,#@@@@@@@@/////////////////////////,,',,,~~~~~~~,''',,@@@$@@+> ",
+" 000000e;f;f;;f;ff;fffffffffffggfgfggegfgggggdghggdhhdhdhdhhdddhddhhhdhhddcdhhdddhdddgddhdddddhddddddhdddhdhcddhddddddddddpdddddpddpdddddjdpdpdpdppdpppddpdjddpdodpppppdodopppppppopvpppvppppopvpvpvpvypypvpvvypvvypyvpyvvyvvvvvvvvyvvyvvvyvvyvyyyvyyoa000000)$#####-*]]~]*~,!~,~~!~,~~******~*{***~~~!~!~!~!!~!~~~,''''''''''!%@@@@@@@@@@@@@@$11$118181133333777|||||[|[|[[[[<[<[<<<k///////tu////_54_///>,,,!,'!~~~~~~~~'''',,*@@@@$@@@(////////////////////////',,,'!~~~~~~~''''',@@@@@@@> ",
+" 000000c;;;;f;f;f;f;ffeffffffffegfgfggggggggggghghgdgdghdgdhdghhdgdhdhdhhhdddhdchddgdddddhdhdhdddhdhddddcdddddddddddddddddddddddddddddpdddddpdphoddppddodpddddopdppppppppppppppppppdppopppppppvvpvvvvpppvvpyvpppypvvpvvpvvvvvyvyvyvvyvyvyvyvyvyvvyyvyyyj000000b.##%#-==]]]]=~~,~!,~,~~~*****{*****~***~~!~!~!~!!~,~!~~,''''''''',~#@@$@@@@@@@@@@@$1111111$1123333|7|||||[||[[[[[[[<[<<<_////uxww/////n5n////&~'!,!,~~!~~~~~!'''',,&@@@@@@@@////////////////////////,,,,,,~~~~~~~'''',,@@@@@@@> ",
+" 000000ef;f;;f;ff;fffffffeffgfgfgfggfgggggggggghgddhdhddhdhhddhdhddhhdhddhhhdhdhddhddhdhddddhddhddddddhdddhddddddhdddddddjdddddjddpddjddpdpddppdpdpdpppdpdodppdppppppppppppppppppopvppvpvpopvvpppopypvopypvpvypvvpypyvyvyvvypvvvvvyvyvvvyvyvvyvyvyvvyyoya000000+####====]*==*~!!~~,~,~**~*~*&@&~~**~~**~*~~!!~!~~!!~!!~!,'''''''','*+@@@$>@@@@@@@@$$111$//11823333|7|||||[|[|[[[[[<[<<<k////uwwwt////^55_///(~,!,,'!~~~~~~~~,'''',,@@@@@@@@>///////////////////////,'~',,~~*~~~~''''',@@@@@$@> ",
+" 000000a;;;f;f;f;fff;ffffffffgfffgggfgggggggdghgghhhhhhdghdhhhhhdhhdhhdhhddhdhdddgddddhdhdhdddhddhddhdddddddddddddddddddddhddddddddpdddddpdpdpdpppdppdpdppdpdppppdppdodpppppppopppdvvppppppvpvpvvppppyvpvppyppypyvovpypvvyvvvyvvyvvyvvyvyvvyvyvyvyyvyvyyoa000000b.##--===]=**=*~,!,~~~~******^/^.q~****~*~~~~!~!!!~!~!~!~!,,''''''''',-$@@@@@@@@@@@@@$^////^111113333||||||[|[[[[[[[[<<<<_////xwx(/////n55////#~',!!,~!~~~~~~!'''',,&@@@@@@@@///////////////////////'','',~~~~~~~,''',,@$@@@@@> ",
+" 000000cf;f;f;f;ff;ffffffffgfgfggfgggggggggggghgdhdhdhdhddgdhddhdhdhdhhdhdhdhddgdddhhdhddddhdhdddddhdddhddddddddddddddddpddddddddpdddpddpddppdpdddodpdpppdpdppdodppppdppodpppdpppopvppvpopvpvpvvvvvvppvypypvpyppypvvvpvvvvvvvyvvyvyvvvyvyvyvvvyvyvyyvyvyyy000000b.##========*=*~!~~,~~*~****$/////&**~*~***~~~~~!~!~!~!!~~!!,''''',''''!%@@$>$@@@@@@@@@>////11312123333|||||||[[[[<[[[<<<<^///(s////////45n////*!,,,,,~~~~~~~~,''''',#@@$@@$@>//////////////////////,,'~'!~~~~~~~,'''',@>@@@@@> ",
+" 000000e;;;f;f;ffffffffffffgfgfgefgfggggggghggghgdgdhhhhhdhdgdhchhdhdhdhdhddgddddhddhddhdhdddddhddddddddhddddhddddddddddddddddddpdpddddddpdddodppddpdpdppdodpppppppppppppppppvppppoppppppvpvpvpypppyvpvppvpyppypvvvvyvvyvyvvvvvvvvyvvyvvvvyvyvyvyvyvyyyyooa000000).*==-==-=*=*=*,~,~~*****~&///////^&***~~~***~~~~,~~~!~!!~~~~,''''''''',~#@@@@@@@@@@@@@@(//^11113112333|||[|[|[[[[<[<[<<<k/////////////_55^///@'!'!,,!!~~~~~~~'''',,*@>@@@@+@(/////////////////////,,',',~~*~~~~''''',@$@@@@@> ",
+" 000000a;f;f;fff;ffffffeffgfgfgfggggggggggggghhgddhdhhdhdhhchdhdhddgdhdhdhdhdddhdddhdddhdddhddhdddhddhdddddddddddddddddddddddjddpddddpdpdpdpdpdpddppdpppdppdppdpppppppppppppopppoppppopvpyppvpvpppyppvvvvypvpvvpyvyvpvyvvvvvvvvyyvyvyvyvyvyvvyvvyvyvyvvvyyyj000000)%]]===-=-*=*=*~!~,~*~r~**@/////////>m~**~~~~~~~~~,~,~~!!!!!~~!,''''''''''*.@@$>@@@@@@@@@>//^18112312233}||||[|[[[[[[<[<<<^/////////////n54////&!,'!'!~~~~~~~~,'''',,&@@@@@@@@/////////////////////',,,'!~~~~~~~''''',@@@@@$@> ",
+" 00000ac;f;f;f;ff;ffffffffgfgfgfgfggggggghghggdgdgdgdhhdgddgddgdhddhdhhdcdhdhddgdhcdhddhdhddcdhdddhdddhddddhdddddhddddddddddddpdddpddpddppdpdpdjppdppddppdpppppppppdopppppppdpppppvpppvpppvpvpopyppvpypvpvpypyyppvvvvvvvyvvyvyyvvyvyvyvyvyvyyyyvyvyyyvyyvyyo0000000*]!]=-*-==*=*=*,~~******&////n///////$*~*~**~*~~~~~~!~!~~~~!~~~!,''''''''',-+@@$@@@@@@@@@+>/111111821223}3}|[|[[[[[[[<<<<k/////////////^55_///(!,!,,,,~~~~~~~~,''',,!$@@@@@@@>////////////////////,','',~~~~~~~,'''',@@@@@@@> ",
+" 000b00l;f;f;fffffffffffgfefggggggggggggggdghgdhdhddhcdgddhddhddhhhdhddhdhddhdddddddhddddddhdddddddddhddddddddddddddddjdddddddddpdddpddpdpdpdpdddppdododppopppppppppppppopppppoppvpppvpvpyppoppvpypyppypypvvpvpypyvvovvyvvvvvvyvvvvvvvvvvvvvvyyvyvyvyvvyyyyya000000-]=!*=-=&=*=*=*~~~***~**^///_66n///////#*~~*~*~*~~~~~~!!!!!!!~!~~,'''''''''',-@@@@$@@@$@@@@@>111121131213}3}||[|[[[<[[<<<:_/////////////nn/////#,,,,!'~~~~~~~~!''''',-@@@@@@@@////////////////////,',,',~~~*~~~''''',@@@@@@@> ",
+" 000000a;f;ff;f;ffffffgfffggfgffgfgggggghggghghhhhdgdhhddhhdgdhdhdhhdhhdhddgdddgdhdhdhdhdhddddhddhdhddddddddddhdddddddddddddddpddddjddpdpdppdpdppdpddpdpddpdppppppppppppdpppopppvppvvpvppppvvpypppypvpvppvpvypypyvpvvyvvvvvvyvyvyvyyyvyyvyyyyyvvyvyyvyvyyvoyyj000000c]]]*=-=--=*=***~*~****#////66666_//////^&*~*~~~~~~~~~!~!~!~!~!~!~~,''''''''',!%$>@@@@@@@$@@@>$11121131222322/2[[[[[[<[<<<</////////////////////*'!',,,~~~~~~~~,''',,,$@$@@@@@>///////////////////',,,',~~~~~~~,''',,$>$@@@@> ",
+" 000000eff;f;ffffffffeffgfffgggggggggggggghghgdhdhhdhdhdgddgddgdhdhdhddgddhddgddddhdddddhddhddddhddddhddddddhdddddddddddddjddpdddddddppphjdpddppdpdppdpdopppppppppppppopppppppppvppppopvvpyppvvvypvyvypyypypvpypvvypypvyvvypvvvvyvvvyvvyvvyvyyyvyvvyyvyvyyyyoy0000000*]]=*-*-=-*==******~*&^///n6666666_//////^-~*~*~~~~~~~~!~!!~!!~~~~~!,'''''''''']#@@@@@@@@@@@@@$$1111212122///^}[[[[[<[<<<<k////////////////////@,!,,',!~~~~~~]~''''',*@>@@$@@@(//////////////////,'',,'*~~~~*~''''''@@@@@$@> ",
+" 0000000lf;ff;fffffffffgffgggfgfggggggghghghghhdgdhdgdhhdgddhddhdhdcdhddhddddddhdhddhdhdddhddhdddddhdddddddddddddddddddddddddpdjdpdddphpppddpppdpdppdodpdppdppppppdoppdpppppppvppopyvvvyyyvyyyyyyyyyyyyyyyyyyyyyvyyvvvvvyvvvyvyvyvvyvyvyvyyvyvvyyyyyvyyyvyyvyyoa000000l]=**-*-=-==********~@///^666666666n^//////@**~~*~~~~~~~~!~!~~!!~!~~~~,''''''',,,*$@@@@@@>$@@>@>$$111121^/////$}[[[[[[<[<<<^////////////////////-',,,,,~~~~~~~~,'''',,#$@@@@@@@//////////////////','',,~~*~~~~,''',,@@@@@@@> ",
+" 000000ae;ffffffffffffgfggffggfggggggggghgghgdhdhdgddhhdhdhdhhhdhdhdhdhddgdhdhddhddhdddhddhddddhddddddddhdddddddddddddddddddddddddpdpppdpdppddpdodpdpddppppppppppppppopppppovovyyyyyyyvvyyyvyvyvyyyvypyvvyvyvvyyyvyyyyyyvyvvyvyvyyvyvyvyvyvyvyvvyvyvyyyyyvyyyyyo000000)**=*=--*-*=*-&***~*&////n66666666666n///////$*~~~~~~~~~~~!~!~~!~!~!!!!!,''''''''',&+@@@$@@@@@@@>@$111^////////2}}[<[<<[<<<k///////(u///////////(~,,','!~~~~~!~~''''''!@@@@$@@@>/////////////////,,,','~~~~~~~''''',@@@@@@@> ",
+" 00000acf;f;fe;ffffegffefgfgegggggggghghghggdhhhhddgdhdgdhhddhhdhhddgdhdddhddhddhdddhdcdddddhdddddddddddddddddddddddddddddpdppdddpdddpdpdpddpdpdpdodppppdpppdpppppppppvvovyvyvyvyvvyvyyvyvyvyyyvvyvyyyyyyyyyyyvyvyyyvyyyyyyyyyvyvvvvvvvyvyvyvyyyvvyvvyvyvyvvyyyo000000a===**-*---=**&*9***(///_66666666666666n//////^#**~~~~~~~~~~,~~!~!~~!~~~!,''''''''',,&@@@>$@@@@@@>>@^//////////^2}}}[[[<<<:<_////(tww////////////#',!,,'~~~~~~~~~'''',,&@@@@@@@@/////////////////''',',~~~*~~~,''',,@@@@@@@> ",
+" 000000effff;ffffefffffggfgggfgggggggghggdgdgddhdgdhdgddhdgdhdhhdhdhddhdhddhdcdddhdhddddhddddddhddhdcdddhddddddddjddddddpddpdddpddpdpdppddpodppdpddpdodpppppppppppopyyvyyvyvyvyvyyyvyyyyvyyyypyyyyvyvyyyyyyyyyvyyyvyyyyvyyvyyyyyyyyyyvvyvyvyvvvyyvyvzvyyyvyyvyyya000000-**=*=-*-*-***&***$////66666666666666666_//////>&~*~~~~~~~~~~,~~!~!~!!~~~~,''''''',',!#@@@@@@$@@$@@@@(////////^22k}k[<<<<<<<////uwwwx////////////~,,,,,,~~~~~~~~,''''',+@@@@@@@>////////////////,,',,,~*~~~~~''''',@@$>$@+> ",
+" 000000aff;fffffffgffgfgfgfgfgggggggghgghgggdhdgdhdhhdhdhdddhhdhdhdhdhddhddgddhhddddhdhddhddhdcddddddddddddddddddddddphoddpdddpddpdpdpddodpdpdpdppodpdppppppppdopyyvvyvyvyyyvyyyyvyvyvyvyyyvyyvyyvyyyyyvyyvyvvyyvyyyyyvyyvyyyvvyvyyvyyyyvyvvyyyyyvyyvyyvyyvyyvyyyj000000e==***-=-=-*&*&***////n6666666666666666665^//////>=*~~~~~~~~~~~~~!~!~~!!~!~~,''''''''''*#>$@@@@@@@@@@@>////////^22kk}k[[<<k_/////xwws////////////+'',,',!~~~~~~~!'''',,*@@@@@@@@(///////////////''''''~~~*~~~,''',,@@@@@@@> ",
+" 000000ae;ffffffffffgfgfggggggggggghghghgdddhhhdhhdhdhhhdgdhdhdhdhdhdhdhdhddddddhdhddddhdddhddddhdhddddddddddddddddddddddddpddddpdppdpdpdpdpppdpddpdoppdppppovyvvvyyvyyvyvyvyvyvyvyyvyvyvyvyvyyvyyyvvvyypyyyyyyyvyyyvyvyyyyyyyyyyyyyyyyyyyzvyvvvyvyyvyyyvyyvyyvyyy000000)*=*=*=&=&--***&*$////666666666666666666666n^//////$*~~~~~~~~~~,~~~!~~~!~!!~~!,'''''''','-+@@@@@@$@@@@+@>///////_22kk}k<<_////////t(//////////////-,,,,,'~~~~~~~~,'''',,#@$>$@@@@///////////////,',,',~~~~~*~''''',@@@@@@$> ",
+" 000000cff;ffffffgffgfgfgfggggggghgggghgggghddgdhhhdgdhddhdhdgdhdhdhddhddhddhhdddddhdhddddddddhdddddddddddcddddddddddpddjdddpdjhpdppdpdpdpdpdppppdpddppppvoypyvyyvyyvyyvyyvyvyvyyvyvyvyyvyvyyyvyyvyyyvyvyvyyvyvyyyvyyyyyyyvyyyyyvyyvyyyyyyyyvzyyvvzvyyvyyyyyvyyyyoa000000**==**=*-*&&*&*&(///n66666666666666666666666n///////#*~~~~~~~~~!~~!!!!~~~~!!~!!'''''''''',&@@@@@@@@@@@@@@(//////2222kk2//////////////////////////>!'!',,!~]~~~~~~'''',,~@@@@@@@@.#%&-%-==,',',','',,,~~~~~~~,''',,@@@@@@@> ",
+" 000000g;ffffffeffgfgfggfgggggggggghghgdgdddgddhdhhdddhcdhhhddhdhdhdhddhddhddddhdhdddddhddhddhddddddddddddddddddddddddddddpddddpdpdpdpddpppdpdppdodpppoyyvvyyvyvvyvyvypyyvyyvyvyvyyvyvyvyvyvyyvyvyyvyyyyyyyyyoyyyyyyyoyyyyyvyyyyyyyyvyyyyyvzvyvyzvyvzvyyvyyvyyyyyya000000&**=**---&*&**&+>//_66666666666666666666666666_//////(&~~~~~~~~~~~~~~!!~~~~~~~~~,'''''''','!%@@@@@$@@@@@@@@(////^kk22kk///////////////////////////%,,,,,'~~~~~~~~!''''',&@@@@@@@+##%-&%-=*',''',','',~~*~~~~''''',@@@@@@@> ",
+" 000000affffffffffegfgfgfggggggggghghgghghghdhhhdhchhhhdhdhdhdhdcdhddhdhdhdhhdhddddhdhdddddhdddddddddhddddddddddddddjpddpphpddpdpdpdpddppdppdppdpdppppyppyvyvyvyvyypyyyyvvvyvvyvyyyvyvyyyvyvyvyvyyvyyyvyyyvovyyyoyyvoyyvoyyoyoyvyyyyyyyyyyyyyyyyyyyyyvyvyvyyyyvyyyyj000000e==*=**&*---&=%>>//66666666666666666666666666665_//////>&~~~~~!~~~!~!~~!~~!~!~~!~~,'''''''',,*#@@@@@@@@$@@@@>(///_k_k22_///////////////////////////*',',,,~~~~~~~~,''',,,+@@$@@@@+%%%--&==!'',,',''',~~~*~~~,'''',>$@@@@@> ",
+" 000000bffefffffgfgfgggggggggggghggghgdghddhhdhhhdhdhddhhdhhdhdhdhdhdcddhddddddhdhddcdddhdddddhdddddddddddddddddddddddddddjddpdpddpdjdppdpdppjdpppoyyyvyyvvyvyvyyvyyvvvyyyvyyyvyvvyyvyvvyyyyyvyyvyyyyovyyoyyyyvyvyoyyyyyyoyyyyyyvyyyyvyyyyyyyyyyyyyyyyyyyvyyyyyvyyyy000000)*=*=**-&*&*&*>@>(n666666666666666666666666666666n^//////.*~~!~~~,~~!~!!~!~!~!~!~~~!,''''''''',*+@@@$>@@@@@@@@>//(2k2n2k///////////////////////////>!,,',,!~*~~*~~!''''',*>@@@@$@@##%-%--==,'''',,'',~~~~~~~''''''@@@@@$@> ",
+" 00000adfffffegfffffgfgfggggggggghghghghgdhdgddgdhdgdgdhdhdhhdhddgdddgdhdhdhdhdddhdddddddddhddddddddddddddddddjdddddddppdddddpddpdpddpdpppdpddppvyvvpyvvyvyvyvvvyvvyyvyvvyvvvyvyyvyvyvyvyvvvvyvyoyypyyyvoyvyvoyyoyyyyyovovyoyyoyyyyvyyyyvyvyyyyyyyyyyyyyzyyvyyyyyyyoa000000*=*=*=*--&*&#>@>(666666666666666666666666666666666n///////#~~~~~~~!~!~!~~~~~~~~!!~~!!'''''''',,,&+@@@@@@@@@$@@@>/^_2k2k_///////////////////////////&'!',,,~~~~~~~~,''',,,#@@@@@@@+#%%-%--=~,',''',',~~~*~~~,'''',@@@@@@@> ",
+" 000000effffffffggfggfgggggggghghghghgdgdhhdhdgddgddddhdgdhddhdgdddhddddhdddddhdddhddhdhdddddhddhddhddddddddddhdpddddpddddpdpddppdpppdppdppdppypypyyvyvyvyvvyyvyvyvvyvyyyvyyvyvyvvyvyvyvyyyyvyyvyoyyyyoyyyyoyyoyoyovoyyyyyyyoyoyoyyyyoyyyyyyyoyyyyyyvyyvvyyyyyvyyyyyj000000&=*==-*&=--&@>>@n56666666666666666666666666666666666n//////(%~~!~~~~!~!~!!~!~!~~!~~~~~,''''''''',,&@@@@@$@@@@@@@@^2_2_kk^///////////////////////////,,',,,!~~~~~~~~''''',~@@@@@@@@.#%&%&-==~,,,,,!,!~~~~~*~''''',@@@@@@@> ",
+" 000000aeffeffgfgfggfggeggggggggggghggdghhdhhdhdhdddgdhddhdcdhddddddhddhdhdhdhddhddhddddddddddddddddddddddddddddddpdddpdpdddpdpddppddppdpdpppyvyvyypypypyvyvvvvyvyvyyvyvyvvyvvyvvyyvyvyvyyvyoyovyyvyopyypyvoyvyyyyyyyyyoyoovoyyyyoyoyoyyyyoyyyyyyyoyyyyyyyyyyyyvyyyyyj000000e****&*-&=&#@@>$n5666666666666666666666666666666666665_//////>&!~~!~!~!~~!~!~~!~~~~~~!~~,''''''',,,~#@@@@@@@@@@@@@@_22_k2///////////////////////////.,!',,'~~~~~~~~!'''',,&@@@@@@@@##%%%%&&=~~~~~~~~~~~~~~~,''',,$>$@@@@> ",
+" 0000000ffffgffgfgfggggdghdgdhddhdhddhhddhhhdgdhhdgddhdgdhdhdhdhdgdhddhddddhddhdddhdddhddhdddddddddddddddddddddpddddjhpdddpdpdppdpdppdpdppooyypypypypyyvyvyyyyvyvyvvvyvvyvyvyyvyvvyvyvyyvyyvyvyyvoypyoyooyyyoyyovooooyyoyyyyyooyyyyyyyoyoyyyyoyyoyyyoyyyyyyyyyyyyyyyyy000000b==**-*&*&&++>>k:n6666666666666666666666666666665666666n_//////@*~!~~,~~,~!~!~~!~!~~~~~~~!,'''''''',,*.@@@@@@$@$@@@@$_2_k_///////////////////////////~,,',,,~~~~~~~~,''',,,+@@$@@+@$##&#&%&&*~~~~~~~*~*~~~~''''',@@@@@$@> ",
+" 00000ajffffffgfgfgfgghhdgdhdgdgdgdghhhdgdhddhhdhdhhhdddhdhdhdhdddddhdhdhddhddddddddddddddddddddddddddddddddddddddpddpddpdpdppdpdpddppdppyvpyvvypyvyyvvyyvvvvyyyyvyyvyyvvyyvyvyvyyvyyyyoyovoyoyoyyyoyoyyyooyvyoyoyyyyvoyyyoyoyyyoyyoyyyyyoyoyyyyyoyyyoyyyyyyyyyyyyyyyoa00000a**&**&*-&.$.+_k:k566666666666666656665655555565655656666n///////.~~~!~!~~,~!!~~~!~~~!~~~~!!,''''''',,,&+$@@@@@@@@@@@@$_2_///////////////////////////>',',',~~~~~~~~!'''',,&$>@@@@@@#$#&%&&--*~~~*~~~~~~~~~,'''',@@@@@@@> ",
+" 000000cfffgfgfggggggghhdhdgdhhdddhddhhhdhchhhdhdhddhhhdhdhdhddgdgdhdhddddhddchdhdhdhddhcdhddddhdddddddddddddpddjdddpddddppdpdpdpdjdpdjypyyvyypyyvypvvyvvyyyvyvvyvyvyvvyvyyvyvyvvyovovyoyyyyvoyyooyoyypoyyyooyoyyyyoyyooyyoyooyyyoyyooooyoyoyooyyyoyyyyyoyoyyyyyyyyyyyj000000=**&*=*-%).++:}:n:6656555555555555555556565555655556565665n///////%~~,~!~!~,~,~!~!~~!!!~~~!~!''''''''',,&@@@@@@@@@@@@@@$k_///////////////////////////-,',',,]~~*~~~~,''''',#@@@@@@@+##&#&%&m**~~~~~~~~*~~~''''''@@@@@@@> ",
+" 00000aggffefgffgefggggdghhdgdhhgdghhdhhdhhdhcdgdhhhdhhdhdhdhddddddcdddhdddhdddddddddhddddddddddddhdddddddddddpddddpdpdjhpdddjdpddppddpdppdppppdpppppppppppvpvppppppppvvvppvpvvvvpvvvppvpppvvvpvpvvvvpyvpvvyvyvvvvvvvyyyyvyyyyoyyyyyyyyyyyyyyyyoyyyyyoyyyyyyyyyyyyyzyyj000000&-*&*&*&.++.1nk<kk555556555555565555655555655555655555655655_//////>&~~~,~!~!~!!!~~~~~~~~~~!~~,'''''''',,*&@@@@@@@@@@@>@@_^///////////////////////////,',,',!~~~~*~~~'''',,*$>$@@@@@$###&%&--*~~~*~*~~~~~~,''',,@@@@@@@> ",
+" 000000gefgfgfggggggggdhdhdgddgddhddhhdgdhdgdhddhdhdhdhdhdhdddhddgdddhddhddddhddhddhdddhddddhddcdddddddddjddddddddpdddddppdpdddpppdppdppdppdpppppppppppvppppppvpvpppvpvpvvpvvpvvvvpvvvpvvvvvvvypvvvvyvvvvvvvvvyvvyyvvvvvyvvvyyvvvyvyyyyyyoyyoyyyooooyooyoyoyoyyyyyyyyyo000000e*&**-*++..$}}:}n<4565555555555555555555555555555555555556566n_//////@=~~!!!~,~~,!~~~~!~~~~~~~~~,'''''''',,*#@@@@@@@@$@@@@@///////////////////////////#,',',,~~~~~~~~!''''',%@@>$>+@@#$&%%&&-**~~~~~~*~~~~''''',@@$>$@@> ",
+" 000000agfgfgfgfgfgggggdgdgddgdhhhdgdchddhhddddgdhdhdhdhdhdhdgdhddddhdhddhdhddhdddddddddddddddddddddddddddddddpddpdddpdppddppppddppdpppdpdpppppppppppppppppppppvpvpvvpvpppppvvvvvvvvvvvvvvvvvvvvpyvvpvvyvyvvyvvyvyvvyyyvyvvyyvvyyyvyvvyvyyyyoyoooyyyozyyozozozyoyyyyyyzo000000)**&*&&>+$)&2}:}<}n55555555555455554555555555555555555555555656n^//////.*~~,~!!,~!!!~~~!!~~!!~~~~~,''''''',,,*$@$@@@@@@@@@@@>//////////////////////////~',,'',~~~~*~~~''''',~)@@@@@@++##&#&%&-*~~~~~~~~*~~,''',,@@@@@@+> ",
+" 000000aegffggfggggggggdhdhdgdgghghghghggggggghhghghgcghghedgdghgghgdgdedghghgdedgcghghhgdghhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhdhhhdhhdhhhhhdhdhhhdhhhdhdhdhdhdhddhdhddddddhddddhddddddddddddddddddddddddddddddddddddpdppdddpdddpdppdpdpppopoooyozjyjyzjyooooozoyyyyyyy000000b*&**&+>@+$}}2}n}:}6555554545454454545444455454555555555555555555n///////#~!!~~~,~,!!~~~!~~~!~!~~~~!''''''',',,-@@@@@@@@$@@@@@>////////////////////////>,',,,'~~~~~~*~!''''''&@@@@@@$@$##&#&&-&~~*~*~~~~~~''''',@@@@@@@> ",
+" 000000jffgfgfggggggggghhhhhdgfeffefffffegffefffefeffffeffffeffefeffefffffefefffffffefeflffeffffffeffflfflffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffflfffflffflffffffffffffffffffffffffffffffflfffffffffffffffffflffffoooyyyoyoyozzoyoyoyyyyyoa00000a&*-*#@>>.})}8<}k}k454545454544544545445545445454545455555555556555_//////(&!,!,~!~!,!!~~!~~~~~~~~~~~,''''''''',~&@@@@@@@@@@@@@@(///////////////////////=,'',',~~~~~~~~,'''',,#>$@@@@@+###&%&&-*~~~~~~~*~~,''',,@@@@@@@> ",
+" 000000cgfgeggfggggggggdhhdgdffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffflfflffflffffffflffffffffffffffffffffffffffffffffffffffffffffffffffffffffffflfflfflfffffffflfffflfffflffflffflfffflffffflffffffffflfffffflfflfffflfffyyoyjyoyozjozjzoozyyyyyza000000**&&@>@>13}32}}:}k:4545444544544444444444444544444544545455555555554_//////>&,~,~,~,~,!~~~~~~~~~~~~~~!,'''''''',,~#@@@@@@$@@@@@@>(/////////////////////(,,,',,!~*~~~*~~''''',*@@@@@@@@$#&#&%&-&~~~~*~~~*~''''',@@@@$@@> ",
+" 00000adfgfggggggggghgggdhhdhgf;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;l;;l;ll;;;;l;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ljazozoyjzoyyojzoyoyyyyyya000000&**#>>@>}3232}k:}kk44445444444444444444444444454444544444445454555555n^//////@*,~,~,!!,,,!!!~!!~~~~~~~~~,''''''',,,*$@@@@@@@@@@@@@>/////////////////////#,',',,~~*~~~~~,'''',,#@@@@@@@@##%#&%&&**~~~*~~~~,''',,@@$>@@@> ",
+" 000000cfgfgfgggggggghgddhhhhgf;]!]~]~]!]~]]]]~]!]!]~]]]]!]!]!]!]!]]]]~]~]]]]]]~~]!]!]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]a0aoooooyozjyyooyoyyyyzyz00b000)*&>>>$n}238}&k}}}<5444444444444444444444444444444444445444445455555555n///////#~!~,~~,~,!!!~~~~~~~~~]~!~!,''''''''',-+@@@@@$@@@@@@@>////////////////////,',''',~~~~~~~~''''''!@@$>$@@@$##&#&%&-*~~~~~*~~''''',@@@@@@$> ",
+" 000000agggggggggggggghghhhdhhff;]]]!]!]]]]]!]!]]]]]!]]!]]]]]]]!]]]]!]]]]]!]!]!]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]']]']]]']]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]*aajoyzjzoooyjzozoyyyyyyyj000000e&$@>>$}}8}8}}}k}k}:44:444:4444:44:4:4:444:44:444444444444544444454555555n///////%~,,~!,!!,,!!!~~~~~~~!~!~~!''''''''',,&@@@@@@@@$@@@@@(//////////////////@,'',,'~~*~~*~~~'''',,&@>)@@@@@$##&%&&&-~*~*~~~~,''',,@@@@@@@> ",
+" 0000000gegfggggggghghghdhhhdhgf;!]]]]]!]!]!]]]!]!]]]!]]!]]!]!]]]!]]]!]!]]]]]]]]!!]!]!]!]!]]!]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]']]]']]]]]]]]]]]]']]]']]]']]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]']]]]]]']]']]']]']]]']]]]]]]]]]]]]]]]]a0joozjyzjyzoojyjzjzyyyyy000000)#>>@>k<}})}88}}}}}n4:44:44::4:4:4:4:4:4::4:4:44:44444444444454444445455454_//////>&,,~,,~~,!,!!~~~~~~~~~!~~~,'''''''',,~%@@@@@@@@@@@@@@(/////////////////=,'',',~~~~~~~~,''''',.@@>+@@++##&#&%&***~~*~*~''''',@@@@@@@> ",
+" 000000jgfggggggggggghgghhhhdhgl;]]!]!]]!]]]!]]]!!!'!]'!!!!!!]'!!!'!!!!!!,!,!!']!!!!!!!!!!!']'!']']']']']']']']']']']']']']']']']']']']']'']'']]'']]']']']']']']']']']']']'']'']'']'']'']'']'']'']'']''']']']']'']]']]']]']']']]'']'']'']']']!]]*aajjyjzooyjyjzzoyyoyzyyyy000000b@@>>2}k}}8}28}}}}}}:4:4::4:4:::4::4:4::4::4:4::4::::4:4444444444445454455554^//////@=,~,,,!,~!,,~~~~~~~~~~~~~~,''''''',,,~$@@@@@@$@@@@@@>////////////////(,',',',*~~*~~~~''''',*@$@@@@@@$###&%&&-*~~~~~~,''',,@$@@@@+> ",
+" 000000aggggggggghghgghhdhhhhhgf;]]!]]!]]!]]''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''',!0aayzjyjzjyyjooozjyyyyzyya0000a0^@>@k}}<}388}&k}}}}4:4::4::::::::::::::::::::4::4:4:4::4:4:444444444454445455n^//////.*~,~,,,~,!,!~~]~!~~~~~~~~!,'''''',,,,*+@@@@@@@@@@@+@>//////////////.,,'''',~~~~~~~~,'''',,#@@@@@@+@##&#&#&*&~~~*~~''''',@@@@@@@> ",
+" 000000jggggggghgggdghgghdhdhhg;;!]]!]!]!]!''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''',0aaojoyoooozjzyjoyoyyyyyza00000a>@>k}}}}}}823}}}}}}<::::::::::::::::::::::::::::::::::4::4:4:444444444454554555n//////(#!~'~,,!,,,,!~~~~~~~~~~~~~!'''''''',,,&$@@>$@@@@@@@@@>//////////>-=*],,,'',~~~*~~~~,'''',~@$>$@@@@$##&%&&&-*~~~~~,'''',>$>$@@@> ",
+" 000000cgggggggghghgghgdhhhhdhgf;]]']]]!]''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''a0jyoyjzoooooozjyzjzyyyyya000000>>$}}}}}}2&}&}3}}}}}:::::::::::::::::::::::::::::::4:::::4:4::4:44444444445445454_//////(&!,!,~,!,,,,~!!~~~~~~~~~~~,''''''''',,%+@@@@$@@@@@@@@(//////(%&&-===!','!~*~~~*~~!''''',&@)>@@@+@#$&#%&&&=*~*~~''''''@@@@@@$> ",
+" 00000aoggggggghgghgdghgddhdhhgf;!]]!!]!''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''',,a0jjzyjojzyjzojzoooyyzyyza00000a>>k}}}}}}}}8283}}}}}:<:<:<:<:<:<:<<:<:<:<:<<:<:::::::::::::::4:::::4:4444444445455n_//////>=~'!,,!!,!,!~]!~~~~~~~~~~~,''''''',,,~#@$>@@@@@@@@@@>(///.&&-%---==*]~~~~~~~~~~~,'''',!$@@$@@@@+##&#%&&&*~~~~,'''',+@@@@@@> ",
+" 000000cgggghghghggdgdhhhhhhhhgf;]!]!]'''''',''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''aaaojyzjyjzjozjoozjyyyyyyj000000>2}}}}}}}}&}&}8}}}}}::<<:<:<:<<<<:<<<<:<:<:<<::<:<:::::::::::::444:4:4:4444444445555n^//////+*!'~'!,!,,,~~~~~~~~~~~~~~~,'''''',,,,=.@@@$@@@@@@@@@>@%#%%--%------**~~~*~~~~~!''''''-@@>@@$>+)##&%%&&-*~*~''''',@@@@@@@> ",
+" 00000aoggggghgghhghggghhhdhdhff;]']!]'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''',0aayjyjyjyjyjoyjzooyyyzyzj000000)}}}}}}}}}8}&}&}3}3}<:<<<<<<<<:<<<<<<<<<<<<:<<<<:<<<<<:::::::::::::::4:4:4444454444555n///////#~,!,!',,,,,!~~~~~~~~~~~~~!'''''''',,,&+@>$>$@@@@@@@@.#%#%%-&%&%&&-&=*~~~*~~~~,''''',#@$>@+>$@###%%&&&*~~~,''',,@$@@@@@> ",
+" 000000cghghgghghghgdhdgdhhhhhgf;]]!]'','''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''',aaazjyjzjyjzozjozjyozyyyzj000000_}}}}}}}}}38383}}3}3<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<:<:<<<<:<::::::::4::4:4:44444444/^n554_///////%,'~,!,'!,,!~~~~~~~~!~~~~~!''''''''',,&+@@@@@$@@@@@@@.%#%%%%%&%%&-&-**~~~*~*~''''',~@@+@@@@@$##&#&%&-*~~''''',@>@@@$@> ",
+" 000000dghggghghghhhghhhhdhdhhff;']!!''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''',a0jjyjyojzojojzojzooyyzozj0000002}}}}3}}3}}813&3}3}}}<<<<<<<}<<<<<<<<<<<<<<<<<<<<<<<<<<:<:<:<<:<::::::::4::4:4444_///_4554_//////>&'!,!,,',,,,~~~~~~~~]~~~~~,'''''''',,,#@@@@@@@@@@@@@+####&#&#&%&&&--**~~~~~!'''',,&@@@@@@@+###%%&&&-~~,'''',@$@@@@@> ",
+" 000000cggghghghgdghhhghdhhhdhgf;]']'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''',aaaoozjyjyjzjyjojyjzyyyyyj000000}}}3}}}3}}88833}3}3}}<<<<}<<<[<}<[<[<}<[<[<<[<<<<<<<<<<<<<<<:<:<::::::::::4:4:44://////_555n_//////@*'!,!','!,,!~~~~~~~~~~~~~~,'''''''',,*#@@@@$@@$@@@@@+#$##&%%#&&&&-&***~~~~,'''',,+@@@@@@@$##&#&&&&*~''''',@@@@@@@> ",
+" 00000adhghgghgdghghhhhhhdhhdhff;]]!'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''',0ajjojyjzjyjojzyjzooyzyzzj000000)}}}}3}}3}}}&3&33}33}<}<<[<[<[<[<[<[<[<}[<[<<[<[<[<[<<<<<<<<<<<:<<<<:::::::::4:4^////////n455n///////.~,,!,!',,,,!~~~~~~~~~~~~~!,''''''',,,-$@>@@@@@@@@$@++#$##&&#%%&&&-&=*~*~!''''',&@$>$@@@+$##&%%&&-~,'''',@@@@@@@> ",
+" 000000hgggdghghhhhhhhghhhddhhgf;'']'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''',a0ayjojyjyjyjyjzjzjyyyyzyz000000k}3}3}3}3}888}88}3}3}[[<[[[[<[[[[<}[<[<<[[[[[<[[<[<<[[<<<<<<<<<<<::<<::::::::::n//////////^n555n///////%~,,,,,'',,,!~~~~*~~~~~~~~!'''''''',,,&+@@@@@@@@@@@@++##$%&%#&%&&&-&**~~,''''',#@)>@@@@)$##&%&&-*,'''',@@@@$@@> ",
+"000000bddgghgdghhghghhhhdhhhdhff;]!''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''',aajjzjyjzjyjzjojjojyyzyozj000000)}3}3}3}3}}3&83&}3333[<[[<[[[[<[[[[[[[[[<[<[[[<[[<[[[<[[[<[<<<<<<<<<:<<::::::::////(////////_4554_//////(&',,,!,!',,,!~~~*~~~~~~~~~!'''''''',,~&+>$@@@@@@@@@@$+###&#&#%%&&-&-=~~''''',*>@$>$>@+$###%%&&-,''',,$@@@@@@> ",
+"0000000dgghghgdgdhhhhghhhdhdhdgf;']''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''a0aoojyjoozjjozjyzjzyyzyyz000000_}}3}3}3}3383888}3}3}[[[[[[[[[[[[[[[[[[[[[[[[[[[[[<[<[<[<<[[<[<<<<<<<<<<::::::_////xwt////////_555_///////>=!',',!'!,,,~~~~~~~~~~~~~~,'''''''',,~#@@@@@@@$@@@@@+$###&%&#&%&&&--*!''''''%@@@@@@@+##&&#&&-~'''',@@@@@$@> ",
+"000000adhghgdghhgcghhhhhdhdhhhgf;]!'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''~aaaojzjojojyjzjojjoozyyzyo00000j)}3}3}33}3}8888933373}[[[[[[[[[[[[[[[[[[[[[}[[[[[[[[[[[[[[<<[<[<[<<<<<<<<<<::<////uwwwt/////////nn//////////+*,!',,,,,,,,~*~~~~~~~~~~~~,''''''',,,*$>$@@@@@@@@@@@+$####&%%%&&&-&=!'''',~+@@$@@@@$#$&%%%&=!'',,@@@@@@@> ",
+"0000000hgdghhhhhhhhhghghhdhhdhgf;']''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''a0jzjjyjzjojojyjyyjyyzyyzy000000)[3}33}33}83888833}33|[[[[[[[|[|[[|[|[|[[[[[[[[[[[[[[<[[[[[<[<[<[<<<<<<<<<<<:^////twww////////////////////////#!,,','!',,,!~~*~~~~~~~~~~!'''''''',,,-+>$>+@@@@@@+@@)$###&#&%%&&&-=]'''',-@)>@@@@@)##&#&&=]''',@@@@@@@> ",
+"00000b0dhghhghghhghhhhhhdhhdhhgl;'!''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''',0aajzjyjojzojzjzjojyyyyyzo00000b)}3}333}9}3383&8933333[[|[|[|[[[|[[|[[|[||[|[[[[[[[[[[[[[[[[[[<[<[[[<<<<<<<:k/////((xu/////////////////////////(%,,!',,',,,,~~~~~~~~~~~~~~!''''''''',,&@@$>$@@@@>$@++$###%#&%#&%&&==!'',,$@@@@@@@$##%&#%==!'',>$@@@@@> ",
+" 000000dgdghhhhhgdhghcghdhdhdhgf;]'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''aaajjyjzjojzjjjjzjyyyzyzoz0000002}33}333}333&38383333||[}|[|[||[||[||[|[|[|[|||[|[|[[|[[[[[[[[[[[<<[[<<<<<<<<_^//////////////////////////////////>%!,'!',',''!~~*~~~*~~*~~~~,'''''''',,!#@)>@@$@@@@@@@+$###&#&%#&%&==]!''*@@$>$>@@$##&#&%=='''@@@@@$@> ",
+" 00000achhhhhghhhhhhhhhhhhdhhhgf;'!''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''',0ajjzjjjzjjjoyjjzjjozyyzyj00000a)333}333333338&8933333|||[||[|[|||||[|||[|||[|[|[|[|[[[[[[[[[[[[[[[[<[[[<<<<<<<_///////////////////////////////////(-,,,,',,,',!~~~~~~~~~~~~~~,'''''''',,*#@@@@@@@@@@@@@+$####&#&%%%===],,#@@@@@@@+$##%%-==],,@@@@@@@> ",
+" 000000dhhghhhhghhghghhhdhhddhgf;']''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''',aaajjoojjyzjzjzjjzzozyyyzo000000}}3333333333&388833337[|[||[|||[|||||[||||[|||||[|[||||[|[[[[[[[[[[[[<<[[<[<<<<<k_//////////////(////////////////////>*,,',',',,,!~~~~*~~*~~~~~!,''''''',,,*+@@@@@$>+@@@+@+##$&%#&&#--===!~+@@@@@@@+##&#&-==,,@@$>@@@> ",
+" 000000chhhhghhhhhhhhhhhhdhdhhgf;!'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''',0aaojzjyjjjjjjojjjjyyyzyzj0000008}333|3333338m8m939333||||||||||||||||||||||[|||||||[|[||[|||[[[[[[[[[[[<[<[<[<<<<k^///////////uwx(////////////////////@~',,,,'',,,!~~~~~~~*~~~~~!''''''',,,,&@$>@@@@@@@@@$+$#####&%&%--===&@+@@@@@+$##&%--=~,@@@$@@+> ",
+" 000000dhhgchhghghhghhhhhdhdhdgf;'!''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''a0aojjjzjyjyzjzjooozyzyyyj000000}83333333333988&333939[|||||||||||||||||||||||||||[||||[||[[|[|[}[[[[[[[[[<[<<<<<<<<k//////////xwww//////////////////////#,',,','',,,~~~*~~~~*~~~~~!'''''',,,,,%@$@@@$>$@@>+@@$###&#&%----*-+>@$>+@@+##&%--==!@@@@@@@> ",
+" 000000cdhhhhhhhhhhhhghhhdhdhhgl;',''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''',aajjoojjjojjjjjjjzjyyyzyzj00000j333333333333m898993739||||||||||||||7|||||||||||||||||||[|||}|[|[|[[[[[[[[[<[[<[<<<<<<_///////(wwws////////////////////////&,',',,,',,,~~~~~~~~~*~~~~,''''''',,,~)@@>)@@@+@@@@++$###%%%-&-&*&>>@@@@@+$##--%-**@@@@@@@> ",
+" 000000jhgdghghhghghhhhhhdhdhdgf;']''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''',a0azjjzjzjzjojzjjjjzyzyzzj000000333333939333m8&8939393|7|77|7|77|77||77|77|7||||||||||||||[|||[|[|[|[[[[[[[[<[<[<<<<<<<<_///////tx//////////////////////////(*,',',''',,!~~*~~*~~~~~~~~,''''''',''*+$>@@@@@@@@@+@$####&-%--&-+>@>$>$@@##&----*>@@@@$@> ",
+" 00000acdhghhhhhhhhhghhhhdhdhhgf;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''',0aajzjjjjjjjzjjzjzoyzyzyzj00000a3339333339338m3&893939|7|7|77|777|777|77|7|777|77||||||||||||||||[|[|[[[[[[[[[[<[<<<<<<<:k^///////////////////////////////////@]''',,,''''!~~~~~*~~*~~~~!'''''''',,,-$@@@@@@@@@@@@@$###%-%--&&>>>>>@@@+##-%-&*@>@@@@@> ",
+" 000000adgdhhgchhghhchhhdhdhdhgf;]'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''aaajjjzjzjzjjzjjjjjzyyzyz00000003333337939398m8m893939|7|7|77|7|7|7|7|7|777|7|7||77|7||||||||[|||[|[|[|[[[[[[[[[[[<<<<<<<::^////////////////////////////////////.,''',',,',,~~~~~~~~~~~*~~!'''''''''',&@@$>$@@@@@@@@@+##%%--&-#>>@>>@$@##----->@$>@@@> ",
+" 00000aahhghhhghhhghhhghdhdhdhgf;,',''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''!a0jjjjjjjjjjjjojzoozyzyzza00000a33393933739389m8m39i397777777777777777777|7777777|77|77|7||||||||||||[|[|[[[[[[[<[[[[<<<<<k///////////////////////////////////////%,',,''''''!~~*~~~*~~~*~~~,''''''',,,~)@@+>$@@@@@@@@@###%%&&&@^>>>>>>+#%%&-&>>>$@@@> ",
+" 000000ahhhhghhhhhhghhhhhdddhhgf;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''aaajyjojzjozjjzjjjzyyyzyz000000b337393939393m8mm893993777|7|77777777777777|77|7777|77|7|77||7||||||[|||[|[|[[[[[[<<[<[<<<<^////////////////////////////////////////(-',,,',',~~~~~*~~~~~~*~*~~,'''''',,''*#@@@>$>$@@+@@@+#%%%-&#>>>>>@@>##%--&>@>@@@+> ",
+" 000000ahhhhhhghhhhhhhhhdhhddhgf;!''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''',,0aajjzjjjjjjjjjzjjozyzyyza00000b9393393939398m8mm899397777777777777777777777777|77777777|777||||||||||[||}^}[[[[[[[[[<[<<_///////////////////////////////////////////-''',',~*~~~~~~*~~~~~~~~~~!,'''''''',,&++>)@@@@@@@@+@.#)%%&+>>^>>>>@#%%-&>>@>@@@> ",
+" 0000000dhhhhhhhghhhghhhhdddhhgf;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''aaajjjjzjjzjzjjjjzozyzyzy000000)}939393979939m8m8939987777777777777777777777777777|7|777|7|7777||||||||||^//$}[[[[[<[<[<[///////////////////////////////////////////(,'','',~~~*~~~~~~*~*~*~*~~~~!''''''''',,&+>@@@$>$@@@@++.%%%%>>>>>>>>+.#-&>>>>$@@> ",
+" 0b0000bhdghghhhhhghhghhhdhddhgl;','''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''!a0jojjjjjjjjjjjjjjzzyzyzz00000b)93939393939898mmm9939977777777777777777777777777777777777777||77|7||||||2/////1}[[[[[[[[<2//////////////////////////////////////////-','',,~~~~~*~~~~~~~~~~~~*~~*~~!'''''''',,~&@@@>)@@@@@@@@++&#+>>>^>>>>.#)&>>>>>@@> ",
+" 000000dhhhhghghhhhhhhhdhdhdhgf;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''',aaajjzjjjzjjjzjzjjyyzyyzj0000002379397993i399m88m89939i77i777i77i7i777i77i77i777777777777|7777777|7|||||////////2[[[[[<[<<<_///////////////////////////////////////>,','''~~~*~~~~*~~~~~*~~~~~*~~~~~~,'''''''',,*#@@@>@@@@@@@@@+##>^>>>>>>>##%^>>>@@$> ",
+" 000000chhhhhhhhghhgchhdddhdhgf;','''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''!a0jjjazjjazjjjjjjjyzyzyzj00000a83939393999998mmm9999997i777i77i77i7i7i797i7777i7i771$377777777|7|77|7||$/////////^2[[[[[[[[<k^/////////////////////////////////////!,'',',~~~~~~~~~*~*~~~~*~*~~~*~~*~~~,''''''',,,*$@$@@@@@@@@@@+.+>>>>>>>>@+#>^>^>>@> ",
+" 000000jdhghhghhhhhhhhhhdhddhgf;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''',aaajjjjjjjjjjjjjzzyzyyzya0000008939939939393m8mm8999987i7i7i7i7i7i7i7i777iii7i77i78///17777777777|77|73////////////_}[[[<[[<<<k^//////////////////////////////////#','',']~~~~~*~~~~~~~*~~~~~~*~~*~~~~~~!,''''''',',-+@@$>$@@@@@@@@>^>^>^>>>++(>>>>>@> ",
+" 000000adhhhchhhhhghhhdddhddhgl;'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''!a0azjjjjjjjjzjjjjjyyzyzz000000b9793999399999m8mmm98999i7i7i7i7i7i7i7i7iii77i7i7i77^////^17777777777|7|^/////////////(_[[[[<[[<<<k/////////////////////////////////,,','',~~~*~~~~~~~~~~~~*~*~~~~~~~*~*~~~~!'''''''',,~&@@@+>$>$@@+@+@>(>>>>^@+(/(^>>>> ",
+" 000000ahhhghghghhhhhhdhddhddgf;',''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''aaajjzjjzjjjjjzajzzyyzyz000000b3939399793998mmm8m999997i7iiii7iii7i7i7i7ii7i7i7ii8///////^377777777772/////////////////2[[[<[<[<<<_//////////////////////////////-''',',~~~~~~~~*~*~*~~~~~~~*~*~*~~~~~*~*~~~,'''''''',,~#@@@)>@@@@@@@@@>(^>>>+^(>((>>^/ ",
+" 0000000dhhhhhhhhghhhdchhphdhgf;'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''!a0ajjjjjazjjjjjjjyyzyzyz00000b)3999i89899999m8mmm99999ii7i7i7ii7iiiiiiii7iiiii7i7^/////////$377777777///////////////////^k[[<[<[<<<<^///////////////////////////>',,'',!~~~*~*~~~~~~~~*~~~~~~~~~~~*~*~~~~~~*~~,'''''',,,,*$@@+>$@@@@@@@+>>>(>>(/((^(>(/ ",
+" 0000000cdghghhhhhghhdddddddhgf;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''',''aaajjjjzajjjjjjzjzyzzyzo000000$79398i9993999mmm8m899997iiiiiiiiiiiii7iiii7i7iiii$////////////$7777771/////////////////////^}[[<[<<<k////////////////////////////]'''','~~~~~~~~~~~~~~~~~*~*~~*~*~~~~~~*~*~~*~~~!,''''''''',-$@@@>$>+@@@@@@>^>(((^(((>(// ",
+" 00b000ddhhhghhghhhhhdhdhddhgf;'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''!a0azjjjjjjjzjjjjjyzyzyzj00000088979893999998mmmmm99999iiii7ii7iii7iiiiiiiiiii7i3//////u(///////17773////////////uwt/////////_}[<[<<_///////////////////////////.,'',,'~~~~~~~~*~*~~*~~~~~~~~~~~~~*~~*~~~~*~~*~*~~!''''''''',,&+@@)>$@@@@@@@>>^>/(((^((// ",
+" 000000cdhhhhhhchghhdhdddhddgf;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''',''aaaajjjjjjjjjzajzzyzyzzj00000089999999999999m8mmm99999i7iiiiiiiiiiiii7iiiiiiiii$/////(wwt///////^87^////////////xwww//////////2<[[k////////////////////////////,,'''',~~*~~*~~~~~~~~~*~*~~*~*~*~~~*~~~*~~~~~*~~*~*~!'''''''',,~&>@@@>$@@@@+@>>((/(((((/// ",
+" 000000adhhhghhghhhhddhdhphhgf;'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''!a0ajjjjazjjjjjjjyyzyzyzj00000a9938i98i989999mmmmm99m99iiiiiiiiiiiiiiiiiiiiiiii8/////(xwww(////////^////////////uwwwt////////////k<^///////////////////////////-'',',,~~~~~~~~~~~~~*~~~~~~~~~~~~*~~~*~~~~*~*~~*~~~~~~~,''''''',,,~#@@@@@@@@@@@^>>^((^(//// ",
+" 000000ahdghhhhhhhhdhddddhddgf;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''',''aaajjzjjjjjjjjjjzyzyzzz000000b39999999999999mmmmm99999iiiiiiiiiiiiiiiiiiiiiiii^//////xwwt///////////////////////(xx//////////////////////////////////////////(,,'''',~~~~~~~*~~*~~~~~~~~*~~~*~~~~~~~*~*~~~~~~~**~*~*~~~,'''''''',,*+@@@$>@@@@>(((/((((/// ",
+" 0000000ddhhhhghhhhdddhddhdhgf;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''',0a0jajjjjjjjzjjjzzyzyyz000000)97989999999998&mmmm9qqq9iiiiiiiiiiiiiiiiiiiiiiii8^//////>t////xx(//////////////////////////////////////////////////////////////]'',,',~~~~~~~~~~~~~~~*~*~~~*~~~~*~*~*~~~~~~~*~*~~~*~~~~*~~!''''''',,,,&+@@$@@@@>>>>((^(/////",
+" 0000000cdhhghhhghhhdhjhddddgf;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''aaajjjjjz0zajjjzyzyzzyj000000299999i899999m9mmmmm99999iiiiiiiiiriiririiiiiiiiiii1(/////////uwwwu////////////////////////////////////////////////////////////+,,''''~~~*~~*~~*~~~*~~~~~~~~~~~*~~~~~~*~*~*~~~~~*~~~*~*~~**~~!'''''''',,~+@>+@@+>^>(((((/////",
+" 000000adhhhhhhhhhddhddhddhgf;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''',0aajjjjjjjajjjayzyzyzza00000098999999999999mmmmmm9mqq9iririririirriiiririiririrrii$////////xwww(////////////////////////////////////////////////////////////,'''',,~~~~~~~~~~*~~~~~~~*~~~*~~~~~~*~~~~~~~~*~*~~*~*~~~*~~~**~~,''''''',,&@@@@@++(>^(/^(/////",
+" 000000adhhhghghhhdddhdddhdgf;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''a0ajjz0zajjjjjjzzzzyzz000000b99999999999m99mm&mmm999qriiiiiriririrriirirriiiriiiiii9$///////tws///(u///////////////////////////////////////////////////////%,',','~~~~~~~~~~~~~~~*~~~~~~~~~*~*~~~*~~*~*~~~~~*~~~*~~~*~*~~~**~~,'''''',&@@@@@@@>(>(((//////",
+" 0000000ddchhhhhhdhddhddddhgf;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''',aa0jajjjjjjjjjjzyyzyzz000000)998999999999m9mmmmmmqqqmirirrriririirirririiriririririii9^///////////xwx(////////////////////////////////////////////////////(,,'','!~~~~~~~~~~~~*~~~*~~~*~~~~~~~*~~~*~~~~~~*~~~~*~~*~*~~~~~*~~*~!''''''!)$>$@@@@^(>((///////",
+" 000000cdhghhghhhddhjhddddhhgl;eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeea0jjjjjjjjzazazzzzyzya00000029i999999qm99q9mmm&mq9q9qiriririrrrrirririrrrririririiiiiir8/////////uwwwt////////////////////////////////////////////////////*','','~~~*~~*~~~~~~~~~~~~*~~~*~~~~~~*~~~~*~~*~~*~*~~*~~~*~*~*~*~~*~,''''''-@@@@@@@>>>^(////////",
+" 000000jddhchhhhddhdhddhdhddabaa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aa0aaajjjjjajaajayyzyzyza0000009999999m9999q9m&mmmm9mqqmririrrririrrirrrrririrrirrirrrrriiii1///////uwww////////////////////////////////////////////////////@',',''~~~~~~~~~~~~~*~*~~~~~~~~~~*~~*~~~*~~~*~~~~~~~*~~~*~~*~~~*~~*~!''''''!@@@@@@@+>((>>////////",
+" 000000ahdhhghhhhddddhdddddhjaaaaaacaaaaacaaaaacaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0aaa0ajjjjjjjjjjjzyzyzzz000000b999999999qq999qmmmmmq99q9rrrriirrrrirrririrrrrrrrirriiiiririri9$///////su///(wu//////////////////////////////////////////////!''',',~~~~~~~~~*~~~~~~~*~~~~*~~~~~~~~*~~*~~~*~*~~~~~*~~*~~~*~~~*~*~''''','#@@@@$@@@^>>(/////////",
+" 0000000dhhhhgchddhdddddhdddhdhdhddphdhdpdddhddpddddhddddddddddddddddddddddvovovvoopovooopooooooooooooooooojoojooojoojojojjojjojjjjjjjjjjjjjjjjjjjjjjajjajjajajajajajaajaajaajaajaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaajaajajjjjj0zajjjjjzzyzyzj000000$999999q9q99mqqmmm&mmqqqqqirirrrrirrrrrrrrrrrririrrirrrrririririi9^//////////xwwx////////////////////////////////////////////#',',''~~~~~~~~~~~~~~~~~~~~*~~~*~~~~*~~~~~~*~~~~~~*~*~~~*~~*~~**~~*~,''''''*@$@@@@@@>>(^(/////////",
+" 000000jdhhhhhhdhddhdhddddddddddddddddddddddddddddpdddddddddpddddddoddodppvvvvvvvvvovvvovvvvvvopopovopoovopopooopoooodjpoopjoopjdododojdjojpjojojdjjpjojdjojdjjojjjjdjjjjojjjjjjjjjjojjjjjjjjjjjjjjjjjjjjjjjjjjajjajajjajjjjjjjajjjjjjjjjjjjjaajjjjajjjjjzzzyzzzj0000008999m99m9q9q99qmmmmmmqqqqqirrrrrrrrrrrirrrrrrrrrrrrrrirrririririiri8(///////uwwwt///////////////////////////////////////////(,''',',~~~~~~*~~~~~~~~*~~~~~~~~~~*~~~~*~*~~~~*~*~~~~~*~~*~~*~~~~*~~~'''''''.@@@@@@$>>(>(///////// ",
+" 000000addhhhhhddddddddhddddddddddddpddddddpddpdddpdddddpppddpodpppdpdpppvvyyyvyvyvvvvyvoyooovovoovooopoooooopooojpoooojpjoooojoojjjojojdoojojdjjjjojjjjjjjjjdjjjdjjadjjjjjjjjjjdjjjjjjjjjjjjjajjjjajajajajajajjajjjjajjjajajajjjaajaajajajajjajajjajajajzyzyzyz0000000m9999q99m99q9mqmm-mmmqq9qrrrrirrrirrrrrrrrrrrrrrrrrrrrrirrrririrriiii1///////uxw////////////////////////////////////////////=',',',~~~~~~~~~~~~*~~~~~*~~*~~~~~~~~~~~~~~~*~~~~~~*~~~*~~*~~*~*~~*~,''''''&@@@@$>)@>^(>////////// ",
+" 000000ahdhhghhphddhdddddddddddddddddddddddddodddddoddoddpppddpddpdppdpdpyvvvvvovyovyovypyvvovovvovooyovpooooooopooooooooopjpjojdjpojdjjjojdjjjoodjjdjdjojdjjjjdajjjjjjjdajdjjjjajjjjjjjjjcjjjjjjjcoajjjjjjjajjajjjcjjjajajajajjjjajjjjjajjjjjjjjjjjjjjjzzyzzzzy000000b99m99q9q9q9q9qqmmmm-qqqqqrrrrrrrrrrrrrrrrrrrrrrirrirrrrririrrriirrriri9$///////(///////////////////////////////////////////>,''','~~~~~~~~~~~~~~~~~~~~~~~~*~~*~~*~~~*~*~~*~*~*~~~*~~~~~*~~~*~~~~'''''',+@@@@@@@>>>(////////// ",
+" 0000000dddhhhhdhddddhddddddddddddddddddddddddddpdddpddpdddpdpdppppdpdppvvyvyvyvvvvyovyvovoovovoyovovoooopooopooojooopjpjojojpoojojojooojojojdjjjojjjjjjjjjjjjjjjdjjjjjjjjjajjjjjjjjcjjcjjjjjjcjajjjjcjajcjjjjjjajjjjjajjjjjjjjajjjjjjajjajjajjj0jjjjajazyzzozzj00000a$999999q9q9qqqqqm-mmmqqqqmrrrrrrrrrrrrrrrrrrrrrrrrrrrirrrrrrrirrriiiriiii8$/////////////////////////////////////////////////!',,'',~~~~~~~~~*~~~~~~~~~*~~~~~~~~~~~*~~~~~~~~~~~~~*~~~~**~~~*~~*~~,''''',%@@@$@@@@>^(////////// ",
+" 000000adhdhhddddddddddhdddddddddddddddddodddpdddpdppdpdpdodpdpdpppppdpovyvvvyvoyvvvooyvyvyyovovooovoopooopoooopooojoooooopjjjpoojojojpjojdjjojdaoojojdjjojdjjjjjjdjjcojjjdjcojcojjjjjjjjjjjjjjjjajjjjjjjajcjajjajajajajajjajjajaaaajjjajjajj0zjjaj0zajzzyzzzy00000008q9qm99q9q9q9m9qmmm-mqmqqqrrrrrrrrrrrrrrrrrrrrrrrrr8qrrrrrrrrriirrririiiiii8^//////////////////////////////////////////////#,''','~~~~~~~~~~~~~~~~*~~~~~*~*~~~~*~~~~*~*~*~~*~*~~~*~*~~~*~~~*~~~,''''''*@@@@@@@@^>/////////// ",
+" 0000000ddhhhdddhdhdddddddddhddddddjdddddddddodddpdpdpdpddpdpdppddodpppvyvvyvvyvvyoyvypovoovvooovovoooooooooopoooopoooooojjpoojojpjpjojojjojdjjjojjdjjjjdajjjdjjjjjjjocjjjjjjjjjjjjjjjjjjjjajjcjjjjjajjajjjjjjajajajajjajajjajjajjjjaajajjajjjajjjjjaazzyzzzyz000000b99999qq9mqq9qqqq-mmmmqqqqqrrrrrrrrrrrrrrrrrrrrrrrrq/($irrirrrrrriririrriiriir1(///////////////////////////////////////////(,''''',~~~~~~~~~~~~*~~~~~~~~~~~~~*~~~~~~~~~~~~*~~~~*~~~~~~*~~**~~*~~,''''',#@@)$@@+@/////////// ",
+" 0000000cddhhhddddddddddhddddddddddhddodddpdddddpddpdpdodpppodpdodpdppvvyvyvyvvovvvovyyovvooovvovoovpopovooooojpjojpjopjpjoojpjpjojojpjdjjojjjojjdjjjdjjjjjjjjjjjjjjjjjjjjjjjjjjjcjjjjcjajjjjjjjcjjajjajjajjajjjjjjajajajaajajjajjajjjjjajjjajajajajjjyzzozzza000000)im9q99q99qmq9qmqmm-mqqqqqqirrrrrrrrrrrrrrrrrrrrrrr>///^8{rrirrrrrririrriiiiiii8///////////////////////////////////////////-,,,,',!~~~~~~~~~~~~~~~~~~*~*~~~~~~~~~*~~~~~*~~~~*~~~~*~~~~~*~~~*~~~,''''''-@@$>@@@@^////////// ",
+" 000000addhhdhdddddddddddddddjddddpdddddpddddddpddppddpdpddpdpdpdppdpyyvvyvovyvoyyyvovvoovyvooyovooooooooooooojpojpjojojpjpjojjpjojojjojojojjdjjjjjjjjjjdjjjadjjjdjjjjjocjjjcjjjocjjjjjjjcjajjjjjjjjjajjajjcjajaajajjajjajjjaajaajaajajajajajajjjajajzyzzzyz000000b999q9mq9qqq9qqqqm-mmmmqqqqrrrrrrrrr{r{r{{r{r{r{rr{8//////^9rrrrrrirrrrrirririii^//////////////////////////////////////////>''''''!~~~~~~~~~*~~~~~~~~~~~~~*~~*~*~~~~~*~~~~~*~~~~*~~*~*~~~~*~~~~~'''''',+@@@@@$@>////////// ",
+" 0000000dddhdddddddddhddddddddddddpddpdddddoddpdpdddodppdppppppppdodvvvvyvyvvyyvvyovoovvovopoypooopoopoopoooopoooooopjpjojoodjojojdjojdjjdjjojjojjdjjdjajjdjjajjcjjjjjjjjjcojjjjjjjajjjcojjcjajajajcjjajcjjjjajjjajaajajaajajajajajjajajjjajajj0zaajzzyzyzzj000000)m9999q9q9qqqqqqqmm-m=qqqqq{rr{r{r{r{r{rr{r{r{r{rrr(////////$9rrrrrriirriiririi8///////////////////////////////////////////],'',,,~~~~~~~~~~~~~~~~*~~~~~~~~~~~~~~*~~~~~*~*~~~*~~~~~~~~*~*~~**~~,''''''%@@@@@@@@///////// ",
+" 000000adddhddddddddddddddddddddddpdddjddddpddppdppdpdppddpddpdppdpvvyvvyvovvovovvovyooyoyoooyovooooojpjpjpooojpjoojojpjpjjojdjojjdjjjojjdjjjjdjjjjjjjdjjjjjojjjadjjjjjjjjjjjajajjjjajajajjjjajjajjjjajjjaaajaajjajajajajajajjajajajjj0jajjajjaajjazzzzzzza000000&9mq9q9q9mq99mqqq-qmmmqqqqqr{rr{r{r{rrr{{r{r{r{r{{$///////////$rrrrrrrirrririr9///////////////////////////////////////////#'','''~~~~~~~~~~~~~*~~~~~~~~~~~*~~~~~~~~~*~~~~~~*~~~~*~*~*~~~~~~~~*,''''''~@+@$@+@@>////// ",
+" 0000000hdddddhddhjdhddddddddddddddpddddpddpdpdppddpdpdpppdododpvpvyvyvyvyvyvyvoyvovvvopovovopoopoopooooooooooooopjpjpjjpjojojojojjoojdjjjjdjjjjdjjdajjjjjjcjjjojajcjjcjjjajjjjjjcjjjjjjjjajajjcjajcjaajajjajajaajaajajajajajajaaajaajjjajjj0jjaajzzzjzyzz0000000399q9qmq9qqqqqqqmmm-mmqqqqqr{rrrr{r{{{rr{r{r{r{rrq/////////////^8rrrrrrirririi$///////////////////////////////////////////,,'''',~~~~~~~~~~~~~~~~~~~~~~~*~~~*~*~~~*~~~~~*~~~~*~~~~~~~~*~~*~*~~,''''',#@@@@@@@@////// ",
+" 0000000jdhddddddhdddddddddddddjddpddddpddddpdpddppppppdodpdpdppdpvvvvyvvvovyoyvovyooyyovooooooooooopooopjpjpojpjoojojoojooojdjojdjojjjojojjjdjjjjjjjjjjcojoocjjjjjojjojjjjcjjcjjjajcjaajjcjjajjjajjjjaajajajcjajajajaajajaaaajajajjjajj0ajjjajaj0yyzzyzzj000000)9q99q9q9q9mqqqqqq-mq-q*qqqr{rr{{{r{rrr{{r{r{r{r{{$///////////////>8rrrirririr9///////////////////////////////////////////-,'',,,~~~~~~~~~~!~~~~~~~~~*~~~~~~~~~~*~~~~~*~~~*~*~~~*~~*~*~~~*~~~~,''''',=@+@+@@$@(///// ",
+" 000000ahpddddddddddcdpdddddddddddpdpdddodpdpdodpdpddpddppppppvppyvyvvyvyvvyvovyvovopoovovvovopyooooooooooojpjoojpodjpjpjjpjjojojjdjojojdajjjjjdjjjdjjojjjaojjjjjajcjjajjjjajjajjjjjjajjjjjajajaajaajjjajajjjajajaajajaajjjajajajaaajajjjajjjajjzzyzzyzz000000089m99q9qqqq9qqmqmmm-mmqqqqqr{{rrr{r{{r{r{r{r{r{rrm//////////////////$9rrrrrrii>//////////////////////////////////////////>,',''',~~~~~~~~~~~~~~~~*~~~~~~~~~*~~~~~~~~~~~~~~~~~*~~~*~~~~*~~~*~*,,''''',+@@$@@@@>//// ",
+" 0000000hjhddddddddpddddddpddddpdddpdpddddppddpdpdpdodpdpdpppppppvyvyvoyvyovyvovovyyovooyooooooopooopjoojoooojpjojojojojojoojdjojjjdadjjjpjjdjajjjjjjjjocjjjjjocjjjjjocjjjjjjjcjjaajjcjajajcjjajjcjaajajajaaajjaajaajajaaaajajaajajajajajaaajaaayzzyzzyz000000bq99qq9q9mqqqqqqqq-mq-mqq*qq{r{r{{rrr{r{rr{r{r{r{{^////////////u///////$ririrr8///////////////////////////////////////////*''''',~]!~~~~~~~~~~~~~~~~~~~~~~~~~*~~~~~*~~~*~~~~~~~~*~~~~*~~~*~~~~,''''''%$@@@+@@+///// ",
+" 0000000jhddddddddddddddddddppdddjddpdpdpddpdppdppdpdpdodpppppppovvyvvvyvvoyovyvoovooypypovovooooooooopjpjooooojdjopjpjpjjpjojjodjjjjjjojjjjjjdjjdajjdajojjjjcjjjjjjajjjcjjcjjjajjcjjjajajjjaajajajajajcjajjaaajajajaajjajaajajajaajaajaajajjjazzzzyzzza000000)999qm9qq99qqqqqqmm-mmqqqqq{rr{rr{{{{~~~{!{{{{{rrm////////////uwwu///////8rrr9///////////////////////////////////////////+,,',''~~~~~~~~~~~~~~~~~~~~*~~~~~~~~~~*~~~~~*~~~*~~*~*~~*~*~~~*~~~*~,''''''!@@@@@@@$>//// ",
+" 0000000ddddhdddddddddddddddpddddddpdpddopdpdppdpdpdpdpppdppdppvyvyoyovoyvvyopovyopovjyoovjpopoopoopooopjpjpjooojojojjjpjojjdjjjjjojdjjdjocjjjjajojajjjjcjjjjocjjcjjjjjjajjjajjajjajcjajaajajajajajaajjaaajajaajaaajaaajajajaajajaajajaajaj0jajzyzzzyz000000099q99qmqqqqmqqqqq-mq-m=qqqq{r{{{{~,!'~,',,!'!!{{{{m(//////////wwww////////(8r$///////////////////////////////////////////,',',',~~~!~~~~~~~~~~~~~~~~~~~~*~~~~~~~~~*~~~~~~~~~~~~~~~~~~*~~~~~~~,''''',#+@+@$@@@///// ",
+" 0000000jhddddddddpdddjhpdddddpddddpdppddpdppdpdodododpppppvppvvvyvvyvyvyovvoyyovovypypyooooooooojojoojoooojpjpjopjoopjojojojjpjpjjjjjjjjoojjdjjjjcojjjojjjjjjjjjjjcjajjjajajjcjjajjjajcjjacjajaajajaajajaajaajaajaajaaajaaaaajaaajaaajaajajazzzyzyzza000000)89qq999qqqqqqmqqqm-q-qqqqqq{r{{,,!!!!!~!!!!~!,!!{{rr8////////(xwwu//////////^(//////////////////////////////////////////%'',','~~~~~~~~~~~~~~~~~~~~~~~*~~~~~*~*~~~~~~~~*~~*~~*~~*~*~~~~*~*~~,''''''=@+@@@@+@(//// ",
+" 0000000ddddddddddddddddjddpddpdpddpddppdodpdpddpddppdppppdppvvyvyvvyvoyvoyopoyopyjoooovovoopooopopooopjoojojoojjdjjojjdjdjojjjjpjjdjjdjjjjjadjjojjcjjjjcjjjjjjjjjjdajcjjjjajjajjajaajjajjjjcajcjajaaajjaaaaajaajaaaajaaajajaajaaajaajajaaaazyzzyzzz00000008mq99qqqq9qqqqqqqmqmmm-q=qqq{~!!!~,~,!,,!,~,,!!!!,!]{{{$////////ut//////////////////////////////////////////////////////(',''',,~!~!~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*~~*~~~~~~~~~~~~~~*~*~~~~~~'''''',+@$@+@@+>//// ",
+" 0000000jhdjhdddddddddpdddddpdddpdpdpdpddpdppdppdppppppppvpppvvyvvyyvyvovvvoyovooopypoopjvoooooojooojojyojpjpjdjojoojpjjjjojdjjjjjjjjcjjdjjjjajjjjojjjjjjjjcjjcjajaajjjacjajjajacjajjaajaaajjjajaacjacjaajjaaaajaajjaaajaaaaaaaajaaajaa0jaajzzzzzyzj000000b99mq9q9qmqqqqqqqq--m-qqqqq*{~!!,!,!~,~~,~,'~'~'!!,!!~{{{q$//////////////////////////////////////////////////////////////=',''',~~~~~~!~~~~~~~~~~~~~*~~~~~~~~~~~~*~~~~~~*~~*~~*~*~~~~~~~*~*~~,''''''-@+@@@$@+^/// ",
+" 0000000dddddjdddddpddddjddddpdpddpppdpdpdodpppdpdppdppppppvvyvyvovovyyyoovvyoypyoyoooooooopjpopjpoopjpjpjojojoojpjojoojdjjjojdjdjojojjcjjojjocjjjjjjjjcjjjjjjjajajjjjjjjjcjcjjjjjcjjaaajaaaajajajjajaaaacjjaaaaaaaajaajaajajaaajaaajjjajazzyzyzzz0000000&9q99qmqq9mqqqqqqqqq-m-qqqqq~~!,~,~,~''~'~,~,~!!!,!,,,!{{{rm>///////////////////////////////////////////////////////////+','',,!~~~~~~~~~~~~~~~~~~~~~~~~~*~~~~*~~~~~~*~~~~~~~~~~~~*~*~*~~~~~,''''''!+@@+@@@@>/// ",
+" 0000000pddddhdddddddpddddpdjddpdppddpdppddpdpppppppodpppppvvvyvyvyyvvopypoypopooopoovvooooooyjyjyjojoojooojpjojojjojdjjjjojdjjjjjdaojojjjjjcojjjjcjjjjjjjcjjjcjjjajacjaajjjaaajajaaajjajajaaaajaaaaajajjaacjaaaajaaaaaaaaaaaajaajaj0jaaajzzzzyzzj000000b899q9q99qqq9qqqqqm-mqmqq=q=]!,~,,~'~,~,~,!,~',,~'~'~!,!!{{{rm////////////////((/////////wt(/////////////////////////////,'','',~~~!~~!~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*~~~*~~*~~*~~*~~~~~~~*~*~,'''','#+@$@@+@@// ",
+" 0000000dddpdphjdpddddpddpddpdpdoddpppdppdodddodpdpdvdppppvyvyovypoyvyoyooyoyooyoovoooooooopjpopjopjpjpjdjojpjojdjojjoocojjjjjjjjjjjjjjdjojjjdjjjocjjjjjjocajjjjcajjjjjaajajjaaaajajcaajcajajaaajaajcaaaaaaaajaaaajaaajaajaaaaaaaajajaaazyzjzzzz0000000&9mq9q9qqqqqqqqqqqmq---qqqq*]~'~'~,,~'~'~'~'~~'!,!!,!'!,,,{{{^////////////////wwt///////twww(///////////////////////////%',,'''!~~~~~~]~~~~~~~~~~~~~~~~~~~~*~~~~~*~~~~~~~~~~~~~~~~~*~~*~~~~~,''''''*+@>)$@+@> ",
+" 0000000jhpdddddddpddpddddppddpddppddpdppddoppdpppvppppppvvoyvyoyvypoypovypopypopoovooypojoooojyojoojojjojpjjpjojjdjjooojojdjjdjjjdjjjjajjjjajadajjjcjjcjjjjjcjjjjajaajjjcjjcjjajcjjajajaaajcjacjaaaaaajaaajaaaaaaajaaaaaaaajaajaaaaaj0zzzzzyzzj000000b399q9qqmq9qqqmqqqm-mqmqqq{==!,~,~'~,~'~,~'~'~'~'~'~'~,!,!,~{8////////////////swwws//////wwwx///////////////////////////(,,'',',~~~~~~!~~!~~~~~~~~~~~~~*~~~~~~~~~~~~~~*~~~*~~~*~~~*~~~~~*~*~~'''''',.++@@>+@> ",
+" 0000000dddddpddddddddppdddpdppdpppdoddppddppppppppppppppvvyvvypyoyovypjyoyjooooooopoooopoooopjpoopjpjpoojoojojojjoojjjdjjjjjjdjjjjjjjjocjjjjajjjjjoajjjjajjjajjcjjajcaajaajajaajaajcaajaajaajaaaaajaaaaaaaaaaajaaajaajaaaaaaaj0jaaaaazyzzyzyz0000000#qq99mq99qmqqqqqqqmq-m-=q=q]~~'~'~,~'~,~'~'~,,~'~'~'!'!'!'!'*/////////////////swwx///////(tw(///////////////////////////=,'',',~~~~~!~~~~~~~~~~~~~~~~~~~~~~~~~*~~~~*~~~*~~~*~~~*~~~~*~*~~~~~,''''''-@@$@+@$+( ",
+" 0000000pdddddpddjdddddppdpdpdppddpddpppppppdpodppppppppovyvyvyyvvoyooyvoppypypoooyjopjooopjojojojojpjojpjjdjpjojojdjjjjjdjjjjjjjjdjjdajojdajjjjcjjadajajjcjjjajjacjjjjajajaajajajaajajaaaaaaaaajaaaaaacjaaaajaaaaaaaaaaajaajaajaaajazzjzzzzzj000000b39qq99qqq9qqmqqqqq-m-qmqqq=]]~'~'~'~'~'~'~'!'~'~'~,'~'~'~'~,.////u/////////////(xu/////////(///////////////////////////@,','''~!~~~~~~~~!~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*~~~,,'''''!+@@@@@@@> ",
+" 0000000ddpddpddddpdpddpdpppdpdpdodpddodpdodppppdpoppppvyvyovovoypvvvyoyyoojoooopoyooypjooyoooopjooojpjjojojjdjdjjjpjojjjjdjjjdjajjjjjjjajjjjjjocjajajjajjajajajjjaajcajaajajcaacaaaajcaajaajaaaajcaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaayzzyzzzz0000000&q999qq9qqqqqqqmqqqmq-m-qq*=],,~'~,~'~'~'~,~'~'~,'~,!,~'~',,=////uwws///////////////////////////////////////////////////!,'',',~!~~~~~~~~~~!~~~~~~~~~~~!*~~~~~~~*~~~*~~~*~~~*~~~~*~~*~*~~~*~,''''',%@++@++++/ ",
+" 0000000jddpddppdddpdpddpdpdppdoddpppddppppppppppdpppppvyvvyvyvooyooypoopovovovoyjvojoooojpjpjyjpjoojojpjoojojjjjojjjpjjdajjdajjjjcojjjjjjcjcjjjjjjjcjjajjajcjajcjjajjaajjcajajajjcjaajacjaaaacjaaaajaaaaaaaaaaaaaaaaaaaaaaaaaaaaajzzzzzyzzj000000b3mm99qm99qqmqqqqqq--qmqqq==*~!~'~'~'~'~'~',~',,,'~'',,'~'~'!>////xwww//////////////////////////////////////////////////#','','!~~~!~!~~~~!~~~~~~~~~~~~~~~~~*~~~~~~~~~~~~~~~~~~*~~~~~~~~~*~~,''''''*@+@$@@@@> ",
+" 00000000ddpdddpdpdpdppdppdppdddppppppppdppppdpppoppppvyvoyvovyvyovvyopoyvooooooyooopoopooyjpjojojpjpojojpjojojpjjpjjjjjjojjjjdjojjadjcjjjojjjjjcjajjjcjajajjjajajajajcjaajaaaaaaaaaajaaajcajaaaaaaaajaaaaaaaaaaaaaaaaaaaaaaaaaaa0jzzyzzzzz0000000)999qqq9qq9qqqqqqqqq-m--qqq=]~'~'~'~'~,,,'~',~'~'~'~,~',,',,&/////xwws/////////////////////////////////////////////////(,'','',~!~~~~~~~~~~~!~~~~~~~~~~~~~~~~~~~~~~~~~*~~~*~~~~~~~*~~~*~~~~~'''''',#+@@+@$@@ ",
+" 0000000addpddddpdppddpddppppdpdpddodppppppppppdppppppovvyvyvovvoyyoyooopovovooopjpjyjyjojoopjpjojojjpjjojdjojjojjocojojcojjjajjjjjjjjjjjjjjjajjjjjajjjcjjaajcjacjaajaacjaajcjaaaacaaaaaaaaaaaaajaaaaacaaaaaaaaaaaaaaaaaaaaaaaaajzzyzzyzz0000000b999999qqmqqqqqqqqqmm-qmqq=]*]!!,~',,,'~'~,,,,'~',',',,'~'~',.//////ux////xt(///////////////////////////////////////////-,'',',~~~~!~~!~~~~!*~~!~~~~~~~~~~~~~~~~~~~~~~~~~*~~~*~~~*~~~*~~*~*~,''''''-@+@+@@@+^ ",
+" 0000000adpdpddppddppdppddodpdjdpdppdpppppppppvpppppvyyyoyyovyovoopoyoyooovjpoyoooyjpyjypojoojpjpjpjojpjjojjdjjdjojdjjoocjjojadjjjjjjjcjjcjjjjajcjajajjcjjaajjjjajajajajcjaaajajajcaaaaaaaaaaaaaaacaaaaaaaaaaaaaaaaaaaaaaaaaaaazzyzzzzzz00000008999qmq99q9qqqqmqqq-mq--qqq=*]'~''~'~'~'~'~'~'~'!,~'~'!,,,'!,,-(/////////uwwwu/////////////////////////////////////////>',''',,!~~~~~~~~~~~~!~~~~~~~~~~~~~~~~~*~~*~~~~~~~~~~~~*~~~~~~~~~~~~,'''''',+@$@+@++> ",
+" 00000000ddddpdpdpddpdjddddpdpdppppdoppppdppppppppppvvvyvvyyovoyyopypopooyooyjpooopjyjpjjopjooojojojpjjojdjojjjjjjjjjjjojojjjjjjcoadaojjjjjcjjcojjjjcjjjacjaaaaajacjaaaaaajacjaaaajajaaaaaaacjaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0jajzzzzyzyz0000000b99q999qm9qq9qqqqqqm-q-qm{qq]]~'~,~'~'~',,',,'~',,'~',,','~',,,,,%////////xwww//////////////////////////////////////////!','','~~~!~~~~!~~~~~~!~~~~~~~~~~~~~~~~~~~~~~*~~*~~~*~~~~~~~*~~~*~*~,''''',%+@@+@+@+/ ",
+" 0000000adpdpddpdodpdpdopdppdpdpppdppppppoppppoppppyvyvyovyvopypyoooooopoopyoojyoopjyjpoojpjojpjpjojpjojjjjdjojdjoadjjjjjjocjjjjjajjjjcjjjoajjjcaajjajajjjajjaajajcajaajaaaaaaaaaaaaajcajaaaaaaaaaaaaaaaaaaaaaaaaaj0aa0aaaaaazzzzzzzza00000008999qq9q9qqqmqqqqqqmmmmm-qq*=*'~''~'~''~'~'~,,'~'~''~'~'~',,,',,',!$//////(xwu///((////////////////////////////////////#'',','~!~~!~~!~~~~~~!*~!~~~~~~~~~~~~~~~~~~~~~~~~~*~~~~~~~*~~~*~~~~~,''''''~@+@$@@$@> ",
+" 00000000dpdpdpdpddpppdddoddodpppdopppdppdpppddppppovoovovyyooyooyoypyoyoooopjypjooopjyjoojpjojojojojojpjojjjojjjjjjjdjjjcojjjjjjjcjjjjjcjajjajjjjjjajaajcjacjjaaajacjcjajaaacjaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaajzzzzzzzz0000000$9999999q9q9qqqqmqq-q--m=qqq*=~,,!,',,~'~','''~',,'~'','','~'!,,,'''~q@//////(////xwx///////////////////////////////////,,'''',~~~~~!~!~~!~~~!~~~!~~~~~~~~~~~~~~~~~~~~~~~~~~~~*~~~~~~~~~~~~~,''''''#+@+@+@+@/ ",
+" 00000000pddpdpdppdpppdpdpdpppdpppppppppoppvpoppppyvyvyvoovypypypoooooopyjoypjpjoooojpjooojooojpjojdjojojjdjjjdjjdjjjcojjjoadjjjjjjjajjjjjajjjajajaajcajajajacaajaajaaajcaajaaajcaaaaaaaaaacaaaaaacacaaaaaaaaaaaaaaaaaaaaajzzyzjzzz000000ab99q9q9mq9q9qmqqqqqq-mqmmqqqqq*'!'~'~',','~,~'~',,',',~'~,,,',',',,,'{{rm>////////swwwt/////////////////////////////////-''',',~~!~~!~~~!~~~~~~!~~~~~~~~~~~~~~~*~~*~~~~~~~~~*~~~*~~*~~~~*~*~'''''''-@+@+@+@+( ",
+" 0000000dpppddpdppdppdppdppdopppppdoppppppvdpppovyvyyoyvyooyjyooypyooooyoojyoopoyjpjypjyjpjpjjojdjjjojoojjjdjjjjjjjojjjjjjajjjcojcjjcjajcjajajcjajcojajaajajajaajaaaaaaajaaaaaaajaaaaaacaaaaaaaaaaaaaaaaaaaaaa0aaaaaaaaaazzzzzzyzj000000089999m99q9q9q9qqqqqqm-m--qqq={],!'~'~'~'~'',',',~',~',','~','~',,',,'{{{r{8///////swww/////////////////////////////////>,,'',,,~!~~~~~~~~~~~~~~~~!~~~~~~~~~~~~~~~~~~~*~~~~~~~~~~~~~~~*~~~~~!,''''',.+@+@$@@@ ",
+" 00000000pddppdpdpdpdpppppppdpppppdppppppppvppvpvvyopyvovypypoyopojpypoooypooyjpjyojojpjpjojpjojojdjdjjjojjjjdjjdjjjjjdjadjjjjjjjjjjjjajjjjcjjjjajjaaajcjaaajcajcajcaaaaaaaaaaaaaaaaaaaaaaaacaaaaaaaabaaaaaaaaaaa0aaaa0azzyzyzzzz0000000)999999q99mqmqqmqqqq-mq-qm{qqqq'',,,',,','~'~'~,,',''~,,,'',,',,'!',,'~{{{{{{$///////tu/////////////////////////////////~,'',''~~~~!~~!~~!~~~~~~~*~!~~~~~~~~~~~!~~~~~~~~~~~~~*~~~~~*~~~~~~~~,''''',&+@$+@+@+/ ",
+" 0000000appddpdpppppppppdppppppppppppppppppopppvvyyvoyovooooopoyopojyooojyjojyojpjyjpjojoojojpjjojjjojdjjojjjajjjjjjjajjajjcojjjjjjjjjjjajjaajacjajjcjjajcjaaaaajaajacjaaaaaaacaacaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaajzzzzzyzza000000b8999999qm9q99qqqqqqqmmm-qq=qq*{!,',,,,,,~',','',,~','','~,,',',,,,',',{{{r{r{{r$///////////////////////////////////////+',',',~~!~~~!~!~~!~!~~~~!~~~!~~~~~~~~~~~~~~~~~~~*~~~~~~*~~~~~*~~*~~,'''''']+@+@)@$@> ",
+" 0000000jjpdppdpdpddppppppppppppppppppppppppvpvovoyyvyovypyyoooyopoyopypoopjpjyjpjojpojojpjojoojooojjjdjjjocojjjdadjjocjjjjcjjcjcajcjcjjajjcajjjaaajjaaaaajajcaaaaajaacajcaaaaaaaaaaacaaaaaaaabaaaaaaaaaaaaaaaaaaaaajzzzyzzzzj0000000)999m9q99999qq9qqmqq-m-q-mqqqqq{,'~'~','',,,,,,~''',,,~'''','~','',',''{{{{{{{r{{9>/////////////////////////////////////,',''',!~!~~!~~~!~~!~~~~~~~!~~~~~~~~~~~~~~~~~~~~~~*~~~~~~*~~~~~~~~~~,''''''#+@$@@@+@/ ",
+" 00000000ddpdppdjdpppppdpppppppppppppppopppvpvyvyvvovovooyopypoooyjoojyjpjyoojpjyjpjojpjojjpjojjjjjdjjjjojojjdajjjjjjjojjjjjjjjjjjjjjjajjcjjaaajcjaaaajaaajaajaajaaaaaaaaaaaaaaaacaaaaaaaabaaaaaaaabaabaaa0aa0aaaaaazzzzzzzzz0000000)39999999qqq9qm9qqqqmmmmmmqqqqq{r!',',,'{','~''',~'~'',',~,',,'',,',',,,{{{{{{r{r{rrm///////////////////////////////////%'','''~~~~!~~~~~!~~~!~!~~~~~~!~~~~~~~~~~~~~~*~~~~~~~~~*~~~~*~~~*~~~,''''''=++@@+$@+> ",
+" 00000000dppdppdppdpppppppppdoppppoppppppvpppvovyyvyoyovoyoooooopyoyoooyjoooyjpjyjoojojpjojojpjdojjojdjjjjjjjjjjjcjjjajjjjjjjjjjjajajajajjcjjaajjajajcjcaacaaaacaaaaaaaaaaaaaaaaaaaaabaaaaaaaaabaaaaaaaaabaaaaaaaazzyzzzyzz0000000089999999m999q9qq9qqqq-m-m=qq=qqr{{'~',,,,',',,,,'',',',,',','',',',',''~r{r{r{{r{rr{r^/////////////////////////////////>',''',,!~!~~~~!~~~~!~~~~~~~!~*!~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*~~~~~~'''''',+@+++@++> ",
+" 0000000apdpdppdpppppppppppppppppdpppopvpvvvyyovyovvyovooyoypyooopopyjpopojpjyjpjojpjojpjdjjjojjojjjjjdjjdjjjojojjjjdajcjjcjcjajjcjajjajjajcjaaacaajjaajajaaaajaacaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaa0aaaaabaaba0jzzzzzzzz000000002999999q99q99q9qmqqmmmmmmmqqqqq{{r{',,'',,,',,,',',,,,',',',',,'',,'',',{{{{{{r{r{{r{$(/////////////////////////////////='',,',~~~~!~~~!~~~~~!~~~~~~~~~~~~~~~~~~~~~~~~~~*~~~~~~~~~*~~~~~~~*!,''''''-++@@+@++^ ",
+" 00000000jdppdppppppppppppppppppppopppvpppvpvyvoyvoovoyoypooojpyjyjpjyooooyjpjyjoojpjojojojojojjjpjojjjjjocjjjjadjjajjjjjjjjjcjajjjcjajaajajajjajaaaajcaaajaaaaaaaaaaacaacaacaaabaaaaabaaaaaaaaaaaabaaaaaa0aaaajzzzzzzzzj0000000)89899999999mq9qq9qqqm-q-m=qqqqq{r{{{'~'!',,,,'''~','',,'',,','',','','''{{{r{{{{{{r{r//////////////////////////////////+,,'''',!~~~!~~~~!~!~~~~~!~~~~!~~!~~~~~~~~~~~~~~~~~~~*~~~~~~~~~~*~~~!''''''!@$++$+@+> ",
+" 00000000pdpjdppdoppppdopdodopppppvpvpopppovoyyovyyoovyooypyoyjypjyojoojojyjyjpjojoojojojpjojdjjjjojjdajjojjcojajjcjjjjcjjjjjjjajjjajcjjaaajaacjcjaaaajacaacjaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaabaaaaaa0aaaaaaaaazzzyzjzzz0000000b399999999m999q99qq9qmmmm-qqqqqqq{rr{{',','',',,,',',,'',,',,'',,'',',','{{{{{r{r{r{r{$//////////////////////////////////,','',,~~~,~!~!~~!~!~!~!~~~~~~~~!~~!*~~~~~~~~~~~~~~~~~~~*~~~*~*~~~~~,'''','#+@@+@++@/ ",
+" 00000000ddppdpppppdppppppppppvpppppppvppvoyvvyovoypoooypjoopjyyoopjypoopopjoojpjpjpjoojjojjjpjjdjjjjojjjjojjjjjojocjjjjjajajcajajajajcjajcjajajacjaaajaaaaaaaacaacaaaaaaaaaaaaabaaaabaaaaaaaba0aabaabaaba0azzzyzzzyz00000000893i89999999q99qm9qqq-m-mmqq=qqq{r{{r{~'~'~,,',''~''',,','''',,'','',',''{r{{r{{{r{{rm//////////////////////////////////%''',,'!~~~~~~~~~~~~~~~!~~~~~~~!~~~~~!~~~~~~~~~~~~*~~~~~~~*~~~~~~*~~,''''''*++++@$@+> ",
+" 0000000apppppppppopppppppppppopvppvppvvvvyyvovyoyyopoooypyooopooyjojyjojoopjojjojojdjojdjojjjjjjdjjjojjjjjjjdajjjjjjjajjjjjjjajcjajajaajajaaaajaajaaaaaaaaaaaaaaaaaacabaacaaaaaaaaaaaaaaaaa0aabaaaaaaaaaazyzzzzzzz000000002399999999999qm99q9qmmmmmm-qqqqq{rrr{r{r''','',,',',,,'',',','''',','''',{{{{{{r{{r{rr>/////////////////////////////////(,,'''',~~!~!!~!~!~!~!~~~!~!~~~~~~~!~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~'''''',.+@+++@+> ",
+" 00000000jppppppdpppppppppppvpdpoppoppppvovoyovoypoyoypoyojyoyjpjopypjoyjojojpoojpjjjojjojjjojojjjcjjadjadjjajjajjjjcjjjcjjajajjjcjjajajcaajaaaaaacaaajcaacaaaaaaaaaaaaaaaaabaabaabaaaaabaaaaaaaabaaaaaaazzzzyzzyzz0000000)9999398999999999q9qqmmq-mmqqqqqqrr{{rr{{{,,','',,',''',,',','',','''',''{{r{r{{r{r{r{8//////////////////////////////////=,',',,!~~~~~~!~~~~~~~~~~~!~~~~~*!~~~~~~~~~~~~~~~~~~~*~~~~~~~~*~~~~~'''''',-+$+@@+++( ",
+" 00000000jppppppppppppppppoppvpppppvvvvvyyvvoyooyyoooyjpypojpyjyjojyyjpojyjojojjjoojpjojoojojdjoojojjjjjjadajjcjjcojjcjjjacjjajajaacjaajacjajcjaaajaaaaaaaaaaacaaaabaaaaabaaaaaaaaabaa0aaaabaa0aaaabaaajzozzzzzzj0000000b33939i99999m99qm9q99mm-mmmqqqqqq{rrr{{rr{{{',',','',','''','',''',','''',{{qm{r{{{r{rq(/////////////////////////////////@',''''!~~,~~,~~!~~~!~!~!~~~!~!~!~~~!~~~~~~~~~~~~~~~~~~~~~~~~~~~~*~~!''''''!++@++$@+> ",
+" 00000000jpdpppppppdopppppoppvpvppppppvovyovyypoopypyoyjoyjyopoypjojyjpjpjpjpopjoojjojjjdjjjjjjjjjjocjjjjjjjjjjjjajjjajjjocjajajajjajaaajcaaaajaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaa0aazzzzzzzzzz000000008789989899999999999qqmmm-m-qqqqqqrr{rr{r{r{{{'',',',','',''','',''''''''{{{r>/$qrr{{r{$//////////////////////////////////!,'',',!~~~!~~~~,~~~~!~!~~!~~~~~~~~~~!~~~~~~~~~~~~~~~~~~~~~*~~~~~~~~,''''',%++++@+++/ ",
+" 00000000pppppppppppppopppvppppvvvvvvyyoyoyoyyoyjyjpjpyypoojojyjypjojyjojojjjojdjjojoojjjojjjdjjdajojjjjjcjjjjjjjcjjjaaajaajcjaaajaajajajajacaaacaaacaaaacaaaaaaaaabaaaaabaabaaa0baaaaabaa0aab0aaabjzyzyzzyzj00000000289397999i899999q9q9qmmmmmmqqqqqqr{rrrr{r{rr{{{'','''',',&-'','',''',','~{{{m////${r{rq//////////////////////////////////#'','',!~!~!~~!~~~!!~~~~~~~~!~~~~~~~~!*~!~~~~~~~~~~~~~~~*~~~~~~~*~~~,''''''=+@+@++@$> ",
+" 000000000oppdodopppppppvppvpopppppppvvyvvypoypypyyoyjojyoyopyjpjoopjojpjoooojjjpjoojjjdjjdjjjjjjojjcjjjjojjjcjjjjjajjajjjjajjcjajcjcaaaaaajaaaaaaaaaaaaaaaaaaabaaaabaaaaaaaaaaaaaab0aa0aabaaabaaazzzzzzzzzz00000000)39398i8989999m999m9mmmmmmmqmqqqq{rrr{r{rr{r{r{{{,''','''!//@*''''',''''{{{{r///////8{r>/////////////////////////////////(,',''',~!~~~!~~!~~~~~~~~~!~~~!~~~~~~~~!~~~~~~~~~~~~~~~~~~~~~~~~~~~~~'''''',#@++$@++@ ",
+" 000000000ppppppppoppvpppppppvovvpypoyooyoyooyjyopypoyopjpjyjoyjyjooojopjojojpjjjpjoojjjjjjjjjjjjjojojjjcjjjjajajajjcjjacjajjjaaajajaacjaaaaajaacaaaaaaaaabaaaaaaaaaaaa0aa0baaaaaaaaaaabaaaaaaaaazzzzzzjzz00000000b33979393i999999999999mmm-mmqqqmqqrrrrrrr{r{r{rr{r{~'''','.////.~''''',~{{{r{$////////($//////////////////////////////////-'',','~!~!~~~~!~!~!~~!!~~!~~~!~~!~~~~~~~!~~~~~~~~~~~~~~~~~~~~*~~~*~,''''''-+++@)@$@( ",
+" 000000000ppppppppppppypopvppppopvppyyvoyypoypyoyjopjyoyopjpoojpojopjojojpjojoojjjjjjdjojjdjdjjjjjjcjjjjjjjjjjjjjajjajajjaaaajaaajcajaaacaaacaaaaaacaaaaaaaaaaaaaaabaabaaaaaababaaaaaaa0aaa0aajzzzzyzzzz00000000b>@8398i898i899999999qmmmmmmmqqqqqrrrrrrrrrrr{r{{r{r{{{,''=///////%,',,{{r{{{q////////////////////////////////////////////@','',',~!~!~,~~!~~~!~~~~~!~!~!~~!~~~~~~!~~~~~~~~~~~~~~~~~~*~~~~~~~~,'''''',+@$@$@+@@ ",
+" 00000000appppppvpvpppppvpvvvpvvvvypoyooyyoyjyjpyyoyjoojyoooyjooojooooojjoojjpjooojjjjdjjjajjdajjojjjjjjcjjcjcjjjajjcjaajcjajcjaajaaacjajaaaaaaaaaaaabaacabaabaaaaaaaaaabaa0aaa0babaaaababaajzzozzzyzz00000000b(>>>$839i8i8999999999mmmmm-mqmqqqrirrrr{r{rr{r{rr{r{{{{{{~>////////(m{r{{{{r{>////////////////////////////////////////////!,'''',~~~!~!~~~~!~!~!~~~~~~~~~~~~~~~~~~~!~~~~~~~~~~~~~~*~~~~~~~~~~~,''''''%+++@+@+@/ ",
+" 00000000apppopppoppvppvppvpvvpvpyovyypoopyypyjyjpoyoyjyooojoooopjojoojpjjojjjjjojojjjjjojjjjjjjjjjjjjjjjjjjjajcajajjajjajaaaajaaajjaaaaaaaaacaaaaaaaaaaaaaaabaaaaaabaaaabaaaaaaa0abaaaa0ajzzzzyzzzz000000000@>@>@>@$38i8i898999q9mmmmmmmqq9qqmrrrrrrrrrrrrr{rr{rrr{r{{8///////////>9{{r{{8////(s//////////////////////////////////////#'',,,',~~!~!~~,~~~!~~~~~!~~~!~~!~~~~!~~!*~~!~~~~~~~~~~~~~~~~~~~*~~~,''''''*++++++++> ",
+" 00000000jppppopppyppyppvvvvpoppyyoyoyyooyjyopyjyjpyjpjyjypjyjjyjpjojojopjooojdjjjdjjdajjjjjdajjcjjjjjjjjjajjjjjjajcjajajajaacjaaaaaaaaacjaaaaaaaaaaaaaaaaaaaababaaaaabaaaba0aa0aaaaabaajzzzzzzyzza00000000&*#@>>>@>@198i8i999999mmmmmmm9qqmqrirrrrrrrr{rrr{r{r{r{r{{r//////////////$q{r{(////xwwu////////////////////////////////////,,''',,~~,~~!~~~,~~!~!~,~!~!~!~~!~!~~~~~~~!~*~~~~~~~~~~~~~~~~~~~~~~~'''''''#++@+$@+@ ",
+" 00000000jvpppvpppppvpvpvpvppvvvvoyypoyoypyooopypjoyjpjyjpjopjojojpjpjjjpjjjjjojjjjjjojocjjjojjjjjjcjajcjjajajjcajajaajcjcjaajcaaajcaaaaaaaaaabacaabaaabaaaaaaa0aab0aaa0aabaabaaabaa0azzzzyzzzzzj0000000bl&***#@>>@>>@1i9989999mm%mmmm9mq999rririrrrrrrrr{r{r{r{r{r{$///////////////(${$////swwwt///////////////////////////////////-''''''~~~!~!~,~~~~!~~~~~~~~~~~~~~!~~~~~~~~~~!~~~~~~~~~~~~~~~*~~~~~~,''''''-@$++@+++( ",
+" 00000000avpvpvppypppyppopyvvvyoyoyoypjyyjpyjyjypyooyjyjyojoopjojojojpjjojpjojjojojjjjojjjjjjdajjjjjjjjjajjcjjjjajajajajaajaaajaaajacaaaacaaaaaaaaaaaaaaaaaaaaaaaaaa0aaa0aaaaba0aabazozozzzzzz000000000)q**&*&-$@>@>@>$899999mmmmmmmq99qqqiirrrrirrrr{rrrrrr{rr{rrm////t/////////////(/////(xww///////////////////////////////////>,',,''!~,~~~!~~!~!~!~,~~~~!~~~!~~~~!~~~~!~~!~~~!~~~~~~~~~~~~~~~~~~~!'''''',.++@++++@ ",
+" 00000000avpoppppvppvpypvpvppvypyoyyopyoyjpoyojyjoooopjyjpjyjoyjojojojojojjjjoadadjjjjjocjajjjjjjjjjjjcajjjjajcjjcjaaaajaajcaaaaaajaaaaaaaaaaaaaaaaaaaaba0abaabaaabaabaaba0aabaaaazzzzzzzyzz00000000be**-*****=%$>>>>>>$8898mmmmmmm9qq9qirrriirrrrrrrrrr{rr{r{r{r>///twwt///////////////////uu///uwt/////////////////////////////~','',,~~~,~~!!~!~~~~~~~~!~~!~!~!~!~~~!~~~~~~!~~~~~~~~~~~~~~~~~~*~~~'''''''&+++++@$+/ ",
+" 0000000000vpvpypvpoppppypvypoyypyoyooyopyooooyjpooojoojoojojpjojojjojpjjoojdjjjjjjjjjjjojjjcjjcjajcjjjjcjjaajjajaajajcaacjajaaaaacaaaaaaaaabaaababaaaaaabaaaaaaa0aaaaa0aaba0aabazzyzzzzzzz000000000)**-**&*-*===%@@>@>@>1mm8mmmm99999qiiiirrrirrrrrrrrrrrrrr{r{8///(wwwx////////////////////////xwwx///////////////////////////.,'',''!~,~~~~~~!~~,~!~!!~!~~!~~~~~~!~~~~~~~~~~!*~~~~~~~~~~~~~~~~~~~,'''''']++@$+++@> ",
+" 0000000000oppppvvpypyvpvppvpyoyooyyyjpjyoyoooyooyjyoooyjpoojojpjpjojjojjjjjjjjjdjjdjjjjjjjjjjjjjjjjjajjjcjjaaaaajcjaajjaaaajcaaaaaaaaaaaaaaaaaaaaa0abaaaaabaabaaba0abaaaaaa0aazzzzzzzzzz000000000)****-**=**-***m#@>>@>@$#mmmmm9q9mq9rirriirrrirrrrrrrr{r{rrrr(///(xww(///////////////////////uwwws///////////////////////////,',''''~~!~~,~,~!!~~!~~~~~~~~~~~~!~!~~~!~~~~~~~~!~~~~~~~~~~~~~~~~~~~,''''',#++++@+++/ ",
+" 0000000000pvpyppppvpvvvvypyyoypyjpyyoypjypjyjooooojpjoojyjoojojjojojojpjojoojjjjjajjjjdajjjjjjajjjjajajjaajjjjaaaajaaaaaaaaaaaaaaaaaacaaaaaaaaaabaaaaaba0aa0aaaaabaaabaabaajzzozyzzzjz000000000)!]]***=-*-**-*===m#>>@+.+.8m8m99999iiiiririrrrrrrrrrrrrrrr{rrq$////(s///(wx(//////////////////(xw///////////////////////////-,'',,,!~~~,~~~~!~~~~~!~,~~,~~~!~~~~~!~~!~~~~~~!~~~~~~~~~~~~~~~*~~~~,''''''=+++++$++^ ",
+" 000000000jppypypyppypppppyoyoyyjoyjypyjypyoopjypjyjyjpjojoojoojpjojjjjjjjjjjjjjjjdajjjjjjjjjjjajcjajaajjcajcjajaacajcaaaaaaaaajbaaaaaaabaabaaaaaaaaaaaaabaab0aaabaaa0abazzzzzzzyyzj00000000a&]]*]=]]****&**=*===*m+++.+++.m999q9iiiiiiririirirrrrrrrrrr{r{rrrm>///////twww(////////////////(/////////////////////////////>,''''',~,~~~~,~!~,~,~!~~~~~~,~~~!~!~~~!~~~!~~~~~~!~~~~~~~~~~~~~~~~~!'''''',.@$@++@+@ ",
+" 000000000ovpppypvpvpypyoyopyopyypoojyojyjooyjyjyjpjojyjpjoojojjojojoojojdjojdjjjajjjjjcjjcjjcjjjjjjajajjajajacjajaajaaaaaacaaaaaabaaaaaaaaaaabababaaaa0aa0aaba0aababajzozyzzzzzzz000000000&**=]=]*=*=*=*=&*=*====-&...++>>$m99iiiriiiiirrrirrrrrrrrrrrrrrrrrrrm//////wwwx///////////////////////////////////////////////],,''',~!!~!~!~~~~!~~~~!~,!~~~~~~~~!~~~~!~~~~~~~~!~~!~~~~~~~~~~~~~*~,''''''&++++++++/ ",
+" 000000000jpvpppypypypppoyyyoyjyoyoyoyjyooooooojyjopjojyjoojpjojjojdjjjjjjjjjjjjjjjjjjjjjjjjjjjaajcjjajaaajajaaaaaaaajcaaaaaaaaaajbaaaaaaaaaaaaaaabaabaabaaaaabaa0a0jzzzzzzzzzz000000000b;=*]]*]=]]]*=****=-*-**-=-&%.+@@@>@$9iiiiiiiriiriririrrrrrrrrrrr{rrrrrr$/////tw(//////////////////////////////////////////////+,'',,,,~~,~~~~,~!~~,~!~~~~~~~!!!~!~~~~!~~~~~!~~~~~~~~~~~~~~~~~~~~~~,''''''!+++@$+++> ",
+" 0000000000ypypppppypyyooyopyopoyjpyjpooooyjooyjpojyjojjojjjpjojjjjjdjjjjjjjdjjdajjjjjjajjajjajjjjacjajaajaaacjaacjaajaaaaaaaaaaaaaabaababaaaa0aa0aaaaaaab0a0aabaazzyzzzzzzyz000000000b-!]]=!=]*=*]]]=]=****=*&=-*--&#>>>@>>^8iiiiiiiiririrrrirrrrrrrrrrrr{r{rrrr$////////////////////////////////////////////////////,'','',!!~!~,~~~~~,~~~!~!~~!~~~~~~~~!~~!~!~~~~~~~~!~~!~~~~~~~~~~~~~~'''''''#++++)@$@/ ",
+" 0000000000opyvypypyppypyoyoyoyoyjyoyoyjpjyoojyjojpjypjooojjjojoojjjojdjjjajjjjjjcjjjjajjjcjjjajajjjcjajcajjaajjaaaaaaacaaaacaaaaaaaaaaa0aaabaaaaabaab0aaaabaaaazzzzzjzyzzz000000000a=]]]]]=!=]]*]=**=*===*-*=&=&*=**-$>@>///^8iiiiiiiririirirrrrrrrrrrrrrrr{r{rr/(/////////////////////////////////////////////////%',,'''~!~!~~~~,~!~~~,~!~~~~~!~~~,~~!~!~~~~!~!~~~~*!~~~~~~~~~~~~~~~~,''''''*+++@$@++> ",
+" 0000000000jpppppypvyooyyoooypjyoooojpyjyjoooooyjojjjyjjojojdjjojojjjjjjdjjjjjjojjjjjjcjjjajcjjaajjajaajaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaabaaaabaa0aa0aabaa0aabjzyzzzzzzyz0000000000b*]*!]]!]*=]*=]]]*]*]****--=&=--**=*&@//////$9iiriiirirrrririrrirrrrrrrrrrrrr$//////////////////////////////////////////////////(,''',',!~!~,!~!~~~!~~!~!~,~,~~~,~!~~~~~~!~~~~~~!~!~~~!~~~~~~~~~~~~~!'''''',#+++++++@ ",
+" 00000000000pyvvpyppyoooypyoyoooooyjyjypjyjyjojooopjojpjoojjjojjjojjojjjjjadjajjjcjjjjjjajjjcjajaajajajcjajcaaaaaaaaaaaaabaaaaaaabaabaaab0aaaabaabaaaababaazzzzzzyzzzz0000000000b&=]]]]]]]!!=!]=]=]=]*]==-*=-=&=*=*==**%///////$iiiiiriiirirrirrrrrrrrrrrrrrrq////////////////twu////////////////////////////////*,'''',~!~!~~,~~~,~~,~~!~~~~~~!~~~~!~~!~!~~~!~!~~~~~~*~!~~~~~~~~~~~~,''''''&++$+++++( ",
+" 00000000000ypvvvyvypyoyjyjyoyoypjyoojyjoojypjojjoojyjjjojojjojjjjjjjjjjjjjjjjjjjjjjjcjjajjjjajaajaaaajaaajajcaaaacaaaaajaaaaaaaaaaaaaaaaaab0aaaaaba0aaajzyzzzyzzzzj0000000000).#%-]]]]]]]]]]**]=~=]=]*=-=-=*=*=*=*=~~~].//////^8iiiiiriririrrirrrrrrrrrrrrr$///////////////(wwwt//////////////////////////////@',,,''~,~!~!~!~,~~~!~!~~,~!~~~!~~~~~!~~~!~!~~~~~~~~~!~~~!~~~~~~~~~~,''''''!++++@+$@> ",
+" 0000000000jvypvpyjyoypypyjpyjooooyooyooojypjyjojojojojojojjojojdjjjjocjjjjjjjjajjjjjjjjaajcjajcjajaajaaajaaajaaaacaaaaaabaabaaaaaaaabaaaaa0abaaaaaaazyzzzzyzzzzj000000000b)#####%=!]]]]]]]!]*]=]=]=-=*-=-===*-*=~~!!~~=@//////^8iiiriiririrririrrrrrrrrr8////////////////twww(//////////////////////////////,,'''',~~~,~!!~!~,~~!~!~!~!~,~~~,~~~!~~~~~~~~~!~!~~~~~!~~~~~~~~~~~~~,''''',%++++++++/ ",
+" 00000000000pypyvyyooyjyoyoooyjpyjojyjpjyjjojojojoojojjojojjjjjjjjdjjjojjjjjjjjjcjjajajjjjajajjaajaacjaaaaaaaaaaaaaaaaaaaaaaaababaaa0aabaabaa0babazzzzzzzzzzzj0000000000b+########%=]!=!]]]]]]*]*===-=-*=***=*=*~!~~!~~!->//////$9iiiiririrrirriirrirrrr(/////////////////uxt//////////////////////////////#''',''~,~!~!~~~,~~~~~~,~!~~~!~~~!!!~~~!~~!~!~~~~!~~~~~~~!~~~~~~~~~~,''''''*+++$++++> ",
+" 00000000000jpypoyoyoyoooyjpyjojypyjyjojyjpjyjoojpjoojjdjojdjojjjjjjjajjjjjcjjjjjjjajjajajajaajcaajaaaaaaaaaaaaaaabacaaaaaaaaaaaaaba0aaaa0aaa0ajzyzozzyzjzzz0000000000b############-]]]]]]!]]!==-=-=*-===]=**~~~!~,~,~~~~%(//////$iiiiiriiirirrrirrirr$//////////////////////////////////////////////////(,',,'',~~~~~!,!~~~,~,~~~~~!!~~!~~~~~,~!~~!~~~!~!~~~~~~~~*~!~~~~~~~~!'''''',#++++$++@ ",
+" 00000000000yyyoyoooyoyooyoyoyjjooooojpjoyjpjjjjjojojjjjjjjjjjojjjjjjcjjjjjajaajjjaajcjjaajaajjaaacjacjaaacaaaaaaaaaabaaaaaa0abaaaba0abaabajzzzzzzzzzzzj00000000000)###############-]]]]]]]]===*-=-*]*]=]=!,,,~~!~~,!!~~~#///////1iriirririirrrirrii(////////////////(/////////////////////////////////=,''','~~,~,!~~!!!~~~~~,~!~~!~~~,~~~~~~~!~~!~~~~~~!~!~~~!~~~!~~~~~~~,''''''-++++++++^ ",
+" 000000000000yoyoyooooooooojyyjyjyjyjyjjojjoooojojjojojojjojjjjdjjjojjjjjjjjjjjcjjajjjajcajjaaajjaajaaaaaaaaaaaaaaaaaaab0abaaaaaaabaa0aaazzzyzozzzyzz00000000000b #%##%)########%-!]]]]===-====*=]*]*]!,,,,,~,~!~!,~!!*+////(/^8iiiirirriiirrrr$///////////////////(//////////////////////////////@','',',,~~~~!!~~~!~,~,~~~~~,~!~!~~~~~!~~~~~~~!~!~!~~~~~~~!~~~~~~~~~,'''''',++++++++@ ",
+" 000000000000jyooyoyooyoooyjpjyjpjyjyjyjoojojjojojjojjjjdjjjjjjjjjjjjjjcjajjajjjajaajaajjaajaaaaaaaaaaaaaaacaaabaabaaaaaaaaa0aaaaaabaazzozyzzzyzzzj00000000000b %####%###)%####%=]]======-*=]*=*]],,,,!,,!~,~!~!!!!~!->//////>9iiiiirrriiir8///////////////////////////////////////////////////~,'',',~!~,~~~~,!!~~!~~!!!~~!~!~~~,~,~~,~~!~~~~~~~!~~~~~~~~~!~~~~~~~,''''',%+$++++$@/ ",
+" 000000000000ooyoooyoyjyjyjyjoyjyjpjyjooojoojojojojojjjjjjjjjjjjjjjjjjjcjjjajjcjaajajajajcajcaaajcaaaaaaaaaaaaaaaaaaaabaabaaba0aaazozyzzzzzzzzz000000000000 %####%#########&-==-===]]]*]]]]!'!'!'!,,,,~!!!~,~,,,!&(//////$9iriiiirrri(//////////////////////////////////////////////////#,'',''~,~~~,~,~~~,~!~!~~~~,~~,~~~!~!~~!~!!~!~!~!~~~!~~!~~~~~~!~~~~~,''''''~+++$++++> ",
+" 000000000000jyjyooyjypjyoyjyjojojyjjojojojjpjjjjdjjojjojjjjjjjjjjjjjjjjajjjjjjjajajaajajaajaaaaajacaaaaaaaaaaaabaaaaaaaaa0aaaayzzyzzozzozzj000000000000b #############%%%====*]!]]]=!,,,!'~'~'~,,,,~,~!!~!~,!~#(//////$iiiiiiii1///////////////////////////////////////////////////'','',,~!~,~~~~,~!~,~!~~,~~~~~!!!~~~~~~~~~~~!~~~~~~~!~~~~~~~~!*!~~~~'''''''#++++++++ ",
+" 000000000000jvjyopyjyjpjyjyjyjpjyjyjoojojjoojjojjjojjjjjdjjjadajjjjjjjajaaajcajaajaaajaajaajaaaajbaaaaaaabaaaaaaabaaaabaajozzyzzzzzyzzzz000000000000 %#####%###%#%#%&=]]]]]]],','~'~,,',,,!,!,,~,~,~,~,,,*.///////8ririi9(///x(/////////////////////////////////////////////-,','''!~!~!~,~!~!~!~~,~~~~!~,~~~~~~~~~,~~~~~~~~!!~!~~~!~~~~~~~!~~~~'''''''-+++++$++( ",
+" 000000000000jyojyoyjyoooojyjoojjpjyjjoojjojjjojjjjojjjjjjjjajjajajajjjjjajjajajajaaaaaajacaaaaajaaaaaaaaaaaabaa0abaaajzzzyzyzozzzyz0000000000000b %##%##)%%#%#%#%%-!]]]],'''',''!~'~'!,,!!,!,~,~,!~!!,!*@//////^8iii$///twwt///////////////////////////////////////////>','',',~!!!~~~,~~!!~!~!~,~,~~!~,!!!~,~~~~~~,~~~~~~~!~~~~~!~~~~~~~!~!'''''',.+++++++@ ",
+" 00000000000000poyjyjpjoyjoojoyjojjojojojjojojjojjjjjjjjjjjjjjjjjjjcjaajjjaajajajcjajaaajaaaaaaaaaabaaaabaaa0aaaaaajzzyyzzzzzzzzzj000000000000b %###%#%#%#%####%=]]'''''''~',',~'!',,,,,,,,~~,~,~!~!,&>//////$98///(wwwx///////////////////////////////////////////!,'',',!~~~,~,~~~~~!!~,~~~~~~~~!~~~~!~!~,!!!~~!~!~~~~~!~!~~~~~~!~~~!,''''''&+$++++++/ ",
+" 0000000000000jyyojyyjpjooyjjyjoojjojojojjjjojjojjjjjjjjjjjcjjajjjjjacjajjacjajajaaaaaaaaaaaaaaaaaaaaaaaaaaaaajzzzyzzzzozyzyj00000000000000 %%#%#%%########-,''''''''',~'!,~'~'~!~,!,,,~,,,,!!!!!%(//////^////tww(///(//////////////////////////////////////.'','''~~,!~!~!~,~,~!~~!~,~!!!~~~,!!~~~~~~~~~!~!~!~!~~~!~~~~~~~~~~~~,'''''']++++++$+> ",
+" 00000000000000ypyjyjyjjjyjjyjoojojojjpjojjjjjjojjjjjjjjjjjjajjajjjajajajjaaaaajcjaaaaaaaaaaaaaabaaaaabaajjzozyzzzozyzzzzj0000000000000b %%%%%#%#%###%###..='''''''''','~'!'~','~,!,,~,~,~!,,~,~~#///////////(u////wx(////////////////////////////////////,,'',',!~~!~!~~,~~~~,~!!!~~~~~,~~~~~!~~,~~~~~~~~~~~~~!~~~~!~!~~~!~~!'''''',#++++++++/ ",
+" 000000000000000yyypjyoojyjjpjjojjjojjjjojojojjjjjjjjjjjajjjjjajajjajajaajaajaajaaaajaaaacaaaaaaaaabajjzyzyzzozyzzzzzj00000000000000b %-%-%%%%##########...#]'''''''''',~'~,'~,,'!!,!,~,,!,~,,!!,]+//////////////xwww(//////////////////////////////////-',''',~,!!~!~,~~,~,~~!~~!!~,~~~,~~!~!~~~~~,~~~~~~!~!~~~!~~~~~~~~~~~,''''''=++++$+++^ ",
+" 000000000000000jyyypyjyjojojooojjojojjjjjjjjdadjjjjjjjjjajjjjajajajajjajaaaaajcaaaaaaaaaaaaaaaaazzzyzyzzyzzzzzjzj00000000000000b %%&%%-&% %#######.......%!''''''''''!'~'~,!~'!'~'~,~,~,!!,!!!~=>///////////(xwwx//////////////////////////////////>,',',''~~~!~!~!~~~~~~,~,~,~~~,~~~~!~!~!~!!!~,~!!~!~~~~~!~~~!~~~~~~~!'''''',#+++++++@ ",
+" 0000000000000000jyvyyojyjjjojojjojjojojjjjjjjjjjjjjcjjjcjjajjajajaajaacjaaaajaaaaaaaaaaaaajozyzyzyzyzzzyzyzj000000000000000b %-%-%-%%- %####...#......-,''''''''''~'~',~'~,~'~',,~'~,!,,,,!,&(//////////(tw(//////////////////////////////////],''',,~~,!~!~,~!,~,~!~~!~~,~~~!~,~~~,~!~~~~~!~~~~~!~!~~!~!~~!~!~~~!,''''''&++$+++++/ ",
+" 00000000000000000oyyyyyyojojojjpjjjjjjjjjjajjjjjjoajjjjjcjajajajaajjajaajaaaaaaaaaajjozyzyzyzyzyzzozyzj0000000000000000b -%%-%%&% #......$......#-'''''''''','~,,!','~,~~'~'~,!,!!!,!,~%//////////////////////////////////////////////.,',',',,~~!~!~!~~!~~~!~!~,~~,~,~~~,~~~!~~~~~~~~~,~!~~~~~~~~~~~~~~~~,''''',!++++++++@ ",
+" 00000000000000000jyyypyyyoojjjjojojjjjjjjjjcjjajjajajjjjjcjjcjjaaaaaaaaaaaaajjjoyyzyyzyzyzyzyzyzzj00000000000000000b %%%-%%-%- .....#........#='''''''''',~'~!!,,,,'~'~'~!,,,!,!'!,~.////////////////////////////////////////////,'','',~~!!~,~!~,~!~,~~,~~!~~~~~!~~~,~~!!~,~~~~,~~~~~!~!~!~!~!~~~~!~,''''''%+++++$++/ ",
+" b000000000000000000jyvyyvvyvooojojjjjjjjjjjjajjjjjajajjjajjaajjaajajjoooyoyyyzyyzyzyyzyzyzzzjj00000000000000000b -%-%-%%-% $...+#$.......%]'''''''''','~'~'~~,~,~'~,!!!,!,!,!!,*@/////////////////////////////////////////%,','',!,~~!~!~!!~!~!~,~~,~!!,~!~~,~!~,~~~!~!,~!~~!~~!~!~~~~~~~~~~~~,''''',=++$+++++> ",
+" b0000000000000000000jyyyyyyvyvvyvooooojjjjjjajjjajjaajjjjjojooooyyoyyzyyyzoyzyzyzzyzyzj00000000000000000000b %%%-%%-%% .#+...#.......&!''''''''',,~'~''~'~'~'~'~'!'!,,'!,!,&>//////////////////////////////////////(''','',~~,!~!~!~~!~,~~~~!~!~~!~,~~~~~~!~,~~~~!~~~~~,~~~~!~!~!~!!~~~,'''''',#++++++++ ",
+" )000000000000000000000ayyyyyyyyyyyyyyyyyyvvyoooyyyyyyyoyyyyyyyyzyyyyyyyzyzyzyyyzza00000000000000000000b %-%-%%%-% #+#....$.......-'''''''''''~,!~'~,~,~,~,~!!,,!!',,,,~%(////////////////////////////////////*','','!!~~,~!~!,~!~!~,~~!~!~!~!~,~~,~~~!~!!~~~~,~~!~~~~~~~~~~~~~~!~,''''',-++++++$+( ",
+" b0000000000000000000000jjyyyyyyyyyyyyyyyyyzyzyyyyyyzyyyyyyyyyyyzyzzyzyzojj0000000000000000000000bb %-%%-%-%-% .$.#.#........#='''''''''''~'~'~'~'~'~'~,!'~,!,,',,,~#//////////////////////////////////@,','',,~~,~!~!~!~!~~!!~~,~~,~!~!~~!~~~,~~~~!!~~~!~~~~!~!~!~!~!~~!~~,''''''!.++$+.++> ",
+" b0000000000000000000000000ajyyyyyyzyyzyyyyyzyzyyyyyzzzyzyyzyzozjaa0000000000000000000000000b %-%%-%%-%% #+.+#$..#.....#]''''''''''~'~'~'~'~,~'~!!,,,!'!,,,,,*+////////////////////////////////,,'',''!~~!!~,~!~~!,~~,~~!~!~!~!~~,~~!~~~,~!~~~~~~!~,~~~~!~~~!~~!~~!'''''',%++.+++++/ ",
+" b0000000000000000000000000000jajjjjjzjyoyyyyyyzjjjjjaaa0a0000000000000000000000000000b %-%%-%%-%% ....#+........%!''''''''''~'~,~,~'~,~'~'!'!,!,,'!,,,->/////////////////////////////%','','~~,!~~!~!~,!~~!!~,~~,~~!!~,~~,~!~~~!~!!!!~~~~~!~!~~~~~~~~~~~~,''''''*++++++++> ",
+" )b000000000000000000000000000000000000000000000000000000000000000000000000000000b %%-%%-%%-%% ....#$#+#.....-'''''''''''~'~',~,,~,~!~!!,,,!,,,,,,,&(//////////////////////////(,,'',',,~~!,~,~!~~!!~~!~,~~~!~,~!~,~~,~~,~~,~~~~!~,~~~~~~~!~!~~!~!!~'''''',#++.$.+$+ ",
+" )b0000000000000000000000000000000000000000000000000000000000000000000000b) %-%-%%-%-%% .$.......$#...#-'''''''''''~!~'~'~'~',~'~,',,,,',',,!@/////////////////////////=,''','~~!!~~!~,~!!~~,!~~!~,~~~~~!~!~~~,~~~~~~,~~~~~~,~~,~~!~~~~~~~!,''''''-+.++++++( ",
+" %)b00000000000000000000000000000000000000000000000000000000000000b) %%%-%%%-%%%% ...$..........#='''''''''''~,~,~'~~'~,~!~!',,,,,,,,,=(//////////////////////@,''','~,~~!,~,~!~~!!~~!!~,~~,~,!!~!~~,~~~~,~!!~!!,~!~~!~~!~~~!~!!~~,''''''!#++$).++@ ",
+" %&%)bb0000000000000000000000000000000000000000000000000000bbb %-%-%%-%-%-% .#.#+.........%]'''''''''','~,{',~'~,,~!~'~',,,',,,,&/////////////////////!',,'',!~,!~~!~~,~,~~,~!~~!~!~~~~!~,!~!~~,~~~~!~~~~~!~~!~~~~~~~~~~~~'''''''%++++$+++/ ",
+" %%%%%))bbb000000000000000000000000000000000000000bbb %%%-%%-%-%%%% .+#...$#.#....%,'''''''''',,,~'~,~'~,,~'~!',,,,,,'''#//////////////////#','''',!~~,~!~,~!~!~!~!~!!~,~~,~,~!~~!~,~~~~,~~!!!,~~~~~,~!!!~!~~!~,,'''''*++.+++.+> ",
+" &&%-%%%%%%))bb000000000000000000000000b0bbb) %%-%-%%-%%%-%- ..$#..........#-'''''''''',!!~'~,~,~,~,~,~',,,',,,,,!@///////////////(,,'',,,~~,!~!~,~!~,~!~!~,~~~!~~~~~~,!~~~!~~,~~~~~~~!~!~~~~~~~~~!~~!!'''''''#+$+.++$+ ",
+" %&%-%&%%%%%&%&&% )))) %%-%-%%-%-%-%-% ......$.#.....#='''''''''''~'~'~'~,~'~,~~!,,',,,,'',*>/////////////-''',''!,~~,~,~!~!~!~!!~!~!!!~,,~,~!~~!!,~~!~~,~,~!!~~~,~~!~~~~~~!~~,''''''-.+.+++++( ",
+" %%-%&%&%%%%%&%%%- %%-%-%%%-%-%%%%% ..$#..........#]''''''''''~,~'~'~,~'~,,~!!,',,,,,',,-(//////////>,,,''',~~,~!~!~,~,~!~,~!~!~~!~~~~!~!!~~~!!~~~~~~~~~!!~~~~~~,~,!~~~~,'''''',.+++$.+.@ ",
+" -%-%%&%&%%&-%%%-%% %%%-%-%%%-%-%%%-%- ..+#+#$....#..%!'''''''''''~~,~,!,{'~,~!!!,',,','',,%/////////!,'',,'~,~!~!,~!~!~,~!~~!~,~!~!!!~,~!~,!!~~,~,~!~,~!~~!~!,~!~~~~~~!~,''''''&+.+.++++/ ",
+" %-%%-%-%%-%-%-%%-%%%- %%-%%%-%-%%-%-%-%%%-%- ..$...#........-''''''''',',~,~'!!!,~'~,~!',,,,,',,,,.//////#''''''~~~,~!~~,~,~~!~,~!~!~,~!~~!~~~,~~~,!~~~~~,~~~,!~!~~~~~!~~~,~~,'''''']+.+++.$+@ ",
+" %%-%%-%%%%%-%%-%-%%%%-%%%-%% %%-%%%-%%-%%--%%-%-%%%%%-%- +#....$..#....#=''''''''''',~!!,~,~,~,~!~',,',,'''',!>////,,,,','!,~,~!!~!~!!~,~!~!!~!~!~,~,~,~~~!!~~!!,~~~~!~~~!~~~!~~~,~~~~!'''''',#++$.++++/ ",
+" %-%--%-%-%%-%-%-%%--%%-%%-%%%-%%%-%%%-%%-%%-%-%%-%-%%%%-%%%-%--% ..#+#+.......+#]'''''''''',~,~'~,~,~,!~!!!',,,,,,','=(/-'''','~~~!~,~~'~!~~!~,~,~~!~!~~!~~~~,~~~,!~~~~~,~~~~,~!!~~~,~~!!~~~,''''''=..++.+.+> ",
+" -%%%%-%%%%-%-%%%-%-%-%%-%-%-%%--%%-%%-%%%-%-%%%-%-%-%-%%% ..$.#+#$......%!''''''''''~,~,~,~,~,!,~!!,,'''',',',=','',',!,~'~!,~~~!,~!~!~!!~!!~,~!!!!~~,~!~~!!!,~~,~,~~~~~,~!~~~~~~~,'''''',#++#++$+@ ",
+" %-%-%%%-%-%%%%%-%-%%%%-%%%%-%%-%-%-%%%-%-%-%%% ...$...#......-!''''''''''~,~'~,~,~~,~,!!,,,',,,',,'''','~~~~~,~~,~,~~,~,~~!~,~!~~!~!~!~~!~!!~~~~!~~~~!~~,~~!~~~~~~!!~'''''',&++++$)++^ ",
+" %%%%--%-%%%-%-%%-%-%%-%-%%%-%-%%% #+#.....$.....#-''''''''''',~,~,~,~,~,~!~,',''''',',,''~,!,,~!,~!~!!~!~,~,~!~~!~,~!~!~,~~,~~!!!,~~~,~~~!~!!~~~,~!~~~,''''''!++$++.+.> ",
+" %-%-%% ...$#.........#='''''''''',~,~,~,~!~,!!!~,,,',','',',!~~~~,~~'~!~,~!~!!~,~!!~~!!~~,~~,~~!~~~~!!,~~,~~~~~!!!~!~!~!!'''''''%+.+.+.++/ ",
+" .....#$.......%]'''''''''''~,~,,~,~,~!!!,,,',','',,!,~'~!!~~~,~!~!~~!~!~!~,~!~,~~,~~~,~,!!!~~~~~~~,~,~~~~~~~~~~,''''',=++.+.$++> ",
+" ........#.....%,''''''''''~,~,~,~,,~,~~,!','',',~~~!~!~,~,!~~,~,~,~,~~,~~!!~!~~~,~~~~~~~,~,~!~,~~~!~,!!~~!~!,'''''',#.+.++.++ ",
+" +#+#...$.......-'''''''''''~,~,~,~,~,!~!!!'','!,,~,~,~!~~!,~!~!~!~!~!~,~!~!~!!~~,~,~,!!~!~,~~~~,~~!~~~!~!~~,''''''-+++$+.++( ",
+" ..$#..........#='''''''''',~,~,~,~,~,~!!~,'!~~~!~,~,~,!~~,~!~,~,~,~~!~!!~,~!!~~~~~~~!~!~~!~~~~~~~!~~,~~~,''''''!..+.+.$+@ ",
+" .....#$#......#*'''''''''',~~,!~,~,~~,!~~,!,~,~!~!~~!,~,~!!~!~!~,~!~!!~~!~~,~,~,~!~!~!!,~,~,~~,~!!~~!~'''''',%$++.+.++/ ",
+" ..$...........%!''''''''''!~,~,~,~,~,~,~~,~~'~~,!'~~!~,~~,~!~!~,~!~!!!~,~~~~~~,~~,~~~~~~~!~~~~~!~!~,''''''=.+..+.+.> ",
+" .....#$+#.....-'''''''''''~,~,!~,~!~!,~~'~~,~~~~'~,~!,~!~!~!!~~!!~!~!~!!,~,~~~!~!!!,~~,~~,~!~~!~!'''''',#.++.$+++ ",
+" ..$......#.....-'''''''''',~,~!~,~,~,~,~!,~'~,~~!~,~~!~,~,~~,~!~~,~!~~~~~~!,~,~~~~!!~~!~~~,~!~!,''''''-.+#++.++^ ",
+" #...#$......#.#='''''''''',~,~,~,~!~,~~!~~~!~,~!~,~,~!!~,~!~,!~~~!!!!!,~~~~~!!!,~~~,~~!~~~!~,''''''!#+++$)..@ ",
+" ........$.....%]'''''''''',~,!~,~!~'~,~,!~!~'~!~!~!~~!~,~!~!!!!~~!~~~,~,~~~~~!!~~~,~,~~,~~'''''''%+.$..$++/ ",
+" +#$...........%!''''''''''~,~'~,~,~!~~,~,~~~!~,~!!,~!~,~!~!~!!!~!!!~~~,~!!,~~,~~~~~!~~~,''''''~+..+.+..> ",
+" ..#.#.#.......#-'''''''''',~~~,~~,~,!~!~,!!,~!!~~~,~~!~,~!~!~!~!~~,~~!~~~!!~!~,~~!~!!,'''''''#+.+.+.++ ",
+" .+.+.+#$.#....#='''''''''''~~,~'~~,~,~~,~~,~!,~!~!,~!~~!~,~!~,~!~,~~,~!~~,~!~,~~!~~,''''',-#+.+#+..( ",
+" .$#...........#]'''''''''''~~~,!~!~,~~,!~,~~,~!~~~,!~,~~!~~!~!~~!~~,~,~!~~~~!~!~,'''''',.+$.++$++ ",
+" .+#..#$....#..%!'''''''''',~~'~,~!,~!~!~,~!~!!,~~,~~,~!!!~~,~,~!~~!~!~!~,~~~!~'''''''&+#+....+/ ",
+" .+...#+.......-'''''''''''~~!~,~!~,~,~!!~,~~~,~~,~~!~!~,~~~~!~!~!~!~!~~,~~~,''''''].++.$+.+> ",
+" +#....#$.......=,'''''''''~'~!~,~,~!~~!!~,~!~!!~~,~!~!~!!,~,~!~~,~!~,~!~!!'''''''#..$).+#+ ",
+" .$.....#......#]''''''''''~,~~!~,~,!~~!~,!~!~,~~!~!!~~~~~~~,~~!~!~~,~~~,''''''=...+.$+.^ ",
+" #....+#$......%]'''''''''',,~!~,~!,~,~~!!!!~,~,~!~!!,~!,~~!!~~!!~!~!,'''''',#+.+...++ ",
+" .$#+.#..#.....&,''''''''''~,~~,~~!~,~!~~!~!~!~,~~~!~~~,~~,~,~~,~!~'''''''&+......+/ ",
+" ......$........-'''''''''',,~!,~!!~,~,~!!~,~~!~!,~!!~~,~~~!~!~!~,''''',!...+$+.+@ ",
+" ..#+#+#+#...#.#='''''''''',~~,~~!~!~,~!~~!~!~~!~!~,~~!!~!~!~!!'''''''#.+#+#+.+/ ",
+" .....$........%]''''''''''!!,~!~!!~~!!!!!~,~!~!~~,~~,~!~,~!,''''''*..+$)$.+> ",
+" .#+#..........%!''''''''',~,~,~!!,~!~!~~!~,~!~~!~~~,~~~!,'''''',#+......+ ",
+" ..+#$..$.......-'''''''''',~!~~!~~,~!!!~!~~,~!~,~~~!!!~'''''',-.+...+$+( ",
+" ...#..........#='''''''''',!~,!~!!~!!~!!~!!~~,~,~~~!,''''''!..$.+...@ ",
+" ....#.$#......#*''''''''',,~~!~,~,~~!~!~,~!~~~,~!~'''''',%.......+/ ",
+" ........$.....%!''''''''',,~!~~~,~!~,~~!~,~~!~~,''''''*.+.+$.+.> ",
+" #+#+#.........&'''''''''',!!!!~~!~~!~!~!~,~!,'''''',#.+#.)$.+ ",
+" ..$..#..#......-'''''''''',~,!!~,~!!~!~~!~~'''''''-...+...+^ ",
+" .....$........#='''''''',,,~~!~!~!~,~,~~,''''''!#.$.$...@ ",
+" .....#..#..#..%]''''''''',,~!~,~~~~!~,'''''''%+.....++/ ",
+" #...$.........%!''''''''',,~~!!!~,~,''''''*......+#> ",
+" ......$.......#-'''''''',',!~!~~!~'''''''#.+#+.$.+ ",
+" #+#..#$+#.....#=''''''''',!,~!!'''''''-#$.+....( ",
+" ..............#]''''''''',,~,'''''',....#+#++ ",
+" .+#..#$.......%!''''''''','''''''%.$).$+.+/ ",
+" .$............-,'''''''''''''']......#+@ ",
+" +#+#..#$#.....#-'''''''''''''#..$+#+.+ ",
+" ..$...........#]''''''''''=.....+$#( ",
+" +#...#$.......%]''''''',##+#+#..@ ",
+" ....#...#.....&,''','&+....+.+/ ",
+" $.+....$.......-'''=..$)$..#@ ",
+" .$#...........#%%..#+.#+.+/ ",
+" .+#.#$..#....#.....+#$.> ",
+" +...#+..............+ ",
+" ...$.#$..+#+#$..../ ",
+" ......#..$..#+#+> ",
+" +#.....#+#+..> ",
+" ..$#+....$ ",
+" ).$)++ "};
diff --git a/libimage/error_small.xpm b/libimage/error_small.xpm
new file mode 100644
index 0000000..5c61f2a
--- /dev/null
+++ b/libimage/error_small.xpm
@@ -0,0 +1,183 @@
+/* XPM */
+static char * error_small_xpm[] = {
+"150 115 65 1",
+" c None",
+". c #5F605E",
+"+ c #505352",
+"@ c #454745",
+"# c #727271",
+"$ c #7F807F",
+"% c #909192",
+"& c #C3C3C6",
+"* c #FDFFFC",
+"= c #F4F2F6",
+"- c #2E2F2D",
+"; c #E8E6EA",
+"> c #DFE0EA",
+", c #D6D7DF",
+"' c #7B9FC3",
+") c #5A7EA3",
+"! c #A8A9AD",
+"~ c #050C19",
+"{ c #081933",
+"] c #071F5B",
+"^ c #266BF5",
+"/ c #2E79F5",
+"( c #3684F5",
+"_ c #4699FC",
+": c #5589C0",
+"< c #3C5C7C",
+"[ c #2E5B9A",
+"} c #1558F0",
+"| c #003CEC",
+"1 c #000300",
+"2 c #5BAEFD",
+"3 c #002BA8",
+"4 c #5088D2",
+"5 c #272D33",
+"6 c #9B0300",
+"7 c #A93937",
+"8 c #D05D5A",
+"9 c #FC7D6D",
+"0 c #FC8E7F",
+"a c #FC6E5D",
+"b c #FB6354",
+"c c #BD2221",
+"d c #71A9DD",
+"e c #FDA99D",
+"f c #D02D2B",
+"g c #88CAFF",
+"h c #F39C93",
+"i c #F7B9AD",
+"j c #FC584D",
+"k c #8FB6D9",
+"l c #191B19",
+"m c #991E1E",
+"n c #07369F",
+"o c #312E09",
+"p c #FB4B44",
+"q c #585310",
+"r c #968E1B",
+"s c #E83935",
+"t c #92555F",
+"u c #FC413C",
+"v c #7D4153",
+"w c #872A38",
+"x c #DC2020",
+"y c #F92C2D",
+"z c #A5D7FE",
+" ",
+" ",
+" ",
+" ",
+" ",
+" .............+..+.++.++++++++++++++++++++++@+@+@@@@@@@@@@ ",
+" ..#$%$$$$$$$$$$$$$$$$$$$$$$$$$$$$#$#$#$####$#$#$#$$#$###@@ ",
+" ..&******************************************=****=****=$@- ",
+" ..&**==*=*==*======================;=;=;=;=>==>=;=;=;;=*%@- ",
+" ..&*=**=*=**=*==============;=;;;;;=>=>=>=>;;>;>;;>>>;;*%@- ",
+" ..,*============*======;=;==;=====>=;=>=>=;=;=>=>;=>=>==%@- ",
+" ..&***,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,>>=*%@- ",
+" ..&*==,&%%%%%%%%%%%%%$%%%%$%%%$'$%%%%%%%%)%%%%%%%%%,,>;*%@- ",
+" .#&**>,!~{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{]{]{]{{~,,>=*%@- ",
+" ..&*==,%{^///((_(_____:@@<[__(_(///^^^}}}|||||||||1,>>;*%@- ",
+" ..&*==,!~/((_______2:<@@@@@[[____((//^^^}}}||||||3~,,>==%@- ",
+" ..,*==,%{((____2222<@@+!;.@<<4____(((//^^}}}||||||1,>>==%@- ",
+" ..&*==,!~(____222<@@@%;**&@@<[42____((//^^}}}||||31,,>;*%@- ",
+" ..&*==,!~__2222)+@+#;*=;==#@+):22____((//^^}}|||||1>>>==%@- ",
+" .#&*==,!~__22:+@+.&**;>>;*,@5<::222___((//^}}}|||31,>>;*%@- ",
+" 6666666666 ..&*==>!~222<+++!**;;;;>;=*%@@<:222____(///^}}}|||1,>>==%@- ",
+" 66666666666666666666 ..&*=>>!~2<@++%=*=>=>=>>>>*=+-<):2222__((//^^}}||31>>>=*%@- ",
+" 6666678980009999aaabcc66666 ..,*==>!5+++$;*=;;>>>>,,>>=*&@@<)d222_2_((//^^}}||1>>>;*%@- ",
+" 666680e0000909999a9aa999afc6666 ..&*=>!+++#,**;=;=;>,>!&,;>==.@@):g2222__(//^^}}}31>>>==%@- ",
+" 6668hhhh0h0000909a9aa9aaa99a9abc666 ..&*&#++.&**;;>=>>,,&<1#,,>;*,@@<):2222___(/^^}}|}1>;>==%@- ",
+" 6668eeheh0e00009999aa9aa9aaaa9aa9abc666 ..&%+++!=*=>==>>,,,$1]3~!>>>=*$@@):g222___((/^^}}31;>>;*%@- ",
+" 6668ieeeh0e000000999a9aa9a9a9aa9aaa9aabc666 .++++%;*=;;;>;>,,%{{3||]5>>>>*=+@<)'2222__((/^^}}}1>>>==%@- ",
+" 668eieeee0h0h0009999a9a9a9aa9a9aa9a9a99aajc66 +++$,=*;;==>>,,!@13|||||~%,;>;*!@@<:dg222__(//^}}31>;>;*%@- ",
+" 66hihieehe0e00000999aaa9aa9aaa9a9aaa9aa9a9abf666 +++#&**===>=>,,k.~]|||||||3l,>>>**.@+):2222__((/^^}}1=>>==%@- ",
+" 6mieieeee0e000099999a9a9aa9a9a9a9a9aaaaaaaaa9aj666 ++..!====;;;>,,,#1]}|||||||||{#,>;;*&@@<<)222__((/^^}31>;>;*%@- ",
+" 667eieihehh0h00000999aaa9a8aaaabbbbbbbbbbbbbbbaaajc66 +.++%=**=;==>>,,%~{3}}}||||||||3~&>>;=*$@-11)222_((/^^}}l>>>==%@- ",
+" 668iieihehehh00009999a9a9aaaabbbbbbjjbbbjbjbjjjjjjjjc66 .++%;**=;==;>,,!5~n^^}}}}}}||||||]+,>>>*;++o1<22___(//^^31=;>>*%@- ",
+" 66hieihehh00000009999aa9abbababbbbbbjbjjbjjjjjjjjjjpjjf66 ++.#&**===;;>,,k+~]/^^^^^^^}}}}|||||~!>;>=*!@@1<422_((/^^^}1=>>==%@- ",
+" 68ieieeeeee0h0009999aaabaabbbbbbbbbbbbbbjbjbjjjjjjjjppjf66#.+..&**==;==>>,&#l]////////^^^^^}}}}||n5,>>,*=.@-<:22__(/[]]nl>;;>*%@- ",
+" 67iieeeeh00h00009999aaaababbbabbbbbbjjjjbjjjjjjjjjpjjjpjpf6m#.!=======>>,,#~{((((((((/////^^^}}}}||~$>;;;=&@@5<<[4((n1q~n1=>>==%@- ",
+" 6meiieeehee000090999aa8bbababbbbbbbjbbbbjjbjjjjjjjpjpjpjpjpc67&;*=====>!$+5l][4__(_((_(((////^^}}}}}31,>>>=*#@5111<_([1r1n~;;>==%@- ",
+" 6ceieeeehh0h0000999ababbaabbbbbbbbbbjjjjbjjjjjjjjjjjjpjppppps66t;,===>,&+@@@@<[[[________(((////^^}}}|].;;;;*;@@l11[((/111nl=>>;*%@- ",
+" 66hieihee0e00000999aaaaabbabbbbbbbbjbbbbjjbjjbjjjjpjpjjpjjppppp66i&;>,,k+@.!+@@@<[[442_______((//^^^}}}3~!>>>;=%@-o1{_/[111n1=;>>*%@- ",
+" 68ieeeehhe0000099aaaababbbbbbbbbbjbjbjjbbjjjjjjjsjpjppjppppppppf67!&&,$5@@>==!@@@<<)4:222_____(((//^^}}}n-;>>>*=.@@1{([{~o1]1=;>;*%@- ",
+" 6mhiieee0h0000099aaaaabaabbabbabbjbbbjbjjjjjjjjjjjjjjjjjppjppjppp66%!!5{+@%====>%@@@<<<:42222____((///^}}}~%=>;;*&@+l{(~11r111=;;>*%@- ",
+" 68eehehh0e0000999abababbbbbbajbbbbbbbbjbjjjbjjjjjjjppppppjpjpppups6m#@)[++==;>==*,#@@@<<:::222____((//^^}}n~;>>>=*#@-{[{1111~1==>>*%@- ",
+" 6ceeee0e0h000099abaaababaaaabbbbbbbjjjjjjbjjjjjjjpppjjjjjpppppppppuc6v::.@&*=>=>;=**&.@@@<)::2222___((//^^}}{#=>;;=;++{[~11111l=>>;*%@- ",
+" 68ieehe0e000099abaabababbbj8jbbbbjbbbbbjjjjjjsjjjjjpppppppppppuppuus6m'%.#*=>>;>;;;===!+@@@<)):222___((/^^}^3~&=>;;*%@@11111111==>;=%@- ",
+" 6meehehh0000999aaaaabab8abbjbbbbjbbjjjjjjjjjjjjjppjjjjppjppppppppupuuc6v##&=>=>,,,>>;==*;%@@-<<)::22___((//]1]]@;;>;*=+@51111111=>>>*%@- ",
+" 68eeh00e000009baababbbbjbbbbbbbjbjbbjjbjbjjjjjjjjppppjppppjupppupppuuu6m#%,,;;>,!,,,>;;==*,$@@@<<<:42___(/{lr1n~!>>>;*!@@1111111==>>*%@- ",
+" 6cheee0h000999aba8abaaabbbabbbjbbjbjjbjjjjjjsjjpjjjjjpppjpppuppppuuupsuc6t&&&>>,,~5!,,>>>>=**&.@@@l{::___((/1o1~nl;=>>=*#@-111111=;>;*%@- ",
+" 6che0e0000099ababababbbabbbbbjbjbjbjjbjjbjjjjjjpppppjpjppppppupupppuuuuf6w&&&,,,.{3~@&,>>>;;=*=!+@@@<<:44(((]111]{#=;>;*,@@l11111==>>*%@- ",
+" 680eh0000099aaaaabababbbbbbbjbj8jbjbbjjjjjjjjppjjpppspuuupupupupuuuuuuus66&&&&,!~3||]~.,>>>>;==*=%@@@5<{1<_(/~1o1~1,=>,=*%@-11111=;>;=%@- ",
+" 66hh000000999aaaabaabbabbbbjbbbjjjjjjjjjjjjjpjspuuuuuuuuupuuuuuupuuppuuuu66!&!&&5]|||||]1$>>>>>>==*;$@+@l1~[(({1r11{+==,>==+@l1111==>>*%@- ",
+" 6c0e0h009999bbabbabbbbbbbjabbjbjbjjjjjjjjjpspupusuppupusuusuuuuuuuuuuuuuux68&&!!~|||||||3{5!>>>>>>=*=,#@@@l{[]111111~%=>>;*!+@$!,*=;,;*%@- ",
+" 68000000999aaaaabbbabbbb8jbjbbjbjbbjjjjjssppuuuupuusuuusuuuusususuxusuuups67,!!@n|||||||||31@&>>>>>>==*&+@@-<[~1111111==>>*=#@+%!;=;>>=%@- ",
+" 6690h09aa9abaabbab8babajjbbbjbjbjjjjjjjjpppupppppuppuspuppsusuusuuuusuxusyu6m&&%+3|||||||||||]~.,;;;>>;===!@@-<{1111111@=>>>=,@@.%&>>>;*%@- ",
+" 6600009aa9aaa8aaaabaaabaabababab8jbbabbjjjjjjjjjjpjjjjjjjpjpjppppppsuuuusyuc6!!+[^|||||||}|||||]1$>;>>>>;*=;%@@@-1111111&=>;;*$++$%,,>>*%@- ",
+" 6690099b0iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii8suxusyxct.v4[}}}}}}}}}}}}|}3{l!>=>>>>==*,#@@@l111115==,;=>@@+%!,,;*%@- ",
+" 6600099b0,,,,,>,>,,>,>>>>>>>,>>>>>,,,,,>>>;;;,;,;,;;;;,;;;;;;;;,;,;;&xuxusux6t@([4/}}}}}}}}}}}}}|}n~-&=;;;;;;**&.@@-l1111%*>,=*!@@#%,,>=%@- ",
+" 660999aa0,;;*******=*==*===*=*=*************************************hssssxuf6w<(4[4^^^^^^^^^^^}}}}|}]~.,;>>>;>===!+@@-11l!;=>>==.@+$!,;*%@- ",
+" 6c0999aa0;;********************=*=**=*******************************hfususyx6w[([:(/^/^//^^^^^^^^^}}}3]1$>=>;>>;=*=%@@@+$%!!,>>=&@@<%&>=%@- ",
+" 6c9999aa0;******************************=***************************hxsxsxux6w(44)4(///////////^^^^}n1]3{l!==;>>>;==;$@@@.#%%&,==$@+$%>=%@- ",
+" 6c99a9ab0;**********************************************************ifsssuuy6w444'44/(_((((((/(//^^/1ql1]n1-&==;>>>;==&#@@@.$%!&=;@@.%&=%@- ",
+" 6c99aaab0;********************************************************==hssxxsyy6w(44':4(_(___(_((((//(n1q11111{~+&==;>>>===!++@+.%%!=!@+#%;%@- ",
+" 6ca9a9ab0**********************************************************=hfssssxs6w444'4d__________(_(((/({11lq111~1-;==>>,>===!@@@+#%!&+@.%&$@- ",
+" 669aa9ab9=**********************************************************hfxfssyxcw4'4':d_2_222_2_____(((//n~lo111~111.===>>>>==>$@@@.#!%5@#!$@- ",
+" 66a9aaab0*=*********************************************************ifsxfxux6v_dd''d2222222222_____(((({11111111111%*=,>>>>==,#5+@.%@5@%.@- ",
+" 66a9a9ab0*********************************************************=>hfsxssyfct4:d''d22222222222222{~4((]11111111111%=>>>>>>>===&v@@+#--++-- ",
+" 66aaaaab9=**********************************************************hcffssyc6)ddd'kk2gggg2g2~<222:111{4/({11111111-==>,,,>,>>>=*=!@@@@5--5- ",
+" 66c99abb0**********************************************************=hffsfyy6c'ddk'dk2g22ggg<1o~<2~11ol1{(/11111111&=,>,>>>,,,>,>==;%@@@-5ll ",
+" 6fa9abb9***********************************************************hffxfyy66:kdk'kkggggg2d1lr111111qo111{1111111+==>>,,,>>,,>>>,>==,#@@-111 ",
+" 66aaabb0*=********************************************************=hfffsyxcwddkk!kdgggggggd51qql1111111111111111==>,>>>>,>,>,,,>>>;==.@-l11 ",
+" 66a9bbbb888888888888888888888888888888888888888888888888888888888887ffxuyx6tdkdkkkkgggggzggg'51qql1111111111111$*>,>,>,,>>,,>>,,,>;*,@@-l11 ",
+" 66cabbbbbbbjbjjjjjjjjppuppspspsssssssssssfsfsffffffffffffffffffffffffffyy66'kkkk!kgzgzzzgzgggg)11r111111111111l==>>>>>,>,>,>,,,>,>==#+-l11 ",
+" 6cabbbbbjbjbbbjjjjjpupuuuspspspssssfsfssfsfsfsffsfffffffffffffffffffffyx6mdkkkk!kzgzggzz'gzgggg<1111111111111&=>>,,>,>>,>>,>>,>,=*!@@l111 ",
+" 66bbbbbbbjjjjjjjjjjppupsppspspssjsjssjfsfjffjfsfffffffffffffcffcfffffxyxcvkkkkkkkzzzzzzg11)zzgggl11111111111@==>>>>>,>,>,>,,,,,>==.@-11 ",
+" 66fbbjjbbbjjjjjjjjjpppppsuspssssfsffjfsfffsfffffbfffffffcfcffcfffcfffuy66'kkkk!kzzzzzzz+~115'zg<111111111111;=>,>>>,>>,>,>,>>>,=*%@@11 ",
+" 6cjbbjbjjbjjjjjjppuusupspssjssjspsffjfjfbfffbffffffffffcffcffcfcfcfxyx6mkkkkk!kzzz>>zz511qq15'111111111111#*>>>,>>>,>,>,>>,,,>=;+@l1 ",
+" 66fjbjbjjjjjjjjjjppupuspspssssffjfjffffffffffcffff8ffcfffcfcffcfcffyyc6)kkkk!k&z*;==;;z$~11~11~1111111111l==>>>>,>>,>,>,>,>>>=*#@@1 ",
+" 6cbjbjbjjjjjjjjppupsspssssjssjssfsfsfjffbfffcbcfffcf8cccfcfccfcfcsyx6mkkkkz!k&;;*>;*==>5l111r1lr11111111!=>>>,>>>,>>>,>,>,,;*&@@l ",
+" 66fjbjjbjjjjpjjppuupupsjsssfsfsfjfjffffffff8fcf7ffcfcff7fccfcccffyy66)kkkk!kk;=>==;===!~r151o1111111111-*=>>>>>>>>>,>,>,>>>*=.@5 ",
+" 6cjjjjjjjjjjjpppspsssssspsssjsfffsffjfffffffffffcfccffcfccccfccxyxcwkkkkk!k&;;*;=====#1ooq111111111111;=>>>>,>,>,>,>,>>,,==!@+1 ",
+" 66cbjjjjjjppjjpuupuppssssjfsfssjffjfffbcfffcffcf7ffc7fcccfccccfyy66'kkkkk&k,*;*;=*;*=*&5loqol11111111.*;>>>>>>>>>,>>>,>>>=;+@- ",
+" 66jjjjjjpjpjppsussssjssssfjsffsfffffffffffff7fff7cffcf7f7cccfyyx6vkkkkkk!kz===*>===**>z'~oo111111111==>>>>>>>,>>>>,>,,>=*$@@ ",
+" 666jjjjjjjpjpppuppsssssjfsffjffjff8fff8ff8cffcfcfccc7fccccccuyx66'dkkk&kkz>*==***=*==zzzz5~11111111!*>;>>>>>,>>,>,>>>,;*&+@l ",
+" 66cjjjjpjpjppspsspsjssspfjfsfffffffffcfffffcfcccfccccccfmcuyx66)dkkkkk!kzz>=====*=**>zz'111111111-*=;>>>,>>>>>>>,>,>>**.@@ ",
+" 66fppjjpjpppspspsssssfsssfsjffjffffffccccf7fcf7cccccc7ccsyy66vddk'kk!kzzzz****=***zkzz-111111111,=>>;>>>>>>,>,>>>,>==!+@1 ",
+" 66fjjjpjppuususssssfjffjfffffffffff8fffcff7ccf7cc7ccccfyyc6vddddkk!kkzzzzz=*+!**z%1-<111111111+*;;>>>>>>>>>>>>,>>>*;++- ",
+" 66spjppjpsususpsssssssffjfjffff8ffcf7fccffccccccccmcsyyx6m5+)ddk'kkkzzzzzz'115'zlol1111111111==>;>;>>>>>>>>,>>,>=*%+@ ",
+" 66fpjppppspssssjssfjfsfffffffffcfffffcf7cccf7cmcccxyy66m&&$@+''kkkggzzzzzlq1115lq1q11111111%*;>>;>;>>,>>>>,>>>>*&++l ",
+" 66sjpjuusppssssfssffsfjfffffffffccc7fcfc7ccccc7cuyyc6m!,&&!#+$'kggggzzzklqlq11111r1111111l==;>>>>>>>>>,>>>>,>=*#+@ ",
+" 66cppppssspssssjfssffffs8ffffcf8cffccccfmfmccfyyx667&,&,,&&!.@<gggzggzzz$lq1111111111111,*>>>>;>;>>>>>>>>>>=*!@+1 ",
+" 666ppuusssssssfsfssfsfffffcfcfcfccc7fmfcccmsyyx66w&;,,&,&!&&%5l'ggzggzgk1511qo11111111@==;;>>>>>>>>>>>,>,>*=.+- ",
+" 666sspspssssssjfjfffsfff8fffcf7ccfcccc7cfyyyx66t$#%&,,&&&,,>>$~5dggzgz51115ol11111111==>>>;>;>;>>>>>>>>>=*%+@ ",
+" 666cxsspssssfsfffjfffffffcffffccc7fmffuyyx666 #$#$!&&&,,=;>;,.1<dgz'~1111l11111111$*=>;>;>;>>>>>>>>>>>*,++- ",
+" 666fssssfssfssfffffffff7fccc8ccccfsyyx666m $$#$%!,======;=&@l<g~r111111111111l==>;>;>>>;;;>>>>>>>**#+@ ",
+" 666csssssffsffsfffffffffccccfsuyyx666m %$##$;**===;==>!-~~q1q1111111111&*;>>>;;>>>>;>>>>>>;*!++1 ",
+" 666cfssssfsffffffffcccfssuyyxx666m %%% $...%;*====;===%lllq111111111@==>;;;>>;>>;>>;>>>>*=..- ",
+" m6666ccspsspsfsffsspsuyyxc6666mt %%! ....!**===>===>$15111111111;=;>;>>;>;>>>;>>>>>=*%++1 ",
+" tm66666ccffsssssxfxc66666m7 !% ....&*=*=>=;==,.11111111#*=;;;>;>>>;>;>>;,;;*;.+5 ",
+" twm666666666666666mmw %%% ...$&**==>=>==!111111l==>;>;>>;;>>>>;>>>>=*#.@ ",
+" %twwmmmmmm7w %%%! ...%;**=;=>===#1111!*;;;;;;;;>>;>>>>;>=*&++l ",
+" %!%%% %%%!% ....!=**;=;====+1-*=>=>>;>>;>>;;>;>>>*=#+@ ",
+" %!%%%%%%%%%%%!% ....!**==>;===,>=;>>=>=>;>;;>>;>>;==!++1 ",
+" ...#&**=;;;=*==>=>>>>;;>;>>>>>>;*;.+- ",
+" ...$,**=;;;==>>=>=>;;;>>;;>;;=*$++ ",
+" ....%=**;>>;;>;>>=>>;;>>;>>=*&.+l ",
+" ....!=*==>=>=;;>;;>;;>>>>==#.@ ",
+" ...#&**=>>;;>;;>;>;;;;=*!.+1 ",
+" ...$,**=>=>=>;>;>>>;*;..- ",
+" ...%;*==>>=>;;>=>=*%.+ ",
+" ....!=*=;>>;>>>=*&..l ",
+" ...#&=*=;;;>>**#.@ ",
+" ...$&**=>;=*!..1 ",
+" ...%;*==*=..- ",
+" ....%=**%.+ ",
+" ...#!&..5 ",
+" .....@ ",
+" ..+ "};
diff --git a/libimage/img1.xpm b/libimage/img1.xpm
new file mode 100644
index 0000000..02143eb
--- /dev/null
+++ b/libimage/img1.xpm
@@ -0,0 +1,56 @@
+/* XPM */
+static char * img1_xpm[] = {
+"37 50 3 1",
+" c None",
+". c #000000",
+"+ c #FFFFFF",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ........... ",
+" .....+++++++++. ",
+" ...+++++++++++++. ",
+" .++++++++++++++.. ",
+" .++++++++++++++. ",
+" ..+++....+++++++. ",
+" ...... .+++++++. ",
+" .. .+++++++. ",
+" .+++++++. ",
+" ..++++++.. ",
+" .+++++++. ",
+" .+++++++. ",
+" .+++++++. ",
+" .+++++++. ",
+" .++++++.. ",
+" ..++++++. ",
+" .+++++++. ",
+" .+++++++. ",
+" .+++++++. ",
+" .+++++++. ",
+" ..++++++.. ",
+" .+++++++. ",
+" .+++++++. ",
+" .+++++++. ",
+" .+++++++. ",
+" .+++++++. ",
+" ..++++++.. ",
+" .+++++++. ",
+" .+++++++. ",
+" .........+++++++......... ",
+" .+++++++++++++++++++++++. ",
+" .++++++++++++++++++++++.. ",
+" .++++++++++++++++++++++. ",
+" ..++++++++++++++++++++++. ",
+" ......................... ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" "};
diff --git a/libimage/img2.xpm b/libimage/img2.xpm
new file mode 100644
index 0000000..f4046e4
--- /dev/null
+++ b/libimage/img2.xpm
@@ -0,0 +1,56 @@
+/* XPM */
+static char * img2_xpm[] = {
+"42 50 3 1",
+" c None",
+". c #000000",
+"+ c #FFFFFF",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" .......... ",
+" ......++++++++.... ",
+" ...++++++++++++++++.. ",
+" .+++++++++++++++++++.. ",
+" .++++++++++++++++++++.. ",
+" .++++.........++++++++. ",
+" .+.... ..+++++++.. ",
+" .... ..+++++++. ",
+" . .+++++++. ",
+" .+++++++. ",
+" .+++++++. ",
+" .++++++.. ",
+" ..++++++. ",
+" .++++++.. ",
+" ..+++++.. ",
+" ..++++++. ",
+" ..++++++.. ",
+" ..+++++... ",
+" ..+++++.. ",
+" ...+++++.. ",
+" ..++++++.. ",
+" ..+++++... ",
+" ...+++++.. ",
+" ..++++++.. ",
+" ..++++++.. ",
+" ..++++++.. ",
+" ...+++++... ",
+" ..++++++.. ",
+" ..+++++++. ",
+" ..++++++++............... ",
+" .+++++++++++++++++++++++. ",
+" .+++++++++++++++++++++++. ",
+" .+++++++++++++++++++++++. ",
+" ..++++++++++++++++++++++.. ",
+" .+++++++++++++++++++++++. ",
+" ......................... ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" "};
diff --git a/libimage/img3.xpm b/libimage/img3.xpm
new file mode 100644
index 0000000..c83fa64
--- /dev/null
+++ b/libimage/img3.xpm
@@ -0,0 +1,56 @@
+/* XPM */
+static char * img3_xpm[] = {
+"42 50 3 1",
+" c None",
+". c #000000",
+"+ c #FFFFFF",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ............. ",
+" ....+++++++++++.... ",
+" .+++++++++++++++++... ",
+" .+++++++++++++++++++.. ",
+" .++++++++++++++++++++. ",
+" ..++.........+++++++++.. ",
+" ..... ...++++++++. ",
+" . ..+++++++. ",
+" .+++++++. ",
+" .+++++++. ",
+" .++++++.. ",
+" .++++++. ",
+" ..+++++.. ",
+" ..+++++.. ",
+" ...+++++.. ",
+" .........+++++... ",
+" .++++++++++++.. ",
+" .++++++++++++. ",
+" .++++++++++++... ",
+" ..++++++++++++++.. ",
+" .........++++++++.. ",
+" ...+++++++.. ",
+" ..+++++++. ",
+" .+++++++. ",
+" .+++++++. ",
+" .+++++++. ",
+" .+++++++. ",
+" ..++++++.. ",
+" . .+++++++. ",
+" ... ..++++++.. ",
+" .+.... ....+++++++. ",
+" .++++..........+++++++++.. ",
+" ..+++++++++++++++++++++... ",
+" .+++++++++++++++++++++.. ",
+" ..++++++++++++++++++... ",
+" .....++++++++++..... ",
+" ............ ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" "};
diff --git a/libimage/img4.xpm b/libimage/img4.xpm
new file mode 100644
index 0000000..3be99a9
--- /dev/null
+++ b/libimage/img4.xpm
@@ -0,0 +1,56 @@
+/* XPM */
+static char * img4_xpm[] = {
+"44 50 3 1",
+" c None",
+". c #000000",
+"+ c #FFFFFF",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ........... ",
+" ..++++++++.. ",
+" ..+++++++++. ",
+" .++++++++++. ",
+" ..++++++++++. ",
+" ..+++++++++++. ",
+" ..+++++++++++.. ",
+" ..+++...++++++. ",
+" ..+++.. .++++++. ",
+" ..+++.. .++++++. ",
+" ..+++.. .++++++. ",
+" ..++++. .+++++.. ",
+" .++++.. ..+++++. ",
+" ..+++.. .++++++. ",
+" ..+++.. .++++++. ",
+" ..+++.. .++++++. ",
+" ..+++.. .+++++.. ",
+" ..+++.. ..+++++. ",
+" ..+++.. .++++++. ",
+" ..+++.. .++++++. ",
+" .++++. .++++++. ",
+" ..++++. .++++++. ",
+" .+++++............++++++...... ",
+" .+++++++++++++++++++++++++++. ",
+" .+++++++++++++++++++++++++++. ",
+" ..+++++++++++++++++++++++++++. ",
+" .++++++++++++++++++++++++++++. ",
+" .................++++++....... ",
+" .++++++. ",
+" .++++++. ",
+" .++++++. ",
+" .+++++.. ",
+" ..+++++. ",
+" .++++++. ",
+" ........ ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" "};
diff --git a/libimage/img5.xpm b/libimage/img5.xpm
new file mode 100644
index 0000000..9aa80d3
--- /dev/null
+++ b/libimage/img5.xpm
@@ -0,0 +1,56 @@
+/* XPM */
+static char * img5_xpm[] = {
+"43 50 3 1",
+" c None",
+". c #000000",
+"+ c #FFFFFF",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ....................... ",
+" .++++++++++++++++++++.. ",
+" .++++++++++++++++++++. ",
+" ..++++++++++++++++++++. ",
+" .+++++++++++++++++++++. ",
+" .+++++++++++++++++++++. ",
+" .+++++................. ",
+" .+++++. ",
+" ..++++.. ",
+" .+++++. ",
+" .+++++. ",
+" .+++++. ",
+" .+++++.......... ",
+" ..++++++++++++++... ",
+" .+++++++++++++++++... ",
+" .+++++++++++++++++++.. ",
+" .++++++++++++++++++++.. ",
+" .++..........+++++++++. ",
+" ..... ..++++++++.. ",
+" .. ..++++++++. ",
+" ..+++++++. ",
+" .+++++++. ",
+" .+++++++. ",
+" .+++++++. ",
+" .++++++.. ",
+" .++++++. ",
+" ..++++++. ",
+" . ..++++++.. ",
+" ... ..+++++++. ",
+" .+..... ...+++++++.. ",
+" .+++++.........++++++++.. ",
+" ..+++++++++++++++++++++.. ",
+" .++++++++++++++++++++... ",
+" ...++++++++++++++++... ",
+" .....+++++++++.... ",
+" ........... ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" "};
diff --git a/libimage/img6.xpm b/libimage/img6.xpm
new file mode 100644
index 0000000..e915233
--- /dev/null
+++ b/libimage/img6.xpm
@@ -0,0 +1,56 @@
+/* XPM */
+static char * img6_xpm[] = {
+"41 50 3 1",
+" c None",
+". c #000000",
+"+ c #FFFFFF",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ......... ",
+" ....+++++++.... ",
+" ...+++++++++++++... ",
+" ...++++++++++++++++. ",
+" ..++++++++++++++++++. ",
+" ..+++++++.........+++. ",
+" ..++++++... ..... ",
+" ..+++++... . ",
+" ..+++++.. ",
+" .++++++. ",
+" ..+++++.. ",
+" .++++++. ",
+" ..+++++.. ",
+" .++++++. ....... ",
+" ..++++++. ...+++++.... ",
+" .+++++++....++++++++++.. ",
+" .++++++++++++++++++++++.. ",
+" .+++++++++++++++++++++++.. ",
+" ..++++++++++......++++++++. ",
+" .+++++++++... ..+++++++.. ",
+" .++++++++.. ..+++++++. ",
+" .++++++++. .+++++++. ",
+" .+++++++.. .+++++++. ",
+" .+++++++. .+++++++. ",
+" .+++++++. .+++++++. ",
+" .+++++++. .++++++.. ",
+" .+++++++. .++++++. ",
+" ..++++++. ..++++++. ",
+" .++++++. .++++++.. ",
+" .++++++.. ..++++++. ",
+" ..++++++.. ...++++++.. ",
+" ..++++++......+++++++.. ",
+" .++++++++++++++++++.. ",
+" ..++++++++++++++++.. ",
+" ...++++++++++++... ",
+" ....++++++.... ",
+" ........ ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" "};
diff --git a/libimage/img7.xpm b/libimage/img7.xpm
new file mode 100644
index 0000000..d9ef9e5
--- /dev/null
+++ b/libimage/img7.xpm
@@ -0,0 +1,56 @@
+/* XPM */
+static char * img7_xpm[] = {
+"43 50 3 1",
+" c None",
+". c #000000",
+"+ c #FFFFFF",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ........................... ",
+" .++++++++++++++++++++++++.. ",
+" .++++++++++++++++++++++++. ",
+" .++++++++++++++++++++++++. ",
+" ..++++++++++++++++++++++++. ",
+" .++++++++++++++++++++++++.. ",
+" .................+++++++.. ",
+" .+++++++. ",
+" ..++++++.. ",
+" ..++++++.. ",
+" .+++++++. ",
+" ..++++++.. ",
+" .++++++.. ",
+" ..++++++. ",
+" ..++++++.. ",
+" .+++++++. ",
+" ..++++++.. ",
+" ..++++++.. ",
+" .+++++++. ",
+" ..++++++.. ",
+" ..++++++.. ",
+" .+++++++. ",
+" ..++++++.. ",
+" ..++++++.. ",
+" .+++++++. ",
+" ..++++++.. ",
+" ..++++++.. ",
+" .+++++++. ",
+" ..++++++.. ",
+" .++++++.. ",
+" ..++++++. ",
+" ..++++++.. ",
+" .+++++++. ",
+" ..++++++.. ",
+" .......... ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" "};
diff --git a/libimage/img8.xpm b/libimage/img8.xpm
new file mode 100644
index 0000000..3baee10
--- /dev/null
+++ b/libimage/img8.xpm
@@ -0,0 +1,56 @@
+/* XPM */
+static char * img8_xpm[] = {
+"42 50 3 1",
+" c None",
+". c #000000",
+"+ c #FFFFFF",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" .......... ",
+" .....++++++++.... ",
+" ...+++++++++++++++.. ",
+" ..++++++++++++++++++.. ",
+" ..++++++++++++++++++++.. ",
+" ..++++++++......++++++++.. ",
+" .+++++++... ..++++++++. ",
+" ..+++++++. ..+++++++. ",
+" .+++++++.. .+++++++. ",
+" .+++++++. .+++++++. ",
+" .+++++++. .++++++.. ",
+" .+++++++. ..++++++. ",
+" ..++++++.. .++++++.. ",
+" ..++++++.. ...+++++.. ",
+" ..++++++......+++++... ",
+" ...+++++++++++++... ",
+" .+++++++++++++. ",
+" ..+++++++++++++. ",
+" ...++++++++++++++.. ",
+" ...+++++......++++++.. ",
+" ..+++++... ..++++++.. ",
+" ..+++++.. ..++++++. ",
+" ..++++++. .++++++.. ",
+" .++++++.. .+++++++. ",
+" .++++++. .+++++++. ",
+" ..++++++. .+++++++. ",
+" .+++++++. .+++++++. ",
+" .+++++++. ..++++++.. ",
+" .+++++++. .+++++++. ",
+" .+++++++.. ..+++++++. ",
+" ..+++++++.. ...+++++++.. ",
+" .++++++++......++++++++.. ",
+" ..++++++++++++++++++++.. ",
+" ..++++++++++++++++++.. ",
+" ...++++++++++++++... ",
+" ....+++++++..... ",
+" ......... ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" "};
diff --git a/libimage/img9.xpm b/libimage/img9.xpm
new file mode 100644
index 0000000..31debe9
--- /dev/null
+++ b/libimage/img9.xpm
@@ -0,0 +1,56 @@
+/* XPM */
+static char * img9_xpm[] = {
+"39 50 3 1",
+" c None",
+". c #000000",
+"+ c #FFFFFF",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ........ ",
+" ....++++++.... ",
+" ...++++++++++++... ",
+" ..++++++++++++++++.. ",
+" ..++++++++++++++++++. ",
+" ..+++++++......++++++.. ",
+" ..++++++... ..++++++.. ",
+" .++++++.. ..++++++. ",
+" ..++++++. .++++++. ",
+" .++++++.. .++++++.. ",
+" .++++++. .+++++++. ",
+" ..++++++. .+++++++. ",
+" .+++++++. .+++++++. ",
+" .+++++++. .+++++++. ",
+" .+++++++. ..+++++++. ",
+" .+++++++. .++++++++. ",
+" .+++++++.. ..++++++++. ",
+" ..+++++++.. ...+++++++++. ",
+" .++++++++......++++++++++.. ",
+" ..+++++++++++++++++++++++. ",
+" ..++++++++++++++++++++++. ",
+" ..++++++++++....+++++++. ",
+" ....+++++... .++++++.. ",
+" ....... .++++++. ",
+" ..+++++.. ",
+" .++++++. ",
+" ..+++++.. ",
+" .++++++. ",
+" ..+++++.. ",
+" . ...+++++.. ",
+" .... ...++++++.. ",
+" ..++.........+++++++.. ",
+" .++++++++++++++++++.. ",
+" .++++++++++++++++... ",
+" ..+++++++++++++... ",
+" ....+++++++.... ",
+" ......... ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" "};
diff --git a/libimage/pnm.c b/libimage/pnm.c
new file mode 100644
index 0000000..3b59db9
--- /dev/null
+++ b/libimage/pnm.c
@@ -0,0 +1,512 @@
+/***************************************************************************
+ * pnm.c
+ * (C) Copyright 2004 Andreas Brachold <vdr04-at-deltab.de>
+ * based on works from Fabian E. Bustamante <fabianb-at-cs.umd.edu>
+ * Created: Thu Aug 7 2004
+ ****************************************************************************/
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+/* #include <sys/stdtypes.h> for size_t */
+#include <stdarg.h>
+#include <errno.h>
+
+#include "pnm.h"
+
+/*****************************************************************************
+Print error message and get out
+@param variable list of arguments explaining error
+@return - none*/
+void cPNM::error(char* format, ... )
+{
+ if(m_szError)
+ free(m_szError);
+ m_szError = NULL;
+
+ va_list args;
+ va_start( args, format );
+
+ (void) vasprintf( &m_szError, format, args );
+ va_end( args );
+}
+
+
+/*****************************************************************************
+Get pnm magic number (P1, P2, ...)
+@param file - source file
+@return - magic number*/
+bool cPNM::MagicNumber(FILE* f,unsigned int& nFormat)
+{
+ int ich1, ich2;
+
+ ich1 = getc( f );
+ if ( ich1 == EOF ) {
+ cPNM::error( "End of file, read error reading magic number" );
+ return false;
+ }
+ ich2 = getc( f );
+ if ( ich2 == EOF ) {
+ cPNM::error( "End of file, read error reading magic number" );
+ return false;
+ }
+ nFormat = ich1 * 256 + ich2;
+ return true;
+}
+
+/*****************************************************************************
+Get next significant character, i.e. jump over comments
+@param file - source file
+@param - Next significant character*/
+bool cPNM::getchar(FILE* f, char& nNext)
+{
+ register int ich;
+ register char ch;
+
+ ich = getc( f );
+ if ( ich == EOF )
+ {
+ cPNM::error( "End of file, read error" );
+ return false;
+ }
+ ch = (char) ich;
+
+ if ( ch == '#' ) {
+ do {
+ ich = getc( f );
+ if ( ich == EOF )
+ {
+ cPNM::error( "End of file, read error" );
+ return false;
+ }
+ ch = (char) ich;
+ } while ( ch != '\n' && ch != '\r' );
+ }
+
+ nNext = ch;
+ return true;
+}
+
+
+/*****************************************************************************
+Get next bit from file
+@param file - source file
+@param - Next bit*/
+bool cPNM::getbit(FILE* f, bit& nNext)
+{
+ register char ch;
+
+ do {
+ if(!getchar( f, ch ))
+ return false;
+ } while ( ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' );
+
+ if ( ch != '0' && ch != '1' )
+ {
+ cPNM::error( "junk in file where bits should be" );
+ return false;
+ }
+ nNext = ( ch == '1' ) ? 1 : 0;
+ return true;
+}
+
+
+/*****************************************************************************
+Get next byte from file
+@param file - source file
+@param - Next byte */
+bool cPNM::getrawbyte(FILE* f,unsigned char& nNext )
+{
+ register int iby;
+
+ iby = getc( f );
+ if ( iby == EOF )
+ {
+ cPNM::error( "End of file, read error" );
+ return false;
+ }
+ nNext = (unsigned char) iby;
+ return true;
+}
+
+
+/*****************************************************************************
+Get integer from file
+@param file - source file
+@param - Next integer */
+//bool cPNM::getint(FILE* f, unsigned int& nNext)
+template<class T> bool cPNM::getint(FILE* f, T& nNext)
+{
+ register char ch;
+ register T i;
+
+ do {
+ if(!getchar( f,ch ))
+ return false;
+ } while ( ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' );
+
+ if ( ch < '0' || ch > '9' )
+ {
+ cPNM::error( "junk in file where an integer should be" );
+ return false;
+ }
+ i = 0;
+ do {
+ i = i * 10 + ch - '0';
+ if(!getchar( f,ch ))
+ return false;
+ } while ( ch >= '0' && ch <= '9' );
+ nNext = i;
+ return true;
+}
+
+
+/*****************************************************************************
+Read Header from file
+@param FILE* f - source file
+@return bool - true successful reading*/
+bool cPNM::readHeader(FILE* f)
+{
+ if(!MagicNumber(f,m_nFormat) /* Read magic number. */
+ || !getint(f,m_nWidth) /* Read size. */
+ || !getint(f,m_nHeight))
+ return false;
+
+ /* Check magic number. */
+ switch (PNM_FORMAT_TYPE(m_nFormat)) {
+ case PPM_TYPE:
+ /* Read m_nColorDepth. */
+ if(!getint(f,m_nColorDepth))
+ return false;
+ if ( (m_nColorDepth) > PPM_MAXMAXVAL )
+ {
+ cPNM::error( "ColorDepth is too large" );
+ return false;
+ }
+ return true;
+
+ case PGM_TYPE:
+ /* Read ColorDepth. */
+ if(!getint(f,m_nColorDepth))
+ return false;
+ if ( m_nColorDepth > PGM_MAXMAXVAL )
+ {
+ cPNM::error( "ColorDepth is too large" );
+ return false;
+ }
+ return true;
+
+ case PBM_TYPE:
+ m_nColorDepth = 1; /* pbmmaxval */
+ return true;
+
+ default:
+ cPNM::error( "bad magic number - not a ppm, pgm, or pbm file" );
+ }
+
+ return false;
+
+}
+
+
+/*****************************************************************************
+Read Row from file with Black White Values
+@param FILE* f - source file
+@return bool - true successful reading*/
+bool cPNM::readpbmrow(FILE* f, bit* pBits)
+{
+ register unsigned int w;
+ register int bitshift;
+ register unsigned char item;
+ register bit* bP;
+
+ switch ( m_nFormat ) {
+ case PBM_FORMAT:
+ for ( w = 0, bP = pBits; w < m_nWidth; ++w, ++bP )
+ {
+ if(!getbit( f,*bP ))
+ return false;
+ }
+ return true;
+
+ case RPBM_FORMAT:
+ bitshift = -1;
+ item = 0;
+ for ( w = 0, bP = pBits; w < m_nWidth; ++w, ++bP ) {
+ if ( bitshift == -1 ) {
+ if(!getrawbyte( f,item ))
+ return false;
+ bitshift = 7;
+ }
+ *bP = ( item >> bitshift ) & 1;
+ --bitshift;
+ }
+ return true;
+
+ default:
+ cPNM::error( "Wrong fileformat" );
+ return false;
+ }
+} /* end cPNM::readpbmrow() */
+
+
+/*****************************************************************************
+Read Row from file with Gray Values
+@param FILE* f - source file
+@return bool - true successful reading*/
+bool cPNM::readpgmrow( FILE* f, gray* pGrays)
+{
+ register unsigned int w;
+ register gray* gP;
+ bit* pBits;
+ register bit* bP;
+
+ switch ( m_nFormat ) {
+ case PGM_FORMAT:
+ for ( w = 0, gP = pGrays; w < m_nWidth; ++w, ++gP ) {
+ if(!getint( f,*gP ))
+ return false;
+ }
+ return true;
+
+ case RPGM_FORMAT:
+ for ( w = 0, gP = pGrays; w < m_nWidth; ++w, ++gP ) {
+ if(!getrawbyte( f,*gP ))
+ return false;
+ }
+ return true;
+
+ case PBM_FORMAT:
+ case RPBM_FORMAT:
+ if(!allocrow( &pBits )
+ || !readpbmrow( f, pBits ))
+ {
+ freerow( pBits );
+ return false;
+ }
+ for ( w = 0, gP = pGrays, bP = pBits; w < m_nWidth; ++w, ++gP, ++bP )
+ *gP = ( *bP == PBM_WHITE ) ? m_nColorDepth : 0;
+ freerow( pBits );
+ return true;
+
+ default:
+ cPNM::error( "Wrong fileformat" );
+ return false;
+ }
+}
+
+
+
+/*****************************************************************************
+Read Row from file with Colored Values
+@param FILE* f - source file
+@return bool - true successful reading*/
+bool cPNM::readppmrow(FILE* f, pixel* pixelrow)
+{
+ register unsigned int w;
+ register pixel* pP;
+ register pixval r, g, b;
+ gray* pGrays;
+ register gray* gP;
+ bit* pBits;
+ register bit* bP;
+
+ switch ( m_nFormat ) {
+ case PPM_FORMAT:
+ for ( w = 0, pP = pixelrow; w < m_nWidth; ++w, ++pP ) {
+ if(!getint( f,r )
+ ||!getint( f,g )
+ ||!getint( f,b ))
+ return false;
+ PPM_ASSIGN( *pP, r, g, b );
+ }
+ return true;
+
+ case RPPM_FORMAT:
+ for ( w = 0, pP = pixelrow; w < m_nWidth; ++w, ++pP ) {
+ if(!getrawbyte( f,r )
+ ||!getrawbyte( f,g )
+ ||!getrawbyte( f,b ))
+ return false;
+ PPM_ASSIGN( *pP, r, g, b );
+ }
+ return true;
+
+ case PGM_FORMAT:
+ case RPGM_FORMAT:
+ if(!allocrow(&pGrays)
+ || !readpgmrow( f, pGrays ))
+ {
+ freerow( pGrays );
+ return false;
+ }
+ for ( w = 0, gP = pGrays, pP = pixelrow; w < m_nWidth; ++w, ++gP, ++pP ) {
+ r = *gP;
+ PPM_ASSIGN( *pP, r, r, r );
+ }
+ freerow( pGrays );
+ return true;
+
+ case PBM_FORMAT:
+ case RPBM_FORMAT:
+ if(!allocrow( &pBits )
+ || !readpbmrow( f, pBits ))
+ {
+ freerow( pBits );
+ return false;
+ }
+ for ( w = 0, bP = pBits, pP = pixelrow; w < m_nWidth; ++w, ++bP, ++pP ) {
+ r = ( *bP == PBM_WHITE ) ? m_nColorDepth : 0;
+ PPM_ASSIGN( *pP, r, r, r );
+ }
+ freerow( pBits );
+ return true;
+
+ default:
+ cPNM::error( "Wrong fileformat" );
+
+ return false;
+ }
+}
+
+
+/*****************************************************************************
+Read Row from file with any Values
+@param FILE* f - source file
+@return bool - true successful reading*/
+bool cPNM::readrow(FILE* f, xel* xelrow)
+{
+ register unsigned int w;
+ register xel* xP;
+ gray* pGrays;
+ register gray* gP;
+ bit* pBits;
+ register bit* bP;
+
+ switch ( PNM_FORMAT_TYPE(m_nFormat) ) {
+ case PPM_TYPE:
+ return readppmrow( f, (pixel*) xelrow );
+
+ case PGM_TYPE:
+ if(!allocrow(&pGrays)
+ || !readpgmrow( f, pGrays ))
+ {
+ freerow( pGrays );
+ return false;
+ }
+ for ( w = 0, xP = xelrow, gP = pGrays; w < m_nWidth; ++w, ++xP, ++gP ) {
+ PNM_ASSIGN1( *xP, *gP );
+ }
+ freerow( pGrays );
+ return true;
+
+ case PBM_TYPE:
+ if(!allocrow(&pBits)
+ || !readpbmrow( f, pBits ))
+ {
+ freerow( pBits );
+ return false;
+ }
+ for ( w = 0, xP = xelrow, bP = pBits; w < m_nWidth; ++w, ++xP, ++bP ) {
+ PNM_ASSIGN1( *xP, *bP == PBM_BLACK ? 0: PNM_MAXMAXVAL );
+ }
+ freerow( pBits );
+ return true;
+
+ default:
+ cPNM::error( "Wrong fileformat" );
+ return false;
+ }
+}
+
+
+
+
+cPNM::cPNM()
+{
+ m_nFormat = 0;
+ m_nColorDepth = 0;
+ m_nHeight = 0;
+ m_nWidth = 0;
+ m_szError = NULL;
+}
+
+cPNM::~cPNM()
+{
+ if(m_szError)
+ free(m_szError);
+}
+
+
+
+
+/*
+int main()
+{
+ cPNM pnmImage;
+ FILE *f=fopen("test.pnm", "r");
+ if(f)
+ {
+ xel* pRow = NULL;
+ register unsigned int w;
+ register unsigned int h;
+
+ if(pnmImage.readHeader(f))
+ {
+ for(h = 0;h < pnmImage.GetHeight() && h < GetHeight();++h)
+ {
+ if(!pnmImage.allocrow(&pRow)
+ ||!pnmImage.readrow(f, pRow) )
+ break;
+
+ xel* pP = pRow;
+ if(pnmImage.GetWidth() < GetWidth())
+ w = (GetWidth() - pnmImage.GetWidth()) / 2;
+ else w = 0;
+ for(;w < pnmImage.GetWidth() && w < GetWidth();++w,++pP)
+ {
+ unsigned char* pImageRGB = m_pImageRGB + (((h*GetWidth())+w)*3);
+ if(pnmImage.GetColorDepth() == 255)
+ {
+ *(pImageRGB + 0) = (unsigned char) PPM_GETR(*pP);
+ *(pImageRGB + 1) = (unsigned char) PPM_GETG(*pP);
+ *(pImageRGB + 2) = (unsigned char) PPM_GETB(*pP);
+ }
+ else if(pnmImage.GetColorDepth() == 1)
+ {
+ *(pImageRGB + 0) = (unsigned char) PPM_GETR(*pP)==0?0x00:0xFF;
+ *(pImageRGB + 1) = (unsigned char) PPM_GETG(*pP)==0?0x00:0xFF;
+ *(pImageRGB + 2) = (unsigned char) PPM_GETB(*pP)==0?0x00:0xFF;
+ }
+ else
+ {
+ *(pImageRGB + 0) = (unsigned char) (PPM_GETR(*pP)*255 / pnmImage.GetColorDepth()) & 0xFF;
+ *(pImageRGB + 1) = (unsigned char) (PPM_GETG(*pP)*255 / pnmImage.GetColorDepth()) & 0xFF;
+ *(pImageRGB + 2) = (unsigned char) (PPM_GETB(*pP)*255 / pnmImage.GetColorDepth()) & 0xFF;
+ }
+ }
+ pnmImage.freerow(pRow);
+ pRow = NULL;
+ }
+ if(pRow)
+ pnmImage.freerow((char*)pRow);
+ }
+ fclose(f);
+ }
+}
+*/
diff --git a/libimage/pnm.h b/libimage/pnm.h
new file mode 100644
index 0000000..f28e829
--- /dev/null
+++ b/libimage/pnm.h
@@ -0,0 +1,218 @@
+/***************************************************************************
+ * pnm.h
+ * (C) Copyright 2004 Andreas Brachold <vdr04-at-deltab.de>
+ * based on works from Fabian E. Bustamante <fabianb-at-cs.umd.edu>
+ * Created: Thu Aug 7 2004
+ ****************************************************************************/
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+
+#ifndef _pnm_h
+#define _pnm_h
+
+/*
+ * CONFIGURE: PGM can store gray values as either bytes or shorts. For most
+ * applications, bytes will be big enough, and the memory savings can be
+ * substantial. However, if you need more than 8 bits of grayscale resolution,
+ * then define this symbol.
+ */
+//#define PGM_BIGGRAYS
+
+/*
+ * CONFIGURE: Normally, PPM handles a pixel as a struct of three grays.
+ * If grays are stored in bytes, that's 24 bits per color pixel; if
+ * grays are stored as shorts, that's 48 bits per color pixel. PPM
+ * can also be configured to pack the three grays into a single longword,
+ * 10 bits each, 30 bits per pixel.
+ *
+ * If you have configured PGM with the PGM_BIGGRAYS option, AND you don't
+ * need more than 10 bits for each color component, AND you care more about
+ * memory use than speed, then this option might be a win. Under these
+ * circumstances it will make some of the programs use 1.5 times less space,
+ * but all of the programs will run about 1.4 times slower.
+ *
+ * If you are not using PGM_BIGGRAYS, then this option is useless -- it
+ * doesn't save any space, but it still slows things down.
+ */
+/* #define PPM_PACKCOLORS */
+
+/* END CONFIGURE */
+
+
+typedef unsigned char bit;
+const bit PBM_WHITE = 0;
+const bit PBM_BLACK = 1;
+
+/* Magic constants. P1,P2,P3...P6 */
+
+const unsigned char PBM_MAGIC1 = 'P';
+const unsigned char PBM_MAGIC2 = '1';
+const unsigned char RPBM_MAGIC2 = '4';
+const unsigned short PBM_FORMAT = (PBM_MAGIC1 * 256 + PBM_MAGIC2);
+const unsigned short RPBM_FORMAT = (PBM_MAGIC1 * 256 + RPBM_MAGIC2);
+#define PBM_TYPE PBM_FORMAT
+
+const unsigned char PGM_MAGIC1 = 'P';
+const unsigned char PGM_MAGIC2 = '2';
+const unsigned char RPGM_MAGIC2 = '5';
+const unsigned short PGM_FORMAT = (PGM_MAGIC1 * 256 + PGM_MAGIC2);
+const unsigned short RPGM_FORMAT = (PGM_MAGIC1 * 256 + RPGM_MAGIC2);
+#define PGM_TYPE PGM_FORMAT
+
+const unsigned char PPM_MAGIC1 = 'P';
+const unsigned char PPM_MAGIC2 = '3';
+const unsigned char RPPM_MAGIC2 = '6';
+const unsigned short PPM_FORMAT = (PPM_MAGIC1 * 256 + PPM_MAGIC2);
+const unsigned short RPPM_FORMAT = (PPM_MAGIC1 * 256 + RPPM_MAGIC2);
+#define PPM_TYPE PPM_FORMAT
+
+#ifdef PGM_BIGGRAYS
+typedef unsigned short gray;
+#define PGM_MAXMAXVAL 65535
+#else /*PGM_BIGGRAYS*/
+typedef unsigned char gray;
+#define PGM_MAXMAXVAL 255
+#endif /*PGM_BIGGRAYS*/
+
+typedef gray pixval;
+
+#ifdef PPM_PACKCOLORS
+
+#define PPM_MAXMAXVAL 1023
+typedef unsigned long pixel;
+#define PPM_GETR(p) (((p) & 0x3ff00000) >> 20)
+#define PPM_GETG(p) (((p) & 0xffc00) >> 10)
+#define PPM_GETB(p) ((p) & 0x3ff)
+#define PPM_ASSIGN(p,red,grn,blu) (p) = ((pixel) (red) << 20) | ((pixel) (grn) << 10) | (pixel) (blu)
+
+#else /*PPM_PACKCOLORS*/
+
+#define PPM_MAXMAXVAL PGM_MAXMAXVAL
+typedef struct
+{
+ pixval r, g, b;
+} pixel;
+
+#define PPM_GETR(p) ((p).r)
+#define PPM_GETG(p) ((p).g)
+#define PPM_GETB(p) ((p).b)
+#define PPM_ASSIGN(p,red,grn,blu) do { (p).r = (red); (p).g = (grn); (p).b = (blu); } while ( 0 )
+
+#endif /*PPM_PACKCOLORS*/
+
+typedef pixel xel;
+typedef pixval xelval;
+#define PNM_MAXMAXVAL PPM_MAXMAXVAL
+#define PNM_ASSIGN1(x,v) PPM_ASSIGN(x,v,v,v)
+
+
+/* Macro for turning a format number into a type number. */
+#define PBM_FORMAT_TYPE(f) ((f) == PBM_FORMAT || (f) == RPBM_FORMAT ? PBM_TYPE : -1)
+#define PGM_FORMAT_TYPE(f) ((f) == PGM_FORMAT || (f) == RPGM_FORMAT ? PGM_TYPE : PBM_FORMAT_TYPE(f))
+#define PNM_FORMAT_TYPE(f) ((f) == PPM_FORMAT || (f) == RPPM_FORMAT ? PPM_TYPE : PGM_FORMAT_TYPE(f))
+
+
+class cPNM
+{
+ unsigned int m_nFormat;
+ unsigned int m_nColorDepth;
+ unsigned int m_nHeight;
+ unsigned int m_nWidth;
+ char* m_szError;
+protected:
+ /*****************************************************************************
+ Print error message and get out
+ @param variable list of arguments explaining error
+ @return - none*/
+ void error (char* format, ... );
+
+ /*****************************************************************************
+ Get pnm magic number (P1, P2, ...)
+ @param file - source file
+ @return - magic number*/
+ bool MagicNumber(FILE* f,unsigned int& nFormat);
+
+ /*****************************************************************************
+ Get next significant character, i.e. jump over comments
+ @param file - source file
+ @param - Next significant character*/
+ bool getchar(FILE* f,char& nNext);
+
+ /*****************************************************************************
+ Get next bit from file
+ @param file - source file
+ @param - Next bit*/
+ bool getbit(FILE* f, bit& nNext );
+
+ /*****************************************************************************
+ Get next byte from file
+ @param file - source file
+ @param - Next byte */
+ bool getrawbyte(FILE* f, unsigned char& nNext);
+
+ /*****************************************************************************
+ Get integer from file
+ @param file - source file
+ @param - Next integer */
+ //bool getint(FILE* file, unsigned int& nNext);
+ template<class T> bool getint(FILE* f, T& nNext);
+
+
+ bool readpbmrow(FILE* file, bit* bitrow);
+ bool readpgmrow(FILE* file, gray* grayrow);
+ bool readppmrow(FILE* file, pixel* pixelrow);
+
+public:
+ cPNM();
+ virtual ~cPNM();
+
+
+ bool readHeader(FILE* Infp);
+ bool readrow(FILE* file, xel* xelrow);
+ inline unsigned int GetHeight() const { return m_nHeight;}
+ inline unsigned int GetWidth() const { return m_nWidth;}
+ inline unsigned int GetColorDepth() const { return m_nColorDepth;}
+ /*****************************************************************************
+ Get an array from memory to hold 'm_nWidth' elements of size 'size'
+ @param int size - of each element
+ @return char* - Pointer to allocated array */
+ template<class T> bool allocrow(T** p)
+ {
+ *p = (T*) malloc( m_nWidth * sizeof(T) );
+ if (*p == (T*) 0 )
+ {
+ cPNM::error( "out of memory allocating a row" );
+ return false;
+ }
+ return true;
+ }
+ /*****************************************************************************
+ Free memory chunk previously assigned
+ @param char *p - pointer to memory chunk
+ @return - none */
+ template<class T> void freerow(T *p)
+ {
+ if(p != NULL)
+ free((char*)p);
+ }
+
+ const char* GetError() const { return m_szError; }
+};
+
+
+#endif /* _pnm_h */
diff --git a/libimage/xpm.c b/libimage/xpm.c
new file mode 100644
index 0000000..9572ec4
--- /dev/null
+++ b/libimage/xpm.c
@@ -0,0 +1,224 @@
+/***************************************************************************
+ * xpm.c
+ *
+ * (C) Copyright 2004 Andreas Brachold <vdr04-at-deltab.de>
+ * Created: Thu Aug 11 2004
+ *
+ ****************************************************************************/
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <vdr/tools.h>
+
+
+#include "img1.xpm"
+#include "img2.xpm"
+#include "img3.xpm"
+#include "img4.xpm"
+#include "img5.xpm"
+#include "img6.xpm"
+#include "img7.xpm"
+#include "img8.xpm"
+#include "img9.xpm"
+
+#include "error.xpm"
+#include "error_small.xpm"
+
+#include "xpm.h"
+
+cXPM gOverlay_Image_1(img1_xpm);
+cXPM gOverlay_Image_2(img2_xpm);
+cXPM gOverlay_Image_3(img3_xpm);
+cXPM gOverlay_Image_4(img4_xpm);
+cXPM gOverlay_Image_5(img5_xpm);
+cXPM gOverlay_Image_6(img6_xpm);
+cXPM gOverlay_Image_7(img7_xpm);
+cXPM gOverlay_Image_8(img8_xpm);
+cXPM gOverlay_Image_9(img9_xpm);
+
+cXPM gOverlay_Error(error_xpm);
+cXPM gOverlay_ErrorSmall(error_small_xpm);
+
+cXPM::cXPM(char* pXPM[])
+:m_Colors(NULL)
+{
+ char **p = pXPM;
+ int c;
+ if (4 != sscanf(*p, "%d %d %d %d", &m_nWidth, &m_nHeight, &m_nColors, &c)
+ || c != 1 )
+ {
+ esyslog("imageplugin: ERROR! faulty 'values' line in XPM: '%s'", *p);
+ return;
+ }
+
+ m_Colors = new colormap[m_nColors];
+
+ for (unsigned int n = 0; n < m_nColors; ++n) {
+ bool bColorNone = false;
+ const char *s = *++p;
+ if (int(strlen(s)) < c) {
+ esyslog("imageplugin: ERROR! faulty 'colors' line in XPM: '%s'", s);
+ return;
+ }
+ (m_Colors+n)->color = *s;
+ s = skipspace(s + c);
+ if (*s != 'c') {
+ esyslog("imageplugin: ERROR! unknown color key in XPM: '%c'", *s);
+ return;
+ }
+ s = skipspace(s + 1);
+ if (strcasecmp(s, "none") == 0) {
+ s = "#000000";
+ bColorNone = true;
+ }
+ if (*s != '#') {
+ esyslog("imageplugin: ERROR! unknown color code in XPM: '%c'", *s);
+ return;
+ }
+ (m_Colors+n)->rgb = strtoul(++s, NULL, 16) | (bColorNone?0xFF000000:0x00000000);
+ }
+
+ m_pXPM = p;
+ for (unsigned int l = 0; l < m_nHeight; l++) {
+ const char *s = *++p;
+ if (strlen(s) != m_nWidth * c) {
+ esyslog("imageplugin: ERROR! faulty pixel line in XPM: %d '%s'", l, s);
+ m_pXPM = NULL;
+ return;
+ }
+ }
+}
+
+cXPM::~cXPM()
+{
+ if(m_Colors)
+ delete[] m_Colors;
+}
+
+
+bool cXPM::Overlay(unsigned char* pRGBMem,unsigned int nMemWidth,unsigned int nMemHeight,unsigned int OffLeft,unsigned int OffTop) const
+{
+ if(!m_Colors || !pRGBMem || !m_pXPM)
+ return false;
+
+ char **p = m_pXPM;
+
+ for(unsigned int h = 0;
+ h < m_nHeight
+ && h+OffTop < nMemHeight;
+ ++h)
+ {
+ const char *s = *++p;
+ for(unsigned int w = 0;w < m_nWidth
+ && w+OffLeft < nMemWidth;
+ ++w,++s)
+ {
+ unsigned int rgb;
+ if(GetColor(*s,rgb))
+ {
+ unsigned char* pImageRGB = pRGBMem +
+ ((((h+OffTop)*nMemWidth)+w+OffLeft)*3);
+
+ *(pImageRGB + 0) = (unsigned char)(rgb >> 16) & 0xFF;
+ *(pImageRGB + 1) = (unsigned char)(rgb >> 8 ) & 0xFF;
+ *(pImageRGB + 2) = (unsigned char)(rgb ) & 0xFF;
+ }
+ }
+ }
+
+ return true;
+}
+
+
+
+bool cXPM::GetColor(unsigned int color,unsigned int& rgb) const
+{
+ unsigned int n = 0;
+ for(;n<m_nColors;++n)
+ {
+ if((m_Colors+n)->color == color)
+ {
+ rgb = (m_Colors+n)->rgb;
+ return (0 == (rgb & 0xFF000000)); // Check for color "none"
+ }
+ }
+ return false;
+}
+
+bool cXPM::Overlay(const char sz,unsigned char* pRGBMem,
+ unsigned int nMemWidth,unsigned int nMemHeight,
+ cXPM::ePlacement place, unsigned int nOffLeft,unsigned int nOffTop,
+ unsigned int nWidth,unsigned int nHeight)
+{
+ cXPM* p = NULL;
+ switch(sz)
+ {
+ case '1': p=&gOverlay_Image_1;break;
+ case '2': p=&gOverlay_Image_2;break;
+ case '3': p=&gOverlay_Image_3;break;
+ case '4': p=&gOverlay_Image_4;break;
+ case '5': p=&gOverlay_Image_5;break;
+ case '6': p=&gOverlay_Image_6;break;
+ case '7': p=&gOverlay_Image_7;break;
+ case '8': p=&gOverlay_Image_8;break;
+ case '9': p=&gOverlay_Image_9;break;
+ case 's': p=&gOverlay_ErrorSmall;break;
+ }
+ if(p)
+ {
+ switch(place)
+ {
+ case TopLeft:
+ return p->Overlay(pRGBMem,
+ nMemWidth,nMemHeight,
+ nOffLeft,
+ nOffTop);
+ case TopRight:
+ return p->Overlay(pRGBMem,
+ nMemWidth,nMemHeight,
+ nOffLeft+nWidth-p->m_nWidth-16,
+ nOffTop);
+ case Center:
+ return p->Overlay(pRGBMem,
+ nMemWidth,nMemHeight,
+ nOffLeft + nWidth/2 - p->m_nWidth/2,
+ nOffTop + nHeight/2 - p->m_nHeight/2);
+ case BottomLeft:
+ return p->Overlay(pRGBMem,
+ nMemWidth,nMemHeight,
+ nOffLeft,
+ nOffTop+nHeight-p->m_nHeight);
+ case BottomRight:
+ return p->Overlay(pRGBMem,
+ nMemWidth,nMemHeight,
+ nOffLeft+nWidth-p->m_nWidth-16,
+ nOffTop+nHeight-p->m_nHeight);
+ }
+ }
+
+ return false;
+}
+
+bool cXPM::Error(unsigned char* pRGBMem,unsigned int nMemWidth,unsigned int nMemHeight)
+{
+ return gOverlay_Error.Overlay(pRGBMem,
+ nMemWidth,nMemHeight,
+ (nMemWidth - gOverlay_Error.m_nWidth)/2,
+ (nMemHeight - gOverlay_Error.m_nHeight)/2);
+}
diff --git a/libimage/xpm.h b/libimage/xpm.h
new file mode 100644
index 0000000..3e9fbb5
--- /dev/null
+++ b/libimage/xpm.h
@@ -0,0 +1,65 @@
+/***************************************************************************
+ * xpm.h
+ *
+ * (C) Copyright 2004 Andreas Brachold <vdr04-at-deltab.de>
+ * Created: Thu Aug 11 2004
+ *
+ ****************************************************************************/
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+
+#ifndef _xpm_h
+#define _xpm_h
+
+class cXPM
+{
+
+ char **m_pXPM;
+ unsigned int m_nWidth;
+ unsigned int m_nHeight;
+ unsigned int m_nColors;
+
+ struct colormap
+ {
+ unsigned int color;
+ unsigned int rgb;
+ } *m_Colors;
+protected:
+ bool GetColor(unsigned int color,unsigned int& rgb) const;
+public:
+ cXPM(char* pXPM[]);
+ virtual ~cXPM();
+
+ enum ePlacement
+ {
+ TopLeft,
+ TopRight,
+ Center,
+ BottomLeft,
+ BottomRight
+ };
+
+ bool Overlay(unsigned char* pRGBMem,unsigned int nMemWidth,unsigned int nMemHeight,unsigned int OffLeft,unsigned int OffTop) const;
+ static bool Overlay(const char sz,unsigned char* pRGBMem,
+ unsigned int nMemWidth,unsigned int nMemHeight,
+ cXPM::ePlacement place, unsigned int nOffLeft,unsigned int nOffTop,
+ unsigned int nWidth,unsigned int nHeight);
+ static bool Error(unsigned char* pRGBMem,unsigned int nMemWidth,unsigned int nMemHeight);
+};
+
+#endif /* _xpm_h */
diff --git a/liboutput/Makefile b/liboutput/Makefile
new file mode 100644
index 0000000..acac360
--- /dev/null
+++ b/liboutput/Makefile
@@ -0,0 +1,58 @@
+#
+# Makefile for a Video Disk Recorder plugin
+#
+# $Id$
+
+VDRDIR = ../../../..
+DVBDIR = ../../../../../DVB
+FFMDIR = ../../../../../ffmpeg
+
+### The C++ compiler and options:
+
+CXX ?= g++
+CXXFLAGS ?= -O0 -g -Wall -Woverloaded-virtual
+
+-include $(VDRDIR)/Make.config
+
+### The directory environment:
+
+
+INCLUDES += -I$(VDRDIR)/include -I$(DVBDIR)/include
+
+DEFINES += -D_GNU_SOURCE
+
+LIBS +=
+
+ifdef FFMPEG_STATIC
+ INCLUDES += -I$(FFMDIR)
+ DEFINES += -DHAVE_FFMPEG_STATIC
+endif
+
+### The object files (add further files here):
+
+OBJS = encode.o stillimage.o stillimage-player.o
+
+### Implicit rules:
+
+%.o: %.c
+ $(CXX) $(CXXFLAGS) -c $(DEFINES) $(INCLUDES) $<
+
+# Dependencies:
+
+MAKEDEP = $(CXX) -MM -MG
+DEPFILE = .dependencies
+$(DEPFILE): Makefile
+ @$(MAKEDEP) $(DEFINES) $(INCLUDES) $(OBJS:%.o=%.c) > $@
+
+-include $(DEPFILE)
+
+### Targets:
+
+all: liboutput.a
+
+liboutput.a : $(OBJS)
+ $(AR) $(ARFLAGS) $@ $(OBJS)
+# $(CXX) $(CXXFLAGS) -shared $(OBJS) $(LIBS) -o $@
+
+clean:
+ @-rm -f $(OBJS) $(DEPFILE) *.a *.so *.tgz core* *~
diff --git a/liboutput/encode.c b/liboutput/encode.c
new file mode 100644
index 0000000..351e30f
--- /dev/null
+++ b/liboutput/encode.c
@@ -0,0 +1,369 @@
+/***************************************************************************
+ * encode.c
+ *
+ * (C) Copyright 2004 Andreas Brachold <vdr04-at-deltab.de>
+ * Created: Thu Aug 5 2004
+ *
+ ****************************************************************************/
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#ifndef HAVE_FFMPEG_STATIC
+#include <dlfcn.h>
+#endif
+
+#include "encode.h"
+#include <vdr/device.h>
+#include <vdr/tools.h>
+
+typedef void (*f_avcodec_init)(void);
+typedef void (*f_avcodec_register_all)(void);
+typedef AVCodec *(*f_avcodec_find_encoder)(enum CodecID id);
+typedef AVCodecContext *(*f_avcodec_alloc_context)(void);
+typedef AVFrame *(*f_avcodec_alloc_frame)(void);
+typedef int (*f_avcodec_open)(AVCodecContext *avctx, AVCodec *codec);
+typedef int (*f_avcodec_close)(AVCodecContext *avctx);
+typedef int (*f_avcodec_encode_video)(AVCodecContext *avctx, uint8_t *buf, int buf_size,const AVFrame *pict);
+typedef int (*f_avpicture_fill)(AVPicture *picture, uint8_t *ptr, int pix_fmt, int width, int height);
+
+#if FFMPEG_VERSION_INT <= 0x000408
+typedef int (*f_img_convert)(AVPicture *dst, int dst_pix_fmt,AVPicture *src, int pix_fmt,int width, int height);
+#elif FFMPEG_VERSION_INT >= 0x000409
+typedef int (*f_img_convert)(AVPicture *dst, int dst_pix_fmt,const AVPicture *src, int pix_fmt,int width, int height);
+#else
+ #error "there is a unknow ffmpeg version or FFMPEG_VERSION_INT isnt defined"
+#endif
+
+#ifdef HAVE_FFMPEG_STATIC
+
+static f_avcodec_init fn_avcodec_init=avcodec_init;
+static f_avcodec_register_all fn_avcodec_register_all=avcodec_register_all;
+static f_avcodec_find_encoder fn_avcodec_find_encoder=avcodec_find_encoder;
+static f_avcodec_alloc_context fn_avcodec_alloc_context=avcodec_alloc_context;
+static f_avcodec_alloc_frame fn_avcodec_alloc_frame=avcodec_alloc_frame;
+static f_avcodec_open fn_avcodec_open=avcodec_open;
+static f_avcodec_close fn_avcodec_close=avcodec_close;
+static f_avcodec_encode_video fn_avcodec_encode_video=&avcodec_encode_video;
+static f_avpicture_fill fn_avpicture_fill=avpicture_fill;
+static f_img_convert fn_img_convert=img_convert;
+
+#else
+
+static f_avcodec_init fn_avcodec_init;
+static f_avcodec_register_all fn_avcodec_register_all;
+static f_avcodec_find_encoder fn_avcodec_find_encoder;
+static f_avcodec_alloc_context fn_avcodec_alloc_context;
+static f_avcodec_alloc_frame fn_avcodec_alloc_frame;
+static f_avcodec_open fn_avcodec_open;
+static f_avcodec_close fn_avcodec_close;
+static f_avcodec_encode_video fn_avcodec_encode_video;
+static f_avpicture_fill fn_avpicture_fill;
+static f_img_convert fn_img_convert;
+
+
+#define DLSYM(cast,sym,func) \
+ sym=(cast)dlsym(m_hLibAvcodec, func); \
+ bSuccess &= (sym != NULL); \
+ if(sym == NULL) \
+ esyslog("imageplugin: Link to function %s failed", func);
+
+void *cEncode::m_hLibAvcodec = NULL;
+
+
+/*******************************************************************************
+
+*/
+bool cEncode::InitLibAVCodec(void){
+
+ bool bSuccess=false;
+ // Let ld.so search the libary
+ const char* szLibary = "libavcodec.so";
+ m_hLibAvcodec=dlopen(szLibary, RTLD_LAZY);
+ if(!m_hLibAvcodec)
+ {
+ esyslog("imageplugin: Loading %s failed : %s", szLibary,dlerror());
+ }
+ /* map the lib's syms to our wrapper */
+ else {
+
+ bSuccess=true;
+ DLSYM(f_avcodec_init,fn_avcodec_init,"avcodec_init");
+ DLSYM(f_avcodec_init,fn_avcodec_register_all,"avcodec_register_all");
+ DLSYM(f_avcodec_find_encoder,fn_avcodec_find_encoder,"avcodec_find_encoder");
+ DLSYM(f_avcodec_alloc_context,fn_avcodec_alloc_context,"avcodec_alloc_context");
+ DLSYM(f_avcodec_alloc_frame,fn_avcodec_alloc_frame,"avcodec_alloc_frame");
+ DLSYM(f_avcodec_open,fn_avcodec_open,"avcodec_open");
+ DLSYM(f_avcodec_close,fn_avcodec_close,"avcodec_close");
+ DLSYM(f_avcodec_encode_video,fn_avcodec_encode_video,"avcodec_encode_video");
+ DLSYM(f_avpicture_fill,fn_avpicture_fill,"avpicture_fill");
+ DLSYM(f_img_convert,fn_img_convert,"img_convert");
+ }
+
+ return(bSuccess);
+}
+
+/*******************************************************************************
+
+*/
+void cEncode::CloseLibAVCodec(void){
+
+ if (m_hLibAvcodec)
+ dlclose(m_hLibAvcodec);
+ m_hLibAvcodec = NULL;
+}
+#endif
+
+/*******************************************************************************
+
+*/
+cEncode::cEncode()
+: m_pavCodec(NULL)
+, m_pImageFilled(NULL)
+, m_pImageYUV(NULL)
+, m_nFrames(4)
+, m_nData(0)
+, m_pMPEG(NULL)
+, m_pImageRGB(NULL)
+{
+ m_bLoaded = false;
+ m_pFramesSize = new unsigned int[m_nFrames];
+
+ m_bUsePAL = (cDevice::PrimaryDevice()->GetVideoSystem() == vsPAL);
+ m_nWidth = 720;
+ m_nHeight = m_bUsePAL ? 576 : 480;
+
+
+ m_nMaxMPEGSize = m_nWidth*m_nHeight * 3; //It see for me 500kb are enough, therefore should the double really enough memory
+
+ /* Allocate bufers */
+ if(NULL == (m_pMPEG=(uint8_t *)malloc(m_nMaxMPEGSize*3)) //~1200kb
+ || NULL == (m_pImageRGB=(uint8_t *)malloc(m_nWidth*m_nHeight*3)) //~1200kb
+ || NULL == (m_pImageFilled=(uint8_t *)malloc(m_nWidth*m_nHeight*3)) //~1200kb
+ || NULL == (m_pImageYUV=(uint8_t *)malloc(m_nWidth*m_nHeight*3/2))) //~600kb
+ {
+ esyslog("imageplugin: Failed to alloc memory for bitmaps.");
+ return;
+ }
+
+ m_pavCodec = fn_avcodec_find_encoder(CODEC_ID_MPEG2VIDEO);
+ if (!m_pavCodec) {
+ esyslog("imageplugin: Failed to find CODEC_ID_MPEG2VIDEO.");
+ return;
+ }
+ m_bLoaded = true;
+}
+
+bool cEncode::Register()
+{
+ #ifndef HAVE_FFMPEG_STATIC
+ if(!InitLibAVCodec()) {
+ esyslog("imageplugin: Failed to InitLibAVCodec.");
+ return false;
+ }
+ #endif
+ fn_avcodec_init();
+ fn_avcodec_register_all();
+ return true;
+}
+
+void cEncode::UnRegister()
+{
+#ifndef HAVE_FFMPEG_STATIC
+ CloseLibAVCodec();
+#endif
+}
+
+void cEncode::ClearRGBMem()
+{
+ if(m_pImageRGB && m_bLoaded)
+ memset(m_pImageRGB,0,m_nWidth*m_nHeight*3 );
+}
+
+/*******************************************************************************
+
+*/
+cEncode::~cEncode(void)
+{
+ if(m_pImageYUV)
+ free(m_pImageYUV);
+
+ if(m_pImageFilled)
+ free(m_pImageFilled);
+
+ if(m_pImageRGB)
+ free(m_pImageRGB);
+
+ if(m_pMPEG)
+ free(m_pMPEG);
+ delete m_pFramesSize;
+}
+
+/*******************************************************************************
+
+*/
+bool cEncode::Encode()
+{
+ m_nData = 0;
+
+ if (!m_bLoaded){
+ dsyslog("imageplugin: libavcodec is'nt successful loaded.");
+ return false;
+ }
+
+ bool bSuccess = false;
+ unsigned int i;
+ AVCodecContext *pAVCC = NULL;
+ AVFrame *pAVF = NULL;
+ int nSize=m_nWidth*m_nHeight;
+
+ if(NULL == (pAVCC = fn_avcodec_alloc_context())
+ || NULL == (pAVF = fn_avcodec_alloc_frame())) {
+ esyslog("imageplugin: Failed to alloc memory for AVCODEC and CONTEXT.");
+ goto encexit;
+ }
+
+ pAVCC->bit_rate=1000000; //1000kbit
+ pAVCC->width = m_nWidth;
+ pAVCC->height = m_nHeight;
+ pAVCC->frame_rate=GetFrameRate();
+ pAVCC->frame_rate_base=1;
+ pAVCC->gop_size=m_nFrames-1; //IPB //1 => Encode only I-Frames, bigger
+ pAVCC->max_b_frames=1;
+ pAVCC->flags |= CODEC_FLAG_QSCALE;
+
+ if (fn_avcodec_open(pAVCC, m_pavCodec)<0) {
+ esyslog("imageplugin: Coldn't open Codec.");
+ goto encexit;
+ }
+
+ pAVF->data[0]=m_pImageYUV;
+ pAVF->data[1]=m_pImageYUV+nSize;
+ pAVF->data[2]=pAVF->data[1]+nSize/4;
+ pAVF->linesize[0]=m_nWidth;
+ pAVF->linesize[1]=pAVF->linesize[2]=m_nWidth/2;
+ pAVF->quality = 1;
+
+ // Convert RGB to YUV
+ if(!fn_avpicture_fill((AVPicture*)m_pImageFilled, m_pImageRGB, PIX_FMT_RGB24,
+ m_nWidth, m_nHeight)) {
+ esyslog("imageplugin: failed avpicture_fill");
+ goto encexit;
+ }
+
+ if(fn_img_convert((AVPicture*)pAVF->data, PIX_FMT_YUV420P,
+ (AVPicture*)m_pImageFilled, PIX_FMT_RGB24, m_nWidth, m_nHeight)) {
+ esyslog("imageplugin: failed convert RGB to YUV");
+ goto encexit;
+ }
+
+ // Encode Frames which defined with m_nFrames
+ for(i=0; i<m_nFrames && m_nData < m_nMaxMPEGSize; ++i)
+ {
+ int nFrameSize = fn_avcodec_encode_video(pAVCC, m_pMPEG + m_nData, m_nMaxMPEGSize - m_nData, pAVF);
+ if(nFrameSize < 0)
+ {
+ esyslog("imageplugin: Failed add %d frame, insufficient memory.",i);
+ bSuccess = false;
+ break;
+ }
+ bSuccess = true;
+ m_nData += nFrameSize;
+ *(m_pFramesSize + i) = nFrameSize;
+ }
+
+ if(bSuccess && m_nData < m_nMaxMPEGSize) // if sufficient place present
+ {
+ //add four bytes end sequnce
+ memcpy(m_pMPEG + m_nData,"\0x00\0x00\0x01\0xb7",4);
+ m_nData += 4;
+ *(m_pFramesSize + i - 1) += 4;
+
+ }
+ else bSuccess = false;
+
+ #ifdef TESTCODE
+ if(bSuccess)
+ Save("/tmp/imagetest.mpg");
+ #endif
+
+encexit:
+ if (pAVCC)
+ {
+ fn_avcodec_close(pAVCC);
+ free(pAVCC);
+ }
+
+ if(pAVF)
+ {
+ free(pAVF);
+ }
+ return bSuccess;
+}
+
+
+
+
+#ifdef TESTCODE
+/*******************************************************************************
+
+*/
+bool cEncode::Load(const char* szFileName)
+{
+ int nSize = m_nWidth*m_nHeight;
+ FILE *inf=fopen(szFileName, "r");
+ if(inf)
+ {
+ fseek(inf, 15, SEEK_SET);
+ fread(m_pImageRGB, 1, nSize*3, inf);
+ fclose(inf);
+ return true;
+ }
+ return false;
+}
+
+/*******************************************************************************
+
+*/
+bool cEncode::Save(const char* szFileName) const
+{
+ if(m_pMPEG && m_nData)
+ {
+ FILE * outf=fopen(szFileName, "w");
+ if(outf) {
+ fwrite(m_pMPEG, 1, m_nData, outf);
+ fclose(outf);
+ return true;
+ }
+ }
+ return false;
+}
+#endif
+
+
+#if 0
+int main(){
+ cEncode e;
+ e.Load("test.pnm");
+ e.Encode();
+ e.Save("test.mpg");
+ return 0;
+}
+#endif
diff --git a/liboutput/encode.h b/liboutput/encode.h
new file mode 100644
index 0000000..0491927
--- /dev/null
+++ b/liboutput/encode.h
@@ -0,0 +1,92 @@
+/***************************************************************************
+ * encode.h
+ *
+ * (C) Copyright 2004 Andreas Brachold <vdr04-at-deltab.de>
+ * Created: Thu Aug 5 2004
+ *
+ ****************************************************************************/
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _ENCODE_H
+#define _ENCODE_H
+
+extern "C"
+{
+#ifdef HAVE_FFMPEG_STATIC
+# include <libavcodec/avcodec.h>
+#else
+# include <ffmpeg/avcodec.h>
+//#include <libavcodec/avcodec.h>
+#endif
+}
+
+//#define TESTCODE
+
+class cEncode
+{
+ AVCodec *m_pavCodec;
+ unsigned int m_nMaxMPEGSize;
+ uint8_t *m_pImageFilled;
+ uint8_t *m_pImageYUV;
+protected:
+ const unsigned int m_nFrames;
+ unsigned int m_nData;
+ uint8_t *m_pMPEG;
+ uint8_t *m_pImageRGB;
+ bool m_bUsePAL;
+ unsigned int m_nWidth;
+ unsigned int m_nHeight;
+ unsigned int* m_pFramesSize;
+
+#ifndef HAVE_FFMPEG_STATIC
+private:
+ static void *m_hLibAvcodec;
+protected:
+ static bool InitLibAVCodec(void);
+ static void CloseLibAVCodec(void);
+#endif
+ bool m_bLoaded;
+public:
+ cEncode();
+ virtual ~cEncode();
+
+ /*Load Shared Library and Register Codec*/
+ static bool Register();
+ /*UnLoad Shared*/
+ static void UnRegister();
+
+ bool Encode();
+ inline const uint8_t *Data() const { return m_pMPEG; }
+ inline unsigned int Size() const { return m_nData; }
+ inline unsigned int GetFrames() const { return m_nFrames; }
+ inline unsigned int GetFrameRate() const { return m_bUsePAL?25:30; }
+
+ inline unsigned int GetHeight() const { return m_nHeight; }
+ inline unsigned int GetWidth() const { return m_nWidth; }
+ inline unsigned int GetBorderHeight() const { return 16; }
+ inline unsigned int GetBorderWidth() const { return 16; }
+
+ inline uint8_t *GetRGBMem() { return m_pImageRGB; }
+#ifdef TESTCODE
+ bool Load(const char* szFileName);
+ bool Save(const char* szFileName) const;
+#endif
+ void ClearRGBMem ();
+};
+
+#endif /* _ENCODE_H */
diff --git a/liboutput/stillimage-player.c b/liboutput/stillimage-player.c
new file mode 100644
index 0000000..947157e
--- /dev/null
+++ b/liboutput/stillimage-player.c
@@ -0,0 +1,61 @@
+/***************************************************************************
+ * stillimage-player.c
+ * (C) Copyright 2004 Andreas Brachold <vdr04-at-deltab.de>
+ * Created: Thu Aug 5 2004
+ *
+ ****************************************************************************/
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "stillimage-player.h"
+#include "stillimage.h"
+
+cStillImagePlayer::cStillImagePlayer(ePlayMode PlayMode)
+ : cPlayer(PlayMode)
+ , m_bActive(false)
+ , m_StillImage(this)
+{
+}
+
+cStillImagePlayer::~cStillImagePlayer()
+{
+ Detach();
+}
+
+void cStillImagePlayer::Activate(bool On) {
+ if (On) {
+ m_bActive=m_StillImage.Init();
+ } else {
+ if (m_bActive) {
+ m_StillImage.Stop();
+ m_bActive=false;
+ }
+ }
+}
+
+void cStillImagePlayer::Play(const uchar *Data, int Length) {
+#if VDRVERSNUM < 10318
+ PlayVideo(Data, Length);
+#else
+ PlayPes(Data, Length,true);
+#endif
+}
+
+bool cStillImagePlayer::Wait() {
+ cPoller Poller;
+ return DevicePoll(Poller, 100);
+}
diff --git a/liboutput/stillimage-player.h b/liboutput/stillimage-player.h
new file mode 100644
index 0000000..b3aa712
--- /dev/null
+++ b/liboutput/stillimage-player.h
@@ -0,0 +1,55 @@
+/***************************************************************************
+ * stillimage-player.h
+ * (C) Copyright 2004 Andreas Brachold <vdr04-at-deltab.de>
+ * Created: Thu Aug 5 2004
+ *
+ ****************************************************************************/
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef STILLIMAGEPLAYER_H
+#define STILLIMAGEPLAYER_H
+
+#include <vdr/thread.h>
+#include <vdr/player.h>
+
+#include "stillimage.h"
+
+class cStillImagePlayer
+ : public cPlayer {
+ friend class cStillImage;
+ bool m_bActive;
+protected:
+ cStillImage m_StillImage;
+public:
+ cStillImagePlayer(ePlayMode PlayMode);
+ virtual ~cStillImagePlayer();
+
+ inline unsigned int UseWidth() const
+ { return m_StillImage.GetWidth() - (m_StillImage.GetBorderWidth()*2); }
+ inline unsigned int UseHeight() const
+ { return m_StillImage.GetHeight() - (m_StillImage.GetBorderHeight()*2); }
+protected:
+ virtual void Activate(bool On);
+ void Play(const uchar *Data, int Length);
+ bool Wait();
+
+ virtual bool Worker(bool bDoIt) = 0;
+};
+
+
+#endif
diff --git a/liboutput/stillimage.c b/liboutput/stillimage.c
new file mode 100644
index 0000000..9058ff0
--- /dev/null
+++ b/liboutput/stillimage.c
@@ -0,0 +1,210 @@
+/***************************************************************************
+ * stillimage.c
+ * (C) Copyright 2004 <vdr04-at-deltab.de>
+ * Created: Thu Aug 5 2004
+ *
+ * parts of the code (c) Peter Seyringer
+ * (c) Marcel Wiesweg
+ ****************************************************************************/
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+
+#include <string.h>
+#include <unistd.h>
+
+#include "stillimage.h"
+#include "stillimage-player.h"
+
+
+
+cStillImage::cStillImage(cStillImagePlayer *pl)
+{
+ m_bThreadRun=false;
+ m_bEncodeRequired = false;
+ player=pl;
+}
+
+cStillImage::~cStillImage()
+{
+
+}
+
+bool cStillImage::Init()
+{
+ return Start();
+}
+
+void cStillImage::Stop()
+{
+ if (!m_bThreadRun)
+ return;
+ m_bThreadRun=false;
+
+ Cancel(3);
+}
+
+void cStillImage::Action(void)
+{
+ m_bThreadRun=true;
+ bool bMPEGValid = false;
+ bool bQueueEmpty = false;
+ bool bFreeze = true;
+
+ unsigned int nFrame = 0,nFrameOff=0;
+ int nMircoSekWait;
+ while (m_bThreadRun) {
+
+ nMircoSekWait = 10000;
+ bQueueEmpty = player->Worker(false);
+ if(!bQueueEmpty)
+ {
+ if(!bFreeze)
+ {
+ player->DeviceFreeze();
+ bFreeze = true;
+ }
+ player->Worker(true);
+ }
+
+ if(m_bEncodeRequired
+ && bQueueEmpty)
+ {
+ //convert data to MPEG
+ Lock();
+ bMPEGValid = Encode();
+ Unlock();
+
+ if (!m_bThreadRun)
+ break;
+ m_bEncodeRequired = false;
+ nFrame = 0;
+ nFrameOff=0;
+ if(bFreeze)
+ {
+ player->DevicePlay();
+ bFreeze = false;
+ }
+
+ }
+
+
+ if(bMPEGValid
+ && bQueueEmpty)
+ {
+ /* Methode A ************************************************************/
+ /*timeval timenow,diff,timestart;
+ gettimeofday(&timestart, 0);
+
+ for (int i=1;i<=25 && player->active;i++) {
+ gettimeofday(&timenow, 0);
+ timersub(&timenow, &timestart, &diff);
+ BuildPesPacket(Data(), Size(),(int)(diff.tv_sec*90000.0 + (diff.tv_usec*90000.0)/1000000.0));
+ player->Wait();
+ }*/
+
+ /* Methode B ************************************************************/
+ //for (int i=1;i<= 25 && player->m_bActive;i++) {
+ // BuildPesPacket(Data(), Size(),i);
+ //}
+
+ /* Methode C ************************************************************/
+ //BuildPesPacket(Data(), Size(),1);
+
+ /* Methode D ************************************************************/
+ //player->DeviceStillPicture(Data(), Size());
+
+ /* Methode E ************************************************************/
+
+
+ unsigned int nFrameSize = *(m_pFramesSize + nFrame);
+ if(nFrameSize) // Skip empty Frames
+ {
+ BuildPesPacket(Data() + nFrameOff, nFrameSize,1);
+ nFrameOff += nFrameSize;
+ }
+ if(++nFrame>=m_nFrames)
+ {
+ nFrame = 0;
+ nFrameOff = 0;
+ }
+
+ nMircoSekWait = (1000000/(GetFrameRate()*4)); // Wait duration off 1/4 frame
+
+ if (!m_bThreadRun)
+ break;
+ }
+ //Reduce CPU load!!!
+ usleep(max(10000,nMircoSekWait));
+ }
+ m_bThreadRun=false;
+}
+
+
+//taken from mp1osd.c
+void cStillImage::BuildPesPacket(const unsigned char *data, int len, int timestamp) {
+#define PES_MAX_SIZE 2048
+ int ptslen = timestamp ? 5 : 1;
+ static unsigned char pes_header[PES_MAX_SIZE];
+
+ // startcode:
+ pes_header[0] = pes_header[1] = 0;
+ pes_header[2] = 1;
+ pes_header[3] = 0xe0;
+
+ while (len > 0)
+ {
+ int payload_size = len; // data + PTS
+
+ if (6 + ptslen + payload_size > PES_MAX_SIZE)
+ payload_size = PES_MAX_SIZE - (6 + ptslen);
+
+ // construct PES header: (code from ffmpeg's libav)
+ // packetsize:
+ pes_header[4] = (ptslen + payload_size) >> 8;
+ pes_header[5] = (ptslen + payload_size) & 255;
+
+ if (ptslen == 5)
+ {
+ int x;
+
+ // presentation time stamp:
+ x = (0x02 << 4) | (((timestamp >> 30) & 0x07) << 1) | 1;
+ pes_header[8] = x;
+ x = ((((timestamp >> 15) & 0x7fff) << 1) | 1);
+ pes_header[7] = x >> 8;
+ pes_header[8] = x & 255;
+ x = ((((timestamp) & 0x7fff) << 1) | 1);
+ pes_header[9] = x >> 8;
+ pes_header[10] = x & 255;
+ }
+ else
+ {
+ // stuffing and header bits:
+ pes_header[6] = 0x0f;
+ }
+
+ memcpy (&pes_header[6 + ptslen], data, payload_size);
+ player->Wait();
+ player->Play(pes_header, 6 + ptslen + payload_size);
+
+ len -= payload_size;
+ data += payload_size;
+ ptslen = 1; // store PTS only once, at first packet!
+
+ }
+}
diff --git a/liboutput/stillimage.h b/liboutput/stillimage.h
new file mode 100644
index 0000000..453692c
--- /dev/null
+++ b/liboutput/stillimage.h
@@ -0,0 +1,54 @@
+/***************************************************************************
+ * stillimage.h
+ * (C) Copyright 2004 Andreas Brachold <vdr04-at-deltab.de>
+ * Created: Thu Aug 5 2004
+ *
+ ****************************************************************************/
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef STILLIMAGE_H
+#define STILLIMAGE_H
+
+#include <vdr/thread.h>
+#include "encode.h"
+
+class cStillImagePlayer;
+
+
+class cStillImage
+: public cThread
+, public cEncode {
+
+ cStillImagePlayer *player;
+ volatile bool m_bThreadRun;
+ volatile bool m_bEncodeRequired;
+protected:
+ virtual void Action(void);
+ void BuildPesPacket(const unsigned char *data, int len, int timestamp);
+
+public:
+ cStillImage(cStillImagePlayer *);
+ virtual ~cStillImage();
+
+ bool Init();
+ void Stop();
+ bool EncodeRequired() const {return m_bEncodeRequired;}
+ void EncodeRequired(bool b) {m_bEncodeRequired = b;}
+};
+
+#endif
diff --git a/list.c b/list.c
new file mode 100644
index 0000000..a96b70b
--- /dev/null
+++ b/list.c
@@ -0,0 +1,221 @@
+/*
+ * Image plugin to VDR (C++)
+ *
+ * (C) 2004 A. Brachold <vdr04-at-deltab.de>
+ *
+ * This code is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This code is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <string.h>
+
+#include "list.h"
+#include "data-image.h"
+#include "setup-image.h"
+
+#include <vdr/tools.h>
+
+cActivSlideShow theSlideShow;
+
+cActivSlideShow::cActivSlideShow()
+: m_pCurSlideShow(NULL)
+, m_nCurrentImage(0)
+, m_nTotalImages(0)
+, m_pCurImage(NULL)
+{
+
+}
+
+void cActivSlideShow::Remove(cFileSource* src){
+
+ if(m_pCurSlideShow) {
+ cImage* p = m_pCurSlideShow->cList < cImage >::First();
+ while (p) {
+ if(p->CompareBaseDir(src)) {
+ bool bRemoveCurrent = (m_pCurImage == p);
+ m_pCurSlideShow->cList < cImage >::Del(p);
+ p = m_pCurSlideShow->cList < cImage >::First();
+ if(bRemoveCurrent) {
+ m_nCurrentImage = 1;
+ m_pCurImage = p;
+ }
+ } else {
+ p = m_pCurSlideShow->cList < cImage >::Next(p);
+ }
+ }
+ m_nTotalImages = m_pCurSlideShow->cList < cImage >::Count();
+ }
+}
+
+void cActivSlideShow::Assign(cSlideShow *pCurSlideShow)
+{
+ Shutdown();
+ m_nCurrentImage = 0;
+ m_nTotalImages = 0;
+ m_pCurSlideShow = pCurSlideShow;
+ if(m_pCurSlideShow) {
+
+ m_nTotalImages = m_pCurSlideShow->Count();
+ // get real Index of FirstImage
+ unsigned int i = 0;
+ bool bFound = false;
+ if(m_pCurSlideShow->FirstImage()) {
+
+ cImage* p = m_pCurSlideShow->cList < cImage >::First();
+
+ for (;p && i < m_nTotalImages; ++i) {
+ // find out first image
+ if(0 == strcmp(::basename(p->Name()),m_pCurSlideShow->FirstImage())) {
+ m_pCurImage = p;
+ bFound = true;
+ break;
+ }
+ p = m_pCurSlideShow->cList < cImage >::Next(p);
+ }
+ }
+ if(bFound) {
+ m_nCurrentImage = i+1;
+ }
+ else {
+ m_pCurImage = m_pCurSlideShow->cList < cImage >::First();
+ m_nCurrentImage = 1;
+ }
+ }
+}
+
+void cActivSlideShow::Shutdown(void)
+{
+ if(m_pCurSlideShow) {
+ delete m_pCurSlideShow;
+ m_pCurSlideShow = 0;
+ m_pCurImage = 0;
+ m_nCurrentImage = 0;
+ m_nTotalImages = 0;
+ }
+}
+
+cSlideShow *cActivSlideShow::SlideShow(void)
+{
+ return m_pCurSlideShow;
+}
+
+
+cImage* cActivSlideShow::GetImage()
+{
+ return m_pCurSlideShow?m_pCurImage:NULL;
+}
+
+
+bool cActivSlideShow::NextImage(int nOffset)
+{
+ if(m_pCurImage && m_nCurrentImage < m_nTotalImages) {
+ cImage *pNewActiv;
+ for (int i = 0; (i < nOffset) && (m_nCurrentImage < m_nTotalImages); ++i) {
+ pNewActiv = m_pCurSlideShow->cList < cImage >::Next(m_pCurImage);
+ m_pCurImage = pNewActiv;
+ ++m_nCurrentImage;
+ }
+ return m_pCurImage != NULL;
+ }
+ else if(ImageSetup.AutoRepeat) {
+ cImage *pNewActiv;
+
+ pNewActiv = m_pCurSlideShow->cList < cImage >::First();
+ m_pCurImage = pNewActiv;
+ m_nCurrentImage = 1;
+
+ return m_pCurImage != NULL;
+ }
+ return false;
+}
+
+
+
+bool cActivSlideShow::PrevImage(int nOffset)
+{
+ if(m_pCurImage && m_nCurrentImage > 1) {
+ cImage *pNewActiv;
+
+ for (int i = 0; (i < nOffset) && (m_nCurrentImage > 1); ++i) {
+ pNewActiv = m_pCurSlideShow->cList < cImage >::Prev(m_pCurImage);
+ m_pCurImage = pNewActiv;
+ --m_nCurrentImage;
+ }
+ return m_pCurImage != NULL;
+ }
+ else if(ImageSetup.AutoRepeat) {
+ cImage *pNewActiv;
+
+ pNewActiv = m_pCurSlideShow->cList < cImage >::Last();
+ m_pCurImage = pNewActiv;
+ m_nCurrentImage = m_nTotalImages;
+ return m_pCurImage != NULL;
+ }
+ return false;
+}
+
+
+bool cActivSlideShow::GotoImage(unsigned int nNewPictureIndex)
+{
+ if(m_pCurImage && nNewPictureIndex > 0 && nNewPictureIndex < m_nTotalImages) {
+ cImage *pNewActiv;
+ pNewActiv = m_pCurSlideShow->cList < cImage >::First();
+ m_pCurImage = pNewActiv;
+ m_nCurrentImage = 0;
+ for (unsigned int i = 0; i < nNewPictureIndex; ++i) {
+ pNewActiv = m_pCurSlideShow->cList < cImage >::Next(m_pCurImage);
+ m_pCurImage = pNewActiv;
+ ++m_nCurrentImage;
+ }
+ return m_pCurImage != NULL;
+ }
+ return false;
+}
+
+int cActivSlideShow::GetJumpNames(int nOffset,cImage* pImage[],const unsigned int nMAX_BILDER)
+{
+ unsigned int i;
+ cImage *pNewActiv;
+ int nBilder = 0;
+
+ if(m_pCurImage && m_nCurrentImage > 0) {
+
+ int nJumpFirst = m_nCurrentImage + nOffset;
+
+ if(nJumpFirst > ((int)(m_nTotalImages - nMAX_BILDER + 1)))
+ nJumpFirst = ((int)(m_nTotalImages - nMAX_BILDER + 1));
+ if(nJumpFirst < 1)
+ nJumpFirst = 1;
+
+ m_nCurrentImage = nJumpFirst;
+ if(m_nCurrentImage > m_nTotalImages)
+ m_nCurrentImage = m_nTotalImages;
+ if(m_nCurrentImage < 1)
+ m_nCurrentImage = 1;
+
+ pNewActiv = m_pCurSlideShow->cList < cImage >::First();
+ for (int n = 1; n < nJumpFirst && pNewActiv; ++n)
+ pNewActiv = m_pCurSlideShow->cList < cImage >::Next(pNewActiv);
+ for (i = 0; (i < nMAX_BILDER) && ((nJumpFirst + i) <= m_nTotalImages) && pNewActiv; ++i,++nBilder) {
+
+ pImage[i] = pNewActiv;
+ dsyslog("imageplugin: File%d: %s", i, pNewActiv->Name());
+ if((nJumpFirst + i) == m_nCurrentImage)
+ m_pCurImage = pNewActiv;
+ pNewActiv = m_pCurSlideShow->cList < cImage >::Next(pNewActiv);
+ }
+ }
+ return nBilder;
+}
diff --git a/list.h b/list.h
new file mode 100644
index 0000000..97824ca
--- /dev/null
+++ b/list.h
@@ -0,0 +1,60 @@
+/*
+ * Image plugin to VDR (C++)
+ *
+ * (C) 2004 Andreas Brachold <vdr04-at-deltab.de>
+ *
+ * This code is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This code is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#ifndef ___IMAGE_LIST_H
+#define ___IMAGE_LIST_H
+
+class cSlideShow;
+class cImage;
+class cFileSource;
+
+class cActivSlideShow
+{
+ /** The current active filelist with the slide*/
+ cSlideShow *m_pCurSlideShow;
+ /** Current number of Image in slide */
+ unsigned int m_nCurrentImage;
+ /** Total number of Images in slide */
+ unsigned int m_nTotalImages;
+ /** Reference to current Image of this player */
+ cImage *m_pCurImage;
+
+public:
+ cActivSlideShow();
+ void Assign(cSlideShow *pCurSlideShow);
+ void Shutdown(void);
+ cSlideShow *SlideShow(void);
+ cImage* GetImage();
+ bool PrevImage(int nOffset);
+ bool GotoImage(unsigned int nNewPictureIndex);
+ bool NextImage(int nOffset);
+ int GetJumpNames(int nOffset,cImage* pImage[],const unsigned int nMAX_BILDER);
+
+ void Remove(cFileSource* src);
+
+ /** Deliver the current number of viewed Image */
+ int ImageCurrent(void) const { return m_nCurrentImage; }
+ /** Deliver the total number of viewed Image */
+ int ImageTotal(void) const { return m_nTotalImages; }
+
+} extern theSlideShow;
+
+#endif //___IMAGE_LIST_H
diff --git a/menu-commands.c b/menu-commands.c
new file mode 100644
index 0000000..8aaaaec
--- /dev/null
+++ b/menu-commands.c
@@ -0,0 +1,163 @@
+/*
+ * Image plugin to VDR (C++)
+ *
+ * (C) 2004 Andreas Brachold <vdr04-at-deltab.de>
+ *
+ * This code is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This code is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <errno.h>
+#include "menu-commands.h"
+#include "i18n.h"
+
+cImageMenuCommands::cImageMenuCommands(const char *szTitle, cImageCommands *pCmds, const char *szFileName /*= NULL*/)
+: cOsdMenu(m_szTitle = strdup(szTitle))
+, m_pCmds( pCmds )
+, m_szFileName(NULL)
+, m_bImageChanged(false)
+{
+ SetHasHotkeys();
+ if(szFileName)
+ m_szFileName = strdup(szFileName);
+
+ int i = 0;
+ cImageCommand *p = NULL;
+ for(;NULL != (p = m_pCmds->Get(i));++i)
+ {
+ Add(new cOsdItem(hk(p->Title())));
+ }
+ SetHelp(tr("Execute"), NULL, NULL, tr("Back"));
+ Display();
+}
+
+cImageMenuCommands::~cImageMenuCommands()
+{
+ if(m_szTitle)
+ free(m_szTitle);
+ if(m_szFileName)
+ free(m_szFileName);
+
+ if(m_pCmds)
+ delete m_pCmds;
+}
+
+eOSState cImageMenuCommands::Execute(void)
+{
+ errno = 0; // unset error
+ char *sz = NULL;
+ cImageCommand *p;
+ struct stat stFile,stChanged;
+ if(0 == stat(m_szFileName, &stFile)
+ && NULL != (p = m_pCmds->Get(Current())))
+ {
+ bool bConfirmed = true;
+ if (p->Confirm()) {
+ asprintf(&sz, "%s?", p->Title());
+ bConfirmed = Interface->Confirm(sz);
+ free(sz);
+ }
+ if (bConfirmed) {
+ asprintf(&sz, "%s...", p->Title());
+ OSD_InfoMsg(sz);
+ free(sz);
+ const char *szResult = p->Execute(m_szFileName);
+ if(0 == stat(m_szFileName, &stChanged))
+ {
+ m_bImageChanged = stFile.st_mtime != stChanged.st_mtime;
+ }
+ if (szResult)
+ return AddSubMenu(new cImageMenuResult(p->Title(), szResult, fontFix));
+ return osEnd;
+ }
+ return osContinue;
+ }
+ const char* szErr = errno?strerror(errno):tr("Operation failed");
+ asprintf(&sz, "%s (%s)", szErr,m_szFileName);
+ OSD_ErrorMsg(sz);
+ free(sz);
+ return osContinue;
+}
+
+eOSState cImageMenuCommands::ProcessKey(eKeys nKey)
+{
+ eOSState state = cOsdMenu::ProcessKey(nKey);
+
+ if (state == osUnknown) {
+ switch (nKey) {
+ case kRed:
+ case kOk: return Execute();
+ case kMenu:
+ case kBack:
+ case kBlue:
+ return osBack;
+ default: break;
+ }
+ }
+ return state;
+}
+
+
+
+cImageMenuResult::cImageMenuResult(const char *szTitle, const char *szText, eDvbFont Font)
+: cOsdMenu(szTitle)
+, m_szText(szText)
+{
+#if VDRVERSNUM < 10307
+ Add(new cMenuTextItem(szText, 1, 2, Setup.OSDwidth - 2, MAXOSDITEMS, clrWhite, clrBackground, Font));
+#endif
+ SetHelp(NULL, NULL, NULL, tr("Back"));
+}
+
+
+eOSState cImageMenuResult::ProcessKey(eKeys nKey)
+{
+#if VDRVERSNUM >= 10307
+ switch (nKey) {
+ case kUp|k_Repeat:
+ case kUp:
+ case kDown|k_Repeat:
+ case kDown:
+ case kLeft|k_Repeat:
+ case kLeft:
+ case kRight|k_Repeat:
+ case kRight:
+ DisplayMenu()->Scroll(NORMALKEY(nKey) == kUp || NORMALKEY(nKey) == kLeft, NORMALKEY(nKey) == kLeft || NORMALKEY(nKey) == kRight);
+ return osContinue;
+ default: break;
+ }
+#endif
+
+ eOSState nState = cOsdMenu::ProcessKey(nKey);
+ if (nState == osUnknown) {
+ switch (nKey) {
+ case kOk:
+ case kMenu:
+ case kBack:
+ case kBlue:
+ return osBack;
+ default: nState = osContinue;
+ }
+ }
+ return nState;
+}
+
+#if VDRVERSNUM >= 10307
+void cImageMenuResult::Display(void)
+{
+ cOsdMenu::Display();
+ DisplayMenu()->SetText(m_szText, true);
+}
+#endif
diff --git a/menu-commands.h b/menu-commands.h
new file mode 100644
index 0000000..07b7c33
--- /dev/null
+++ b/menu-commands.h
@@ -0,0 +1,60 @@
+/*
+ * Image plugin to VDR (C++)
+ *
+ * (C) 2004 Andreas Brachold <vdr04-at-deltab.de>
+ *
+ * This code is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This code is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#ifndef __MENU_COMMANDS_H
+#define __MENU_COMMANDS_H
+
+#include "image.h"
+#include "commands.h"
+#include <vdr/menuitems.h>
+#include <vdr/interface.h>
+
+class cImageMenuCommands
+: public cOsdMenu
+{
+ cImageCommands *m_pCmds;
+ char *m_szFileName;
+ bool m_bImageChanged;
+ char *m_szTitle;
+protected:
+ eOSState Execute(void);
+public:
+ cImageMenuCommands(const char *szTitle, cImageCommands *Cmds, const char *szFileName = NULL);
+ virtual ~cImageMenuCommands();
+ virtual eOSState ProcessKey(eKeys Key);
+ bool HasImageChanged() const { return m_bImageChanged; }
+};
+
+
+class cImageMenuResult : public cOsdMenu
+{
+ const char *m_szText;
+protected:
+#if VDRVERSNUM >= 10307
+ virtual void Display(void);
+#endif
+public:
+ cImageMenuResult(const char *Title, const char *Text, eDvbFont Font = fontOsd);
+ virtual eOSState ProcessKey(eKeys Key);
+};
+
+
+#endif //__MENU_COMMANDS_H
diff --git a/menu-image.c b/menu-image.c
new file mode 100644
index 0000000..52e2e83
--- /dev/null
+++ b/menu-image.c
@@ -0,0 +1,115 @@
+/*
+ * Image plugin to VDR (C++)
+ *
+ * (C) 2003 Kai Tobias Burwieck <kai@burwieck.net>
+ *
+ * This code is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This code is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <ctype.h>
+#include <dirent.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <typeinfo>
+
+#include "image.h"
+#include "menu.h"
+#include "data-image.h"
+#include "menu-image.h"
+#include "control-image.h"
+#include "i18n.h"
+
+#include <vdr/status.h>
+
+
+// --- cMenuImageBrowse ---------------------------------------------------------
+
+cMenuImageBrowse::cMenuImageBrowse(void)
+: cMenuBrowse(ImageSources.GetSource(), true,tr("Image browser"))
+{
+ sourcing = false;
+ SetButtons();
+}
+
+void cMenuImageBrowse::SetButtons(void)
+{
+ SetHelp(tr("Play"), 0, tr("Source"), currentdir ? tr("Parent") : 0);
+ Display();
+}
+
+eOSState cMenuImageBrowse::Source(bool second)
+{
+ if(HasSubMenu())
+ return osContinue;
+
+ if(!second) {
+ sourcing = true;
+ return AddSubMenu(new
+ cMenuSource(&ImageSources, tr("Image source")));
+ }
+ sourcing = false;
+ cFileSource *src = cMenuSource::GetSelected();
+ if(src) {
+ ImageSources.SetSource(src);
+ SetSource(src);
+ NewDir(0);
+ }
+ return osContinue;
+}
+
+eOSState cMenuImageBrowse::ProcessKey(eKeys Key)
+{
+ eOSState state = cMenuBrowse::ProcessKey(Key);
+
+ if(!HasSubMenu() && state == osContinue) {
+ // eval the return value from submenus
+ if(sourcing)
+ return Source(true);
+ }
+
+ if(state == osBack && lastselect) {
+ char *name = lastselect->Path();
+ char *full = source->BuildName(name);
+ cDirItem *item = cMenuBrowse::GetSelected();
+ if(item) {
+
+ //FIXME use a nonblocking way
+ //OSD_InfoMsg(tr("Building SlideShow..."));
+
+ cSlideShow *newss = new cSlideShow(item);
+ if(newss->Load() && newss->Count()) {
+
+ cImageControl::SetSlideShow(newss);
+ state = osEnd;
+ }
+ else {
+ OSD_ErrorMsg(tr("No Files!"));
+ delete newss;
+ state = osContinue;
+ }
+ lastselect = NULL;
+ }
+ free(full);
+ free(name);
+ }
+ if(state == osUnknown && Key == kYellow)
+ return Source(false);
+ return state;
+}
diff --git a/menu-image.h b/menu-image.h
new file mode 100644
index 0000000..86442d7
--- /dev/null
+++ b/menu-image.h
@@ -0,0 +1,41 @@
+/*
+ * Image plugin to VDR (C++)
+ *
+ * (C) 2003 Kai Tobias Burwieck <kai@burwieck.net>
+ *
+ * This code is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This code is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#ifndef ___MENU_IMAGE_H
+#define ___MENU_IMAGE_H
+
+#include <vdr/osdbase.h>
+#include <vdr/menuitems.h>
+
+#include "menu.h"
+
+// ----------------------------------------------------------------
+class cMenuImageBrowse:public cMenuBrowse {
+ private:
+ bool sourcing;
+ void SetButtons(void);
+ eOSState Source(bool second);
+ public:
+ cMenuImageBrowse(void);
+ virtual eOSState ProcessKey(eKeys Key);
+};
+
+#endif //___MENU_IMAGE_H
diff --git a/menu.c b/menu.c
new file mode 100644
index 0000000..96c1a12
--- /dev/null
+++ b/menu.c
@@ -0,0 +1,402 @@
+/*
+ * image plugin to VDR (C++)
+ *
+ * (C) 2004 "Interpohl" <interpohl@vdr-portal.de>
+ * (C) 2004 A.Brachold <vdr04-at-deltab.de>
+ * (C) 2004 O.Kreuzinger <Onno@Kreuzinger.biz>
+ * (C) 2003 Kai Tobias Burwieck <kai@burwieck.net>
+ * (C) 2001,2002 Stefan Huelswitt <huels@iname.com>
+ *
+ * This code is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This code is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <ctype.h>
+#include <dirent.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "menu.h"
+#include "i18n.h"
+
+// --- cMenuBrowseItem ----------------------------------------------------------
+
+class cMenuBrowseItem:public cOsdItem {
+private:
+ cDirItem * item;
+ virtual void Set(void);
+public:
+ cMenuBrowseItem(cDirItem * Item);
+ cDirItem *Item(void) { return item; }
+};
+
+cMenuBrowseItem::cMenuBrowseItem(cDirItem * Item)
+{
+ item = Item;
+ Set();
+}
+
+void cMenuBrowseItem::Set(void)
+{
+ char *buffer = 0;
+ asprintf(&buffer, item->Type == itFile ? "%s" : "[%s]", item->Name);
+ SetText(buffer, false);
+}
+
+// --- cMenuBrowse ------------------------------------------------------
+
+cDirItem *cMenuBrowse::lastselect = 0;
+
+cMenuBrowse::cMenuBrowse(cFileSource * Source, bool Dirselect,const char *title)
+: cOsdMenu(title)
+{
+ currentdir = parent = 0;
+ delete lastselect;
+ lastselect = 0;
+ list = new cDirList;
+
+ dirselectable = Dirselect;
+
+ SetSource(Source);
+ LoadDir(currentdir);
+ SetButtons();
+}
+
+cMenuBrowse::~cMenuBrowse()
+{
+ free(parent);
+ free(currentdir);
+ delete list;
+}
+
+cDirItem *cMenuBrowse::CurrentItem(void)
+{
+ cMenuBrowseItem *item = (cMenuBrowseItem *) Get(Current());
+ return item ? item->Item() : 0;
+}
+
+void cMenuBrowse::SetButtons(void)
+{
+ SetHelp(tr("Select"), 0, 0, currentdir ? tr("Parent") : 0);
+ Display();
+}
+
+void cMenuBrowse::SetSource(cFileSource * Source)
+{
+ source = Source;
+ free(currentdir);
+ currentdir = 0;
+ free(parent);
+ parent = 0;
+ source->GetRemember(currentdir, parent);
+}
+
+bool cMenuBrowse::LoadDir(const char *dir)
+{
+ Clear();
+
+ //FIXME use a nonblocking way
+ //OSD_InfoMsg(tr("Scanning directory..."));
+
+ if(!list->Load(source, dir))
+ return false;
+
+ cDirItem *item = list->First();
+ while(item)
+ {
+ Add(new cMenuBrowseItem(item),
+ (parent && !strcmp(item->Name, parent)));
+ item = list->Next(item);
+ }
+ return true;
+}
+
+bool cMenuBrowse::NewDir(const char *dir)
+{
+ if(LoadDir(dir))
+ {
+ char *ncur = dir ? strdup(dir) : 0; // don't free currentdir first, as (new)dir may
+ free(currentdir);
+ currentdir = ncur; // be a reference to somewhere inside currentdir as well
+
+ cDirItem *item = CurrentItem();
+ source->SetRemember(currentdir, item ? item->Name : 0);
+
+ SetButtons();
+ return true;
+ }
+ OSD_ErrorMsg(tr("Error scanning directory!"));
+ return false;
+}
+
+eOSState cMenuBrowse::Parent(void)
+{
+ eOSState res = osContinue;
+
+ if(currentdir)
+ {
+ free(parent);
+ char *ss = strrchr(currentdir, '/');
+ if(ss)
+ {
+ *ss++ = 0;
+ parent = strdup(ss);
+ ss = currentdir;
+ }
+ else
+ parent = strdup(currentdir);
+
+ if(!NewDir(ss))
+ res = osEnd;
+ }
+ return res;
+}
+
+eOSState cMenuBrowse::Select(bool isred)
+{
+ eOSState res = osContinue;
+ cDirItem *item;
+
+ if((item = CurrentItem()))
+ {
+ switch (item->Type)
+ {
+ case itParent:
+ if(!isred || !dirselectable)
+ res = Parent();
+ break;
+ case itDir:
+ if(!isred || !dirselectable)
+ {
+ char *d = item->Path();
+ if(!NewDir(d))
+ res = osEnd;
+ free(d);
+ break;
+ }
+ // fall through to itFile
+ case itFile:
+ lastselect =
+ new cDirItem(source, currentdir, item->Name,
+ item->Type);
+ res = osBack;
+ break;
+ default:
+ break;
+ }
+ }
+ return res;
+}
+
+eOSState cMenuBrowse::ProcessKey(eKeys Key)
+{
+ eOSState state = cOsdMenu::ProcessKey(Key);
+
+ if(state == osUnknown)
+ {
+ switch (Key)
+ {
+ case kOk:
+ state = Select(false);
+ break;
+ case kRed:
+ state = Select(true);
+ break;
+ case kBlue:
+ state = Parent();
+ break;
+ case kMenu:
+ state = osEnd;
+ break;
+ default:
+ break;
+ }
+ }
+ if(state == osEnd || state == osBack)
+ {
+ cDirItem *item = CurrentItem();
+ if(item)
+ source->SetRemember(currentdir, item->Name);
+ }
+ return state;
+}
+
+// --- cMenuSourceItem ----------------------------------------------------------
+
+class cMenuSourceItem:public cOsdItem {
+ private:
+ cFileSource * source;
+ virtual void Set(void);
+ public:
+ cMenuSourceItem(cFileSource * Source);
+ cFileSource *Source(void) { return source; }
+};
+
+cMenuSourceItem::cMenuSourceItem(cFileSource * Source)
+{
+ source = Source;
+ Set();
+}
+
+void cMenuSourceItem::Set(void)
+{
+ char *buffer = 0;
+ asprintf(&buffer, "%s\t%s\t%s",
+ source->NeedsMount()? (source->Status()? "*" : ">") : "",
+ source->Description(), source->BaseDir());
+ SetText(buffer, false);
+}
+
+// --- cMenuSource --------------------------------------------------
+
+cFileSource *cMenuSource::selected = 0;
+
+cMenuSource::cMenuSource(cFileSources * Sources,
+ const char *title):cOsdMenu(title, 2, 20)
+{
+ selected = 0;
+ current = Sources->GetSource();
+ cFileSource *source = Sources->First();
+ while(source) {
+ cOsdMenu::Add(new cMenuSourceItem(source), source == current);
+ source = Sources->Next(source);
+ }
+
+ SetHelp(tr("Select"), tr("Mount"), tr("Unmount"), tr("Eject"));
+ Display();
+}
+
+bool cMenuSource::DoMount(cFileSource * src)
+{
+ bool res = src->Mount();
+ RefreshCurrent();
+ DisplayCurrent(true);
+ return res;
+}
+
+bool cMenuSource::CheckMount(void)
+{
+ cFileSource *src = selected ? selected : current;
+ if(src->NeedsMount() && !src->Status()) {
+ OSD_ErrorMsg(tr("Selected source is not mounted!"));
+ return false;
+ }
+ return true;
+}
+
+eOSState cMenuSource::Select(void)
+{
+ if(HasSubMenu() || Count() == 0)
+ return osContinue;
+
+ cFileSource *src = ((cMenuSourceItem *) Get(Current()))->Source();
+ if(src->NeedsMount() && !src->Status())
+ {
+ if(!DoMount(src))
+ OSD_ErrorMsg(tr("Mount failed!"));
+ }
+ if(!src->NeedsMount() || src->Status())
+ {
+ selected = src;
+ return osBack;
+ }
+ return osContinue;
+}
+
+eOSState cMenuSource::Mount(void)
+{
+ if(HasSubMenu() || Count() == 0)
+ return osContinue;
+
+ cFileSource *src = ((cMenuSourceItem *) Get(Current()))->Source();
+ if(src->NeedsMount() && !src->Status())
+ {
+ if(DoMount(src))
+ OSD_InfoMsg(tr("Mount succeeded"));
+ else
+ OSD_ErrorMsg(tr("Mount failed!"));
+ }
+ return osContinue;
+}
+
+eOSState cMenuSource::Unmount(void)
+{
+ if(HasSubMenu() || Count() == 0)
+ return osContinue;
+
+ cFileSource *src = ((cMenuSourceItem *) Get(Current()))->Source();
+ if(src->NeedsMount() && src->Status())
+ {
+ bool res = src->Unmount();
+ RefreshCurrent();
+ DisplayCurrent(true);
+ if(res)
+ OSD_InfoMsg(tr("Unmount succeeded"));
+ else
+ OSD_ErrorMsg(tr("Unmount failed!"));
+ }
+ return osContinue;
+}
+
+eOSState cMenuSource::Eject(void)
+{
+ if(HasSubMenu() || Count() == 0)
+ return osContinue;
+
+ cFileSource *src = ((cMenuSourceItem *) Get(Current()))->Source();
+ if(src->NeedsMount())
+ {
+ bool res = src->Eject();
+ RefreshCurrent();
+ DisplayCurrent(true);
+ if(!res)
+ OSD_ErrorMsg(tr("Eject failed!"));
+ }
+ return osContinue;
+}
+
+eOSState cMenuSource::ProcessKey(eKeys Key)
+{
+ eOSState state = cOsdMenu::ProcessKey(Key);
+
+ if(state == osBack && !CheckMount())
+ state = osContinue;
+ if(state == osUnknown)
+ {
+ switch (Key)
+ {
+ case kOk:
+ case kRed:
+ return Select();
+ case kGreen:
+ return Mount();
+ case kYellow:
+ return Unmount();
+ case kBlue:
+ return Eject();
+ case kMenu:
+ CheckMount();
+ return osEnd;
+ default:
+ break;
+ }
+ }
+ return state;
+}
diff --git a/menu.h b/menu.h
new file mode 100644
index 0000000..263d879
--- /dev/null
+++ b/menu.h
@@ -0,0 +1,75 @@
+/*
+ * Image plugin to VDR (C++)
+ *
+ * (C) 2003 Kai Tobias Burwieck <kai@burwieck.net>
+ *
+ * based on MP3/MPlayer plugin to VDR (C++)
+ * (C) 2001,2002 Stefan Huelswitt <huels@iname.com>
+ *
+ * This code is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This code is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#ifndef ___MENU_H
+#define ___MENU_H
+
+#include "image.h"
+#include "data.h"
+// ----------------------------------------------------------------
+
+class cMenuBrowse:public cOsdMenu {
+ private:
+ eOSState Select(bool isred);
+ eOSState Parent(void);
+ bool LoadDir(const char *dir);
+ protected:
+ static cDirItem *lastselect;
+ //
+ cDirList *list;
+ cFileSource *source;
+ bool dirselectable;
+ char *currentdir, *parent;
+//
+ bool NewDir(const char *dir);
+ void SetSource(cFileSource * Source);
+ cDirItem *CurrentItem(void);
+ virtual void SetButtons(void);
+ public:
+ cMenuBrowse(cFileSource * Source, bool Dirselect, const char *title);
+ ~cMenuBrowse();
+ virtual eOSState ProcessKey(eKeys Key);
+ static cDirItem *GetSelected(void) {return lastselect;}
+};
+
+// ----------------------------------------------------------------
+
+class cMenuSource:public cOsdMenu {
+ private:
+ static cFileSource *selected;
+ cFileSource *current;
+ //
+ eOSState Mount(void);
+ eOSState Unmount(void);
+ eOSState Eject(void);
+ eOSState Select(void);
+ bool DoMount(cFileSource * src);
+ bool CheckMount(void);
+ public:
+ cMenuSource(cFileSources * Sources, const char *title);
+ virtual eOSState ProcessKey(eKeys Key);
+ static cFileSource *GetSelected(void) { return selected;}
+};
+
+#endif //___MENU_H
diff --git a/player-image.c b/player-image.c
new file mode 100644
index 0000000..f8818f8
--- /dev/null
+++ b/player-image.c
@@ -0,0 +1,478 @@
+/*
+ * Image plugin to VDR (C++)
+ *
+ * (C) 2004 Andreas Brachold <vdr04-at-deltab.de>
+ * 2003 Kai Tobias Burwieck <kai@burwieck.net>
+ *
+ * This code is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This code is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <signal.h>
+#include <wait.h>
+
+#include "player-image.h"
+#include "control-image.h"
+#include "setup-image.h"
+#include "data-image.h"
+#include "image.h"
+#include "list.h"
+#include "i18n.h"
+
+#include "libimage/pnm.h"
+#include "libimage/xpm.h"
+
+
+const char *g_szConvertScript = "imageplugin.sh";
+
+
+//----------cImagePlayer-------------
+
+
+
+
+cImagePlayer::cImagePlayer(cSlideShow *pCurSlideShow)
+: cStillImagePlayer(
+#if VDRVERSNUM >= 10308
+ (ImageSetup.m_bLiveAudio != 0)?pmVideoOnly:
+#endif
+ pmAudioVideo
+ )
+, m_bConvertRunning(false)
+, m_szError(NULL)
+{
+ theSlideShow.Assign(pCurSlideShow);
+}
+
+bool cImagePlayer::GetIndex(int &nCurrent, int &nTotal, bool /*bSnapToIFrame*/)
+{
+ // Notify other status-plugins with one picture as one second
+ nCurrent = theSlideShow.ImageCurrent();
+ // Notify other status-plugins count of picture as totalcount of seconds
+ nTotal = theSlideShow.ImageTotal();
+
+ return true;
+}
+
+cImagePlayer::~cImagePlayer()
+{
+ // Remove Slideshow
+ theSlideShow.Shutdown();
+}
+
+
+void cImagePlayer::Activate(bool bOn)
+{
+ if(bOn) {
+ if(theSlideShow.GetImage())
+ {
+ cStillImagePlayer::Activate(bOn);
+ Convert("");
+ }
+ }
+ else
+ cStillImagePlayer::Activate(false);
+}
+
+
+bool cImagePlayer::NextImage(int nOffset)
+{
+ return theSlideShow.NextImage(nOffset);
+}
+
+
+
+bool cImagePlayer::PrevImage(int nOffset)
+{
+ return theSlideShow.PrevImage(nOffset);
+}
+
+
+bool cImagePlayer::GotoImage(unsigned int nNewPictureIndex)
+{
+ return theSlideShow.GotoImage(nNewPictureIndex);
+}
+
+
+bool cImagePlayer::Convert(const char *szChange)
+{
+ cImage* pImage = theSlideShow.GetImage();
+ if(pImage)
+ {
+ cShellWrapper* pCmd = new cShellWrapper;
+ pCmd->bClearBackground = true;
+ pCmd->nOffLeft = m_StillImage.GetBorderWidth();
+ pCmd->nOffTop = m_StillImage.GetBorderHeight();
+ pCmd->nWidth = UseWidth();
+ pCmd->nHeight = UseHeight();
+
+ // Build image_convert.sh "source.jpg" "/tmp/image/dest.pnm" 720 576 0 0 0 0 original
+ pCmd->szPNM = strdup(pImage->NamePNM());
+
+ asprintf(&pCmd->szCmd, "%s \"%s\" \"%s\" %d %d %d %d %d %s",
+ g_szConvertScript,
+ pImage->Name(),
+ pCmd->szPNM,
+ pCmd->nWidth,
+ pCmd->nHeight,
+ 0,
+ 0,
+ 0,
+ szChange ? szChange : "");
+
+ Exec(pCmd);
+ return true;
+ }
+ return false;
+}
+
+
+
+bool cImagePlayer::ConvertJump(int nOffset)
+{
+ register unsigned int w,h;
+ const unsigned int MAX_BILDER = 9;
+ cImage* pImage[MAX_BILDER];
+ for (w = 0; w < MAX_BILDER; ++w)
+ pImage[w] = NULL;
+ int nBilder = theSlideShow.GetJumpNames(nOffset,pImage,MAX_BILDER);
+ if(nBilder > 0
+ && pImage[0]) {
+
+ unsigned int nMatrix = (nBilder < 5) ? 2 : 3;
+
+ for (h = 0; h < nMatrix; ++h)
+ for (w = 0; w < nMatrix && pImage[(h*nMatrix)+w]; ++w)
+ {
+ cShellWrapper* pCmd = new cShellWrapper;
+
+ pCmd->bClearBackground = (w == 0 && h == 0);
+ pCmd->nWidth = UseWidth();
+ pCmd->nHeight = UseHeight();
+ pCmd->nWidth /= nMatrix;
+ pCmd->nHeight /= nMatrix;
+ pCmd->nOffLeft = (pCmd->nWidth * w) + m_StillImage.GetBorderWidth();
+ pCmd->nOffTop = (pCmd->nHeight * h) + m_StillImage.GetBorderHeight();
+
+ // Build image_convert.sh "source.jpg" "/tmp/image/source.jpg-i.pnm" 256 192 0 0 0 0 original
+ pCmd->szPNM = strdup(pImage[(h*nMatrix)+w]->NameIndex());
+
+
+
+ asprintf(&pCmd->szCmd, "%s \"%s\" \"%s\" %d %d %d %d %d %s",
+ g_szConvertScript,
+ pImage[(h*nMatrix)+w]->Name(),
+ pCmd->szPNM,
+ pCmd->nWidth,
+ pCmd->nHeight,
+ 0,
+ 0,
+ 0,
+ /*szChange ? szChange : */"");
+ pCmd->szNumber = '0'+((h*nMatrix)+w)+1;
+
+ Exec(pCmd);
+ }
+ return true;
+ }
+ return false;
+}
+
+
+bool cImagePlayer::ConvertZoom(const char *szChange, int nZoomFaktor,
+ int nLeftPos, int nTopPos)
+{
+ cImage* pImage = theSlideShow.GetImage();
+ if(pImage)
+ {
+ cShellWrapper* pCmd = new cShellWrapper;
+ pCmd->bClearBackground = true;
+ pCmd->nOffLeft = m_StillImage.GetBorderWidth() + (nLeftPos>0?0:(nLeftPos*-1));
+ pCmd->nOffTop = m_StillImage.GetBorderHeight() + (nTopPos>0?0:(nTopPos*-1));
+ pCmd->nWidth = UseWidth();
+ pCmd->nHeight = UseHeight();
+
+ // Build image_convert.sh "source.jpg" "/tmp/image/dest.pnm" 720 576 0 0 0 0 original
+ pCmd->szPNM = strdup(pImage->NameZoom());
+
+ asprintf(&pCmd->szCmd, "%s \"%s\" \"%s\" %d %d %d %d %d %s",
+ g_szConvertScript,
+ pImage->Name(),
+ pCmd->szPNM,
+ pCmd->nWidth,
+ pCmd->nHeight,
+ nZoomFaktor,
+ nLeftPos>0?nLeftPos:0,
+ nTopPos>0?nTopPos:0,
+ szChange ? szChange : "");
+
+ Exec(pCmd);
+ return true;
+ }
+ return false;
+}
+
+
+void cImagePlayer::LoadImage(cShellWrapper* pShell)
+{
+ cPNM pnmImage;
+ bool bSuccess = false;
+ register unsigned int nHeight = m_StillImage.GetHeight();
+ register unsigned int nWidth = m_StillImage.GetWidth();
+ register unsigned int nOffLeft = 0;
+ register unsigned int nOffTop = 0;
+ errno = 0;
+ if(!pShell || pShell->bClearBackground)
+ m_StillImage.ClearRGBMem();
+
+ if(pShell && pShell->szPNM)
+ {
+ nHeight = std::min(nHeight,pShell->nHeight);
+ nWidth = std::min(nWidth,pShell->nWidth);
+ nOffLeft = pShell->nOffLeft;
+ nOffTop = pShell->nOffTop;
+
+ FILE *f=fopen(pShell->szPNM, "r");
+ if(f)
+ {
+ xel* pRow = NULL;
+ register unsigned int w;
+ register unsigned int h;
+
+ if(pnmImage.readHeader(f))
+ {
+ register unsigned int nColorDepth = pnmImage.GetColorDepth();
+
+ if(pnmImage.GetWidth() < nWidth)
+ nOffLeft += (nWidth - pnmImage.GetWidth()) / 2;
+
+ if(pnmImage.GetHeight() < nHeight)
+ nOffTop += (nHeight - pnmImage.GetHeight()) / 2;
+
+
+ for(h = 0;
+ h < pnmImage.GetHeight()
+ && h < nHeight
+ && h+nOffTop < m_StillImage.GetHeight();
+ ++h)
+ {
+ if(!pnmImage.allocrow(&pRow)
+ ||!pnmImage.readrow(f, pRow) )
+ break;
+
+ xel* pP = pRow;
+ for(w = 0;w < pnmImage.GetWidth()
+ && w < nWidth
+ && w+nOffLeft < m_StillImage.GetWidth();
+ ++w,++pP)
+ {
+ uint8_t* pImageRGB = m_StillImage.GetRGBMem() +
+ ((((h+nOffTop)*m_StillImage.GetWidth())+w+nOffLeft)*3);
+
+ if(nColorDepth == 0xFF) // normal 8-bit at any canal (24 Bit) colordepth
+ {
+ *(pImageRGB + 0) = (uint8_t) PPM_GETR(*pP);
+ *(pImageRGB + 1) = (uint8_t) PPM_GETG(*pP);
+ *(pImageRGB + 2) = (uint8_t) PPM_GETB(*pP);
+ }
+ else if(nColorDepth == 1) // black/white image
+ {
+ *(pImageRGB + 0) = (uint8_t) PPM_GETR(*pP)==0?0x00:0xFF;
+ *(pImageRGB + 1) = (uint8_t) PPM_GETG(*pP)==0?0x00:0xFF;
+ *(pImageRGB + 2) = (uint8_t) PPM_GETB(*pP)==0?0x00:0xFF;
+ }
+ else // Adjust other colordepth to 8bit
+ {
+ *(pImageRGB + 0) = (uint8_t) (PPM_GETR(*pP)*255 / pnmImage.GetColorDepth()) & 0xFF;
+ *(pImageRGB + 1) = (uint8_t) (PPM_GETG(*pP)*255 / pnmImage.GetColorDepth()) & 0xFF;
+ *(pImageRGB + 2) = (uint8_t) (PPM_GETB(*pP)*255 / pnmImage.GetColorDepth()) & 0xFF;
+ }
+ }
+ pnmImage.freerow(pRow);
+ pRow = NULL;
+ }
+ if(pRow)
+ pnmImage.freerow((char*)pRow);
+ else
+ {
+ if(pShell->szNumber && ImageSetup.ShowNumbers)
+ cXPM::Overlay(pShell->szNumber,m_StillImage.GetRGBMem(),
+ m_StillImage.GetWidth(),m_StillImage.GetHeight(),
+ cXPM::TopRight,nOffLeft,nOffTop,pnmImage.GetWidth(),pnmImage.GetHeight());
+
+ bSuccess = true;
+ }
+ }
+ fclose(f);
+ }
+ }
+ if(!bSuccess) {
+
+ // Merge Errorimage with Encoder-Memory
+ if(pShell && pShell->szNumber)
+ cXPM::Overlay('s',m_StillImage.GetRGBMem(),
+ m_StillImage.GetWidth(),m_StillImage.GetHeight(),
+ cXPM::Center,pShell->nOffLeft,pShell->nOffTop,pShell->nWidth,pShell->nHeight);
+ else
+ cXPM::Error(m_StillImage.GetRGBMem(),
+ m_StillImage.GetWidth(),m_StillImage.GetHeight());
+
+ // Build Error, depends PNM Errormsg or system messages
+ char szErr[128];
+
+ if(pnmImage.GetError())
+ strncpy(szErr,pnmImage.GetError(),sizeof(szErr));
+ else if(errno) {
+ int nErr = errno;
+ szErr[sizeof(szErr)-1] = '\0';
+ if(0 != strerror_r(nErr,szErr,sizeof(szErr)-1)) {
+ szErr[0] = '\0';
+ }
+ }
+
+ // Make Syslog entry
+ if(pShell && pShell->szPNM && szErr)
+ esyslog("imageplugin: Error until read %s : '%s'", pShell->szPNM, szErr);
+ else if(pShell && pShell->szPNM)
+ esyslog("imageplugin: Error until read %s", pShell->szPNM);
+ else
+ esyslog("imageplugin: Error until read image %s",szErr?szErr:"");
+
+ { // Copy Errormessage forward to OSD Thread
+ cMutexLock lock(&m_MutexErr);
+ if(m_szError)
+ free(m_szError);
+ if(szErr)
+ asprintf(&m_szError, "%s : %s", tr("Image couldn't load"),szErr);
+ else
+ m_szError = strdup(tr("Image couldn't load"));
+ }
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+/** void cImagePlayer::ExecFailed()
+Show a precompiled Image if Exec can't find a converted Image
+@param - char* szErr
+@return - nothing */
+void cImagePlayer::ExecFailed(cShellWrapper* pShell,const char* szErr)
+{
+ if(!pShell || pShell->bClearBackground)
+ m_StillImage.ClearRGBMem();
+
+ // Merge Errorimage with Encoder-Memory
+ if(pShell && pShell->szNumber)
+ cXPM::Overlay('s',m_StillImage.GetRGBMem(),
+ m_StillImage.GetWidth(),m_StillImage.GetHeight(),
+ cXPM::Center,pShell->nOffLeft,pShell->nOffTop,pShell->nWidth,pShell->nHeight);
+ else
+ cXPM::Error(m_StillImage.GetRGBMem(),
+ m_StillImage.GetWidth(),m_StillImage.GetHeight());
+
+
+ // Store Message for OSD-Thread
+ {
+ cMutexLock lock(&m_MutexErr);
+ if(m_szError)
+ free(m_szError);
+ m_szError = strdup(szErr);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+/** void cImagePlayer::ErrorMsg()
+ThreadSafe Method to show messages from Worker thread,
+this functions is called only from cImageControl::ProcessKey(kNone)
+@return - nothing */
+void cImagePlayer::ErrorMsg()
+{
+ char* szErr = NULL;
+ {
+ cMutexLock lock(&m_MutexErr);
+ szErr = m_szError;
+ m_szError = NULL;
+ }
+ if(szErr)
+ {
+ OSD_ErrorMsg(szErr);
+ free(szErr);
+ }
+}
+
+const char* cImagePlayer::FileName(void) const
+{
+ cImage* pImage = theSlideShow.GetImage();
+ return pImage?pImage->Name():NULL;
+}
+
+void cImagePlayer::Exec(cShellWrapper* pCmd)
+{
+ if(pCmd->szPNM)
+ m_bConvertRunning = true;
+ if(pCmd) {
+ cMutexLock lock(&m_Mutex);
+ if(!m_queue.add(pCmd))
+ delete pCmd; //Queue full or Thread is dead
+ }
+}
+
+bool cImagePlayer::Worker(bool bDoIt)
+{
+ bool bQueueEmpty;
+ cShellWrapper *pShell = NULL;
+
+ { // Protect the queue ++
+ cMutexLock lock(&m_Mutex);
+ if(bDoIt && !m_queue.empty()) {
+ pShell = *m_queue.begin();
+ m_queue.erase(m_queue.begin());
+ }
+ bQueueEmpty = m_queue.empty();
+ } // ++
+
+ if(!pShell)
+ {
+ m_bConvertRunning = m_StillImage.EncodeRequired();
+ return bQueueEmpty;
+ }
+
+ if(pShell->szCmd) {
+ //dsyslog("imageplugin: executing script '%s'", pShell->szCmd);
+
+ ImageSetup.SetEnv();
+ if(0 == SystemExec(pShell->szCmd))
+ {
+ if(pShell->szPNM) {
+ LoadImage(pShell);
+ m_StillImage.EncodeRequired(true);
+ }
+ }
+ else
+ {
+ esyslog("imageplugin: script execution failed '%s'", pShell->szCmd);
+ if(pShell->szPNM) {
+ ExecFailed(pShell,tr("Script execution failed"));
+ m_StillImage.EncodeRequired(true);
+ }
+ }
+ }
+ delete pShell;
+ return bQueueEmpty;
+}
diff --git a/player-image.h b/player-image.h
new file mode 100644
index 0000000..6a784de
--- /dev/null
+++ b/player-image.h
@@ -0,0 +1,148 @@
+/*
+ * Image plugin to VDR (C++)
+ *
+ * (C) 2004 Andreas Brachold <vdr04-at-deltab.de>
+ * 2003 Kai Tobias Burwieck <kai@burwieck.net>
+ *
+ * This code is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This code is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#ifndef ___DVB_IMAGE_H
+#define ___DVB_IMAGE_H
+
+#include <vector>
+#define __STL_CONFIG_H
+
+#include <vdr/thread.h>
+#include <vdr/player.h>
+#include <liboutput/stillimage-player.h>
+
+class cImage;
+class cSlideShow;
+
+struct cShellWrapper {
+
+ char* szCmd;
+ char* szPNM;
+ char szNumber;
+ bool bClearBackground;
+ unsigned int nOffLeft;
+ unsigned int nOffTop;
+ unsigned int nWidth;
+ unsigned int nHeight;
+
+ cShellWrapper()
+ : szCmd(NULL)
+ , szPNM(NULL)
+ , szNumber('\0')
+ {
+ }
+
+ virtual ~cShellWrapper() {
+ if(szPNM)
+ free(szPNM);
+ if(szCmd)
+ free(szCmd);
+ }
+};
+
+struct cShellWrapperQueue
+ : public std::vector<cShellWrapper*>
+{
+ virtual ~cShellWrapperQueue()
+ {
+ iterator i = begin();
+ const_iterator e = end();
+ for(;e!=i;++i)
+ delete(*i);
+ clear();
+ }
+
+ inline size_t max_size() const
+ {
+ return 64;
+ }
+
+ inline bool add(cShellWrapper* pCmd) {
+ if(size()<max_size())
+ {
+ if(NULL == pCmd->szPNM || !pCmd->bClearBackground) //Pregeneration or Index
+ push_back(pCmd);
+ else {
+ // Remove all other viewed images from queue
+ iterator i = begin();
+ while(end()!=i) {
+ if((*i)->szPNM) {
+ delete(*i);
+ erase(i);
+ }
+ else
+ ++i;
+ }
+ //Place next viewed image at front of the queue
+ insert(begin(),pCmd);
+ }
+ return true;
+ }
+ return false;
+ }
+
+};
+
+class cImagePlayer
+: public cStillImagePlayer
+{
+
+ volatile bool m_bConvertRunning;
+
+ cMutex m_Mutex;
+ cShellWrapperQueue m_queue;
+ cMutex m_MutexErr;
+ char* m_szError;
+protected:
+ void Exec(cShellWrapper* pCmd);
+
+ void LoadImage(cShellWrapper* pShell);
+ /** Show Errorimage if operation failed*/
+ void ExecFailed(cShellWrapper* pShell,const char* szErr);
+
+ virtual void Activate(bool On);
+ virtual bool Worker(bool bDoIt);
+public:
+
+ cImagePlayer(cSlideShow *pCurSlideShow);
+ virtual ~ cImagePlayer();
+ bool IsConvertRunning() const { return m_bConvertRunning; };
+ bool NextImage(int Step);
+ bool PrevImage(int Step);
+ bool GotoImage(unsigned int nNewPictureIndex);
+
+ /** Deliver the filename from the current number of viewed Image */
+ const char* FileName(void) const;
+
+ bool Convert(const char *szChange);
+ bool ConvertZoom(const char *szChange, int nZoomFaktor,
+ int nLeftPos, int nTopPos);
+ bool ConvertJump(int Step);
+
+ /** Returns the current and total frame index, optionally snapped to the
+ nearest I-frame.*/
+ virtual bool GetIndex(int &Current, int &Total, bool SnapToIFrame);
+ /** ThreadSafe Method to show messages from Worker thread*/
+ void ErrorMsg();
+};
+
+#endif //__DVB_IMAGE_H
diff --git a/scripts/imageplugin.sh b/scripts/imageplugin.sh
new file mode 100755
index 0000000..2afd734
--- /dev/null
+++ b/scripts/imageplugin.sh
@@ -0,0 +1,160 @@
+#!/bin/bash
+# script for vdr-imageplugin to convert the selected image to pnm-image
+# needs : netpbm-progs > anytopnm pnmscale pnmfile pnmcut pnmflip
+#
+# History:
+# 2004-08-12 Initalrelease, Andreas Brachold <vdr04-at-deltab.de>
+# base on prior work for convert.sh
+# by Onno Kreuzinger <o.kreuzinger-at-kreuzinger.biz>
+# Andreas Holzhammer and <Interpohl-at-vdr-portal.de>
+#
+################################################################################
+# Userconfig:
+################################################################################
+# if your install external software like netpbm outside /bin:/usr/bin, adjust folder
+PATH=/usr/local/bin:$PATH
+# Set to "yes" to speedup reviewed image
+TMPCACHE=no
+# Set to "no" if this script work and your don't need success messages
+VERBOSE=yes
+# Set to "yes" if this script don't work, only usable if your self execute the script from shell
+DEBUG=no
+
+################################################################################
+# and now the script
+################################################################################
+[ "z$DEBUG" = "zyes" ] && set -xv
+SCRIPTNAME=$(basename "$0")
+{
+[ "z$VERBOSE" = "zyes" ] && echo "called '$SCRIPTNAME $*'"
+
+if [ $# -lt 7 ] ; then
+ echo "Usage: $SCRIPTNAME infile outfile WIDTH HEIGHT ZOOMFACTOR LEFTPOS TOPPOS [FLIPCMD]" 1>&2
+ echo " e.g.: $SCRIPTNAME in.png out.pnm 720 576 0 0 0" 1>&2
+ echo " or .: $SCRIPTNAME in.png out.pnm 720 576 3 360 360 left" 1>&2
+ echo "" 1>&2
+ echo "WIDTH - Width of TVScreen (720)" 1>&2
+ echo "HEIGHT - Height of TVScreen (480..576)" 1>&2
+ echo "ZOOMFACTOR - Zoomfactor (0....10)" 1>&2
+ echo "LEFTPOS - Offset from left on Zoommode (0......)" 1>&2
+ echo "TOPPOS - Offset from top on Zoommode (0......)" 1>&2
+ echo "FLIPCMD - optional should image flip (left,right,original)" 1>&2
+ exit 1
+fi
+
+ # Defaultvalue, overwrite with env from plugin
+ ASPECT_RATIO="${ASPECT_RATIO:-"4:3"}"
+
+ # check requirement external programs
+ REQUIREMENTS="anytopnm pnmscale pnmfile pnmcut pnmflip"
+ for i in $REQUIREMENTS
+ do
+ type "$i" > /dev/null 2>&1
+ [ $? -ne 0 ] && echo -e "$SCRIPTNAME: Error ! External required program: \"$i\" not found !\n Please adjust PATH or install it inside follow Folder \"$PATH\" \n" && exit 1
+ done
+
+ INFILE="$1"
+ OUTFILE="$2"
+ OUT_DISPLAY_X=$3
+ OUT_DISPLAY_Y=$4
+ ZOOMFACTOR=$5
+ LEFTPOS=$6
+ TOPPOS=$7
+ FLIPCMD="$8"
+
+ OUTDIR=$(dirname "$OUTFILE")
+ [ ! -d "$OUTDIR" ] && mkdir -p "$OUTDIR"
+
+ TMPFILE="$OUTFILE.tmp"
+ PARFILE="$OUTFILE.par"
+
+ # remove precreated files if called with flip "left","right" or "original"
+ [ -s "$OUTFILE" -a "$FLIPCMD" != "" ] && rm -f "$OUTFILE"
+ [ -s "$TMPFILE" -a "$FLIPCMD" != "" ] && rm -f "$TMPFILE"
+
+ if [ -s "$OUTFILE" ] ; then
+ [ "z$VERBOSE" = "zyes" ] && echo "Success! Convert not required, $OUTFILE exists already!"
+ exit 0
+ else
+
+ # Convert Image to PNM File
+ [ ! -s "$TMPFILE" ] && anytopnm "$INFILE" > "$TMPFILE"
+ # if success
+ if [ -s "$TMPFILE" ] ; then
+
+ # Get image resolution
+ RES=`echo $( pnmfile < "$TMPFILE" -)` # checked with netpbm 10.0,
+ # Parse pnmfile output "-: PPM raw, 768 by 576 maxval 255" => 768 x 576
+ X_RES=$(echo -e "$RES"| cut -d " " -f 4)
+ Y_RES=$(echo -e "$RES"| cut -d " " -f 6)
+
+ # set flip command
+ case "$FLIPCMD" in
+ right )
+ FLIP="pnmflip -rotate270"
+ SWAPRES=$X_RES;X_RES=$Y_RES;Y_RES=$SWAPRES
+ ;;
+ left )
+ FLIP="pnmflip -rotate90";
+ SWAPRES=$X_RES;X_RES=$Y_RES;Y_RES=$SWAPRES
+ ;;
+ *)
+ FLIP="cat";
+ ;;
+ esac
+ # Save config for plugin as readable file
+ echo "$X_RES" "$Y_RES" "$FLIPCMD" > "$PARFILE"
+
+ # define aspect ratio depends plugin setup
+ if [ $ASPECT_RATIO = "16:9" ] ; then
+ SCALE_MIN_ASP=163
+ SCALE_MAX_ASP=178
+ else
+ SCALE_MIN_ASP=125
+ SCALE_MAX_ASP=133
+ fi
+
+ # if zoom image, zoom it with factor
+ if [ "$ZOOMFACTOR" -gt 0 ] ; then
+
+ ZOOM_X=$(($X_RES*$ZOOMFACTOR))
+ ZOOM_Y=$(($Y_RES*$ZOOMFACTOR))
+
+ $FLIP < "$TMPFILE" | pnmscalefixed -xysize $ZOOM_X $ZOOM_Y | \
+ pnmcut -pad -left $LEFTPOS -top $TOPPOS -width $OUT_DISPLAY_X -height $OUT_DISPLAY_Y \
+ > "$OUTFILE"
+
+ # else scale image to TV Screensize
+ else
+
+ if [ "$((${X_RES}00 / ${Y_RES}))" -lt $SCALE_MIN_ASP ] ; then
+ OUT_DISPLAY_X=$((${OUT_DISPLAY_Y}000 / $Y_RES * $X_RES / 1000))
+ elif [ "$((${X_RES}00 / ${Y_RES}))" -gt $SCALE_MAX_ASP ] ; then
+ OUT_DISPLAY_Y=$((${OUT_DISPLAY_X}000 / $X_RES * $Y_RES / 1000))
+ fi
+
+ $FLIP < "$TMPFILE" | \
+ pnmscalefixed -xysize $OUT_DISPLAY_X $OUT_DISPLAY_Y \
+ > "$OUTFILE"
+
+ fi
+ # if'nt tmpfile use for next view, remove tmp file
+ [ "z$TMPCACHE" = "zno" ] && rm -f "$TMPFILE"
+ fi
+ fi
+
+ if [ -s "$OUTFILE" ] ; then
+ [ "z$VERBOSE" = "zyes" ] && echo "Success! Stopped with created $OUTFILE"
+ exit 0 # Creation seem success, tell it with 'exit 0' to plugin
+ fi
+ [ "z$VERBOSE" = "zyes" ] && echo "Error! Stopped without found created $OUTFILE, converting should failed !"
+ exit 1 # Hmm, created is failed tell it with 'exit 1' to plugin
+
+
+###### >>>>>>> !!! Only one of the follow lines are allowed (begins always with 2>&1 ) !!!
+### Dump any message to syslog to see use cat /var/log/messages | grep imageplugin
+} 2>&1 | logger -s -t "$SCRIPTNAME"
+### If your wish don't any message logging
+# 2>&1 > /dev/null
+### If your wish old style message logging
+# 2>&1 > /tmp/image/convert.log
diff --git a/scripts/mount.sh b/scripts/mount.sh
new file mode 100755
index 0000000..65cb6e0
--- /dev/null
+++ b/scripts/mount.sh
@@ -0,0 +1,35 @@
+#!/bin/bash
+#
+# This script is called from VDR to mount/unmount/eject
+# the sources for MP3 play and/or image sources,
+#
+# argument 1: wanted action, one of mount,unmount,eject,status
+# argument 2: mountpoint to act on
+#
+# mount,unmount,eject must return 0 if succeeded, 1 if failed
+# status must return 0 if device is mounted, 1 if not
+#
+# ok -> changed mount to not abort, if eject -t did fail, e.g. for a usb storage device
+
+action="$1"
+path="$2"
+
+case "$action" in
+mount)
+ eject -t "$path" &>/dev/null # close the tray, but not do/print anything if that fails
+ mount "$path" || exit 1 # mount it
+ ;;
+unmount)
+ umount "$path" || exit 1 # unmount it
+ ;;
+eject)
+ eject "$path" || exit 1 # eject disk
+ ;;
+status)
+ cat /proc/mounts | grep -q "$path" # check if mounted
+ if [ $? -ne 0 ]; then # not mounted ...
+ exit 1
+ fi
+esac
+
+exit 0
diff --git a/setup-image.c b/setup-image.c
new file mode 100644
index 0000000..8cf05d0
--- /dev/null
+++ b/setup-image.c
@@ -0,0 +1,145 @@
+/*
+ * Image plugin to VDR (C++)
+ *
+ * (C) 2004 Andreas Brachold <vdr04-at-deltab.de>
+ * (C) 2003 Kai Tobias Burwieck <kai-at-burwieck.net>
+ *
+ * based on MP3/MPlayer plugin to VDR (C++)
+ * (C) 2001,2002 Stefan Huelswitt <huels-at-iname.com>
+ *
+ *
+ * This code is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This code is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <vdr/tools.h>
+#include <vdr/config.h>
+#include "setup-image.h"
+#include "i18n.h"
+
+cImageSetup ImageSetup;
+
+const int cImageSetup::m_cSSMin = 2;
+const int cImageSetup::m_cSSMax = 300;
+
+// --- cImageSetup -----------------------------------------------------------
+
+cImageSetup::cImageSetup(void)
+
+{
+ SlideShow = 0;
+ SSsec = 10;
+ strncpy(TempDir, "/tmp/image", sizeof(TempDir));
+#if VDRVERSNUM < 10307
+ ShowDate = 1;
+#endif
+ AutoRepeat = 0;
+ ShowNumbers = 1;
+#if VDRVERSNUM >= 10308
+ m_bLiveAudio = 0;
+#endif
+ m_bHousekeeping = 1;
+}
+
+bool cImageSetup::SetupParse(const char *szName, const char *szValue)
+{
+ if(!strcasecmp(szName, "SlideShow")) SlideShow = atoi(szValue);
+ else if(!strcasecmp(szName, "SSsec"))
+ {
+ SSsec = atoi(szValue);
+ if(SSsec < m_cSSMin) SSsec = cImageSetup::m_cSSMin;
+ if(SSsec > m_cSSMax) SSsec = cImageSetup::m_cSSMax;
+ }
+ else if(!strcasecmp(szName, "TempDir")) strn0cpy(TempDir,szValue,sizeof(TempDir));
+#if VDRVERSNUM < 10307
+ else if(!strcasecmp(szName, "ShowDate")) ShowDate = atoi(szValue);
+#endif
+ else if(!strcasecmp(szName, "AutoRepeat")) AutoRepeat = atoi(szValue);
+ else if(!strcasecmp(szName, "ShowNumbers")) ShowNumbers = atoi(szValue);
+#if VDRVERSNUM >= 10308
+ else if(!strcasecmp(szName, "LiveAudio")) m_bLiveAudio = atoi(szValue);
+#endif
+ else if(!strcasecmp(szName, "Housekeeping")) m_bHousekeeping = atoi(szValue);
+ else return false;
+ return true;
+}
+
+// --- cMenuSetupImage --------------------------------------------------------
+void cMenuSetupImage::Store(void)
+{
+ ImageSetup = m_tmpSetup;
+ SetupStore("SlideShow", ImageSetup.SlideShow);
+ SetupStore("SSsec", ImageSetup.SSsec);
+ SetupStore("TempDir", ImageSetup.TempDir);
+#if VDRVERSNUM < 10307
+ SetupStore("ShowDate", ImageSetup.ShowDate);
+#endif
+ SetupStore("AutoRepeat", ImageSetup.AutoRepeat);
+ SetupStore("ShowNumbers", ImageSetup.ShowNumbers);
+#if VDRVERSNUM >= 10308
+ SetupStore("LiveAudio", ImageSetup.m_bLiveAudio);
+#endif
+ SetupStore("Housekeeping", ImageSetup.m_bHousekeeping);
+}
+
+cMenuSetupImage::cMenuSetupImage(void)
+: m_tmpSetup(ImageSetup)
+{
+ SetSection(tr("Image"));
+ Add(new cMenuEditBoolItem(tr("SlideShow ?"), &m_tmpSetup.SlideShow, tr("no"), tr("yes")));
+ Add(new cMenuEditIntItem (tr("Slide duration (sec)"), &m_tmpSetup.SSsec, cImageSetup::m_cSSMin, cImageSetup::m_cSSMax));
+ Add(new cMenuEditBoolItem(tr("Repeat SlideShow"), &m_tmpSetup.AutoRepeat, tr("no"), tr("yes")));
+#if VDRVERSNUM < 10307
+ Add(new cMenuEditBoolItem(tr("Show Filedate on OSD"), &m_tmpSetup.ShowDate, tr("no"), tr("yes")));
+#endif
+ Add(new cMenuEditBoolItem(tr("Show Numbers on index image"), &m_tmpSetup.ShowNumbers, tr("no"), tr("yes")));
+#if VDRVERSNUM >= 10308
+ Add(new cMenuEditBoolItem(tr("Live Audio from primary Device"), &m_tmpSetup.m_bLiveAudio, tr("no"), tr("yes")));
+#endif
+ Add(new cMenuEditStrItem (tr("Directory with temporary files"), m_tmpSetup.TempDir,sizeof(m_tmpSetup.TempDir), "abcdefghijklmopqrstuvwxyz/-"));
+ Add(new cMenuEditBoolItem(tr("Remove temporary files"), &m_tmpSetup.m_bHousekeeping, tr("no"), tr("yes")));
+}
+
+
+
+
+
+void cImageSetup::SetEnv(void) const
+{
+ unsigned int i;
+ struct cImageCommandEnv {
+ const char* szEnv;
+ const char* szValue;
+ } nEnvironTable [] =
+ {
+ {"ASPECT_RATIO",Setup.VideoFormat?"16:9":"4:3"}, // Get from DVB-Setup
+ {"CONVERT_TEMPDIR",TempDir},
+ };
+
+ for(i=0;i < sizeof(nEnvironTable)/sizeof(*nEnvironTable);++i)
+ {
+ if(nEnvironTable[i].szValue) {
+ if(0!= setenv(nEnvironTable[i].szEnv, nEnvironTable[i].szValue,1))
+ esyslog("imageplugin: can't setenv '%s = %s'", nEnvironTable[i].szEnv, nEnvironTable[i].szValue);
+ }
+ else {
+ if(0!= unsetenv(nEnvironTable[i].szEnv))
+ esyslog("imageplugin: can't unsetenv '%s'", nEnvironTable[i].szEnv);
+ }
+ }
+}
diff --git a/setup-image.h b/setup-image.h
new file mode 100644
index 0000000..b0a1f58
--- /dev/null
+++ b/setup-image.h
@@ -0,0 +1,73 @@
+/*
+ * Image plugin to VDR (C++)
+ *
+ * (C) 2003 Kai Tobias Burwieck <kai@burwieck.net>
+ *
+ * This code is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This code is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#ifndef ___SETUP_IMAGE_H
+#define ___SETUP_IMAGE_H
+
+extern const char *g_szConvertScript;
+
+#include <vdr/menuitems.h>
+
+class cImageSetup
+{
+public:
+ int SlideShow;
+ int SSsec;
+ char TempDir[260];
+#if VDRVERSNUM < 10307
+ int ShowDate;
+#endif
+ int AutoRepeat;
+ int ShowNumbers;
+#if VDRVERSNUM >= 10308
+ int m_bLiveAudio;
+#endif
+ int m_bHousekeeping;
+
+ /** Minimum Value for Slideshow */
+ static const int m_cSSMin;
+ /** Maximum Value for Slideshow */
+ static const int m_cSSMax;
+
+public:
+ cImageSetup(void);
+ void SetEnv(void) const;
+
+ bool SetupParse(const char *szName, const char *szValue);
+};
+
+
+// ----------------------------------------------------------------
+
+class cMenuSetupImage
+ :public cMenuSetupPage
+{
+ cImageSetup m_tmpSetup;
+protected:
+ virtual void Store(void);
+public:
+ cMenuSetupImage(void);
+};
+
+
+extern cImageSetup ImageSetup;
+
+#endif //___SETUP_IMAGE_H